# RedDuck Academy — full course content

> Every public lesson as Markdown. Each lesson lists its Source URL for citation.

# How it works

Source: https://academy.redduck.io/courses/blockchain-basics/welcome-to-the-redduck-academy/how-it-works

Welcome to your first course. The blockchain basics course is applicable not only to developers but also to any people with any background who would like to learn more about blockchain. 

Before you start, here is a quick overview of how the course works.

### What you'll do here

Each course consists of lessons. There are four types of them:

- **Lectures**: you're on one right now.
- **Tests**: lists of questions and answer options. You have to select the correct answers.
- **Coding tasks**: Not applicable for the blockchain basics course. For dev-oriented courses, you would be requested to write code in a browser editor and submit it. The code is reviewed automatically with the use of AI and pre-defined tests.
- **Projects**: Same as coding tasks, but larger scale. They are larger builds in your own GitHub repo that you submit as a link, instead of writing the code directly in the browser. Same as with coding tasks, projects also get reviewed automatically with the use of AI and pre-defined tests.

The programming tasks/projects build up not syntax knowledge, but intrinsic understanding of a successful Web3 engineer's mindset, which is why we had them for years in our private blockchain academy open only to trainees and employees. Now they're open to everyone.

### How your progress is tracked

- At the bottom of each lesson, the **Go to next lesson** button also marks the lesson complete.
- A Certificate can only be issued if you completed all lessons within a course.
- Your rank on the leaderboard reflects how many lessons you've completed overall.

### Navigation

- The 'Continue' button on the home page redirects you to the last lesson where you left off.
- Use the course outline on the left to move between lessons of a course.

### A few tips

- Try inferring patterns from the lessons. Don't simply memorize the answers, rather try to extract the ultimate thinking pattern applicable to a particular case.
- Take your time. This isn't a course for quickly learning syntax. This academy is built to harden your mindset, not get a few tips on coding.

That's it. Let's get it started.

## FAQ

### What types of lessons are in the RedDuck blockchain course?

Lectures for reading, tests for checking the theoretical knowledge, coding tasks for lightweight in-browser coding challenges, projects for larger-scale programming submitted as a GitHub link.

### How do I earn the certificate for a course here?

You earn a course's certificate by completing every lesson in it.

### I'm stuck on a coding task, what should I do?

There is no ultimate answer to this, but re-reading the lecture directly above the task usually helps.

---

# What is web3

Source: https://academy.redduck.io/courses/blockchain-basics/what-is-web3/what-is-web3

Let's take a look at the three eras of the internet. 

## Web1: the read-only internet

The first web was a static document library. You opened a page, you read what someone else had published. Maybe you posted on a forum. But mostly you consumed content by reading it. The infrastructure was relatively decentralized: anyone could run a server, anyone could publish, and no single company sat between you and the content. But the experience of the era was limited. You couldn't talk back, you couldn't collaborate, you couldn't build, you mostly just clicked links.

This was the internet from roughly 1991 to about 2004. By the end of it, most people who used the internet read more than they wrote, because writing was hard: you needed your own server, your own software, your own audience. Almost nobody had all three.

## Web2: the read-write internet

Then the platforms arrived. Facebook in 2004, YouTube in 2005, Twitter in 2006, Instagram in 2010. Suddenly anyone could publish without owning infrastructure. You wrote a post, the platform hosted it. You uploaded a video, the platform stored it and served it to the other users.

This was a genuine revolution. The number of people publishing on the internet went from millions to billions within a decade. The cost of reaching an audience dropped to near zero, as you just needed to sign-up somewhere to post. A whole new type of jobs came into existence, because suddenly there was an audience large enough to support it.

But there was a price to it, and over time, it became impossible to ignore.

Every post you wrote lived on a server owned by another company. It also tracked every single of your actions. Every minute of attention was sold to an advertiser. The platforms could change the rules at any time, demonetise your account, suspend you, ban you, lose your data or sell to someone else. Your followers were not your followers, but rather followers that the company allowed you to have on your account. Your identity was just a row in someone else's database, and you only got to keep it as long as the database operator allowed.

For most users, that was fine. The cost was invisible, and the convenience was enormous. But for some categories of activity, especially anything involving money or freedom of speech, this trade-off was painful. By 2010s, a generation of developers was asking a question:

- What if the internet had a way for users to actually own things again, preserving the experience?

## Web3: the read-write-own internet

Web3 is the answer. The single most important difference from web2 is that **ownership lives in the protocol rather than the platform**. Protocol here means 'set of rules' that everyone has to follow. When you hold a digital asset in your wallet, no platform sitting between you and the rest of the network can quietly take it away, like in Web2. Instead, this digital asset follows the rules that the entire network has set and hence it cannot be taken away. When you connect your wallet to a new application, you bring your identity and your history with you, the same way you bring your laptop from one office to another. When you publish, you publish to a network controlled in a decentralized and fair way, rather than to a single server hosted by a single company.

This is not a thought experiment. The infrastructure has been live for a while now and the numbers are concrete:

- More than **$300 billion** in stablecoin value, regularly moving through wallets and settling trillions of dollars of transfers each year.
- Around **$100 billion** locked into decentralised financial protocols, providing lending, trading, and yield without any bank or broker involved.
- Tens of millions of active wallet addresses transacting every month.
- Developer activity in web3 has grown steadily for ten years, even through bear markets, long stretches of falling prices, when the price headlines suggested otherwise. The people building have not gone away.
- Major companies, including ones you've heard of in every consumer category, are integrating with this infrastructure for payments, identity, and asset transfer.

The infrastructure works. Real people use it for real things. The reason most developers haven't released anything on it yet is mostly that the tooling, the languages, and the mental models are different enough from web2 that getting started takes some work and mindset shift. This is what this academy is for.

## What's in this course

This course covers what blockchains and Web3 actually are, how the cryptographic primitives work, and Bitcoin basics. Other than that, there are also smart-contract development courses available for Ethereum and Solana. There is also an engineering course, which focuses on ideas that apply to all blockchains and software engineering rather than one specific chain and one language. What you learn here will hold up regardless of which chain you specialise in afterwards.

The next lesson introduces web3 as a layered ecosystem and zooms in on where blockchains specifically sit within it. Everything you've ever seen described as web3, from wallets to DeFi to NFTs to decentralised social networks, sits on top of blockchain infrastructure. The lesson after that, and the rest of the course, is about what that infrastructure actually is.

## FAQ

### What is the difference between web2 and web3?

Data Ownership. Both Web2 and Web3 give you read and write functionality, but Web2 keeps the custody over your content, while Web3 allows you to preserve self-custody over it.

### Can a company or a government take an asset out of my wallet?

Only the party that has access to your keys can move anything out of the wallet. If you were forced into sharing the keys, then it's possible even for Web3.

### Is Web3 used for anything at scale yet?

More than $300 billion of value moves through wallets in fiat-pegged coins named stablecoins, settling trillions of dollars of transfers a year. Around $100 billion sits in decentralised finance protocols that allow you to take loans and earn interest on your holdings. Tens of millions of wallet addresses transact every month. That can definitely be considered "at scale".

### Do I need to know how to code to start with web3?

Not for the non-technical knowledge. The "blockchain basics course" assumes no technical knowledge, but the following courses are aimed strictly at developers with pre-requisite knowledge.

---

# Where blockchain fits in web3

Source: https://academy.redduck.io/courses/blockchain-basics/what-is-web3/where-blockchain-fits-in-web3

So far we know that web3 is the version of the internet where users actually own their identity and content. But let's see what part makes that possible. There's a whole stack to it: applications sit on top and use smart-contracts, but everything in the end is dependent on the core layer of a blockchain.

## The stack

The diagram below shows the stack. Each layer depends on the one below it:

<svg role="img" viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Web3 stack: Wallets, Applications, Smart contracts &amp; Blockchain</title><desc>Four stacked layers from top to bottom: Wallets, Applications, Smart contracts &amp; and Blockchain. The bottom Blockchain layer is highlighted as the trust layer, shared and tamper-evident with no central operator, that everything above depends on.</desc>
  <!-- Wallets at top -->
  <rect x="60" y="20" width="600" height="65" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="50" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Wallets</text>
  <text x="360" y="70" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">Self-custodial and custodial wallets allowing users to perform transaction</text>
<!-- dApps layer -->
  <rect x="60" y="100" width="600" height="80" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="125" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">[Decentralized] Applications</text>
  <text x="360" y="145" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">Decentralized Finance, Decentralized Governance, Decentralized Gaming</text>
  <text x="360" y="163" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">Social, Lending, Identity, Earning, Tokenization, Crowdfunding</text>

<!-- Smart contracts layer -->
  <rect x="60" y="196" width="600" height="65" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="226" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Smart Contracts (On-chain programs)</text>
  <text x="360" y="246" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">Programs holding value allowing to perform actions mentioned in applications above</text>

<!-- Blockchain layer (highlighted) -->
  <rect x="60" y="276" width="600" height="80" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="301" text-anchor="middle" font-size="15" fill="#ffffff" font-weight="bold">DLT / Blockchain</text>
  <text x="360" y="321" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffffff">Distributed Ledger, the Core layer</text>
  <text x="360" y="338" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffffff">Handles the shared & tamper-resistant recording of transactions without a central operator</text>
</svg>

## Layer 1: DLT
The DLT <i>(Distributed Ledger Technology)</i> itself is the core layer everything else depends on. Blockchains are one-of-many DLTs types, so even though it's the most frequently used one, it's still worth noting there are many other DLTs that aren't blockchains, such as <a href="https://hedera.com/"></a>. 

A DLT is a shared record of state, replicated across thousands of computers, agreed on by all of them. Because it's a decentralized technology, the ledger is modifiable only by following rules built into the system. The ledger part means that we're not storing the final data itself, we're storing the actions that led to the final data, namely **transactions**.

Blockchains protect data tampering by including a "chain" in which every node is dependent on the previous node, so if any data on the previous nodes is tampered, the following nodes have to be amended as well, which makes it either impossible to do, or very obvious to spot.

## Layer 2: On-Chain PROGRAMS (SMART CONTRACTS)
The smart contract layer depends on the blockchain one, because you can't make an on-chain program without having the chain. Blockchains run these programs and validate their behaviour. It is automatic as the behaviour should adhere to the blockchain protocol's rules, otherwise it would be rejected automatically by the majority of blockchain validators.

## Layer 3: [De]centralized applications

Decentralized applications (also called dApps) utilize the on-chain programs in order to display certain user experience to the end users. For example, a lending dApp such as <a href="https://app.aave.com/">Aave</a> shows a nice and convenient UI to the end users, but at the same time, utilizes on-chain programs that contain the lending and borrowing functionality. There are plenty of dApps - NFT Marketplaces, Decentralized Exchanges, and even decentralized games (such as gambling based on verifiable random functions).

## Layer 4: Wallets

What happens when you attempt to interact with a decentralized application, is it will ask you to confirm the transaction in your wallet. Because Web3 is self-custodial in data and identity, you have to sign your actions yourself. You do that by just clicking "Confirm" on your separate Wallet application, which in turn contains your keys and signs the transactions on your behalf upon your confirmation.

If you were to send a transaction without a valid signature, it just wouldn't be accepted by the network and would be automatically rejected, not even subject to conditions and specifics of the Layers #2 and #3 of this stack. 

This stack is the whole point. Majority, if not everything you've ever heard described as web3 lives inside it. The course you're reading right now is about the bottom layer, because once you understand the bottom layer, every layer above it starts making a lot more sense instantly.

## What a blockchain actually gives you

Blockchains don't do anything magical. They simply achieve a few very specific things that just happen to be achievable with the blockchain architecture better than with anything else:

**Permanent ownership.** When you hold something on the chain, whether it's a coin or a username, or any kind of digital asset, it's recorded in a way that no single party can revoke. This is achieved with the node dependency mechanism (through cryptographic hash functions - more on that later), which makes it extremely difficult in computational sense to revoke anything that you possess. Unless, what you possess has explicit rules of being revokable through a transaction somebody else can perform. Even if your asset were to be revoked, the transactions in which you had the asset would not be reverted, so you would still preserve the history of the events.

**Programmable money.** Money in web2 is database entries at a bank, governed by software you can't read and can't interact with, and bound by banking hours, settlement windows, and rules that change every few years unilaterally. Money on a public blockchain however, is a value within a shared ledger, transferable in seconds at any hour, controlled by on-chain programs that anyone can read. 

A trade that would take a bank settlement system three working days happens in seconds or minutes. A loan that would take a credit check, a meeting, and a signature can be issued by a smart contract in one transaction, because it matches the configured public protocol rules.

With that in mind, anybody can create their own version of a lending system, a governance or insurance protocol, or an earnings one. Hence, the money on-chain are programmable and the only limit is the deterministic nature of a blockchain and your imagination. 

**Global by default.** A new app on a public blockchain is reachable on day one by a teenager in Lagos, a developer in Buenos Aires, a small business in Manila, and a fund manager in Singapore. There is no compliance manager flying around closing deals to make the app legal. The chain doesn't ask where you are. It's just a shared protocol that everyone with an internet connection can use, and so can we use the products inside of it.

Taken together, these properties are built into the layer where apps live, free and unlimited, rather than under control of an organization that can take them away at any moment. They enable categories of products that could not exist before.

## What's next

Next comes the cryptography lesson that explains how cryptography makes the blockchain layer possible at all. 

Hashes, keys, signatures, the actual mathematics of "no one can fake this".

For non-technical people, these words can be scary, but in reality, the high-level concept is very simple and is far away from rocket-science difficulty. 

You'll see!

## FAQ

### Which layer does everything else in web3 depend on?

The DLT / Blockchain layer, the trust layer. Smart-contracts (On-chain programs) sit on top of it.

### What is a smart contract?

An on-chain program, that runs on a blockchain.

### What can a blockchain do that a normal web service cannot?

Immutable transactions, censorship-resistance and self-custodial ownership with global reach.

---

# Hashing

Source: https://academy.redduck.io/courses/blockchain-basics/cryptography/hashing

> The previous lesson ended with a promise: the next thing you'd learn is the cryptography that makes blockchains possible at all. In this lesson we'll examine hash functions.

## The one-sentence definition

Hash functions take content of any input and type, and return an output of a fixed size, no matter the input size. Sometimes they're called 'digest' functions but the industry standard is to refer to them as hash functions. 

Let's run one of the most popular hash functions SHA-256 on three different inputs:

```
SHA-256("hello")
  → 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

SHA-256("hello!")
  → ce06092fb948d9ffac7d1a376e404b26b7575bcc11ee05a4615fef4fec3a308b

SHA-256("Lorem ipsum dolor sit amet, consectetur adipiscing elit...")
  → 5bd6045a7697c48316411ff00be02595cf3d8596d99ba12482d18c90d61633cb
```

As you can see, the output is always the same length, 256 bits, written as 64 hex characters, no matter the input size. 

One added character (`hello` → `hello!`) produced a completely different output, and that's one more property of hash functions: they produce output that looks very random, even though the hash function itself works the same way every time you call it. So if you were to run a hash function on 'hello' multiple times, you'd get the same output - because the function is deterministic. The randomness in the outputs is what we call 'avalance effect'.

So far we can infer that the hash functions have the following properties:

- **Deterministic computations:** hash function behaviour is the same on your laptop, on a Bitcoin node in Tokyo, and on a Solana validator in Frankfurt. If it were different, no blockchain could use them, as blockchains require the code to execute in the same way for all validators in order to reach an agreement.
- **Avalanche effect:** Despite the hash function working the same way every time you run it, you still get random-looking results for different inputs. One bit flipped in your input and you now have a completely different output again.

But there are more properties that cryptographic hash functions follow:

- **Pre-image resistance:** We can't extract the initial input from the output of a hash function. Many people tried, everyone failed so far.
- **Collision resistance:** Finding another input that produces the same hash as some other input is computationally impossible.
- **Fast to compute, slow to invert:** Computing the hash of a 1 MB file takes milliseconds. But finding an input that produces a given hash requires trying inputs one by one. For SHA-256, that's roughly 2²⁵⁶ attempts in the worst case, a number comparable to the count of atoms in the observable universe. The asymmetry is the whole point.

The above properties are based on one simple constraint - we can't get any useful information from the output regarding the inputs that they were processed with, so the best we can do to find an input for a specific hash is bruteforcing them. That's 2^256 attempts, because of 256 bits in the output where each bit can have 2 possible values (1 or 0) gives us exactly that many attempts to try. This number is so big, that if we were to take all the computers that humanity ever produced and multiply that by a million of trillions, we would still not be able to bruteforce that in a trillion years. The gap is so huge that statement of trillion years is actually underselling it.

<svg role="img" viewBox="0 0 720 180" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>"hello" vs "hellp" hash outputs differing in about half their bits</title><desc>The diagram shows the hash of "hello" above the hash of "hellp", where only one letter changed. Roughly half the bits flip in the output, which is the avalanche effect.</desc>
  <text x="30" y="40" font-family="monospace" font-size="13" fill="#000000" font-weight="bold">"hello"</text>
  <text x="30" y="65" font-family="monospace" font-size="11" fill="#565653">2cf24dba5fb0a30e26e83b2a...</text>
  <text x="30" y="82" font-family="monospace" font-size="11" fill="#565653">...c5b9e29e1b161e5c1fa7425e</text>

<text x="30" y="120" font-family="monospace" font-size="13" fill="#000000" font-weight="bold">"hellp"</text>
  <text x="30" y="145" font-family="monospace" font-size="11" fill="#ed4937">fdd7585e08c4e2afd71dcabd...</text>
  <text x="30" y="162" font-family="monospace" font-size="11" fill="#ed4937">...b4636c89d557a3f42db9e204</text>

<text x="430" y="40" font-size="13" fill="#000000" font-weight="bold">One letter changed</text>
  <text x="430" y="62" font-size="12" fill="#565653">in the input.</text>
  <text x="430" y="100" font-size="13" fill="#000000" font-weight="bold">Roughly half the bits</text>
  <text x="430" y="122" font-size="12" fill="#565653">flip in the output.</text>
  <text x="430" y="160" font-size="12" fill="#565653" font-style="italic">This is the avalanche effect.</text>
</svg>


## The playground

The playground below has two inputs pre-filled with `hello` and `hello!`, each wired to its own hashing node. The two output hashes share zero structure even though the inputs differ by one character. Edit either input and watch them shift independently. That live behavior is the practical lesson.

[interactive playground](https://plgrnd.io/#flow=N4IgbiBcDMA0IDsD2ATApgZygbVASxShAA5j0AWARmOgAZoBjc80lAI2nLXIDYBOAEwoAZvwE8AhgFYQ8AC4BPAA5oiAFQCiADTWyQSpBjxy8SBFFAAPKAFpKPHgDpalPpUpTaA8px5Sp8Aq2dI4A7C7kAsS0fFKhHm4AvvAoEnISFiByaJZyRAAWaAA2RUggySAAtmgSGACuAE5ohJCgAO4EcvlQdLTwhXgA5vl5kFKUFRjFaAzZLcISRVMpDRKDg3gIg1ALS2jJ+C0g4lxsPKE1EhJs5FJ8wnyh0CihKJTClLRSbLQ8DLR6RQqIgACQAggBlEF6AxGExmTLWSCUUICRx8ZgJYh8HjQHjEVGBYJOcShfi0Zj8R7eCqpdKZfK1bqQY4MYTedjSYQ-CR0NDiNA0NgCCQMb58fkSyhseyUNBSBjvCShSJSNBPCnQPECPjQYhsPjEbzleDVWqNZqZDooLpQDx9EADYajKQ8SbTWaWyC7ZYgFCrdabbbexZTA4gAhESLEKQPMXEXkq0IfAnQPii8i8rXoPx8QHKVQszQ6GGGYymcytEBIuyoxx4w20MmPLwOHhEyA8Wj1lGu0LxO5uXi0tIZKvZXIFYqlACEJqqNXqTRa7U6zN6-TQQxGUHG7qKMzmO1DaBWaw2W2Pe3DkZZAjYaD4LwYxEzgvIoV5pHZyoYKFx+KvMq+bAiy4JQqWcIVoidp1n2gjjA40ChP4oQdlI5COP4Aj+LEPACN4LgCCO9JVoyGDMiADBoL8MQCNyGJkPcCwMK8lCJjw3AUsKPBsChKFsAwipyjRUgSLwHjCGgwjkFJDDQLytD6vOZpLl6q42sy9qbtuLpuvAUwHp68wnmegaXiG14ALrwM0gyYDgoAYEgjTUUQrDcNQdCMMwrAcFwvCCCIYiSDIBkuQ01EghICAoAe7lkJ5ND0EwLBkP53BiMFgihTYE5yDYDS6YCEgNPZozHDwpznJc1y3PcjzPGxHxfD8fwAvIpXldFsXxXeVVoGcFxXHVdwPE8LxvC13y-P8NibEodQFQewh5PAt7VgoNh2WgAD6u0eVQyU+Wl7CcJlQWiDl0iHV5KW+el52BUIV3iNIeU5AVRXOjYJyDTVI03GNjWTe8nwze1f1DbVQMNRNzXg21c0LUtNgrXk4bOa5hYgNGsZ8PGiYfimTzpkwWbPGguZ6FjkVoD1cU43jcZSAmyHE9QpMZhTOZ3B9uSFcVnVlWgFX3o+z6vhI76fnqxA-qEf4AQSqShCVItyAzfXHA+T6K1LMtfvLIqK-+eIq8q80IIty3SWtEZHJYW07ftzME6zRPJpzabc1qlO5m7hPs17qZk5mfu83w-Nfbpv265Lb6vrL34m0r5tAai8f64nH5GwraeAarVs22jdvlDZ4B4GgbQGA0oxWFADh8I4xDUKQH6fHQDgdtAHjOJQ5D0LEfC0BSGHwAAXkgSCVHa9b+B4MacNAAhasQ5yJIkQA?view=true)

## Why blockchains can't exist without it

**Integrity checking.** This one predates blockchain by decades. Software distributors publish a file alongside its hash. You download the file, hash it yourself, and compare. If your hash matches the published hash, the file wasn't modified in transit. If even one byte was changed, by a network error, a malicious mirror, anything, your hash will be completely different from the published one and you'll know. This is the simplest possible use of a hash function and it's the seed of every other security property in the rest of the course.

**Block linking.** Every block in a blockchain contains the hash of the previous block as one of its fields. The diagram below shows what that looks like in practice.

<svg role="img" viewBox="0 0 720 200" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Block N-1, Block N, and Block N+1 linked by prev_hash fields</title><desc>Three blocks in a row, Block N-1, Block N, and Block N+1, each show a data field and a prev_hash field. Arrows connect each block to the next, showing that a block's prev_hash stores the hash of the previous block.</desc>
  <rect x="40" y="50" width="180" height="100" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="130" y="75" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Block N-1</text>
  <line x1="60" y1="85" x2="200" y2="85" stroke="#565653" stroke-width="1"/>
  <text x="130" y="108" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">data</text>
  <text x="130" y="128" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">prev_hash: a3f8...</text>

<rect x="270" y="50" width="180" height="100" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="75" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Block N</text>
  <line x1="290" y1="85" x2="430" y2="85" stroke="#565653" stroke-width="1"/>
  <text x="360" y="108" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">data</text>
  <text x="360" y="128" text-anchor="middle" font-family="monospace" font-size="11" fill="#ed4937">prev_hash: b7c2...</text>

<rect x="500" y="50" width="180" height="100" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="75" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Block N+1</text>
  <line x1="520" y1="85" x2="660" y2="85" stroke="#565653" stroke-width="1"/>
  <text x="590" y="108" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">data</text>
  <text x="590" y="128" text-anchor="middle" font-family="monospace" font-size="11" fill="#ed4937">prev_hash: d4e9...</text>

<line x1="220" y1="100" x2="270" y2="100" stroke="#ed4937" stroke-width="2"/>
  <polygon points="262,95 272,100 262,105" fill="#ed4937"/>
  <line x1="450" y1="100" x2="500" y2="100" stroke="#ed4937" stroke-width="2"/>
  <polygon points="492,95 502,100 492,105" fill="#ed4937"/>

<text x="360" y="180" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">Each block carries the hash of the previous one.</text>
</svg>

Change anything in a historical block, even a single bit, and that block's hash changes. The next block's `prev_hash` field no longer matches, so it becomes invalid, and so does every block after it. Tampering with the past is detectable by anyone participating in the network. Each block takes only a single hash to verify. This is the property that turns a list of records into a tamper-evident chain and it's the core idea behind blockchains.

## FAQ

### What is a hash function?

Any input of any size in, a fixed-size output out. SHA-256 always returns 256 bits, written as 64 hex characters.

### Why does changing one character produce a completely different hash?

The avalanche effect. One flipped input bit changes about half the output bits.

### How does hashing make a blockchain tamper-evident?

Every block stores the hash of the block before it. Change one bit of an old block and its hash changes, so the next block's stored previous-hash stops matching and every block after it becomes invalid.

### My Keccak-256 hash does not match my SHA3-256 library. Why?

Different padding. Ethereum adopted Keccak-256 before NIST finalized SHA-3, and the finished standard changed the padding, so the same input gives two different outputs from the same underlying algorithm.

### Which hash functions do Bitcoin and Ethereum use?

Bitcoin uses SHA-256, and RIPEMD-160 in a few places. Ethereum uses Keccak-256.

---

# Encoding

Source: https://academy.redduck.io/courses/blockchain-basics/cryptography/encoding

## Encoding is not encryption

Encoding and encryption are completely different things, even though the words sound similar.

**Encryption** scrambles data so only someone with the right key can read it. The output is supposed to be unreadable to everyone else. The next lesson covers encryption properly.

**Encoding** translates data into a different representation so it can travel safely through systems that have constraints on what characters they accept. The output is supposed to be readable. There is no key. Anyone can decode it.

The same two bytes can be encoded as `Hi` in ASCII, `48 69` in hex, `SGk=` in Base64, or `6Wc` in Base58. All four represent the same underlying data. Switching between them does not change the data, it changes how the data looks on the page.

If someone ever tells you they "encoded" a password to keep it safe, they didn't.

## Why raw bytes can't just be displayed

The hash from the previous lesson is 256 bits of binary data. Try to print those 256 bits as ASCII characters directly and you'll get something like this in your terminal:

```
,Ò$Û¥û°£.&è;*ÅùZé¿b^p43 62)8¹$
```

That string contains invisible control characters, characters that break URL parsing, characters that some email systems treat as line endings, and characters that render differently on different operating systems. Pasting it somewhere and getting it back unchanged is impossible to guarantee.

So we encode it. Pick a representation that uses only safe characters, accept that the resulting string will be longer than the raw bytes, and move on. The different encoding schemes you'll meet make different tradeoffs about which character set to use and how long the output ends up.

## The encodings you'll see

Three formats cover almost everything in the ecosystems this course touches.

### Hex

Each byte becomes two characters drawn from `0–9` and `a–f`. The hash output from the previous lesson, `2cf24dba5fb0a30...2938b9824`, is hex. So is almost any long string of letters and numbers you've seen labeled as an "address" or a "transaction hash." The leading `0x` you sometimes see is a convention that says "what follows is hex," borrowed from C.

**Properties:**

- Each hex character is exactly 4 bits, so two characters always represent one byte. Converting back and forth with raw bytes is trivial.
- Output is exactly twice the length of the input. 32 bytes of hash become 64 hex characters.
- Case-insensitive by default. Some systems use mixed case to encode a checksum without changing what the address means, but lowercase the same string and it still resolves to the same data.

Hex is the standard representation for anything that's "raw bytes shown as text." If you're staring at a long string of `0-9a-f` characters, you're looking at hex.

### Base58

Uses 58 characters: all digits and letters except `0`, `O`, `I`, and `l`. Those four are excluded because they look alike in many fonts and would be easy to misread when a human copies a string by hand.

A string like `1A1zP1eP5...5SLmv7DivfNa` is Base58. There's also a variant called Base58Check that adds a few extra checksum bytes so a typo in a copied string is detected before damage is done.

**Properties:**

- Shorter than hex for the same data, because 58 symbols carry more information per character than 16.
- Designed for humans copying strings by hand. The excluded characters are the ones people misread.
- Not aligned to byte boundaries the way hex is, so converting is slightly more involved.

Base58 is the format reached for whenever a system needs to give humans something to copy without losing characters to font ambiguity. Anywhere a user might paste a long secret into a phone keypad or read it aloud, Base58 is the safer choice.

### Base64

Uses 64 characters: `A–Z`, `a–z`, `0–9`, plus `+` and `/`, with `=` for padding. Predates blockchain by more than a decade. It's how email attachments are encoded for transport over text-only systems, how images get inlined into HTML data URLs, and how API tokens get transmitted in HTTP headers.

**Properties:**

- Slightly more compact than Base58 because it has six more symbols in its alphabet.
- Not designed for humans copying strings. Uses both `0` and `O`, both `I` and `l`. Fine for machine-to-machine, bad for "read this aloud over the phone."
- The `+` and `/` characters break URL parsing, so a URL-safe variant exists that swaps them for `-` and `_`.

You'll meet Base64 in plenty of non-blockchain web contexts, and recognising it on sight saves time. The trailing `=` padding is the giveaway.

## Side-by-side

The same 5-byte input (`hello`) encoded four ways:

| Encoding | Output | Length |
| --- | --- | --- |
| Binary (raw) | `01101000 01100101 01101100 01101100 01101111` | 40 bits |
| Hex | `68656c6c6f` | 10 characters |
| Base58 | `Cn8eVZg` | 7 characters |
| Base64 | `aGVsbG8=` | 8 characters |

Same 5 bytes. Four different visual representations. Each one is reversible to the exact same binary input.

The playground below lets you try this yourself. Type any input and watch hex, Base58, and Base64 produced from the same underlying bytes.

[interactive playground](https://plgrnd.io/#flow=N4IgbiBcDMA0IDsD2ATApgZygbVASxShAAYAmAM1IDYB2ATgFYG6BGADgBY1z2OUBjAIZUqDNiwBGgtBIkcQ8AC4BPAA5oiAZQAqAJQCSAOQDiAfQCihgMIB5ACLmFIVUgx5FeJAiigAHlBYWKgA6UmJoDg5iSJoWDjY6NnhlKABaMWCWaGgWGlIchmJmNlIAX3gUQUVBHxA0BH5UPAQAcyIpDDQqeSU0X0UiQWMANQwJYzYAXhBykABbNEEMAFcAJzRCSFAAdwJFAAsoUij4fbQ8Fv2ByG7ZzoAbNH5FDahyQXvOitXBFpbmtqQd6fNDlfCbEAScj8BJoYhsFB0aCcQQUNjQfjEOIMTEsBj5eEMJwqdREbTmAAa2icLjcHi8tX8kFSpCCwRo0A5LGi8VIdBESRAKWZDGgwQYHCyHGyYWYVDoHFmlWqtRe-SIZ3u9yQM3gCyWa1eWxAuxQByg0GIxFO50u1wYLDuaEezyNwK+IBQPz+ALeH06pQAuvANi1MDhQBgkGt+BpIJDobD4YjkRxUeR0biODisfjLWwifAozG0AAJQQIFCPdqJuhwhFIlFojFY7O4vOE1JqxSpVYXK7EwSrMPXEgUaj0JisTjcXgCYSicRSGRyQfDtCKcuV6vxsiUWiMZi8WececiMSSaSyDipZqqZY9x7kAbwAhEXzKVKhtCmUxQmF1smjZps2WY5niBIFv+SYNqm6aZq24EdgWXZ9D2fZ2qke4Toe05cDwp5COeS5XnI2EHlOx4EXwRGLpeK43neD6pE+AxgiAxarLGRDkZOR4ztRZ50cu15OJxsZblWcZjvufF4SeNELheIlyKh-S9v2L4gNU66jiw-ASHQdCkDQCJSCgMTytkVAsGgbBsPw0CCCwKA0PwpBriOkk7iA+mGcZpkoOZllItANl2Q5TkuW5pC3gg96PtwWlvvGH5figYa-rxuFUXOtHKaRUTjhR-H4XlSkkQxanoZpqR+UZJlmYIFkcDQVlhbZ9mOc5rnufVAVNS1bWheFXVRb1sVMYlz4zMG4B4Gg2wuKs1x+FA9AhOI9mcLE4TECIyRQFEIStuEjB0Fa2YcPAABeSBIHMATBNATB4gWETQPkyK0KUpRAA?view=true)

## A practical note for later in the course

Hex and Base58 cause confusion in one specific way: when you start meeting addresses on different systems, some will look like long lowercase-hex strings and others will look like mixed-case Base58 strings. They look so different that it's tempting to assume they represent fundamentally different kinds of data. They don't. Underneath, both formats are usually wrapping the same kind of fixed-size byte sequence that comes out of a cryptographic process. The encoding is the wrapper, the data inside is the same kind of thing.

This will matter again in the wallet-creation lesson, where the same starting secret produces addresses that look very different across different systems. Part of that difference is just the encoding wrapper, and the lesson will show how much.

## FAQ

### Is encoding the same as encryption?

No. Encoding has no key, so anyone can reverse it and nothing stays secret.

### Why is a hash shown as hex instead of the raw bytes?

Raw bytes contain invisible control characters, characters that break URL parsing, and characters some email systems treat as line endings. Hex uses only 0-9 and a-f, so the string survives being pasted and mailed unchanged.

### What's the difference between hex, Base58, and Base64?

Same bytes, different alphabets. Hex uses 0-9 and a-f, two characters per byte, so the output is exactly twice the byte count. Base58 drops 0, O, I, and l so a person copying by hand cannot misread them. Base64 uses 64 symbols including + and /, with = for padding.

### Why do Bitcoin and Ethereum addresses look so different?

Mostly the encoding. Bitcoin addresses are written in Base58, Ethereum addresses in hex with a 0x prefix. Both wrap the same kind of fixed-size byte sequence.

---

# Symmetric and asymmetric encryption

Source: https://academy.redduck.io/courses/blockchain-basics/cryptography/symmetric-and-asymmetric-encryption

> A common assumption is that the blockchain "encrypts" your data. It doesn't. Almost nothing on a public chain is encrypted at the protocol level, and understanding why that's true is the difference between a working mental model and a confused one. This lesson starts with what encryption actually is, then explains where it lives in your stack and where it doesn't.

## The misconception

It is natural to assume the thing protecting your data on a blockchain is "encryption." It sounds right. The space is full of cryptographic jargon, transactions are signed, addresses look like meaningless-looking strings, the chain is described as "secure," so encryption must be doing the work somewhere.

It isn't. Public blockchains do not encrypt transactions, balances, contract storage, or anything else at the protocol level. Every byte of state on a public chain is readable by anyone with a node. Your balance, every transaction you've ever sent, every piece of data you've stored on-chain: all public, all permanently visible.

What protects you on a blockchain is **authentication** (proving you authorized an action) and **integrity** (proving data wasn't tampered with). These properties come from hashing and from digital signatures, a cryptographic primitive covered in a later lesson. They don't come from encryption.

That said, encryption is real infrastructure on the internet, and it does show up around the edges of blockchain systems at specific places. Knowing where is the goal of this lesson.

## What encryption actually is

Encryption transforms data using a **key** so that only someone with the right key can transform it back. The transformed data is called **ciphertext** and looks like random noise. The original data is **plaintext**. Without the key, ciphertext is meant to be useless, with the key, it returns to the exact original byte for byte. The strength of an encryption scheme is the gap between those two outcomes: how expensive it is to recover the plaintext without the key.

The encoding lesson already drew this line: encoding has no key, so anyone can reverse it. The key is exactly what encryption adds, and without it the ciphertext cannot be reversed in any practical amount of time.

There are two families of encryption schemes that differ in how the key works.

## Symmetric encryption

The simplest model. There is one key. Whoever has the key can encrypt new data and decrypt existing data. Both directions use the same key.

<svg role="img" viewBox="0 0 790 220" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Alice to Bob symmetric encryption using one shared key</title><desc>Alice's plaintext is encrypted with a shared key into ciphertext, then sent to Bob who decrypts it with the same key back into plaintext. The same key works on both ends, so whoever holds it can encrypt and decrypt the data.</desc>
  <rect x="30" y="80" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="90" y="105" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Alice</text>
  <text x="90" y="125" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">plaintext</text>

<rect x="200" y="80" width="120" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="260" y="105" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">encrypt</text>
  <text x="260" y="125" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">with shared key</text>

<rect x="370" y="80" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="430" y="105" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">ciphertext</text>
  <text x="430" y="125" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">(random bytes)</text>

<rect x="540" y="80" width="120" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="600" y="105" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">decrypt</text>
  <text x="600" y="125" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">with shared key</text>

<line x1="150" y1="110" x2="200" y2="110" stroke="#565653" stroke-width="2"/>
  <polygon points="192,105 202,110 192,115" fill="#565653"/>
  <line x1="320" y1="110" x2="370" y2="110" stroke="#565653" stroke-width="2"/>
  <polygon points="362,105 372,110 362,115" fill="#565653"/>
  <line x1="490" y1="110" x2="540" y2="110" stroke="#565653" stroke-width="2"/>
  <polygon points="532,105 542,110 532,115" fill="#565653"/>
  <line x1="660" y1="110" x2="700" y2="110" stroke="#565653" stroke-width="2"/>
  <polygon points="692,105 702,110 692,115" fill="#565653"/>

<text x="720" y="105" font-size="13" fill="#000000" font-weight="bold">Bob</text>
  <text x="720" y="125" font-family="monospace" font-size="11" fill="#565653">plaintext</text>

<text x="380" y="190" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">Same key on both ends. Whoever has the key can read and write.</text>
</svg>

The standard algorithm here is **AES** (Advanced Encryption Standard), specifically AES-256, which uses a 256-bit key. AES-256 is what your operating system uses to encrypt your disk, what password managers use to encrypt their vaults, and what almost every "encrypted at rest" system relies on internally.

Symmetric encryption is **fast**. AES-256 can encrypt gigabytes per second on modern hardware. It's the right tool for bulk data.

The limitation is **key distribution**. Symmetric encryption only works if both ends already share the key. If Alice and Bob want to communicate securely and they've never met, how does Alice send Bob the key without an attacker intercepting it? You can't encrypt the key with symmetric encryption because they don't have a shared key yet. This is the central problem of secure communication: to share the key safely you would already need a secure channel, which is the very thing you are trying to create. It's exactly the problem the other family of encryption was invented to solve.

## Asymmetric encryption

Also called **public-key encryption**. Each participant has a **key pair**: two mathematically linked keys that work in opposite directions. One key encrypts, the other decrypts. The two keys are different.

The participant keeps one key secret (the **private key**) and publishes the other (the **public key**) freely. Anyone in the world can take your public key and use it to encrypt a message that only your private key can decrypt. The public key cannot be used to decrypt, only to encrypt.

<svg role="img" viewBox="0 0 870 240" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Alice encrypts plaintext with Bob's public key to send ciphertext, Bob decrypts with his private key</title><desc>Alice's plaintext is encrypted into ciphertext using Bob's public key, then sent to Bob. Bob decrypts the ciphertext back into plaintext using his private key, which only he holds.</desc>
  <rect x="30" y="90" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="90" y="115" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Alice</text>
  <text x="90" y="135" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">plaintext</text>

<rect x="200" y="90" width="160" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="280" y="115" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">encrypt</text>
  <text x="280" y="135" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">with Bob's public key</text>

<rect x="410" y="90" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="470" y="125" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">ciphertext</text>

<rect x="580" y="90" width="160" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="660" y="115" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">decrypt</text>
  <text x="660" y="135" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">with Bob's private key</text>

<line x1="150" y1="120" x2="200" y2="120" stroke="#565653" stroke-width="2"/>
  <polygon points="192,115 202,120 192,125" fill="#565653"/>
  <line x1="360" y1="120" x2="410" y2="120" stroke="#565653" stroke-width="2"/>
  <polygon points="402,115 412,120 402,125" fill="#565653"/>
  <line x1="530" y1="120" x2="580" y2="120" stroke="#565653" stroke-width="2"/>
  <polygon points="572,115 582,120 572,125" fill="#565653"/>
  <line x1="740" y1="120" x2="780" y2="120" stroke="#565653" stroke-width="2"/>
  <polygon points="772,115 782,120 772,125" fill="#565653"/>

<text x="800" y="115" font-size="13" fill="#000000" font-weight="bold">Bob</text>
  <text x="800" y="135" font-family="monospace" font-size="11" fill="#565653">plaintext</text>

<text x="420" y="200" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">Encrypt with the recipient's public key. Only their private key can decrypt.</text>
</svg>

The asymmetry solves the key-distribution problem. Bob publishes his public key on his website. Anyone can encrypt a message to him without ever meeting him. Only Bob, holding the private key, can decrypt.

The price is **speed**. Asymmetric encryption is dramatically slower than symmetric, typically 100 to 1000 times slower depending on the algorithm and the data size. You wouldn't use it to encrypt a large file. The standard algorithms here are **RSA** (older, key sizes 2048 to 4096 bits), and elliptic curve schemes like **ECDH** (newer, smaller keys with equivalent security).

## How they actually get used together

In practice, almost every "encrypted" connection on the modern internet uses both schemes together in a pattern called **hybrid encryption**.

Asymmetric encryption is used briefly at the start to exchange a fresh symmetric key. Then the symmetric key encrypts everything else.

This is how TLS works (the protocol behind HTTPS). When your browser connects to a website, it uses the site's public key from its certificate to negotiate a short-lived symmetric key, then encrypts the rest of the session symmetrically. The asymmetric work happens once per session, the symmetric work handles the bulk traffic. Best of both: no pre-shared secret needed, and fast enough for real volume.

## Where encryption shows up in blockchain systems

Back to the opening misconception. Encryption is critical infrastructure for the internet but plays a small and specific role in public blockchains. Three places it shows up in practice:

**At rest, in your wallet file.** When you install a wallet, the application encrypts your private key on disk using a password you choose. That encryption is symmetric (AES under the hood, with the password run through a key-derivation function first). This is why "remember your password" matters. If you forget it, the wallet file is encrypted and unreadable. The blockchain itself doesn't know or care about this, it's a local security measure done by the wallet software.

**In transit, when you talk to a node.** When your wallet sends commands to a remote node over the internet, the connection is usually wrapped in TLS, which uses the hybrid encryption you just saw. Again, this is standard internet infrastructure, the blockchain protocol doesn't specify it.

**In specialised chains that opt into it.** A few chains are designed around encrypted transactions where the amounts and recipients are hidden from public view. These are the exception rather than the rule, and they involve genuine cryptographic engineering beyond what plain AES or RSA provide.

If you've been told "everything on the blockchain is encrypted," the more accurate phrasing is "everything on the blockchain is authenticated and tamper-evident." Those are different security properties, and conflating them is the misconception that opened this lesson.

## FAQ

### Is my data on a public blockchain encrypted?

No. Every byte of state on a public chain is readable by anyone running a node. Authentication and integrity protect you, and both come from hashing and digital signatures.

### What's the difference between symmetric and asymmetric encryption?

One key or two. Symmetric uses the same key to encrypt and decrypt, so both sides must already share it safely. Asymmetric uses a key pair, a public key anyone can encrypt with and a private key only the owner decrypts with.

### Which algorithms do these two families use?

AES-256 for symmetric, which moves gigabytes per second. RSA with 2048 to 4096 bit keys for asymmetric, or ECDH on an elliptic curve for the same strength with much smaller keys. Asymmetric runs 100 to 1000 times slower.

### If the blockchain is public, why does my wallet still ask for a password?

It encrypts your private key on your own disk. The wallet runs your password through a key-derivation function and locks the key file with AES. Forget the password and that file stays unreadable.

### Why do HTTPS connections use both symmetric and asymmetric encryption?

Hybrid encryption. Asymmetric agrees a fresh symmetric key once, then symmetric encrypts the session.

---

# Public-key cryptography

Source: https://academy.redduck.io/courses/blockchain-basics/cryptography/public-key-cryptography

> The previous lesson introduced the idea of a key pair without explaining how such a thing is possible. Two keys, mathematically linked, where knowing one tells you nothing useful about the other. This lesson is about what makes that work, what specific schemes blockchains use, and why this single mathematical trick is the foundation for almost everything cryptographic that the chain actually relies on.

## The problem this solves

You need a way to prove to anyone in the world that you are who you say you are, **without revealing the secret that makes you "you."**

That sentence packs in several separate requirements:

- "Prove to anyone" means the proof has to be public-verifiable. No trusted middleman who knows your secret.
- "Anyone in the world" means people you've never met, and people who will exist long after you stop being able to answer questions.
- "Without revealing the secret" means the act of proving cannot leak the proof material itself. If proving costs you the secret, you can only prove once.

A password doesn't work for this. Showing your password to a verifier means they now have your password. A traditional shared key doesn't work either. You'd need to share it with every party in advance.

What you need is something asymmetric. A pair of values, mathematically linked, with these properties:

1. One value (the secret) generates the other (the public proof token) easily.
2. The reverse direction (recovering the secret from the public token) is computationally infeasible.
3. The secret can be used to "sign" or "decrypt" things in a way only the holder of the secret could do.
4. The public token can be used by anyone to verify the work without ever holding the secret.

For most of cryptographic history, no such construction was known. In the 1970s a sequence of breakthroughs (Diffie-Hellman 1976, RSA 1977) showed that it could be done, using carefully chosen mathematical operations where the forward direction is fast and the reverse direction is astronomically slow. This is the entire foundation of modern cryptography on the internet, and it is the entire foundation of identity on a blockchain.

## One-way functions

The mathematical idea underneath every key pair is a **one-way function**. Easy to compute in one direction, infeasible to reverse.

A useful intuition: mixing paint. Given two paint colors, you can easily produce the mixture. Given the mixture, separating it back into the two original colors is hopeless. The forward operation is trivial. The reverse operation is intractable.

<svg role="img" viewBox="0 0 720 200" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Private key to public key: easy forward, infeasible reverse</title><desc>A solid arrow shows deriving the public key from the private key takes milliseconds. A dashed arrow shows the reverse is infeasible, taking longer than the age of the universe. This one-way function runs every time you use your wallet.</desc>
  <rect x="40" y="70" width="160" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="120" y="95" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">private key</text>
  <text x="120" y="115" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">(your secret)</text>

<rect x="510" y="70" width="160" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="95" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">public key</text>
  <text x="590" y="115" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">(share freely)</text>

<line x1="200" y1="90" x2="510" y2="90" stroke="#ed4937" stroke-width="2"/>
  <polygon points="502,85 512,90 502,95" fill="#ed4937"/>
  <text x="355" y="80" text-anchor="middle" font-size="12" fill="#ed4937" font-weight="bold">easy: milliseconds</text>

<line x1="510" y1="120" x2="200" y2="120" stroke="#565653" stroke-width="2" stroke-dasharray="6 4"/>
  <polygon points="208,115 198,120 208,125" fill="#565653"/>
  <text x="355" y="145" text-anchor="middle" font-size="12" fill="#565653" font-weight="bold">infeasible: longer than the age of the universe</text>

<text x="355" y="180" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">A one-way function. The forward arrow runs every time you use your wallet.</text>
</svg>

The forward operation in public-key cryptography is some mathematical computation that takes the private key as input and produces the public key as output. Doing it is fast: any device can compute it in microseconds. Reversing it requires an attacker to try enormous numbers of possibilities, and the schemes are designed so that even the largest computer clusters humanity has built would not finish reversing one in the lifetime of the universe.

The strength of a key pair depends entirely on how steep that asymmetry is. A weak scheme might take a determined attacker a few years to reverse. A strong scheme would take longer than the heat death of the universe.

## The schemes blockchains use

Two specific elliptic-curve schemes do almost all of the public-key work in the chains you'll encounter.

**secp256k1.** An elliptic curve standardised in the early 2000s. Used by Bitcoin, Ethereum, and most of the chain families that followed them. The "256" is the key size in bits: 32 bytes of private key material produce a public key on the curve. The "k1" is a parameter designation distinguishing it from a closely related curve, secp256r1.

**Ed25519.** A newer elliptic-curve scheme published in 2011. Cleaner mathematics, faster operations, smaller keys with equivalent security to secp256k1. Used by Solana and a growing number of newer chains. Also used outside blockchain in modern systems like SSH and Signal.

For this course you don't need to be able to do elliptic-curve arithmetic by hand. You need to be able to:

- Recognise the names. When a wallet or library mentions secp256k1 or Ed25519, you know what category of object you're looking at.
- Know the key sizes. 32 bytes for the private key in both. The public key is roughly the same size for Ed25519 and a bit larger for secp256k1 depending on a compression option.
- Understand the asymmetry. Computing the public key from the private key is one fast operation. Going the other way is the entire definition of "secure" in this context.

Both schemes are believed to be secure against any classical computer. Both are vulnerable to a sufficiently large quantum computer running Shor's algorithm.

## Generating a key pair

The private key is just a random 32-byte number. Cryptographically random, generated by an entropy source rather than picked by a human. A human-chosen private key is a guessable private key, and the entire security of the scheme depends on the secret being indistinguishable from random noise to anyone who doesn't have it.

```typescript
private_key = 32 random bytes from a secure entropy source
public_key  = curve_multiply(private_key, generator_point)
```

That's the entire key-generation process at the conceptual level. One call to a cryptographic random number generator. One curve operation, the "easy direction" of the one-way function. Done.

The reason this is so short is that all the difficulty lives in two places. First, in the curve mathematics that makes `curve_multiply` a true one-way function: the result is a point on the curve that anyone with the public key can verify is legitimate, but only computable by someone who knew the private key. Second, in the entropy source: a private key with predictable bytes is no private key at all. Most wallet compromises in history are not breaks of the curve. They are weak entropy.

The playground below has a node that does this generation. Click and you'll see a freshly-derived public key appear in microseconds. Change the private-key bytes and watch the public key change completely. The avalanche-like property you saw with hash functions carries over here.

[interactive playground](https://plgrnd.io/#flow=N4IgbiBcDMA0IDsD2ATApgZygbVASxShAEMAWATgA4AjAVkrWLTRVICZjbqVpyB2AGZtq1AAzEBlPn2oh4AFwCeABzREA0gFEAmgAUAggEkASnJDKkGPPLxIEUUAA8oARja0AdPzZtSANj4-aD9KUVpSeEUoNhcXD3Zycj8XUnY3UWkAX3gUYnliB3MAV2oAGzwAY3U0KMgQSgBlZQBbbWaAMQA1ACkUAEUwXQAtYgA5AHU+8YANUgBrBoAJF2oi0grNFHU8NkWAC0xOgBkAWU0AYTNlACc8MDy0atqQcIQAKyRta4AVaj8BfSLdrEagCWjkAQAEWaCEWYD6AHFaNNHqRrtMbAikOpIXgTkUAOYfADu+gAXso+MZrhhiZRmudiUViQBVFDUND6cipRwVACORTAikcfLQtARQz4DQqoxpEGyIGajAwRWuLEKxII8j2rloongBzwBL28ig0DYCowaFKaAq8nVkAExFKVpy12IBIJeAQBKgTpdaGy+EIdWI5CYKBQgUoMe5bD8FD81ECjFoPj84Rcfj8ZiUqiI30002+V0s1ls9kgTigAFpoC5PKQ+P4wqJ+ODwpFXPwvNBSBk+NBM5RyKIXArcvlCvbHKa6hhiLkMEuV8Rl2uVyAFUq16qHaBNShtWbRPqQIbjXPaOP4FabXaHf7XSAUO7Pd7fY7nVagyACEQ-AqahoFEdAKGIFxKFIAQWEoCQIL4Sg2AqQCBDbARZAUFQ1DqSFDAaXQjn0bRSysGw7EKZxIDYGMPGgaAkMHWgh0SFwki7ai+DiNgx1oDMqFEGJSCHCc8gKKsQBnOd6iaVoOh6fpBhGCYplmBZllWdZNm2XYDgwY4zkubdlT3EMDy1HUYFPA00CNE0zT8S1rVte0Q3ka4ijQN0PS9H0-W-QNYGDAC2D7ag2D4WguBg0haBYTgXBA4hKHBPhyAqYSaNzbCiDwgiiJI+ALDIitKOiEcPEoFJYj40Kh1CiIQFqFJKA8PhRGzPgIrYcgQIzC0cjE6c0FnIhXg+L5fn+QFgVBcEoRhOFEWRVF0UxbFcXxIkkFJCkqRpOkGSZVl2U5blSF5AUhRFMUJSlGU5S3eAdxVNUzJAQ9jyss8L3syB-Cc+9XP8gNvPfPyvwDX9-zqPwUFEKRRHYECwIoDCUAivgKnNAR2pcPgQT4bL8zqRZ9CWUjywoiSqJrXwPFCmJ6HxpDzXIWg+A4kIPBSXr6x6mi-HjaBRKnCS9jXSyQCAqKKkXED-ESrq2DQUgVjDDJoBQPxiAqU9WD18IUHBKgBAqKNesoFAYxCNtyDQGNQu4J7FRMt6NQs3Uftsy8oD4wGXMfAKwd8z8n0DABdeAWAJTAcGCupHEUGsY7QAB9NOyCoOgGCYFh2E4bheEEYQxAkKQZBrZQSnKKoahrW5LxrQDgNA1Xw0g6DYPglnkNQ9DqBrb1q-kGsbQEU1byQVUKhwkgKBoehGGYVgOC4Hh+CEERxEkaRMJADBp+uWfFmIBAUBtIgs8X3OV4L9fi63svd8r6uykqJ4G7syfJOIa5Y+ki3FG7cIJQRgtbHuiE+4VDQhCfe+R-5oHkKfc+l9YZAWAeBTu4C4JOl7ihGBA8h4IBHmPNAE8noJxAEnFOKBY4Z2vjnZe+c15F03qXHeFdB43DuA8T+jcTTNwauFSK0VVZxVyNeJKKV+DpUypQYhpDx4-0PjPOejCl551XoXDeJdt7lz3mYVRx80AoIvuoheTCtH3zYXo5+XCq63HuPafh39cx-wASFMKEUoqgnEfFKR4gZFpQyuaSg7jEHILPuYrxpARG+JihIhK0jUpyLCYoooo9lGUIPkfWeV9wwsCjCEWM7AExJGTH4VM6ZMzZiMXk0x0S0EkEKZGaMpT4yJkqdU+MtS-A1ikl-X2CgPFIIAvDRGyM26pDgRjaQ2M2C4zHATZMESAFmOaXDBG7Upmo1mZjBZSz8aEwyVk8hP8YbUOTqnBhrTikxhHGUrpKZOA1LVtmMMEZ7kdPKUmF5aZenvP6YMgRo8tmTNCtMtG3ADk4zxiswIEydmQr2ejWFiz4UnOHpkshFDfzGPybDJFSMUWq2hXMrGcLlmE3qWojZc9wXIsweS9FRyEU1glhgPYQz7IjMiVfSxmi76sN0U-Thhi+XrKaRY7OQqWE6Mfhwgxr8nF8Prtk+AlyaE3LToyklzL9nzKpccypxLdlksNZSjF1Lkwcsljy0eGjb7yofuw-RL9qBOuYdo11djxUqt4S49V5ytxR3AHgNAxILDXDnNWSA7NSCVUgjGJsLhRB9T8BxGingxz9l4OCU8sVGpkiQEgZorg6JRQbClPs5p6KUACJkTIQA?view=true)

## What this enables

Three different operations can be built on top of the private/public key pair, each using the same mathematical machinery in a different direction.

**Encryption to a public key.** Already covered in the previous lesson. Anyone takes your public key, encrypts a message, and only your private key decrypts. This is used at the edges of blockchain systems but is not how on-chain data is protected.

**Digital signatures.** Use the private key to produce a short value that anyone can verify against the message and the public key. The signature proves that the message was approved by whoever holds the private key, and cannot be forged without it. This is the primitive that authorises every blockchain transaction. Two lessons from now, a dedicated lesson explains exactly how this works.

**Identity.** Your address is derived from your public key. The chain doesn't know your name, your country, or your email. It knows the public key that goes with the private key you control. Possession of the private key is the entire definition of "you" from the chain's perspective. Lose it and there is no recovery. Steal it and there is no insurance.

This is very different from systems with password resets and customer support. There is no customer support. The private key, and only the private key, is the identity. The next lesson covers exactly how that key gets generated, stored, and recovered.

## FAQ

### How can two keys be mathematically linked if neither reveals the other?

Public-key cryptography uses a one-way function. Computing the public key from the private key takes microseconds. Reversing that step would take longer than the lifetime of the universe.

### What is a private key, exactly?

32 random bytes from a secure entropy source. Your public key is derived from it, and your address from the public key. Predictable bytes make a guessable key, and most wallet compromises came from weak entropy.

### What are secp256k1 and Ed25519?

The two elliptic-curve schemes behind almost all blockchain keys. Bitcoin and Ethereum use secp256k1. Solana uses Ed25519, published in 2011 and also used by SSH and Signal. Both take a 32-byte private key.

### What happens if I lose my private key?

Nothing can be done. There is no reset and no support line. The network knows only the public key that pairs with your key, never your name.

### Can a quantum computer break these keys?

A large enough one running Shor's algorithm, yes. No ordinary computer can.

---

# Mnemonic phrases and how a wallet gets created

Source: https://academy.redduck.io/courses/blockchain-basics/cryptography/mnemonic-phrases-and-how-a-wallet-gets-created

> The previous lesson ended on a sentence that should bother you: "the private key, and only the private key, is your identity." If the private key is 32 random bytes, how is a human supposed to back that up safely? You can't memorise hex, you'll mis-type it, you'll lose the paper. The answer is one of the most elegant standards in cryptography, and it's the same standard used by almost every wallet you'll ever touch.

## The problem with 32 random bytes

A private key looks like this:

```
4f3edf983ac636a65a842ce7c78d9aa706d3b113b37b6b1da19c89e5fcca7c84
```

64 hex characters. 32 bytes. Cryptographically random. This is your entire identity on a blockchain. There is no recovery from the issuer because there is no issuer.

Now imagine you have to write that on a piece of paper, store it somewhere safe, and read it back correctly five years from now. You will:

- Mis-write one character and never notice
- Confuse `0` and `O`, or `1` and `l` (hex itself uses only 0-9 and a-f, so it has no O or l, but copying it by hand still invites exactly this kind of visual mix-up)
- Lose the paper to a flood, a fire, or just plain misplacement
- Find it ten years later and not remember which wallet it belonged to

This is a real problem. Early cryptocurrency users lost meaningful amounts of money to exactly this failure mode. So in 2013, a standard was proposed that turned the same 32 bytes of randomness into something humans could actually back up: a short sequence of ordinary English words.

That standard is **BIP 39**.

## The full pipeline

Here's the end-to-end process that runs when you click "create a new wallet" in any modern wallet application.

<svg role="img" viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Wallet creation pipeline: entropy, mnemonic, seed, then master and child keys</title><desc>Four boxes stacked top to bottom and joined by arrows. Entropy becomes a mnemonic phrase, the mnemonic becomes a seed through PBKDF2-HMAC-SHA512, and the seed derives a master key plus a whole tree of child keys and addresses.</desc>
  <rect x="40" y="20" width="640" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="45" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">1. Entropy</text>
  <text x="360" y="65" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">128 or 256 random bits from the OS entropy source</text>

<line x1="360" y1="80" x2="360" y2="110" stroke="#565653" stroke-width="2"/>
  <polygon points="355,102 360,112 365,102" fill="#565653"/>

<rect x="40" y="110" width="640" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="135" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">2. Mnemonic</text>
  <text x="360" y="155" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">12 or 24 words from a fixed 2048-word list, plus checksum bits</text>

<line x1="360" y1="170" x2="360" y2="200" stroke="#565653" stroke-width="2"/>
  <polygon points="355,192 360,202 365,192" fill="#565653"/>

<rect x="40" y="200" width="640" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="225" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">3. Seed</text>
  <text x="360" y="245" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">64 bytes via PBKDF2-HMAC-SHA512 over the mnemonic + optional passphrase</text>

<line x1="360" y1="260" x2="360" y2="290" stroke="#565653" stroke-width="2"/>
  <polygon points="355,282 360,292 365,282" fill="#565653"/>

<rect x="40" y="290" width="640" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="315" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">4. Master key + many child keys + addresses</text>
  <text x="360" y="335" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">a whole tree of key pairs, derived deterministically from the seed</text>
</svg>

Four stages, each with a specific job. Each stage is deterministic: same input always produces the same output. This is why a mnemonic written on paper is a complete backup of every key and every address the wallet has ever generated, even ones it hasn't shown you yet.

## Stage 1: Entropy

The wallet asks the operating system for high-quality random bytes. 128 bits for a 12-word mnemonic, 256 bits for a 24-word one. The bytes come from the OS entropy pool, the same pool that secures TLS connections and SSH sessions on the same machine. A wallet that uses anything weaker is a broken wallet.

This step is short to describe and disastrous to get wrong. Past wallet failures with weak randomness have produced predictable private keys that attackers have systematically drained. The math of the rest of the pipeline only protects you if this first step is genuinely unpredictable.

## Stage 2: Mnemonic generation

The wallet takes the entropy bytes, computes a short **checksum** (the first few bits of a hash of the entropy), and appends it to the entropy. The resulting bit-string is split into 11-bit chunks. Each chunk is a number between 0 and 2047. That number indexes into a fixed list of 2048 English words, defined once and shared by every BIP 39-compliant wallet on earth.

A 12-word example:

```
abandon ability able about above absent absorb abstract absurd abuse access accident
```

These are the first 12 words of the wordlist, used here simply as an illustration. Real mnemonics look more random:

```
army van defense carry jealous true garbage claim echo media make crunch
```

The [wordlist](https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt) is the same for every wallet. It contains no two words sharing a four-letter prefix, no plurals of other entries, no words easily confused with each other. This is why "rabb" is enough to uniquely identify "rabbit" when typing into a recovery field, why no wallet ever shows you the word "horsex" instead of "horse," and why entering the wrong word almost always triggers an "invalid mnemonic" error from the checksum.

A 24-word mnemonic runs the same algorithm with more starting randomness. That is the only difference.

Because that mapping is reversible, the words are the entropy itself in readable form, so restoring a wallet by re-entering them reproduces the exact same bits, and there is no separate stored key that a password would open.

## Stage 3: Mnemonic to seed

The mnemonic itself is not the cryptographic input to anything. The seed is. To convert one to the other, the wallet runs:

```typescript
seed = PBKDF2-HMAC-SHA512(
  password   = mnemonic_as_text,
  salt       = "mnemonic" + optional_passphrase,
  iterations = 2048,
  output     = 64 bytes
)
```

PBKDF2 is a key-stretching function: it takes an input and repeatedly hashes it to produce an output, intentionally slowly. The 2,048 iterations are a deliberate cost that adds milliseconds for a legitimate user but multiplies the work required for an attacker brute-forcing weak mnemonics.

The optional **passphrase** is the part most users don't know exists. If you supply one, any string at all, no length limit, the resulting seed is completely different from the seed you'd get with the same mnemonic and no passphrase. This is sometimes called the "25th word" because it acts like an extra hidden word on top of the visible 12 or 24.

Imagine someone forces you to hand over your mnemonic, at a border crossing, or under threat. If the mnemonic opens one obvious wallet, they drain it and believe they got everything. A passphrase prevents this. The same mnemonic with an empty passphrase opens an obvious wallet. With your secret passphrase it opens a completely different, hidden one. An attacker who has the mnemonic can drain the visible wallet but cannot reach the hidden one without also knowing the passphrase, and has no way to know the hidden wallet exists at all. This property is called plausible deniability.

The output of this stage is always 64 bytes regardless of mnemonic length. That 64-byte seed is what the rest of the system actually uses.

## Stage 4: Seed to master key, and everything below

The 64-byte seed feeds into a tree-derivation algorithm that produces a **master key** and then any number of child keys below it. From the master key you can derive child keys, from each child key you can derive grandchildren, and so on, infinitely many key pairs from one seed.

The mechanics of that tree are detailed enough to deserve a treatment of their own, and we'll come back to them in context when they actually matter. For now, the important property is this: **the entire tree is deterministic.** Same seed, same tree. Same mnemonic, same seed, same tree.

This is why a 12-word mnemonic is a complete wallet backup. Restore it on a new device, the wallet runs the same pipeline, and every key and every address shows up in the same order with the same values. Nothing else needs to be saved.

## Three rules for handling a seed phrase

Three takeaways that should shape how you think about wallets from here on.

**The mnemonic is the wallet.** Lose it and there is no recovery. The wallet software does not have a copy. The wallet vendor does not have a copy. The chain does not know who you are, only that you control a key derived from it.

**Share the mnemonic and you've shared everything.** Anyone who sees your 12 or 24 words can reconstruct every private key derived from it on any device. There is no "read-only" version of a mnemonic. Phishing attacks that ask for your seed phrase are stealing the entire wallet rather than a session token.

**The mnemonic plus a passphrase is two different wallets.** If you use a passphrase, you must back up both the mnemonic and the passphrase, and you must back them up separately. Lose the passphrase and the funds in the hidden wallet are unreachable even though the mnemonic is intact.

The playground below has a mnemonic-generator node. It produces a fresh mnemonic. **Use only the test mnemonics it generates. Never paste a real wallet's mnemonic into any web tool, including this one.**

[interactive playground](https://plgrnd.io/#flow=N4IgbiBcDMA0IDsD2ATApgZygbVASxShAEMAWATgA4AjAVkrWLTRVICZjbqVpyB2AGZtq1AAzEBlPn2oh4AFwCeABzREA0gFEAmgAUAggEkASnJDKkGPPLxIEUUAA8oARja0AdPzZtSANj4-aD9KUVpSeEUoNhcXD3Zycj8XUnY3UWkAX3gUYnliB3MAV2oAGzwAY3U0KMgQfQANPm0ACWhSBABzAGEAFUcigC1NAFUAGQB3NEGAJxGAZQA5DEUEQYraRRQ2dQw-ADVHACE2fXIJsbNlGbwwPLRq2pBSDH3G+flHAHFSJAAvZR-QYtNCLTQAKUcnRGfxmDSQABF9C1etAEAAxCrHXpfahIUh8EbyNC6NiLFx+XoIgDWIwEAEUJgArekAWz+4LGLWILRcCImM0UGG68nmLXB7NI6N6iloVgg2RArMYGCKMxYhQmBHkAAtXLRRPAdWg8J0dfIoNA2IqMGhSmgKsTCJABMRSracjNiJ1OngulBXe60Nl8M6QH4KtRoKJ0BRiC5KKQBCxKBJ43xKGwKhGBKJyAJZAoVGo6gjDPNdGN9NorpZrLZ7JAnNFKJQPNBoJm+NBaD3Ei4kpFonw4mxRC5aH5aFRRDFSD3Fbl8oViY4LXVGs02h0ev0hqNJtM5ksVmsNlsdntDiczhcQIrlcRVernaAtShdZbRIaQMbTebLT8G07QdJ0AzdD0QBQL0fT9TpwKDEMQAIIg-DYdpqDYPhaC4ZNSFoFhOBcaNiEoac+HICp5zYSgzCUVQiDLCsqxreALCsGw7EKZxIBo8gPEoFJYkndCe3QiIQFqFI2z4UQ-ACLC2HIaMp2tHI8gKJsQFXddnled5Ph+f5AWBUEIShGE4URZFUQxLEjhxPECSJEkyQpKlaQZZk2Q5LkeT5AUhRFMUJT+KUZTlW573gR9nw1LT30-GBvyNE0zXXfxgPtR14sDSDoO9X1-RdCDg1gUMiBcJhyGIFAaDQLg0HwuTav8UgKl4KgUkoyhyDo4siAAWTBQaAHlFkMbpaw4htuKgABaYi2A8PxyBiXgMlbDtfCHXjiK8SgAnYHDKGjchaDUqCNMKVkEDQVk7EqIgkAEYkEAAAl1PAMHeqY8mNGZ3rAb7rHejA7E6d6vTANAPowZRFHezpMHkNU0He0o7DtRG3TwWGofupAYfe65iG+tQ2JuO5iUeIgXjeBoPm+X4ASBEEwUhaFYXhJEUTRTFsVxfFCWJUlyUpGk6UZFl2U5bleX5QVhVFcVJWlWV5WipUVTR18QESvVIApH8-3Sy1LttbKwJKoNPUKuCENtJCULqYI0GoSilJQTbSFESNiA6lBaBQFxAj4UhTuoYhRH6hjS3LStq2m+suK0niLq8Sd-D8AlvDcOBJMtHsPG-WgKOgPgYjIvhF2urSdOe178a+n6-t1NBAeBjiwYhqHiBhuGEaRlG0YxrHShx8p8fVB7idJ8mtdi3XNW1Q3oxNtKAMgTL4Et0DctKu3YOKvLgwAXXgFhkawSBcGQsNHEUear7QAB9V+yCoOgGCYFh2E4bgvBBDCDEBIKQMh5rKBKOUKoNR5o3HSvNCMUYYxNRqgmJMKY0yh0zNmCouZ8zUHmn6KB8h5r2lemYcGaoKglhIBQGg9BGDMFYBwLgPB+BCBEOISQ0hCwgGoTMWh3IEAoHtEQT+jCf4sP-uwoBXDQG8IgVAsolRHjwP-BaBQxAZjI10sg6MsZ0GJmTHVbBGYsw5jzAWOiOi9EiLEXQgxqC4wYNMamV0ODLH4OsUQkhRQyEUItM7B+T8X7v0kd-Zhf82GAM4SAnh4CiHXFuPcdRCDzRIPEphbCuEmoEVyLQYi4gyL8EotRSgxCECkPIWgShu8kA0LoZEphv9WEAI4cA7hYC+FUMaUItADjxF1BadImJHT5EJJ6coqmaS4EZK0dpOxaB9HZKwjhageEClERIqUiiVErS0W0bolZQynFrNyZs-JhEim7PIuUw5VSalBOiqAQRtDKrVVqvVRqzU-CtRzh1RIgkKAVF6n0ppZzPloBqnVagDV4V-IBe1TqIKerkEgbMmm8zNG2JObpUZ0T2lyPid0pR-D8j4qhSMhhUS2myLiV0xRSTMWpOxU-F58AXYgEfs-FAyN35VRhd8+FvyDT-NYIC1F3UwXkCFbCn5iLxXIqBV1UFvVWXUweDixBhL6WxM6QoxJfC9UyINZMslLKUlavUS8kJRBeXhNfvKkVCL8ktUlSi4FMqNW3UJggSoGjEFuw9hUL2Ps-ZR0DsHUOR1I7RyeQE2p9SBH9I+XUF1cK3VIs9aqtFsqIUDOpSATNir3USranmn1GK-UPQDRUINAFjl6KICGz25BvYZl9v7aNIcw4R2gFHGOzbTnEFEcMkAbaw0dojT26AQc+1xsHQm-xgS6nBIvuAPGEwLAzHXM2SA51SACQTK2AkLhRAqT8LtGinhxy+14NOb8+EJJ-CQEgVkrh2w4QnGRdoVoOyHRrpkIAA?view=true)

## Why twelve words reproduce everything

You now have the full flow from "random bits" to "a usable wallet." Every stage is deterministic, so re-entering the same twelve or twenty-four words reproduces the exact same seed, the same master key, and the same tree of addresses. That determinism is what lets a short phrase stand in for an entire wallet.

## FAQ

### Can I lose my crypto if I lose my seed phrase?

Yes, and there is no recovery. The wallet software keeps no copy, the vendor keeps no copy, and the chain does not know who you are.

### Why is a seed phrase made of ordinary words instead of a hex string?

Words are far easier to write down and read back than 64 hex characters. A BIP 39 phrase of 12 or 24 words encodes the same random bits, drawn from a fixed list of 2048 words that every compliant wallet shares.

### Is it safe to type my seed phrase into a website or a support form?

No. Anyone who sees those 12 or 24 words can rebuild every key and address in your wallet on any device. A seed phrase has no read-only form, and anything asking for one is phishing for the whole wallet.

### What is the 25th word?

An optional passphrase mixed into the seed. The same words with no passphrase open one wallet, and with your passphrase a completely different hidden one, which gives you plausible deniability if someone forces you to hand over the phrase. Back it up separately, because losing it puts the hidden funds out of reach while the mnemonic still works.

### How do the words become a private key?

PBKDF2-HMAC-SHA512 runs the mnemonic through 2,048 iterations into a 64-byte seed. That seed derives a master key and every child key and address below it.

---

# Digital signatures and signature recovery

Source: https://academy.redduck.io/courses/blockchain-basics/cryptography/digital-signatures-and-signature-recovery

> A blockchain transaction travels across an open network with no return address, no email header, no session cookie. Yet every node receiving it has to decide: did this transaction come from someone authorised to spend these funds? The mechanism that answers that question, billions of times per day across every public chain, is the topic of this lesson. It's also the operational pay-off for everything you've learned so far about key pairs.

## The problem

You hold a private key. You want to tell the network "move some value from my account to this other account." The network has never met you. It will never meet you. It cannot ask for your password because there is no password and no one to receive it.

What you can send is a **transaction** plus a **signature** on that transaction. Anyone with your public key can then check whether the signature is valid for that exact transaction. If it is, the network accepts that the transaction came from whoever holds the private key that pairs with the public key in question. If it isn't, the transaction is rejected.

For this to work, the signature scheme must guarantee three things:

1. **Only the holder of the private key can produce a valid signature** for a given message.
2. **Anyone with the public key can verify** that a given signature was produced for that message by that private key.
3. **The signature is tied to the exact message.** Change one bit of the transaction and the same signature no longer verifies. There is no replaying old signatures on different transactions.

These three properties are exactly what the public-key math from earlier can deliver. The same key-pair construction that powered the encryption examples works in the opposite direction: instead of encrypting *to* the public key, you sign *with* the private key, and anyone verifies against the public key. Different goal, same underlying mathematical asymmetry.

## How sign and verify work

A signature scheme has two operations.

**Sign** takes the private key and a message and produces a signature, which is just a short value, typically 64 or 65 bytes.

**Verify** takes the public key, the message, and the signature, and returns either "valid" or "invalid."

<svg role="img" viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Sign combines private key and message into a signature; Verify checks it</title><desc>On top, a private key and a message go into the sign step, producing a 64 or 65 byte signature. Below, the public key, message, and signature go into the verify step, which returns valid or invalid.</desc>
  <text x="360" y="20" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Sign</text>

<rect x="40" y="35" width="160" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="120" y="55" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">private key</text>
  <text x="120" y="73" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">your secret</text>

<rect x="40" y="100" width="160" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="120" y="120" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">message</text>
  <text x="120" y="138" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">the transaction</text>

<rect x="280" y="65" width="160" height="55" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="90" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">sign</text>
  <text x="360" y="108" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">private key + message</text>

<rect x="520" y="65" width="160" height="55" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="600" y="90" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">signature</text>
  <text x="600" y="108" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">64 or 65 bytes</text>

<line x1="200" y1="60" x2="280" y2="85" stroke="#565653" stroke-width="2"/>
  <polygon points="272,78 282,86 271,88" fill="#565653"/>
  <line x1="200" y1="125" x2="280" y2="100" stroke="#565653" stroke-width="2"/>
  <polygon points="271,98 282,100 272,108" fill="#565653"/>
  <line x1="440" y1="93" x2="520" y2="93" stroke="#565653" stroke-width="2"/>
  <polygon points="512,88 522,93 512,98" fill="#565653"/>

<line x1="40" y1="170" x2="680" y2="170" stroke="#565653" stroke-width="1" stroke-dasharray="4 4"/>

<text x="360" y="195" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Verify</text>

<rect x="40" y="215" width="120" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="100" y="235" text-anchor="middle" font-size="11" fill="#000000" font-weight="bold">public key</text>
  <text x="100" y="252" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">share freely</text>

<rect x="180" y="215" width="120" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="240" y="235" text-anchor="middle" font-size="11" fill="#000000" font-weight="bold">message</text>
  <text x="240" y="252" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">the transaction</text>

<rect x="320" y="215" width="120" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="380" y="235" text-anchor="middle" font-size="11" fill="#000000" font-weight="bold">signature</text>
  <text x="380" y="252" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">64 or 65 bytes</text>

<rect x="480" y="215" width="120" height="50" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="540" y="247" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">verify</text>

<rect x="620" y="215" width="60" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="650" y="247" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">✓ / ✗</text>

<line x1="160" y1="240" x2="180" y2="240" stroke="#565653" stroke-width="2"/>
  <line x1="300" y1="240" x2="320" y2="240" stroke="#565653" stroke-width="2"/>
  <line x1="440" y1="240" x2="480" y2="240" stroke="#565653" stroke-width="2"/>
  <polygon points="472,235 482,240 472,245" fill="#565653"/>
  <line x1="600" y1="240" x2="620" y2="240" stroke="#565653" stroke-width="2"/>
  <polygon points="612,235 622,240 612,245" fill="#565653"/>
</svg>

Verifying does not require the private key. This is the entire point. The wallet that sent the transaction holds the private key. Every node in the world that validates the transaction has only the public key, the message, and the signature. The protocol works because verification with just those three values is enough.

The most common signature scheme on the chains you'll meet is **ECDSA**, the Elliptic Curve Digital Signature Algorithm, used with the same secp256k1 curve that produces the key pairs. ECDSA does most of the signing work: every transaction on the largest chain families produces one. **Ed25519** has its own integrated signing scheme (technically called EdDSA) and is used by some newer chains.

For both schemes the operational properties are the same: sign with the private key, verify with the public key, signatures are ~64 bytes, signing is fast, verification is fast, and forgery without the private key is computationally infeasible.

## Where ECDSA hides a trap

Inside ECDSA's signing operation there's a random value called the **nonce**. Each signature needs a fresh nonce. If you ever sign two different messages with the same nonce by accident, an attacker who sees both signatures can mathematically recover your private key. This is not a theoretical attack, it has been used to drain real wallets.

The fix is a standard called **RFC 6979**, which makes the nonce a deterministic function of the private key and the message instead of relying on the device's random number generator. Every modern wallet uses this. You don't have to remember the standard, you do need to know that "weak randomness in signature generation" is a real category of historical wallet break, and that it's the reason RFC 6979 exists.

## Signature recovery: the operational pay-off

The verify operation above takes the public key as an input. For a blockchain to use this, every transaction would need to carry the sender's public key, which would make every transaction larger and require the network to look up which public keys belong to which accounts.

There is a better trick.

ECDSA has a property called **public-key recovery**. Given a signature and the message, you can directly recover the public key that signed it, with no other input. The public key can be computed from the signature itself.

<svg role="img" viewBox="0 0 720 200" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Message and signature recover the signer's public key</title><desc>The message (the transaction) and the signature (65 bytes with recovery id) both feed into a recover step. That step outputs the public key, which reveals the signer's identity.</desc>
  <rect x="40" y="50" width="180" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="130" y="75" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">message</text>
  <text x="130" y="95" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">the transaction</text>

<rect x="40" y="125" width="180" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="130" y="150" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">signature</text>
  <text x="130" y="170" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">65 bytes with recovery id</text>

<rect x="280" y="90" width="160" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="115" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">recover</text>
  <text x="360" y="135" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">message + signature</text>

<rect x="520" y="90" width="160" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="600" y="115" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">public key</text>
  <text x="600" y="135" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">the signer's identity</text>

<line x1="220" y1="85" x2="280" y2="110" stroke="#565653" stroke-width="2"/>
  <polygon points="272,103 282,111 271,113" fill="#565653"/>
  <line x1="220" y1="155" x2="280" y2="130" stroke="#565653" stroke-width="2"/>
  <polygon points="271,128 282,130 272,138" fill="#565653"/>
  <line x1="440" y1="120" x2="520" y2="120" stroke="#565653" stroke-width="2"/>
  <polygon points="512,115 522,120 512,125" fill="#565653"/>
</svg>

The extra byte in the signature is a **recovery id** that disambiguates between the two mathematically valid public keys that any ECDSA signature could in principle map to. With that extra byte, recovery is unambiguous.

This is why blockchain transactions don't carry the sender's public key. The network derives it from the signature itself as each transaction arrives. The recovered public key, hashed in a chain-specific way, becomes the sender address. If the chain's state shows that this address has the funds being moved, the transaction is accepted. If not, rejected.

On many chains the recovery operation is exposed to smart-contract code as a built-in primitive (most famously named `ecrecover`) and is one of the most-called functions in the whole system. Every transaction triggers it. Every "did this user sign this message?" check uses it.

## What you know now

Three properties are now real to you instead of abstract.

**Authentication on a blockchain works without identity in the traditional sense.** No usernames, no central registry of who-is-who. Just private keys, signatures, and recovered public keys. If the math checks out, the transaction is yours.

**Signatures bind to exact messages.** You cannot reuse a signature on a different transaction. You cannot tamper with a transaction without invalidating its signature. The integrity comes from the math rather than from a trusted intermediary.

**Recovery is the trick that makes the protocol-level math practical.** Without it, every transaction would need to carry a public key. With it, the network derives the signer's identity from the signature itself. This is the operational primitive that the next two lessons (and effectively every chain you'll touch) build on.

## The cryptographic toolkit you now have

You've now seen the four cryptographic building blocks that the rest of blockchain rests on: hash functions, encoding schemes, public-key cryptography, and digital signatures with recovery. With these, you can explain how a wallet authorises a transaction, how a network verifies it without trusting anyone, and how an address ties back to a key. Every structure real chains use, from derivation paths to Merkle trees, is these four primitives combined.

## FAQ

### How does a blockchain know a transaction really came from me?

You send a digital signature with the transaction. Any node checks it against your public key, and a signature valid for that exact transaction counts as authorisation. ECDSA on the secp256k1 curve does most of this work, and newer chains use Ed25519.

### Can someone reuse my signature on a different transaction?

No. A signature binds to the exact message. Change one bit of the transaction and verification fails.

### Why don't blockchain transactions include the sender's public key?

ECDSA can recover it. Given the message, the signature, and a small recovery-id byte inside it, the network computes the public key that signed and hashes it into the sender address. Many chains expose this to smart contracts as ecrecover.

### What happens if a wallet signs two messages with the same nonce?

An attacker who sees both signatures can compute the private key. This has drained real wallets. RFC 6979 derives the nonce from the private key and the message instead of device randomness.

### How big is a signature?

64 bytes, or 65 when it carries the recovery id byte.

---

# Merkle trees

Source: https://academy.redduck.io/courses/blockchain-basics/cryptography/merkle-trees

> You have a list of one thousand things. Someone asks you "is this specific thing on your list?" You want to prove the answer is yes without sending them the whole list. You also want them to be unable to forge a "yes" for anything not actually on it. The data structure that solves this, in the cleverest possible way, is the Merkle tree. It's a structural primitive at the heart of every blockchain in existence, and once you see how it's built, you'll never need to memorise it again.

## Deriving the structure

Start with the problem. You have eight items, A to H. You want a single short value that "summarises" all eight, with two properties:

1. **Tamper-evident.** Change any one of the eight items, and the summary changes. No way to swap items secretly.
2. **Cheap to check a single item.** A verifier shouldn't have to download all eight to confirm `D` is in the set.

A naive first attempt: hash the whole list. `summary = hash(A || B || C || D || E || F || G || H)`. This solves property 1: change anything, the hash changes. But it fails property 2. To verify that `D` is in the set, the verifier has to compute the hash of the entire concatenation, which means they need all eight items in the first place. We've saved nothing.

Better idea: hash each item individually first, then hash the results in pairs, then hash those pair-hashes in pairs, and keep going until you're down to one value. That's a tree.

<svg role="img" viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Merkle tree from leaves A-H up to a single Merkle root</title><desc>Eight items, A through H, are each hashed to form h(A) through h(H) at the bottom level. Pairs of hashes are combined and hashed again at each level up (h(AB), h(CD), h(EF), h(GH), then h(AB,CD) and h(EF,GH)), until only one Merkle root remains at the top.</desc>
  <!-- Leaves (data) -->
  <rect x="20" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="52" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">A</text>

<rect x="105" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="137" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">B</text>

<rect x="190" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="222" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">C</text>

<rect x="275" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="307" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">D</text>

<rect x="380" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="412" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">E</text>

<rect x="465" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="497" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">F</text>

<rect x="550" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="582" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">G</text>

<rect x="635" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="667" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">H</text>

<!-- Level 1: hash of each item -->
  <rect x="20" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="52" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(A)</text>

<rect x="105" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="137" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(B)</text>

<rect x="190" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="222" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(C)</text>

<rect x="275" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="307" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(D)</text>

<rect x="380" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="412" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(E)</text>

<rect x="465" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="497" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(F)</text>

<rect x="550" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="582" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(G)</text>

<rect x="635" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="667" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(H)</text>

<!-- Level 2: pair hashes -->
  <rect x="60" y="160" width="90" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="105" y="185" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(AB)</text>

<rect x="230" y="160" width="90" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="275" y="185" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(CD)</text>

<rect x="420" y="160" width="90" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="465" y="185" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(EF)</text>

<rect x="590" y="160" width="90" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="635" y="185" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(GH)</text>

<!-- Level 3: quadruple hashes -->
  <rect x="125" y="80" width="120" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="185" y="105" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(AB,CD)</text>

<rect x="475" y="80" width="120" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="535" y="105" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(EF,GH)</text>

<!-- Root -->
  <rect x="295" y="10" width="130" height="40" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="35" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Merkle root</text>

<!-- Lines from leaves to level 1 -->
  <line x1="52" y1="320" x2="52" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="137" y1="320" x2="137" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="222" y1="320" x2="222" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="307" y1="320" x2="307" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="412" y1="320" x2="412" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="497" y1="320" x2="497" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="582" y1="320" x2="582" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="667" y1="320" x2="667" y2="280" stroke="#565653" stroke-width="1.5"/>

<!-- Lines from level 1 to level 2 -->
  <line x1="52" y1="240" x2="105" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="137" y1="240" x2="105" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="222" y1="240" x2="275" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="307" y1="240" x2="275" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="412" y1="240" x2="465" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="497" y1="240" x2="465" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="582" y1="240" x2="635" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="667" y1="240" x2="635" y2="200" stroke="#565653" stroke-width="1.5"/>

<!-- Lines from level 2 to level 3 -->
  <line x1="105" y1="160" x2="185" y2="120" stroke="#565653" stroke-width="1.5"/>
  <line x1="275" y1="160" x2="185" y2="120" stroke="#565653" stroke-width="1.5"/>
  <line x1="465" y1="160" x2="535" y2="120" stroke="#565653" stroke-width="1.5"/>
  <line x1="635" y1="160" x2="535" y2="120" stroke="#565653" stroke-width="1.5"/>

<!-- Lines from level 3 to root -->
  <line x1="185" y1="80" x2="360" y2="50" stroke="#565653" stroke-width="1.5"/>
  <line x1="535" y1="80" x2="360" y2="50" stroke="#565653" stroke-width="1.5"/>
</svg>

That's a Merkle tree. The bottom row is the original data. The level above is the hash of each item. The level above that is the hash of each adjacent pair. Keep going: each parent is the hash of the concatenation of its two children. The single value at the top is the **Merkle root**, and it's only 32 bytes regardless of how many items you started with.

For 8 leaves the tree has 4 levels and a total of 15 hashes. For 1,000 leaves it would have 10 levels and about 2,000 hashes. The depth grows logarithmically with the number of leaves, which is the property that makes the whole structure efficient.

## What the root commits to

The root is a single short value that depends on *every* leaf in the tree. If you change one bit of `D`, then `h(D)` changes, then `h(CD)` changes, then `h(AB,CD)` changes, then the root changes. This is the avalanche effect from the hashing lesson: change one bit and the resulting hash looks completely different, and here that change propagates all the way up to the root. The root is a compact, tamper-evident commitment to the entire set of leaves.

Two parties can confirm they have the same set of items by exchanging just the root. If their roots match, every byte of every leaf is identical. If the roots differ by even one bit, something somewhere on the tree differs. They don't have to compare the leaves directly. The 32 bytes of the root are doing all the work.

This is already useful on its own. Beyond the storage and bandwidth it saves, the real point is the structural property: one short hash commits to a large set. That is the foundation the next section builds on.

## The Merkle proof

Now the operation that makes Merkle trees famous.

Suppose you're the verifier. You know only the Merkle root. Someone else, the prover, has the whole tree and wants to convince you that item `D` is one of the leaves. How few hashes do they need to send you, and how can you check?

The answer is they send you a single sequence of hashes called a **Merkle proof**. For `D` in our 8-leaf tree, the proof is exactly three hashes: `h(C)`, `h(AB)`, and `h(EF,GH)`.

<svg role="img" viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Merkle proof path for leaf D in an 8-leaf Merkle tree</title><desc>Eight leaves A to H are hashed and paired upward through h(AB), h(CD), h(EF), h(GH) and further pairs until they reach the Merkle root. The highlighted path shows that proving D only needs h(C), h(AB), and h(EF,GH) combined with D's own hash chain up to the root.</desc>
  <!-- Leaves -->
  <rect x="20" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="52" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">A</text>

<rect x="105" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="137" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">B</text>

<rect x="190" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="222" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">C</text>

<rect x="275" y="320" width="65" height="40" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="307" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#ffffff" font-weight="bold">D</text>

<rect x="380" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="412" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">E</text>

<rect x="465" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="497" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">F</text>

<rect x="550" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="582" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">G</text>

<rect x="635" y="320" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="667" y="345" text-anchor="middle" font-family="monospace" font-size="13" fill="#000000">H</text>

<!-- Level 1 -->
  <rect x="20" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="52" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(A)</text>

<rect x="105" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="137" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(B)</text>

<rect x="190" y="240" width="65" height="40" fill="#ed4937" stroke="#000000" stroke-width="3"/>
  <text x="222" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffffff" font-weight="bold">h(C)</text>

<rect x="275" y="240" width="65" height="40" fill="#e0deda" stroke="#ed4937" stroke-width="3" stroke-dasharray="4 3"/>
  <text x="307" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(D)</text>

<rect x="380" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="412" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(E)</text>

<rect x="465" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="497" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(F)</text>

<rect x="550" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="582" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(G)</text>

<rect x="635" y="240" width="65" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="667" y="265" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(H)</text>

<!-- Level 2 -->
  <rect x="60" y="160" width="90" height="40" fill="#ed4937" stroke="#000000" stroke-width="3"/>
  <text x="105" y="185" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffffff" font-weight="bold">h(AB)</text>

<rect x="230" y="160" width="90" height="40" fill="#e0deda" stroke="#ed4937" stroke-width="3" stroke-dasharray="4 3"/>
  <text x="275" y="185" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(CD)</text>

<rect x="420" y="160" width="90" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="465" y="185" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(EF)</text>

<rect x="590" y="160" width="90" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="635" y="185" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(GH)</text>

<!-- Level 3 -->
  <rect x="125" y="80" width="120" height="40" fill="#e0deda" stroke="#ed4937" stroke-width="3" stroke-dasharray="4 3"/>
  <text x="185" y="105" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">h(AB,CD)</text>

<rect x="475" y="80" width="120" height="40" fill="#ed4937" stroke="#000000" stroke-width="3"/>
  <text x="535" y="105" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffffff" font-weight="bold">h(EF,GH)</text>

<!-- Root -->
  <rect x="295" y="10" width="130" height="40" fill="#e0deda" stroke="#ed4937" stroke-width="3" stroke-dasharray="4 3"/>
  <text x="360" y="35" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Merkle root</text>

<!-- Lines -->
  <line x1="52" y1="320" x2="52" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="137" y1="320" x2="137" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="222" y1="320" x2="222" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="307" y1="320" x2="307" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="412" y1="320" x2="412" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="497" y1="320" x2="497" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="582" y1="320" x2="582" y2="280" stroke="#565653" stroke-width="1.5"/>
  <line x1="667" y1="320" x2="667" y2="280" stroke="#565653" stroke-width="1.5"/>

<line x1="52" y1="240" x2="105" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="137" y1="240" x2="105" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="222" y1="240" x2="275" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="307" y1="240" x2="275" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="412" y1="240" x2="465" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="497" y1="240" x2="465" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="582" y1="240" x2="635" y2="200" stroke="#565653" stroke-width="1.5"/>
  <line x1="667" y1="240" x2="635" y2="200" stroke="#565653" stroke-width="1.5"/>

<line x1="105" y1="160" x2="185" y2="120" stroke="#565653" stroke-width="1.5"/>
  <line x1="275" y1="160" x2="185" y2="120" stroke="#565653" stroke-width="1.5"/>
  <line x1="465" y1="160" x2="535" y2="120" stroke="#565653" stroke-width="1.5"/>
  <line x1="635" y1="160" x2="535" y2="120" stroke="#565653" stroke-width="1.5"/>

<line x1="185" y1="80" x2="360" y2="50" stroke="#565653" stroke-width="1.5"/>
  <line x1="535" y1="80" x2="360" y2="50" stroke="#565653" stroke-width="1.5"/>
</svg>

The red solid nodes (`h(C)`, `h(AB)`, `h(EF,GH)`) are the proof: the sibling hashes that the verifier needs but cannot compute on their own. The red dashed nodes show what the verifier *can* compute themselves once they have `D` and the proof.

The verification:

1. The verifier knows `D` (the item being proved) and the root (already trusted). They receive the proof: three hashes.
2. They compute `h(D)` themselves from `D`.
3. They concatenate `h(C)` from the proof with their own `h(D)` and hash the result, producing `h(CD)`.
4. They concatenate `h(AB)` from the proof with `h(CD)` and hash again, producing `h(AB,CD)`.
5. They concatenate `h(AB,CD)` with `h(EF,GH)` from the proof and hash one more time, producing a value.
6. If that final value equals the root they already trusted, `D` is in the set. If it doesn't, `D` is not in the set (or the proof is forged).

Three hashes travel from the prover. The verifier does four hash operations: one to turn `D` into `h(D)`, then three more to combine up the path to the root. The other five leaves and their hashes never appear. For a tree with 1,000 leaves, the proof would be 10 hashes. For a tree with one billion leaves, the proof would still only be 30 hashes. That logarithmic scaling is the entire trick.

The verifier doesn't need to trust the prover. The hash function is doing the trust work: if the prover sends a wrong sibling hash, the final computed value won't match the root, and the proof fails. If the prover lies about which item they're proving (claims `D` was in the set when it wasn't), they can't construct a sequence of hashes that mathematically rolls up to the trusted root without breaking the hash function itself. This is the hash function's collision resistance: because finding two different inputs that produce the same hash is computationally infeasible, the prover cannot forge a path that leads to the real root.

## Why this matters for blockchains

A blockchain is going to need a way to commit to large amounts of data, often thousands of items at a time, using a single short value. The reason is bandwidth and storage: not every participant in the network can afford to download everything. They need a way to ask "is my piece in there?" and get a cheap, verifiable yes-or-no answer.

The Merkle tree is exactly the structure that solves this. The chain summarises a large collection of data with a single root hash. Anyone who has that root can later be convinced that one specific item belongs to that collection, with a tiny proof that scales with the depth of the tree rather than the size of the collection. However large the collection grows, the proof barely grows with it.

You don't need to memorise where exactly this shows up yet. The lessons on Bitcoin will show you the first one: a single 32-byte value sitting inside every block that summarises every transaction inside it. From there, the same primitive turns up in cross-chain communication, in scaling protocols, in lightweight wallet designs, and in many other places the rest of the course will visit. The pattern to recognise: wherever a system needs to commit to a large set with a small hash and prove individual membership efficiently, there's a Merkle tree underneath.

## FAQ

### How do I prove one item is in a huge list without sending the list?

A Merkle proof. The list is hashed into a tree whose top value, the Merkle root, depends on every item. To prove one item belongs, send only the sibling hashes along its path. The verifier recomputes the root from the item and those siblings and compares. Forging a path means finding a hash collision.

### How big is a Merkle proof?

The depth of the tree. Ten hashes for a thousand leaves, 30 for a billion.

### What does a Merkle root commit to?

Every leaf underneath it, in 32 bytes. Change one bit of any item and the root changes, so two parties comparing roots know immediately whether they hold the identical set.

### Why do blockchains use Merkle trees?

Not every participant can afford to download everything. One root hash commits to thousands of items, and a tiny proof answers whether a specific item is in there.

---

# Cryptographic primitives - Test

Source: https://academy.redduck.io/courses/blockchain-basics/cryptography/cryptographic-primitives

## Before you start:

This test checks whether you actually understand the cryptography building blocks (hashes, encoding, encryption, key pairs) or just remember the words. Several questions deliberately offer plausible-sounding wrong answers, the kind a reader who skimmed the lessons would pick. Read each option carefully before answering.

---

# What is blockchain

Source: https://academy.redduck.io/courses/blockchain-basics/the-blockchain-idea/what-is-blockchain

> You now have the cryptographic primitives. Hashes that uniquely identify data, keys that prove identity, signatures that bind authorship to a message, Merkle trees that commit to large sets in 32 bytes. Standing alone, none of these is a blockchain. A blockchain is what happens when you arrange those primitives in a very specific way to solve a very specific problem. This lesson builds the structure up from scratch, using only the parts you already have.

## The problem we're solving

You want a shared record of facts. A list of "this happened, then this happened, then this happened." Many parties around the world need to agree on the contents of that list. None of them trusts any of the others. There is no central authority you can all defer to.

That's the entire problem. Build a list of facts that everyone can read, anyone can append to, and nobody can secretly edit. With no central authority.

It sounds impossible at first. If everyone has their own copy of the list, who's right when two copies disagree? If anyone can write to it, what stops a bad actor from rewriting history? If there's no central operator, how does the system decide which version of reality is the real one?

The blockchain is the answer to all of those questions at once. It works because of a few simple structural choices, layered one on top of the next.

## The structure, built up

Step one. You need to record a list of items, in order. The word items is just a placeholder. We haven't said yet what they are. The list grows over time as new items are added.

A naive list would look like this:

```
item 1
item 2
item 3
item 4
...
```

This works for one person on one computer. The problem starts when the list is shared. If you mail me a copy and I quietly change item 2 to say something different, you have no way to tell from my copy that anything is wrong. The list has no built-in defence against tampering.

Step two. Apply what you learned about hashing. Group items into batches called **blocks**, and each block carries the hash of the previous block. Tampering with any historical block changes its hash, which means the next block's "previous hash" field no longer matches, which means the chain breaks. Anyone holding the chain can detect this by recomputing one hash per block.

```
[Block 1: items]  →  [Block 2: items, prev_hash]  →  [Block 3: items, prev_hash]  →  ...
```

This gives you a tamper-evident sequence. Each block points back at the one before it via a hash. This is where the name comes from: blocks, linked into a chain.

Step three. Apply what you learned about Merkle trees. Inside each block, the items don't have to be stored as a flat list. They can be summarised by a single Merkle root, also 32 bytes, that commits to every item in the block. The block's header contains just this root plus the previous-block hash plus a few small bits of metadata. The result is a block header that's only around 80 bytes even when the block contains thousands of items.

<svg role="img" viewBox="0 0 720 300" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Blocks N-1, N, and N+1 linked by prev_hash and merkle_root</title><desc>Three blocks in a row, each with a hash, prev_hash, merkle_root, timestamp, and a list of items. Arrows show each block's prev_hash pointing back to the previous block's hash, chaining them together.</desc>

<defs>

<marker id="arr31" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">

<path d="M 0 0 L 10 5 L 0 10 z" fill="#ed4937"/>

</marker>

</defs>

<!-- Block N-1 hash label -->

<text x="120" y="35" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937">hash: a3f8...</text>

<!-- Block N-1 -->

<rect x="20" y="50" width="200" height="200" fill="#e0deda" stroke="#000000" stroke-width="2"/>

<rect x="20" y="50" width="200" height="40" fill="#ed4937" stroke="#000000" stroke-width="2"/>

<text x="120" y="75" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Block N-1</text>

<text x="120" y="110" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">prev_hash: ...</text>

<text x="120" y="128" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">merkle_root: 8a3f...</text>

<text x="120" y="146" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">timestamp</text>

<line x1="35" y1="160" x2="205" y2="160" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>

<text x="120" y="180" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">item</text>

<text x="120" y="198" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">item</text>

<text x="120" y="216" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">item</text>

<text x="120" y="234" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">...</text>

<!-- Block N hash label -->

<text x="360" y="35" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937">hash: b7c2...</text>

<!-- Block N -->

<rect x="260" y="50" width="200" height="200" fill="#e0deda" stroke="#000000" stroke-width="2"/>

<rect x="260" y="50" width="200" height="40" fill="#ed4937" stroke="#000000" stroke-width="2"/>

<text x="360" y="75" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Block N</text>

<text x="360" y="110" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937">prev_hash: a3f8...</text>

<text x="360" y="128" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">merkle_root: b7c2...</text>

<text x="360" y="146" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">timestamp</text>

<line x1="275" y1="160" x2="445" y2="160" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>

<text x="360" y="180" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">item</text>

<text x="360" y="198" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">item</text>

<text x="360" y="216" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">item</text>

<text x="360" y="234" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">...</text>

<!-- Block N+1 hash label -->

<text x="600" y="35" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937">hash: d4e9...</text>

<!-- Block N+1 -->

<rect x="500" y="50" width="200" height="200" fill="#e0deda" stroke="#000000" stroke-width="2"/>

<rect x="500" y="50" width="200" height="40" fill="#ed4937" stroke="#000000" stroke-width="2"/>

<text x="600" y="75" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Block N+1</text>

<text x="600" y="110" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937">prev_hash: b7c2...</text>

<text x="600" y="128" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">merkle_root: d4e9...</text>

<text x="600" y="146" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">timestamp</text>

<line x1="515" y1="160" x2="685" y2="160" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>

<text x="600" y="180" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">item</text>

<text x="600" y="198" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">item</text>

<text x="600" y="216" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">item</text>

<text x="600" y="234" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">...</text>

<!-- Arrows: from each block's prev_hash field, pointing back to the previous block -->

<line x1="260" y1="110" x2="222" y2="110" stroke="#ed4937" stroke-width="2" marker-end="url(#arr31)"/>

<line x1="500" y1="110" x2="462" y2="110" stroke="#ed4937" stroke-width="2" marker-end="url(#arr31)"/>

<text x="360" y="285" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">Each block's prev_hash field points back to the previous block's hash.</text>

</svg>

Step four. Make every party in the network hold a full copy of the chain. Instead of a few servers or a centralised database, thousands of independent computers each run the same software, each hold the same data, and each verify every new block against the same rules. The picture below shows the same chain replicated across many machines.

<svg role="img" viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Same blockchain copied across Node 1 to Node 5, and thousands more</title><desc>Five boxes labeled Node 1 through Node 5 each show the same chain of blocks, with a note for thousands more nodes. Every node holds its own copy of the same chain, so tampering with one copy doesn't change any other.</desc>
  <!-- Node 1 -->
  <rect x="40" y="20" width="200" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="140" y="42" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Node 1</text>
  <rect x="60" y="55" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="90" y="55" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="120" y="55" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="150" y="55" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="180" y="55" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <line x1="85" y1="65" x2="90" y2="65" stroke="#565653" stroke-width="1"/>
  <line x1="115" y1="65" x2="120" y2="65" stroke="#565653" stroke-width="1"/>
  <line x1="145" y1="65" x2="150" y2="65" stroke="#565653" stroke-width="1"/>
  <line x1="175" y1="65" x2="180" y2="65" stroke="#565653" stroke-width="1"/>

<!-- Node 2 -->
  <rect x="280" y="20" width="200" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="380" y="42" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Node 2</text>
  <rect x="300" y="55" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="330" y="55" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="360" y="55" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="390" y="55" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="420" y="55" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <line x1="325" y1="65" x2="330" y2="65" stroke="#565653" stroke-width="1"/>
  <line x1="355" y1="65" x2="360" y2="65" stroke="#565653" stroke-width="1"/>
  <line x1="385" y1="65" x2="390" y2="65" stroke="#565653" stroke-width="1"/>
  <line x1="415" y1="65" x2="420" y2="65" stroke="#565653" stroke-width="1"/>

<!-- Node 3 -->
  <rect x="520" y="20" width="180" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="610" y="42" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Node 3</text>
  <rect x="535" y="55" width="22" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="562" y="55" width="22" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="589" y="55" width="22" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="616" y="55" width="22" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="643" y="55" width="22" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="670" y="55" width="22" height="20" fill="#e0deda" stroke="#000000"/>
  <line x1="557" y1="65" x2="562" y2="65" stroke="#565653" stroke-width="1"/>
  <line x1="584" y1="65" x2="589" y2="65" stroke="#565653" stroke-width="1"/>
  <line x1="611" y1="65" x2="616" y2="65" stroke="#565653" stroke-width="1"/>
  <line x1="638" y1="65" x2="643" y2="65" stroke="#565653" stroke-width="1"/>
  <line x1="665" y1="65" x2="670" y2="65" stroke="#565653" stroke-width="1"/>

<!-- Node 4 -->
  <rect x="60" y="160" width="200" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="160" y="182" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Node 4</text>
  <rect x="80" y="195" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="110" y="195" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="140" y="195" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="170" y="195" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="200" y="195" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <line x1="105" y1="205" x2="110" y2="205" stroke="#565653" stroke-width="1"/>
  <line x1="135" y1="205" x2="140" y2="205" stroke="#565653" stroke-width="1"/>
  <line x1="165" y1="205" x2="170" y2="205" stroke="#565653" stroke-width="1"/>
  <line x1="195" y1="205" x2="200" y2="205" stroke="#565653" stroke-width="1"/>

<!-- Node 5 -->
  <rect x="300" y="160" width="200" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="400" y="182" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Node 5</text>
  <rect x="320" y="195" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="350" y="195" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="380" y="195" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="410" y="195" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <rect x="440" y="195" width="25" height="20" fill="#e0deda" stroke="#000000"/>
  <line x1="345" y1="205" x2="350" y2="205" stroke="#565653" stroke-width="1"/>
  <line x1="375" y1="205" x2="380" y2="205" stroke="#565653" stroke-width="1"/>
  <line x1="405" y1="205" x2="410" y2="205" stroke="#565653" stroke-width="1"/>
  <line x1="435" y1="205" x2="440" y2="205" stroke="#565653" stroke-width="1"/>

<!-- ... -->
  <text x="600" y="200" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">thousands more</text>

<text x="360" y="270" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">Every node holds its own copy of the same chain. Tampering with one copy doesn't change any other.</text>
</svg>

This is the second word in "blockchain network." The chain is the data structure. The network is the set of machines that all hold the same copy of it.

## What you've actually built

Stack these four moves together and look at what falls out.

A shared record exists, because every node holds it.

Tampering with the past is detectable, because each block's hash binds it to the one before, and any change to any historical block invalidates every block after it.

Adding to the present is easy, because anyone running the software can produce a new block that points at the current tip of the chain.

Disagreement can still happen, because two nodes can produce two different new blocks at roughly the same time. The mechanism that decides which one becomes canonical is called **consensus**, and it gets its own lesson later.

That's a blockchain. A replicated, append-only, hash-linked sequence of blocks, where every node holds the same copy, every block proves the integrity of every previous block, and the structure as a whole is tamper-evident even though no central authority maintains it.

Everything else you'll meet in the rest of this course, transactions, smart contracts, tokens, mining, staking, dApps, NFTs, DeFi, all of it, is what people put inside the items, or what they build on top of the chain once they trust its contents. The chain itself is just the structure described above. Once you see that, the mystery is gone.

## FAQ

### If everyone keeps their own copy of a blockchain, what stops someone from editing an old block?

Each block stores the hash of the block before it, a fixed-length fingerprint of its contents. Change anything in an old block and its hash changes, so the next block's previous-hash field stops matching and the break is visible. Every node holds its own copy too, so editing one copy changes nothing anywhere else.

### What is inside a block?

A header and a batch of items. The header holds the previous block's hash, a 32-byte Merkle root covering every item, and a timestamp. That keeps the header near 80 bytes even when the block holds thousands of items.

### How do thousands of computers with no one in charge end up with the same chain?

Every node checks each new block against the same rules, so they all reach the same verdict alone. Consensus settles the rare tie.

### Where is a blockchain stored?

On every node at once. None of the copies is the original.

---

# Blockchain vs database

Source: https://academy.redduck.io/courses/blockchain-basics/the-blockchain-idea/blockchain-vs-database

> The previous lesson built up a blockchain from scratch. A reasonable reaction at this point is "interesting, but isn't that just a database?" The answer is yes, structurally. But the differences are exactly what makes a blockchain useful for the small set of things it's actually good at.

## A blockchain is a ledger

Databases and ledgers are two different tools, and both are old. A database is a general store of records you can add to, change, and erase. A ledger is an append-only record of events, the format banks and accountants have kept for centuries, where entries are only ever added and the earlier history stays intact. Append-only is part of what defines a ledger, the way the category has always worked, so in a blockchain it is a starting assumption built into the design. A blockchain sits in this second category. That is what makes the database comparison worth doing, because most developers reach for a database by habit, and also what limits that comparison, because a blockchain is a ledger first, built to run without a trusted keeper.

## They are both databases, in the loose sense

A blockchain stores records. A database stores records. Both let you write new records and read existing ones. Both have to keep their data consistent under some definition of consistency. Both have to handle many users at once.

Beyond those basics, the similarity ends. Almost every other property they have is different, and most of those differences are deliberate. Below is the comparison every developer has to internalise before the rest of this course makes sense.

<svg role="img" viewBox="0 0 720 480" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Traditional database vs blockchain comparison table across 8 properties</title><desc>A table compares traditional databases and blockchains across eight properties: operations, operator, storage, read access, write rules, speed, cost per write, and what happens if the operator fails. Databases allow create, update, and delete by one trusted party on one server, while blockchains only append, run on every node with no single operator, and the network keeps running even if a node fails.</desc>
  <!-- Column headers -->
  <rect x="20" y="20" width="240" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="140" y="50" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Property</text>

<rect x="260" y="20" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="370" y="50" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Traditional database</text>

<rect x="480" y="20" width="220" height="50" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="590" y="50" text-anchor="middle" font-size="14" fill="#ffffff" font-weight="bold">Blockchain</text>

<!-- Row 1: Operations -->
  <rect x="20" y="70" width="240" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="30" y="100" font-size="13" fill="#000000" font-weight="bold">Operations</text>

<rect x="260" y="70" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="370" y="100" text-anchor="middle" font-family="monospace" font-size="12" fill="#000000">CREATE UPDATE DELETE</text>

<rect x="480" y="70" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="100" text-anchor="middle" font-family="monospace" font-size="12" fill="#000000">APPEND only</text>

<!-- Row 2: Operator -->
  <rect x="20" y="120" width="240" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="30" y="150" font-size="13" fill="#000000" font-weight="bold">Operator</text>

<rect x="260" y="120" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="370" y="150" text-anchor="middle" font-size="12" fill="#000000">one trusted party</text>

<rect x="480" y="120" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="150" text-anchor="middle" font-size="12" fill="#000000">nobody</text>

<!-- Row 3: Storage -->
  <rect x="20" y="170" width="240" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="30" y="200" font-size="13" fill="#000000" font-weight="bold">Storage</text>

<rect x="260" y="170" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="370" y="200" text-anchor="middle" font-size="12" fill="#000000">one server (or cluster)</text>

<rect x="480" y="170" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="200" text-anchor="middle" font-size="12" fill="#000000">every node, fully replicated</text>

<!-- Row 4: Read access -->
  <rect x="20" y="220" width="240" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="30" y="250" font-size="13" fill="#000000" font-weight="bold">Read access</text>

<rect x="260" y="220" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="370" y="250" text-anchor="middle" font-size="12" fill="#000000">whoever the admin allows</text>

<rect x="480" y="220" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="250" text-anchor="middle" font-size="12" fill="#000000">anyone in the world</text>

<!-- Row 5: Write rules -->
  <rect x="20" y="270" width="240" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="30" y="300" font-size="13" fill="#000000" font-weight="bold">Write rules</text>

<rect x="260" y="270" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="370" y="300" text-anchor="middle" font-size="12" fill="#000000">application code, server-side</text>

<rect x="480" y="270" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="300" text-anchor="middle" font-size="12" fill="#000000">protocol + smart contracts</text>

<!-- Row 6: Speed -->
  <rect x="20" y="320" width="240" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="30" y="350" font-size="13" fill="#000000" font-weight="bold">Speed</text>

<rect x="260" y="320" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="370" y="350" text-anchor="middle" font-size="12" fill="#000000">millions of writes/sec</text>

<rect x="480" y="320" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="350" text-anchor="middle" font-size="12" fill="#000000">a few to a few thousand/sec</text>

<!-- Row 7: Cost -->
  <rect x="20" y="370" width="240" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="30" y="400" font-size="13" fill="#000000" font-weight="bold">Cost per write</text>

<rect x="260" y="370" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="370" y="400" text-anchor="middle" font-size="12" fill="#000000">fractions of a cent</text>

<rect x="480" y="370" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="400" text-anchor="middle" font-size="12" fill="#000000">cents to dollars</text>

<!-- Row 8: Failure mode -->
  <rect x="20" y="420" width="240" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="30" y="450" font-size="13" fill="#000000" font-weight="bold">If operator fails</text>

<rect x="260" y="420" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="370" y="450" text-anchor="middle" font-size="12" fill="#000000">system goes down</text>

<rect x="480" y="420" width="220" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="450" text-anchor="middle" font-size="12" fill="#000000">network keeps running</text>
</svg>

Walk down the rows. Almost every cell on the database side describes flexibility, convenience, and speed. Almost every cell on the blockchain side describes constraint, public exposure, and the absence of a controlling party. These are not accidents. The constraints are the product.

## Append-only is a hard rule

In a normal database, the four basic operations are create, read, update, and delete. The update and delete are the dangerous ones, because they let you change or erase the past. They're also indispensable. You change a row when a user updates their profile. You delete a row when a user closes their account. The database doesn't keep an audit trail of every prior version unless you build one yourself.

A blockchain has only two of those four operations. You can write new entries. You can read any entry, current or historical. You cannot update an entry. You cannot delete one. The full history is part of the system by design rather than a feature you opt into.

This sounds limiting, and in most use cases it is. For the small set of use cases where the ability to prove "this happened, exactly this way, and has not been changed since" is more valuable than the ability to keep things tidy, append-only is the entire point. Once a record is in a block deep enough in the chain, no party in the system can convincingly claim it isn't.

## Atomicity carries over, almost everything else doesn't

Traditional databases have a property called atomicity. A transaction in database terms is a bundle of changes that either all happen or all don't happen. You can't end up with a partial update where money got debited from one account but never credited to another. This is one of the four properties usually grouped under ACID, and it's the only one of the four that translates cleanly to the blockchain world.

Blockchain transactions are atomic in exactly the same sense. When a block is added to the chain, every transaction in that block either applies completely or doesn't apply at all. Half-applied transactions don't exist. If a blockchain call would do five things and the fifth one fails, the first four are unwound and the whole call has no effect on the chain's state.

The other three letters of ACID (consistency, isolation, durability) all have analogues in blockchain systems, but the analogues are subtle enough to deserve their own treatment. The point for this lesson is just that atomicity is the one operational property a database developer can carry over without modification.

## Transparency is the default

Every record in a public blockchain is readable by every node, and through the nodes by every user, and ultimately by anyone in the world who wants to look. Open any block explorer (a website that displays the contents of a chain in human-readable form) and you can see every transaction that has ever happened, every balance of every account, every smart-contract call, every event.

There is no "private mode," no "this row is only visible to its owner," no permission system the database operator can use to hide some rows from some users. The chain stores everything in plain view, and the only thing that protects a user's identity is the fact that an address on the chain is a meaningless-looking string of characters rather than a name.

This is a feature for some use cases and a non-starter for others. A public blockchain is the worst possible place to store medical records, internal company data, or anything subject to data-protection regulations. It's an excellent place to store the public history of how a financial protocol has worked over the last decade, because anyone in the world can verify the claims of the protocol's operators by reading the chain directly.

## Replication is built in

A traditional database lives on one server or one cluster of servers, run by one party. Replication exists, but it's an optimisation that you opt into and configure yourself, with replicas that ultimately trust a primary.

A blockchain has replication built into the structure. Every full node holds the entire history. There is no primary. There is no opt-in. If half the nodes in the network went offline tomorrow, the other half would continue running the chain and nothing would be lost. The data is durable because it exists in thousands of places at once, rather than because someone is paying for a backup tape.

The price of this durability is that the network can never go faster than the slowest agreement step among its participants. Every node has to validate every block, every node has to store the whole chain, every node has to keep up with every new transaction. This is the deepest reason blockchains are slow and expensive compared to databases. The slowness is the cost of not trusting any one party.

## When to pick which

A normal database is the right choice for almost every problem most software developers encounter. It's faster. It's cheaper. It's flexible. It's private by default. It has decades of tooling and operational knowledge built around it. Reaching for a blockchain when a database would do the job is a sign of confusion. The next several lessons explain the cases where the trade is genuinely worth it.

A blockchain is the right choice when you need to prove things about state to parties who don't trust each other, or when you can't have a single operator. It's also right when transparency is more valuable than privacy, or when the cost of a single point of failure is unacceptable. That set of cases is small in absolute terms and gigantic in dollars and consequence.

## FAQ

### Isn't a blockchain just a slow, complicated database?

A blockchain is a ledger, an append-only record of events, the format banks and accountants have kept for centuries. It stores records and answers reads like a database. Everything else differs on purpose: append-only, public, replicated across thousands of nodes, no operator.

### Can I delete something once it is on the chain?

No. There is no delete operation and no update operation. Only write and read.

### My call does five things and the fifth fails. Do the first four still happen?

No. Blockchain transactions are atomic, so the whole call is unwound and leaves nothing behind. Atomicity is the one ACID property that carries over from databases unchanged.

### Is a public blockchain a safe place for medical records?

No. Every record is readable by every node, and through them by anyone in the world. There is no private mode and no operator who can hide rows. The only cover a user gets is an address that carries no name.

### How much slower is a blockchain than a database?

A database does millions of writes per second at a fraction of a cent each. A public chain does a few to a few thousand, at cents to dollars a write.

---

# Why we need consensus

Source: https://academy.redduck.io/courses/blockchain-basics/the-blockchain-idea/why-we-need-consensus

> The previous lesson made a strong claim. A blockchain has no operator. Nobody runs it. Yet it somehow holds a consistent record across thousands of independent machines, all of whom can write to it. Forget the cryptography for a moment and just focus on the question. How is that possible at all? The mechanism that makes it possible is called consensus, and it is the single most subtle problem in distributed systems. This lesson explains why the problem is hard, why the obvious solutions don't work, and what the actual answer looks like at the level of "how it works" rather than "which exact algorithm."

## The problem, stated plainly

Imagine a few thousand computers spread across the world. None of them trust any of the others. There is no shared boss, no central server, no court of appeal. New facts come in: new transactions, new blocks, and other new data. Every machine has to decide what to add to its copy of the record. And every machine has to end up with the same record as every other one.

Worse, some of those computers will lie. They'll propose blocks that don't follow the rules. They'll send different versions of "the truth" to different peers. They'll go offline at convenient moments. They'll team up to push fake history. The network has no way to identify the liars in advance, because anyone can join.

The system has to produce one agreed-upon record anyway. Not most of the time. All of the time, forever, as long as the network exists. With strangers, in public, with hostile actors actively trying to break it.

This is the consensus problem, and for most of the history of computer science, it was considered either impossible or only solvable inside small, trusted groups. Bitcoin's design in 2008 was the first time anyone showed it could be solved at internet scale, with anonymous participants, without any pre-existing trust between them.

## The Byzantine Generals framing

The classic way to describe this problem comes from a 1982 thought experiment about generals besieging a city. Several generals surround the city. They have to agree on a single coordinated action: either all attack at dawn or all retreat. Any half-and-half outcome is a disaster. They can only communicate by messengers, and some of the generals are traitors who will lie to make the operation fail.

<svg role="img" viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Byzantine Generals problem: four generals around a city, one a traitor</title><desc>Four generals, A, B, C, and D, surround a city and must all agree to attack or retreat together. Dashed lines show messages passing between every pair of generals and the city, while General C, marked in red, is the traitor who may lie.</desc>
  <!-- The city -->
  <rect x="290" y="135" width="140" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="160" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">The city</text>
  <text x="360" y="180" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">all-or-nothing attack</text>

<!-- General 1: honest -->
  <rect x="40" y="30" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="100" y="55" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">General A</text>
  <text x="100" y="75" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">honest</text>

<!-- General 2: honest -->
  <rect x="560" y="30" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="620" y="55" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">General B</text>
  <text x="620" y="75" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">honest</text>

<!-- General 3: traitor -->
  <rect x="40" y="240" width="120" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="100" y="265" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">General C</text>
  <text x="100" y="285" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">traitor</text>

<!-- General 4: honest -->
  <rect x="560" y="240" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="620" y="265" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">General D</text>
  <text x="620" y="285" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">honest</text>

<!-- Messages between all -->
  <line x1="160" y1="60" x2="290" y2="155" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <line x1="560" y1="60" x2="430" y2="155" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <line x1="160" y1="270" x2="290" y2="175" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <line x1="560" y1="270" x2="430" y2="175" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>

<line x1="160" y1="60" x2="560" y2="60" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <line x1="160" y1="270" x2="560" y2="270" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <line x1="100" y1="90" x2="100" y2="240" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <line x1="620" y1="90" x2="620" y2="240" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>

<text x="360" y="320" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">All generals exchange messages. Some are honest, some lie. They still have to agree on one plan.</text>
</svg>

The generals are stand-ins for nodes on a network. The messengers are network connections. The traitors are malicious or buggy participants. The "agree on attack or retreat" is "agree on which block comes next." Replace the army metaphor with database servers and you have the actual problem facing every blockchain on day one.

The hard part is not "tell honest from dishonest." The hard part is that the honest participants can't tell either, because they only see the messages they receive, and a clever traitor can send different messages to different recipients. Two honest generals can both be confident they've reached the right answer and still end up with opposite plans.

Any system that wants to make decisions without a trusted referee has to solve some version of this problem.

## Why the obvious solutions fail

The first thing most developers reach for is **voting**. Each participant proposes their version of the next block. Everyone votes. Majority wins.

This breaks immediately on an open network, for one reason. There is no way to count "participants" when anyone can create a thousand fake identities for the price of cloud rental. A vote becomes meaningless the moment one party can show up as 51% of the voters by themselves. Creating unlimited fake identities to manipulate a vote is called a **Sybil attack**, and it's the reason every naive consensus scheme fails the moment it touches the open internet.

The second instinct is **first writer wins**. Whoever submits a block first gets to write it. Done.

This breaks because there is no shared clock. Two participants on opposite sides of the planet can each be the "first" from their own perspective, and the network has no way to decide between them. Worse, an attacker with a faster connection can systematically race honest participants and always win.

The third instinct is **fall back to a trusted server**. Let one party be the tiebreaker, just for the cases where the network can't agree.

This works, and it's how most databases handle replication. It's also exactly what a blockchain is trying to avoid. The moment you have a trusted tiebreaker, the system has an operator again. Everything that made the structure interesting is gone.

So voting fails because participants are fakeable. First-wins fails because there is no shared clock. Trusted-server fails because it defeats the purpose. The problem is genuinely hard.

## The actual answer: make participation cost something

The breakthrough is one sentence long. **If you want to vote, you have to spend something real, and you only get to vote once per unit of what you spent.**

Now think about what that does. An attacker can still create a thousand fake voters, but each one has to actually pay the cost. If the cost is large enough, paying it a thousand times is more expensive than the attack is worth. Sybil attacks become economically infeasible rather than technically impossible. The system isn't preventing fake identities. It's making them too expensive to bother with.

What counts as "something real"? Two main answers have shown up in the past two decades. Both use the same general logic.

**Computation.** Each "vote" requires the voter to demonstrate they did a large amount of useless arithmetic. The arithmetic itself is wasted, but the energy bill is real, and energy costs the same regardless of who's paying. An attacker with a thousand fake identities has to pay a thousand times the electricity. This is the family of mechanisms called **proof of work**.

**Stake.** Each "vote" requires the voter to lock up an amount of the network's own currency, with the rule that if they vote dishonestly they lose it. An attacker with a thousand fake identities has to lock up a thousand times the stake, and any visible misbehaviour costs them all of it. This is the family of mechanisms called **proof of stake**.

The mechanics of either family are detailed enough to need their own treatment. Later chain-specific lessons cover them. What you need to take from this lesson is the underlying logic, which is the same for both. Voting in an open consensus system has to be expensive enough that buying enough votes to attack the network costs more than the attack is worth. Once you have that property, the network can hold honest agreement among strangers indefinitely, with no operator and no trusted referee.

## What this buys you

A consensus mechanism with a real participation cost gives the network three properties that no amount of clever programming can produce in its absence.

**One canonical history, agreed by all.** Two honest nodes will converge on the same view of the chain over time, even though they started with no shared trust and exchange only messages.

**Resistance to majority attackers.** The network stays safe as long as attacking it costs more than the attacker could profit from the attack.

**Permissionless participation.** Anyone with the resources to pay the cost can participate. Nobody is keeping a list of who is allowed to vote. The cost itself is the gate.

Those three properties, together, are what a blockchain is actually selling. Cryptography gives it a tamper-evident structure. Consensus gives it the ability to operate that structure without an operator. Without either piece, the other is useless.

## The agreement mechanism you now understand

You now have the agreement mechanism that keeps a blockchain running: a way for mutually untrusted strangers to converge on one shared history by making dishonest participation cost more than it can earn. That is the piece that lets the tamper-evident structure from cryptography operate with no operator and no trusted referee.

## FAQ

### Why can't the network just vote on the next block?

Anyone can join, so one party can rent cloud machines and appear as thousands of voters. Counting identities means nothing when identities are free.

### What is a Sybil attack?

Creating unlimited fake identities to control a vote.

### How do strangers who trust nobody agree on one history?

Voting has to cost something real, so attacking costs more than it could earn. Proof of work ties the cost to electricity. Proof of stake ties it to currency you lock up and lose if you cheat.

### What is the Byzantine Generals problem?

A 1982 thought experiment. Generals around a city must all attack or all retreat, and any split ruins them. They have only messengers, and traitors among them send different messages to different generals, so no honest general can tell who is lying. The generals are nodes.

### Had anyone solved consensus before Bitcoin?

Not at internet scale. Bitcoin's 2008 design was the first to hold consensus among anonymous strangers with no prior trust.

---

# Nodes and the network

Source: https://academy.redduck.io/courses/blockchain-basics/the-blockchain-idea/nodes-and-the-network

> Every lesson so far has referenced "nodes" doing things. Nodes hold copies of the chain. Nodes agree or disagree. Nodes vote in consensus. The word has appeared constantly without ever being defined. This lesson defines it. A node is a piece of software running the blockchain's protocol, and the network is the set of nodes all running the same software at the same time, talking to each other. What sounds like one sentence opens up into a surprisingly varied ecosystem of roles, communication patterns, and economic motivations for running anything at all.

## What a node is

At the simplest level, a node is a program. It runs on a computer somewhere in the world. It connects to other instances of the same program running on other computers. Together, they form the network that maintains the blockchain.

A node does four things, all of the time.

It **holds a copy** of the data the network has agreed on, which usually includes the whole history of the chain or some pruned version of it.

It **listens for new data** from other nodes: new blocks proposed, new transactions broadcast, new connection requests from peers it hasn't met before.

It **validates** everything it receives against the rules of the protocol. If something doesn't follow the rules, the node rejects it and refuses to relay it further.

It **forwards** the things it accepts to other nodes it's connected to, so that valid information propagates across the whole network within seconds.

That's the whole job. Hold the chain, listen, validate, forward. Every node on every blockchain in existence does some version of those four operations. The variations between blockchains are in what counts as "valid" and what the data looks like, but the role of the node is universal.

## The main kinds of node

Not every node does the same amount of work. Most blockchains distinguish at least three role types, and the distinctions matter for how the network as a whole behaves.

<svg role="img" viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Full node, block producer, and light node compared by holds, verifies, proposes blocks</title><desc>This table compares three node types across four columns: what they hold, what they verify, and whether they propose blocks. Full nodes and block producers each hold the entire chain history and state and verify everything independently, but only block producers propose blocks; light nodes hold only block headers, verify specific items via Merkle proofs, and do not propose blocks.</desc>
  <!-- Header -->
  <rect x="20" y="20" width="180" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="110" y="50" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Type</text>

<rect x="200" y="20" width="180" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="290" y="50" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Holds</text>

<rect x="380" y="20" width="180" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="470" y="50" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Verifies</text>

<rect x="560" y="20" width="140" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="630" y="50" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Proposes blocks</text>

<!-- Full node row -->
  <rect x="20" y="70" width="180" height="70" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="110" y="105" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Full node</text>

<rect x="200" y="70" width="180" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="290" y="100" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">the entire chain</text>
  <text x="290" y="118" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">history + state</text>

<rect x="380" y="70" width="180" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="470" y="100" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">everything,</text>
  <text x="470" y="118" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">independently</text>

<rect x="560" y="70" width="140" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="630" y="110" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">no</text>

<!-- Validator/producer row -->
  <rect x="20" y="140" width="180" height="70" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="110" y="170" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Block producer</text>
  <text x="110" y="190" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">(miner or validator)</text>

<rect x="200" y="140" width="180" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="290" y="170" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">the entire chain</text>
  <text x="290" y="188" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">history + state</text>

<rect x="380" y="140" width="180" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="470" y="170" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">everything,</text>
  <text x="470" y="188" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">independently</text>

<rect x="560" y="140" width="140" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="630" y="180" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">yes</text>

<!-- Light node row -->
  <rect x="20" y="210" width="180" height="70" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="110" y="245" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Light node</text>

<rect x="200" y="210" width="180" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="290" y="240" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">block headers</text>
  <text x="290" y="258" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">only</text>

<rect x="380" y="210" width="180" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="470" y="240" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">specific items via</text>
  <text x="470" y="258" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">Merkle proofs</text>

<rect x="560" y="210" width="140" height="70" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="630" y="250" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">no</text>
</svg>

**Full nodes** do the most work and provide the most independence. They download every block, verify every transaction inside it, and keep the entire history on disk. A full node trusts nobody. Whatever it accepts as valid, it accepted because it ran the rules itself. Anyone who wants the strongest possible guarantee that the chain they're seeing is correct runs a full node.

**Block producers** are full nodes with an extra role: they also propose new blocks. Depending on which consensus mechanism a chain uses, these are called miners or validators. The distinction is in how they earn the right to propose: by burning computation in the first case, by locking up stake in the second. Either way, the consensus mechanism from the previous lesson picks them through some costly process, they assemble a block, and the rest of the network either accepts or rejects what they propose. Block producers are usually a small fraction of total nodes.

**Light nodes** sit at the opposite end. They store only block headers instead of full blocks. When they need to verify that a specific transaction was included in a specific block, they ask a full node for a Merkle proof and check it locally. The Merkle-tree primitive from earlier in the course is exactly what makes this efficient. A light node running on a phone can verify membership in a chain that holds terabytes of history.

Which role fits you depends on what you want: a light node backs a fast wallet on a low-resource device, and a block producer earns the chain's rewards.

## How nodes find each other

A node starting up for the first time has a problem. It doesn't know any other nodes. There's no central directory. It needs peers to talk to, but how does it find them?

The answer is that most chains include a small list of well-known **bootstrap nodes** in their software, run by the project's developers or core community. When you start a node for the first time, it connects to one or more of those bootstraps and asks "who else is on the network?" The bootstrap responds with a list of peers it knows about. The new node picks some, connects to them, and asks them the same question. Within a few seconds, the new node has discovered dozens or hundreds of peers and the bootstrap is no longer needed.

This is the same pattern peer-to-peer networks have used for decades. The bootstrap is a starting point rather than a hub. Once a node is up and running, it doesn't depend on the bootstrap at all. The network is genuinely decentralised in steady state.

## How nodes talk

Once two nodes are connected, they communicate via a **gossip protocol**. The pattern is simple. When a node learns something new, a new transaction, a new block, it tells all the peers it's connected to. Each of those peers, if the information is new to them, tells all *their* peers. Within a few hops, almost every node in the network has heard the news. The cost to any individual node is small, but the total propagation is fast and reaches everyone.

A small refinement matters in practice. Nodes don't blindly send full data to each peer because that would waste bandwidth. Instead, they send a short **announcement** ("I have a new block with this hash, do you want it?"). If the receiving node already has it, it ignores the announcement. If not, it asks for the full content. This two-step pattern keeps the network from drowning in duplicate traffic.

The gossip protocol is why a transaction broadcast to a single node reaches the entire network within seconds, even though no node has a direct connection to most of the others. It's also why "broadcasting a transaction" doesn't require knowing anything about the network's structure. You hand the transaction to any node, and gossip propagation does the rest.

## Why anyone runs a node

The economic question matters. Running a full node costs real money: bandwidth, electricity, disk space, the maintenance time of a human operator. Why does anyone bother?

Three reasons cover most operators.

**Block producers** run nodes because the chain pays them to. Whoever produces a valid block earns the block's rewards, which is the primary financial incentive in any blockchain. This is the reason the network has any block producers at all.

**Service operators** run nodes because they sell access to them. Wallet providers, block explorers, indexing services, and the infrastructure layer for most decentralised applications all need nodes to talk to. They run their own, or they pay specialised companies to run them and expose the data via an API.

**Trust-minimising users** run nodes because they want maximum certainty that the chain they're seeing is correct. A user who runs their own full node trusts nobody else's interpretation of the rules. This is a minority of users in absolute numbers, but it's the population that keeps the network honest in steady state. If anyone tried to push a rule change, the trust-minimising operators would notice and refuse to accept it.

## What you now know about nodes

You now know what a node is, what kinds exist, how they find each other, and how they share information. Together with the cryptographic primitives, the blockchain's structure, and the consensus mechanism from earlier lessons, you now hold every component a blockchain runs on. What was an abstract word, "nodes," is now a concrete population of machines with distinct roles and real reasons to participate.

## FAQ

### What does a node do?

Four things, constantly. It holds a copy of the chain, listens for new blocks and transactions, validates them against the protocol rules, and forwards the valid ones.

### What is the difference between a full node, a light node, and a validator?

A full node downloads and verifies every block and stores the whole history. A block producer, called a miner or a validator depending on the chain, does all that and also proposes new blocks for the rewards. A light node keeps only block headers and checks a single transaction with a Merkle proof from a full node, so it runs on a phone.

### How does a brand-new node find peers when there is no directory?

Its software ships with a short list of bootstrap nodes run by the project's community. It connects to one, asks who else is out there, and asks the peers it gets back the same question. Seconds later it has plenty of peers and no longer needs the bootstrap.

### I sent my transaction to one node. How does the rest of the network hear about it?

Gossip. Each node passes anything new to its peers, and a few hops reach everyone. Nodes announce a hash first and send the full data only when asked.

### Do I need to run my own node?

No. Wallets, block explorers, and app infrastructure sell access to theirs. People run their own to avoid trusting anyone else's reading of the rules.

---

# Transaction flow

Source: https://academy.redduck.io/courses/blockchain-basics/the-blockchain-idea/transaction-flow

> By now you have all the parts. Cryptographic primitives that let identities exist and signatures bind authorship to messages. A hash-linked block structure that makes the past tamper-evident. A consensus mechanism that lets strangers agree without an operator. A network of nodes that hold copies, validate everything, and gossip new information to each other within seconds. None of these pieces is a blockchain by itself. This lesson takes the pieces and shows them assembling, in real time, into one operation: a single change to the chain, from the moment a user clicks something in their wallet to the moment that change is part of the permanent record. Every term used in this lesson is something you already understand. The lesson is where they snap together.

## The setup

Imagine a specific event. A user wants to make a change to the chain. The exact nature of the change doesn't matter for this lesson, because the flow is the same regardless. It could be a value transfer, a deployment of new code, an interaction with code already on the chain, anything.

The user has a wallet. The wallet holds the user's private key. The wallet also has access to one or more nodes on the network, either nodes the user is running themselves or nodes operated by a service provider. The chain is currently sitting at some height, with the most recent block being block N. The user is about to create the request that, eventually, will be included in block N+1 or later.

Here is what happens, in order.

## The eight steps

<svg role="img" viewBox="0 0 720 580" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Transaction flow: eight steps from signed request to new chain tip</title><desc>A vertical flowchart lists eight numbered steps of a blockchain transaction, connected top to bottom by arrows. It goes from the user constructing and signing a request, through wallet broadcast and network gossip, to a block producer assembling and broadcasting a new block, other nodes validating it, and the block becoming the new tip of the chain.</desc>
  <!-- Step 1 -->
  <rect x="30" y="20" width="660" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="60" y="50" font-size="14" fill="#000000" font-weight="bold">1.</text>
  <text x="100" y="42" font-size="13" fill="#000000" font-weight="bold">The user constructs the request</text>
  <text x="100" y="60" font-family="monospace" font-size="10" fill="#565653">describing what they want the chain to do</text>

<line x1="360" y1="70" x2="360" y2="85" stroke="#565653" stroke-width="2"/>
  <polygon points="355,80 360,90 365,80" fill="#565653"/>

<!-- Step 2 -->
  <rect x="30" y="90" width="660" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="60" y="120" font-size="14" fill="#000000" font-weight="bold">2.</text>
  <text x="100" y="112" font-size="13" fill="#000000" font-weight="bold">The wallet signs the request</text>
  <text x="100" y="130" font-family="monospace" font-size="10" fill="#565653">with the user's private key</text>

<line x1="360" y1="140" x2="360" y2="155" stroke="#565653" stroke-width="2"/>
  <polygon points="355,150 360,160 365,150" fill="#565653"/>

<!-- Step 3 -->
  <rect x="30" y="160" width="660" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="60" y="190" font-size="14" fill="#000000" font-weight="bold">3.</text>
  <text x="100" y="182" font-size="13" fill="#000000" font-weight="bold">The wallet broadcasts the signed request</text>
  <text x="100" y="200" font-family="monospace" font-size="10" fill="#565653">to a node it knows about</text>

<line x1="360" y1="210" x2="360" y2="225" stroke="#565653" stroke-width="2"/>
  <polygon points="355,220 360,230 365,220" fill="#565653"/>

<!-- Step 4 -->
  <rect x="30" y="230" width="660" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="60" y="260" font-size="14" fill="#000000" font-weight="bold">4.</text>
  <text x="100" y="252" font-size="13" fill="#000000" font-weight="bold">Gossip propagates it across the network</text>
  <text x="100" y="270" font-family="monospace" font-size="10" fill="#565653">every node holds it in a local buffer, having validated it</text>

<line x1="360" y1="280" x2="360" y2="295" stroke="#565653" stroke-width="2"/>
  <polygon points="355,290 360,300 365,290" fill="#565653"/>

<!-- Step 5 -->
  <rect x="30" y="300" width="660" height="50" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="60" y="330" font-size="14" fill="#ffffff" font-weight="bold">5.</text>
  <text x="100" y="322" font-size="13" fill="#ffffff" font-weight="bold">A block producer assembles a new block</text>
  <text x="100" y="340" font-family="monospace" font-size="10" fill="#ffffff">pulling pending requests from their buffer and structuring them</text>

<line x1="360" y1="350" x2="360" y2="365" stroke="#565653" stroke-width="2"/>
  <polygon points="355,360 360,370 365,360" fill="#565653"/>

<!-- Step 6 -->
  <rect x="30" y="370" width="660" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="60" y="400" font-size="14" fill="#000000" font-weight="bold">6.</text>
  <text x="100" y="392" font-size="13" fill="#000000" font-weight="bold">The producer broadcasts the new block</text>
  <text x="100" y="410" font-family="monospace" font-size="10" fill="#565653">via the same gossip network</text>

<line x1="360" y1="420" x2="360" y2="435" stroke="#565653" stroke-width="2"/>
  <polygon points="355,430 360,440 365,430" fill="#565653"/>

<!-- Step 7 -->
  <rect x="30" y="440" width="660" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="60" y="470" font-size="14" fill="#000000" font-weight="bold">7.</text>
  <text x="100" y="462" font-size="13" fill="#000000" font-weight="bold">Other nodes validate the block</text>
  <text x="100" y="480" font-family="monospace" font-size="10" fill="#565653">every signature, every rule, every consensus condition</text>

<line x1="360" y1="490" x2="360" y2="505" stroke="#565653" stroke-width="2"/>
  <polygon points="355,500 360,510 365,500" fill="#565653"/>

<!-- Step 8 -->
  <rect x="30" y="510" width="660" height="50" fill="#000000" stroke="#000000" stroke-width="2"/>
  <text x="60" y="540" font-size="14" fill="#ffffff" font-weight="bold">8.</text>
  <text x="100" y="532" font-size="13" fill="#ffffff" font-weight="bold">The block becomes the new tip of the chain</text>
  <text x="100" y="550" font-family="monospace" font-size="10" fill="#ffffff">every accepting node appends it. the change is now in the record</text>
</svg>

Now walk through each step with the components you already know.

### Step 1: The user constructs the request

In the user's wallet, they describe what they want to do. Send some value. Call some code. Whatever it is. The wallet builds a structured piece of data containing the user's identifier as the sender, the action being requested, a fee the user is offering to pay, and an anti-replay value that makes the request one-time-use. This structured data is the request, and at this point it exists only on the user's device.

No cryptography has happened yet. No network communication has happened yet. The request is just a draft.

### Step 2: The wallet signs the request

The wallet computes a signature over the entire request, using the user's private key. This is the same signing operation from the cryptography lessons. The signature is appended to the request.

Two things matter about this step. The signature mathematically proves the user approved the exact content of the request, and changing even one bit of the request after this point will invalidate the signature. From now on, the request is tamper-evident in the same sense the chain itself is, but at the level of a single message rather than a whole history.

### Step 3: The wallet broadcasts the signed request

The wallet sends the signed request to one node on the network. It might be a node the user is running themselves. More likely it's a node operated by a service provider, accessed through an API the wallet was configured to use.

The receiving node performs a basic check. Is the signature valid against the sender's public key? Does the request follow the rules of the protocol? Does the sender have what they need to make this change happen? If any of these checks fails, the node rejects the request and the flow stops here.

If the checks pass, the node adds the request to its local buffer of pending requests, sometimes called the **mempool**, and the flow continues.

### Step 4: Gossip propagates the request across the network

The receiving node announces the new request to its peers. Each peer that doesn't already have it asks for it, validates it themselves, and adds it to their own buffer. They then announce to *their* peers. Within seconds, most of the network is aware of the request and has independently validated that it's well-formed.

The validation happens at every node rather than just the first one. This is part of what makes the system trustless. The user does not have to trust the first node they sent the request to, because every other node will check the same things independently.

### Step 5: A block producer assembles a new block

Periodically, one participant earns the right under the consensus rules to propose the next block. This is the costly-participation mechanism from the consensus lesson: in proof-of-work systems it's whoever finishes the puzzle, in proof-of-stake systems it's whoever the protocol selects from the staked validators.

That participant looks at their local buffer of pending requests, picks a set of them, usually prioritising the ones offering the highest fees, and assembles them into a block. The block has the structure from earlier: a header containing the hash of the previous block plus a Merkle root summarising all the requests inside, then the requests themselves underneath.

This is the turning point. Up to here, the user's request was waiting in mempools as a pending item. From here, if everything goes right, it's about to become part of the permanent record.

### Step 6: The producer broadcasts the new block

The producer sends the assembled block out via the same gossip mechanism that carried the original request. The announcement-then-fetch pattern keeps bandwidth manageable. Within seconds, most nodes in the network have the new block.

### Step 7: Other nodes validate the block

Every node receiving the block checks it independently. They verify the block's structure. They re-verify every signature on every request in the block. They check that the block follows the consensus rules (the producer was actually entitled to propose, the work or stake was properly demonstrated).

This step is the deepest reason a blockchain works at all. The validation is replicated across every node. No single party is trusted to enforce the rules. If the proposer tried to cheat, the rest of the network rejects the block and nothing happens. This applies whether they included an invalid request or claimed a proposal slot they didn't earn.

### Step 8: The block becomes the new tip

If the block passes validation, every accepting node appends it to their copy of the chain. The user's request is now part of block N+1, and the chain's state reflects the change the request asked for.

The user can now query any node and see that the change has happened. Technically, the change can still be rolled back if the network ends up agreeing on a different version of recent history. The probability is small, and shrinks further with each new block built on top.

## What just happened

Eight steps, every one using a concept already in your head, ending with a permanent change to a shared record that no single party controls.

This is what a blockchain *does*. The rest of this course is about the variations between chains, the edge cases, the failure modes, and the application layer built on top. But the flow above is the core. Every blockchain you'll ever meet runs some version of it.

## The flow you can now trace

You can now trace a change to a blockchain from end to end: a user signs a request in their wallet, the network validates and gossips it, a block producer bundles it into a block, and every node independently checks and appends the result. That is the core loop every chain runs. Every chain you'll meet is a variation on one of these steps, running the same underlying loop.

## FAQ

### What happens between clicking send and my transaction landing on the chain?

Your wallet builds a request with the sender, the action, a fee, and a one-time value that blocks replays, then signs it with your private key. A node checks it and gossip carries it to the rest, into their pending pools. A block producer bundles it into a block and broadcasts that, every node revalidates, and the block becomes the new tip.

### What is the mempool?

A node's local buffer of valid transactions waiting for a block. A block producer picks from it, usually highest fees first.

### Do I have to trust the node I send my transaction to?

No. Every other node validates it again, and again inside the block.

### Can a node change my transaction before passing it on?

No. Your signature covers the whole request, so flipping one bit makes the signature invalid and every node downstream rejects it.

---

# Forks and conflict resolution

Source: https://academy.redduck.io/courses/blockchain-basics/the-blockchain-idea/forks-and-conflict-resolution

> The last lesson assumed the ideal case, where nothing goes wrong. One block producer proposes a block, the network accepts it, the chain grows by one. In reality, the network is geographically spread across the planet and messages take time to travel. Two qualified block producers can finish their work at almost the same instant, in different parts of the world. Each broadcasts a block. Both blocks contain different transactions but point back at the same parent block. For a few seconds, half the network thinks the chain looks like one thing and the other half thinks it looks like something else. This lesson is about what happens in those moments and why it matters even when everything goes right.

## The basic problem

The disagreement comes from physics. Light moves fast but not instantly. A block produced in one city does not appear in every node in the world simultaneously. It propagates outward through the gossip network, hop by hop, and during the seconds it takes to reach everyone, another producer somewhere else might finish their own block. Both blocks are valid. Both follow the rules. Both point at the same parent block as the next link. The chain now has a temporary **fork**: two competing versions of what block N+1 should be.

<svg role="img" viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Block N branching into two competing Block N+1 versions from producer A and producer B</title><desc>Block N, the shared parent, has arrows pointing to two separate Block N+1 candidates: one from producer A and one from producer B. Half the network sees producer A's block first while the other half sees producer B's block first, creating a temporary fork that the network must resolve.</desc> <defs> <marker id="arr1" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto"> <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/> </marker> </defs> <!-- Block N --> <rect x="40" y="110" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/> <text x="100" y="135" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Block N</text> <text x="100" y="155" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">the shared parent</text> <!-- Block N+1 (top) --> <rect x="240" y="40" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/> <text x="300" y="65" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Block N+1</text> <text x="300" y="85" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">producer A</text> <!-- Block N+1 (bottom) --> <rect x="240" y="180" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/> <text x="300" y="205" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Block N+1</text> <text x="300" y="225" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">producer B</text> <!-- Arrows from N to N+1s --> <line x1="160" y1="125" x2="240" y2="80" stroke="#565653" stroke-width="2" marker-end="url(#arr1)"/> <line x1="160" y1="155" x2="240" y2="200" stroke="#565653" stroke-width="2" marker-end="url(#arr1)"/> <!-- Half network labels -->

<text x="450" y="70" font-size="12" fill="#565653" font-style="italic">half the network sees</text> <text x="450" y="88" font-size="12" fill="#565653" font-style="italic">producer A's block first</text>

<text x="450" y="210" font-size="12" fill="#565653" font-style="italic">other half sees</text> <text x="450" y="228" font-size="12" fill="#565653" font-style="italic">producer B's block first</text>

<text x="360" y="270" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">A temporary fork. Both branches are valid. The network has to pick one.</text> </svg>

For the moment, the chain has two heads. A node that received producer A's block first is treating block N+1 from A as the current tip. A node that received producer B's block first is treating block N+1 from B as the tip. Both are doing the right thing according to the rules they know.

If nothing else happened, the network would stay split forever. Two separate histories, each consistent within its own population of nodes, each unable to tell which is "real." That outcome is unacceptable. The whole point of consensus is that everyone ends up with the same view.

## The solution: fork choice rules

Every blockchain protocol includes a **fork choice rule**, which is a deterministic procedure for picking one branch over another whenever a fork appears. The rule has to satisfy two requirements. It has to be computable by any node from local information, no central referee. And it has to be the same rule on every node, so that two honest nodes seeing the same fork will independently arrive at the same answer.

The specific rule depends on the chain. A common family of rules pick the branch that has the most work or stake behind it, on the theory that more cumulative effort means more credibility. Another family uses voting from validators to mark certain blocks as definitively chosen. The shapes vary, but the underlying logic is the same. Take some property that is hard to fake, measure it across the competing branches, and pick the branch that has more of it.

In the moment a fork appears, no branch has any advantage yet. Both branches are just one block past the shared parent. The deciding factor is what happens next.

## The race for the next block

Producers keep producing. Each producer in the network builds on the version of the chain they currently see as the tip. Some producers are building on top of A's block, others on top of B's. The next block in either branch extends that branch's lead.

The moment one branch gets a second block before the other, fork choice rules across the network start swinging in its favour. Nodes that were on the losing branch see the new, longer branch coming through gossip, recognise it as more credible under their fork choice rule, and **switch over**. Their copy of the chain rewrites recent history: they discard the block they had as N+1 and replace it with the winning branch's blocks N+1 and N+2.

<svg role="img" viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Block N forks into winning branch (N+1, N+2) versus orphaned N+1 block</title><desc>Block N splits into two branches. The winning branch extends with N+1 from producer A and N+2 from producer C, while the losing branch's N+1 block from producer B is left orphaned as a dead end.</desc> <defs> <marker id="arr2" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto"> <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/> </marker> </defs> <!-- Block N --> <rect x="40" y="110" width="100" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/> <text x="90" y="145" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Block N</text> <!-- Winning branch (top): N+1 + N+2 --> <rect x="200" y="40" width="100" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/> <text x="250" y="65" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">N+1</text> <text x="250" y="85" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">producer A</text> <rect x="360" y="40" width="100" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/> <text x="410" y="65" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">N+2</text> <text x="410" y="85" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">producer C</text>

<text x="520" y="75" font-size="12" fill="#ed4937" font-weight="bold">winning branch</text>

<!-- Losing branch (bottom): N+1 only --> <rect x="200" y="180" width="100" height="60" fill="#e0deda" stroke="#565653" stroke-width="2" stroke-dasharray="4 3"/> <text x="250" y="205" text-anchor="middle" font-size="13" fill="#565653" font-weight="bold">N+1</text> <text x="250" y="225" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">producer B</text>

<text x="320" y="215" font-size="12" fill="#565653" font-style="italic">orphaned</text>

<!-- Arrows --> <line x1="140" y1="125" x2="200" y2="80" stroke="#565653" stroke-width="2" marker-end="url(#arr2)"/> <line x1="300" y1="70" x2="360" y2="70" stroke="#565653" stroke-width="2" marker-end="url(#arr2)"/> <line x1="140" y1="155" x2="200" y2="200" stroke="#565653" stroke-width="1.5" stroke-dasharray="4 3"/>

<text x="360" y="270" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">The winning branch gets longer. The losing branch becomes an abandoned dead end.</text> </svg>

The block on the losing branch is now called an **orphan**. It was valid when it was produced, the network briefly accepted it, but the fork choice rule no longer points at it. The transactions inside it never made it to the canonical chain. Their effects on state are undone everywhere.

This rewriting of recent history is called a **reorganization**, usually shortened to **reorg**. Reorgs of one or two blocks happen routinely on most networks. They almost never cause problems in practice, because no application built on top of the chain treats a brand-new block as final. The convention is to wait for some number of additional blocks to build on top of any given block before considering its contents settled.

## Why this means "wait for confirmations"

This has a direct, practical consequence. If your transaction has just been included in the most recent block, that block is the most likely block in the chain to be replaced by a reorg. Each additional block built on top of it makes a reorg exponentially less likely. The alternative branch would have to outproduce the current canonical branch by enough to overtake it, and that becomes harder with every block it falls behind.

This is why exchanges, payment processors, and any high-value application waits for some number of additional blocks (called **confirmations**) before treating a transaction as final. The specific number depends on the chain and the value at stake. Small payments might be safe after one or two confirmations. Multi-million-dollar transfers wait for many more. The exact threshold is a risk-management choice, but the underlying principle is the same. Depth in the chain equals safety from reorganisation.

Some chains have an additional mechanism that explicitly marks blocks as **finalised** after a certain depth or after enough validators sign off on them. Once a block is finalised under such a mechanism, the protocol guarantees it will never be reorganised. The notion of "wait for finality" replaces "wait for confirmations" in those chains. The practical effect is similar from the application's perspective: do not treat new blocks as permanent immediately.

## Permanent forks: soft and hard

The forks discussed so far are temporary. The network self-heals within seconds, and nodes that had different views converge on the same view as the fork choice rule plays out. The chain ends up with one canonical history again.

A different kind of fork is not temporary. It happens when the rules of the protocol themselves change, and not every node updates to the new rules at the same time. Two populations of nodes end up running different software. They no longer agree on what counts as a valid block. The fork between them is permanent, because no fork choice rule can bridge a disagreement at the rules level.

There are two kinds of permanent fork: soft and hard.

A **soft fork** tightens the existing rules. Some blocks that used to be valid are no longer valid under the new rules. Critically, every block that's valid under the new rules is also valid under the old rules. Nodes running old software will still accept the new chain. They might be missing some new features, but they aren't excluded. The fork resolves itself if enough block producers move to the new rules, because the old-software nodes follow along automatically.

A **hard fork** changes the rules in a way that's not backwards-compatible. Some blocks valid under the new rules are not valid under the old rules. Nodes running old software will reject new blocks. The two populations split into two separate chains, each with their own state, their own history of transactions after the fork point, and their own future. Both can continue to exist independently, but they are now different networks.

Hard forks are how rule changes that the community can't agree on play out in the open. Each side runs their own version of the software, and the market decides which one accumulates value. Several well-known blockchain projects exist today as the surviving side of a hard fork that split the original community.

## The full operational picture

You now have the full operational picture of a blockchain, including the cases where things do not go smoothly. You can explain why two valid blocks can appear at once, how the longest-chain rule resolves the split, why a transaction becomes harder to reverse as blocks pile on top of it, and how soft and hard forks differ. Temporary disagreement is normal, and the network is built to converge on a single shared history without anyone in charge.

## FAQ

### Why do two valid blocks sometimes appear at the same time?

Messages take time to cross the planet. Two producers in different regions can finish valid blocks moments apart, both pointing at the same parent.

### The chain split in two. How does the network pick a side?

Every protocol has a fork choice rule that each node runs identically with no referee. A common one picks the branch with more work or stake, so the branch that gets the next block wins.

### My transaction was in a block and now it is gone. What happened?

A reorg. Your block lost the fork choice, became an orphan, and its effects on state were undone.

### Why do exchanges make me wait for confirmations?

The newest block is the most likely to be replaced by a reorg, and each block on top makes reversal exponentially less likely. How many to wait for is a risk decision about the amount at stake. Some chains finalise blocks instead, and a finalised block is never reorganised.

### What is the difference between a soft fork and a hard fork?

A soft fork only tightens the rules, so blocks made under them still look valid to old software and nothing splits. A hard fork changes rules old nodes reject, and the network breaks into two chains with separate histories.

---

# Determinism and what blockchains can't do

Source: https://academy.redduck.io/courses/blockchain-basics/the-blockchain-idea/determinism-and-what-blockchains-cant-do

> Every node must compute the same result from the same input. That one sentence is the deepest constraint on every blockchain in existence, and it is easy to miss when you come from web2 development, so it is worth stating plainly. The constraint dictates what kinds of programs can run on a blockchain and what kinds cannot. It dictates why on-chain code can't call an external API. It dictates why "give me a random number" on-chain is harder than it sounds. It dictates a whole class of problems that the ecosystem solves with separate mechanisms layered on top. This lesson is about why the constraint exists, what it rules out, and how blockchains work around it.

## Why determinism

Step back and recall what every node on the network is doing. When a new block arrives, every node independently validates it. Every validation must reach the same answer. If half the nodes accept the block and half reject it, the network is split and consensus has failed. The whole system runs on the assumption that every honest node, given the same input, will produce the same answer.

Now imagine the input includes a piece of code. A smart contract. A program that runs as part of processing the block. The contract reads some data, runs some logic, writes some result back. Every node executing the block must execute that contract and arrive at exactly the same result. Same final state of the contract, same return values, same effects on other parts of the chain. Any variation between nodes means the chain forks immediately, every time anyone calls a contract that varies.

So contracts on a blockchain have to be **deterministic**: same code, same inputs, same outputs, every time, on every node, forever. This is the single property that makes on-chain code possible at all.

<svg role="img" viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Same input and code run on node 1, 2, 3 give all equal outputs</title><desc>One input, the same on every node, flows into a single deterministic code block. That code runs separately on node 1, node 2, and node 3, and each produces an output. Arrows point from the code to all three outputs, labeled all equal.</desc>
  <defs>
    <marker id="arr3" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
  </defs>

<!-- Input -->
  <rect x="40" y="110" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="100" y="135" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Input</text>
  <text x="100" y="155" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">same on every node</text>

<!-- Code -->
  <rect x="240" y="110" width="120" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="300" y="135" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Code</text>
  <text x="300" y="155" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">deterministic only</text>

<!-- Output -->
  <rect x="440" y="40" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="500" y="65" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Output</text>
  <text x="500" y="85" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">node 1</text>

<rect x="440" y="110" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="500" y="135" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Output</text>
  <text x="500" y="155" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">node 2</text>

<rect x="440" y="180" width="120" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="500" y="205" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Output</text>
  <text x="500" y="225" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">node 3</text>

<!-- Arrows -->
  <line x1="160" y1="140" x2="240" y2="140" stroke="#565653" stroke-width="2" marker-end="url(#arr3)"/>
  <line x1="360" y1="130" x2="440" y2="70" stroke="#565653" stroke-width="2" marker-end="url(#arr3)"/>
  <line x1="360" y1="140" x2="440" y2="140" stroke="#565653" stroke-width="2" marker-end="url(#arr3)"/>
  <line x1="360" y1="150" x2="440" y2="210" stroke="#565653" stroke-width="2" marker-end="url(#arr3)"/>

<!-- Equality note -->
  <text x="610" y="125" font-size="14" fill="#ed4937" font-weight="bold">all</text>
  <text x="610" y="145" font-size="14" fill="#ed4937" font-weight="bold">equal</text>
</svg>

The picture is simple. Every node runs the same code on the same input. The outputs must all match. If they don't, something is wrong with the code, the input, or both. There is no fourth option.

## What this rules out

The determinism requirement immediately rules out an entire category of things developers normally take for granted.

**External API calls.** A program running on the chain cannot call `fetch("https://api.example.com/price")`. The API might return a different price to a node in one city than to a node in another. The API might be down when one node calls it and up when another does. The API might rate-limit some nodes and not others. Any of these would cause nodes to disagree on the result of the contract call, and the chain would fork. Smart contract languages on every chain simply don't expose any network operations. There is no HTTP client. There is no DNS lookup. There is no socket library.

**Wall-clock time.** A program running on the chain cannot read the system clock. Different nodes have slightly different clocks. Even with NTP synchronisation, there's drift on the order of milliseconds, and a contract that asked for the current second would get different answers on different nodes. Smart contract languages don't expose `Date.now()` or its equivalent. They expose only the timestamp recorded in the current block, which is a value the consensus mechanism has already agreed on.

**True randomness.** A program running on the chain cannot call `Math.random()` or read from the operating system's random number generator. Each node would produce different random bytes, the contract would behave differently on each node, and consensus would fail. Smart contract languages don't expose any source of true randomness. Contracts that need randomness have to obtain it from somewhere that every node can agree on.

**File system access.** No reading from disk except for the chain's own state. The contract has no concept of "this node's local files" because every node would have different files.

**Hardware-specific behaviour.** Things that produce different results on different CPUs, different operating systems, or different language versions are unsafe. Some chains go to extraordinary lengths to specify exact bit-level behaviour for every operation a contract can perform, so that no two nodes will ever produce different results even when running on different hardware.

The natural reaction at this point is "so how does anything useful get built?" The answer is that blockchains accept the constraint and the ecosystem has invented mechanisms for the operations contracts genuinely need.

## How the ecosystem works around the limits

Three of the categories above have standard workarounds that you'll meet in every chain ecosystem.

**For time, use block timestamps.** Every block has a timestamp field, set by the block producer when proposing the block. The timestamp is a value the consensus mechanism agrees on. Contracts that need "the current time" read the block timestamp instead. The granularity is the block interval, which is good enough for almost any application that needs a notion of time. The block timestamp is not perfectly accurate, and block producers have some discretion to choose it within a narrow range, but contracts that account for this work fine.

**For randomness, derive it from on-chain sources or use a protocol-level mechanism.** A naive approach is to hash some on-chain data and use the result as a pseudo-random number. This works for low-stakes use cases. For higher-stakes randomness (lotteries, gaming, anything where a participant might profit from biasing the result), chains either include a built-in mechanism that gathers contributions from many participants and combines them, or rely on specialised services that produce verifiable random outputs and submit them via signed transactions. The shared property is that every node ends up reading the same random value from somewhere on the chain, rather than rolling dice locally.

**For external data, use oracles.** An **oracle** is an off-chain service that watches some real-world data source (the price of an asset, the result of a sporting event, the weather in a city, whatever the application needs) and submits transactions to the chain that write the data into a smart contract's state. Contracts that need the external data read from the oracle's on-chain contract instead of trying to fetch the data themselves. The data on the chain came from off-chain, but at the moment a contract reads it, it's already been agreed upon by consensus. Every node reads the same value.

The oracle pattern is worth pausing on. It looks like a workaround, and it is, but it's a workaround that imports a different kind of trust into the system. The chain trusts the contract. The contract trusts the oracle. The oracle is operated by some party off the chain, with all the usual risks of off-chain parties. A blockchain application that depends on an oracle is only as trustworthy as its oracle, no matter how good the on-chain cryptography is. Picking the right oracle, or running your own, is an architectural decision that web2 development never required, and it is easy to overlook.

## What blockchains are really for

A blockchain is not a general-purpose computer that happens to be decentralised. It is a very specific kind of computer that has traded a lot of capability for the ability to be verified by everyone, with no operator. Anything that depends on local conditions, like time, randomness, external data, or hardware, is either banned or pushed off-chain. What's left is pure computation over data that everyone can see.

That's the deal. You get a programmable settlement layer that no one controls. Anyone can write code on it. Anyone can verify that code. It handles real value and produces results no one can fake. You give up the ability to call external services from inside that code, the ability to read the wall clock, and the ability to use true randomness. For the use cases blockchains are good for, the trade is enormously worth it. For use cases that need any of the capabilities the trade gives up, a blockchain is the wrong tool.

This is the constraint at the bottom of everything. Every smart contract you'll ever write, on any chain, lives inside this rule. Every weird design choice in smart contract languages, every reason "just call an API" doesn't work, every pattern you'll learn for handling external data, all of it traces back to one requirement. Every node must compute the same result from the same input. The chain can do nothing else, ever.

## The complete picture you now hold

You have a complete conceptual picture of how a blockchain operates: the structure, the comparison to traditional systems, the consensus mechanism, the nodes and the network, the operation loop that ties everything together, the way the network handles temporary and permanent disagreement, and the deepest constraint on what on-chain code can do. Every real chain is a specific set of choices layered on top of these same pieces, and you now have the vocabulary to take any of them apart.

## FAQ

### What does determinism mean for a blockchain?

Same code, same inputs, same output on every node, every time.

### Why can't a smart contract call an external API?

Every node has to compute the same result from the same input. An API can answer one node differently from another, and the chain would fork. Contract languages have no HTTP client, no DNS, no sockets.

### How does a smart contract know what time it is?

It reads the timestamp in the current block, a value consensus has already agreed on. The machine's own clock is off limits, since node clocks drift by milliseconds.

### Can a smart contract generate a random number?

Not from the machine, since every node would roll a different number. Low-stakes contracts hash on-chain data. Where someone could profit from biasing the result, chains use a built-in mechanism that combines many contributions, or a service that submits verifiable random values.

### What is an oracle?

An off-chain service that watches something real, an asset price or a match result, and writes it into a contract's state with a transaction. Contracts read the stored value, which consensus has already agreed on. A contract is only as trustworthy as its oracle.

---

# Blockchain mechanics - Test

Source: https://academy.redduck.io/courses/blockchain-basics/the-blockchain-idea/blockchain-mechanics

This test checks whether you can reason about how a blockchain actually behaves, not just describe what it is. Each question puts you in a specific situation where the right answer requires applying what you learned. Some options sound right but don't survive a careful read.

---

# The Bitcoin design philosophy

Source: https://academy.redduck.io/courses/blockchain-basics/bitcoin/the-bitcoin-design-philosophy

> Bitcoin is the first chain you'll meet in detail, and the one every later chain measures itself against, because it came first and proved the model worked. To understand any later chain, you start by understanding what Bitcoin chose and why. The design choices look strange in isolation. Ten-minute blocks. A scripting language that can't loop. A money supply hardcoded to stop at twenty-one million. None of these are arbitrary. Each one falls out of a tight chain of reasoning that starts with a single question and ends with a working system. This lesson walks that reasoning end to end.

## The question

In late 2008, an unsigned paper appeared on a cryptography mailing list, proposing a system that almost everyone with the relevant background would have said was impossible the month before. The author used the pseudonym Satoshi Nakamoto, and the paper was titled ["Bitcoin: A Peer-to-Peer Electronic Cash System."](https://bitcoin.org/bitcoin.pdf)

The title contains the whole question. Each word matters.

**Electronic** ruled out paper currency and required a system that could exist purely as data, transmitted over the internet. **Cash** ruled out anything that required a third party to clear the transaction, which is what makes physical cash different from a bank transfer. Two people standing in a room exchanging cash do not need permission from a bank to complete the exchange. Cash settles immediately and irrevocably and without an intermediary. **Peer-to-Peer** ruled out a central server that the transaction flows through. The two parties have to be able to transact directly, with no operator in the middle. **System** meant the whole apparatus had to work, end to end, at internet scale, with strangers, indefinitely.

Putting it together: a way for any two parties anywhere in the world to exchange digital value, directly, without permission from any third party, in a system that runs itself.

This had been an open problem in computer science for over twenty years before 2008. Several serious attempts had been made. All of them had failed at the same point: how do you prevent the same digital coin from being spent twice in two different places, when there's no central server keeping the books? This is called the **double-spend problem**, and it is the core obstacle that stopped every previous digital-cash design.

Bitcoin's contribution was an answer to this question that nobody had thought of before. The rest of the design follows from how that answer worked.

## The constraints that fell out

Once you commit to "no central operator," a whole cascade of constraints follows. There's one meta-constraint that motivates all the others: the system has to survive adversarial conditions. With no operator to ban attackers, the system has to keep running while bad actors try to break it from the inside. Liars, cheaters, attackers with millions of dollars to spend on breaking it: all welcome to participate, because there's no gatekeeper to keep them out. Every design choice that follows is made under the assumption that an unknown fraction of participants are hostile.

That meta-constraint produces five operational ones.

**Sybil resistance without identity.** In any open system, the obvious attack is to create a million fake participants and outvote the honest ones. If the system uses identity to prevent this, it needs a way to identify people, which requires a central authority. So the system can't use identity. It needs some other way to make participation costly enough that creating a million fake participants is not economically feasible.

**Public verifiability.** Without a trusted operator to vouch for transactions, every participant has to be able to verify every transaction themselves. This means the rules must be simple enough to compute, the data must be available to everyone, and the verification must produce the same answer on every honest node. No "trust me, I'm the bank."

**Convergence despite network delay.** Information traveling across the internet doesn't arrive everywhere at the same instant. Two participants on opposite sides of the world might see different events first. The system has to handle this gracefully, with all honest participants eventually agreeing on the same history, despite never having a synchronized clock or instant global broadcast.

**Censorship resistance.** If any single party can prevent a transaction from being processed, that party is effectively the operator. The system has to be structured so that no single point of failure can stop anyone from transacting.

**Permanence.** A transaction that settled yesterday has to be just as settled tomorrow. There's no operator to "reverse" it. If the system can selectively forget or rewrite history, the entire trustless guarantee evaporates.

Each of these constraints kills a category of possible designs. A system with a membership list violates Sybil resistance without identity. A system with encrypted state violates public verifiability. A system that relies on a global synchronous clock violates convergence under delay. A system where a gatekeeper can drop transactions violates censorship resistance. A system with mutable history violates permanence. All of these are out.

What's left, after killing every design that fails any constraint, is a very narrow space. Bitcoin's specific shape is what fits inside it.

## The choices that fell out

Now the constraints are the input and Bitcoin's design is the output. Each major choice in Bitcoin's design is a direct response to a specific constraint.

<svg role="img" viewBox="0 0 720 480" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Bitcoin's five constraints mapped to their design choices</title><desc>A table lists five constraints on trustless peer-to-peer cash next to the Bitcoin design choice that answers each one, with arrows connecting each pair. Permanence leads to an append-only hash-linked log, Sybil resistance without identity leads to proof of work, public verifiability leads to simple deterministic validation, convergence under network delay leads to slow block times, and censorship resistance leads to an open permissionless network.</desc>
  <defs>
    <marker id="arr41" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
  </defs>

<!-- Goal at top -->
  <rect x="200" y="20" width="320" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="45" text-anchor="middle" font-size="14" fill="#ffffff" font-weight="bold">Trustless peer-to-peer cash</text>
  <text x="360" y="65" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffffff">no operator, no permission</text>

<!-- Two column headers -->
  <text x="180" y="115" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Constraint</text>
  <text x="540" y="115" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Design choice</text>

<!-- Row 1 -->
  <rect x="40" y="130" width="280" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="150" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Permanence</text>
  <text x="180" y="168" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">settled means settled forever</text>

<line x1="320" y1="155" x2="400" y2="155" stroke="#565653" stroke-width="2" marker-end="url(#arr41)"/>

<rect x="400" y="130" width="280" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="150" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Append-only hash-linked log</text>
  <text x="540" y="168" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">rewriting one block breaks all later ones</text>

<!-- Row 2 -->
  <rect x="40" y="190" width="280" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="210" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Sybil resistance without identity</text>
  <text x="180" y="228" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">no membership list</text>

<line x1="320" y1="215" x2="400" y2="215" stroke="#565653" stroke-width="2" marker-end="url(#arr41)"/>

<rect x="400" y="190" width="280" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="210" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Proof of work</text>
  <text x="540" y="228" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">votes priced in real electricity</text>

<!-- Row 3 -->
  <rect x="40" y="250" width="280" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="270" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Public verifiability</text>
  <text x="180" y="288" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">anyone checks the rules</text>

<line x1="320" y1="275" x2="400" y2="275" stroke="#565653" stroke-width="2" marker-end="url(#arr41)"/>

<rect x="400" y="250" width="280" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="270" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Simple deterministic validation</text>
  <text x="540" y="288" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">limited script, no loops</text>

<!-- Row 4 -->
  <rect x="40" y="310" width="280" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="330" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Convergence under network delay</text>
  <text x="180" y="348" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">no synchronized global clock</text>

<line x1="320" y1="335" x2="400" y2="335" stroke="#565653" stroke-width="2" marker-end="url(#arr41)"/>

<rect x="400" y="310" width="280" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="330" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Slow block times</text>
  <text x="540" y="348" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">ten minutes, conservative on purpose</text>

<!-- Row 5 -->
  <rect x="40" y="370" width="280" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="390" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Censorship resistance</text>
  <text x="180" y="408" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">no single point of control</text>

<line x1="320" y1="395" x2="400" y2="395" stroke="#565653" stroke-width="2" marker-end="url(#arr41)"/>

<rect x="400" y="370" width="280" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="390" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Open permissionless network</text>
  <text x="540" y="408" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">anyone runs a node, mines, transacts</text>

<text x="360" y="455" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">From one goal, five constraints. From the constraints, five design choices.</text>
</svg>

**The ledger is append-only and hash-linked.** Each block carries the hash of the previous one, the structure you've already seen in detail. This is the answer to permanence: rewriting a single old block invalidates every block after it.

**Sybil resistance comes from proof of work.** The right to add to the ledger is sold to whoever can demonstrate the most computational work. Computation costs electricity. Electricity costs the same regardless of how many identities you control. A million fake participants pay a million times the bill. This is the answer to Sybil resistance without identity, and it's specifically a hardware-and-energy answer to a problem that previous designs tried to solve with cryptography alone.

**Validation is deterministic and simple.** Every transaction can be checked by running a small piece of code against the chain's state. The code is intentionally not Turing-complete. There are no loops. There is no recursion. There is no external state. This is the answer to public verifiability: every node, anywhere, running honest software, reaches the same answer on the same input.

**Block times are slow on purpose.** Ten minutes between blocks is not a performance choice. It's a margin of safety for the network. A new block has to propagate to most of the planet before the next one is produced, so that everyone is building on the same most recent block, the tip of the chain. Faster block times mean more chain forks and more wasted work. This is the answer to convergence under delay.

**The money supply is capped.** Twenty-one million bitcoins, ever. Block rewards halve every 210,000 blocks, which is about every four years, so the rate of new issuance slows over time and eventually stops. This is not a technical constraint of the underlying machinery. It's a deliberate economic choice, intended to make Bitcoin a credible store of value in a world where every other currency can be inflated by whoever issues it. Whether that choice was right is a debate for another time. That the choice is deliberate, and built into the protocol so that no one can change it, is what matters here.

**The system changes slowly.** No central party can update the rules. Any change has to be accepted by the people who run nodes. They have strong reasons to be conservative. The system is intentionally hard to upgrade, and developers have a strong deference to backwards compatibility because anything else risks breaking the trustlessness that's the whole point.

Every later lesson is going to keep coming back to this list. Why does Bitcoin use a model where coins are tracked individually rather than as account balances? Survival under adversarial conditions and public verifiability. Why is Script intentionally limited? Public verifiability and validation simplicity. Why is the block reward halving? Permanence of the monetary policy. Once you have the constraints in your head, the answers stop being arbitrary.

## What Bitcoin gave up

The design has costs. They're worth naming up front because the lessons that follow will not pretend they don't exist.

Bitcoin is slow. Settlement takes minutes for a payment and hours for high-value transactions. Bitcoin is expensive at scale, because every node has to validate every transaction and there's a hard limit on how many can fit in a block. Bitcoin is inflexible. The intentional restrictions on Script mean it can't be used to build the complex applications that later chains support. Bitcoin is conservative. Changes that other chains release in months take Bitcoin years, sometimes decades.

These are not bugs. They are the cost of the trustlessness the whole system is built around. Other chains made different trades, and the rest of this course visits some of them. From here, the question is how the specific Bitcoin design works, top to bottom.

## FAQ

### What is the double-spend problem?

Spending the same digital coin twice in two places when no central server keeps the books.

### Who wrote the Bitcoin whitepaper?

Someone using the name Satoshi Nakamoto. The paper, "Bitcoin: A Peer-to-Peer Electronic Cash System", was posted to a cryptography mailing list in late 2008. Who that was is still unknown.

### Why does Bitcoin make miners burn electricity instead of voting?

Anyone can create a million fake identities for free. Proof of work prices each vote in electricity, so a million fake identities pay a million bills.

### Why are Bitcoin blocks ten minutes apart instead of instant?

Ten minutes gives each new block time to reach most of the network before the next one is produced, so miners are all building on the same latest block. Faster blocks would mean more accidental forks and more wasted work. The gap is a safety margin.

### Why is Bitcoin's supply capped at 21 million coins?

An economic choice written into the protocol, meant to produce money nobody can inflate. Block rewards halve every 210,000 blocks, about every four years, so new issuance slows and eventually stops. No central party can change the rule.

---

# The UTXO model

Source: https://academy.redduck.io/courses/blockchain-basics/bitcoin/the-utxo-model

> There is one model of money so common in software that it is easy to mistake it for the only one. A balance is a number stored somewhere. You read the number to find out how much someone has. You change the number to move value around. The number lives in a database row, a struct field, an account object. This is how a bank account looks to its user, and how PayPal and every other traditional payment system look to theirs. It is also not how Bitcoin works. Bitcoin doesn't store balances. Bitcoin stores coins. Discrete, individual coins, each with a value and an owner, each created by one transaction and destroyed by the next. Your balance is not a number that exists anywhere. It is a sum you compute by adding up the coins that happen to be yours. This lesson is about why Bitcoin makes that choice and what it buys.

## Two ways to track money

Imagine you want to build a digital payment system. There are two natural ways to do it.

The first way is the **account model**, and it's the one almost every traditional system uses. The system keeps a table. Each row has a user and a balance. To send money, you decrement one row and increment another. To check a balance, you look up the row. Simple. Familiar. It is the model that feels like the obvious way to build payments.

The second way is the **UTXO model**, and it's the one Bitcoin uses. The system doesn't keep a table of users and balances. It keeps a set of coins. Each coin has a specific value and a specific owner. To send money, you don't update any balances. You destroy some of your coins and create new ones for the recipient. To check a balance, you scan for every coin still belonging to you and add them up.

UTXO stands for **Unspent Transaction Output**. Every coin in Bitcoin started life as the output of some past transaction and has not yet been spent by being used as the input of a later one. The set of every UTXO across the chain is what Bitcoin nodes actually track. There is no balance ledger. There is only the UTXO set.

<svg role="img" viewBox="0 0 720 360" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Account balance table compared to Bitcoin's UTXO set of coins</title><desc>On the left, the account model is a table of balances: Alice has 2.5 BTC, Bob has 0.8 BTC, Carol has 1.2 BTC; paying means updating two rows, and reading a balance means looking up a row. On the right, the UTXO model is a set of coins, such as a 1.0 BTC coin owned by Alice or a 0.3 BTC coin owned by Bob; paying means destroying some coins and creating new ones, and reading a balance means summing your coins.</desc>
  <!-- Account model column -->
  <text x="180" y="30" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Account model</text>
  <text x="180" y="50" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">a table of balances</text>

<rect x="40" y="70" width="280" height="200" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="60" y="95" font-size="12" fill="#000000" font-weight="bold">Account</text>
  <text x="240" y="95" font-size="12" fill="#000000" font-weight="bold">Balance</text>
  <line x1="50" y1="105" x2="310" y2="105" stroke="#565653" stroke-width="1"/>

<text x="60" y="130" font-family="monospace" font-size="11" fill="#000000">Alice</text>
  <text x="240" y="130" font-family="monospace" font-size="11" fill="#000000">2.5 BTC</text>

<text x="60" y="160" font-family="monospace" font-size="11" fill="#000000">Bob</text>
  <text x="240" y="160" font-family="monospace" font-size="11" fill="#000000">0.8 BTC</text>

<text x="60" y="190" font-family="monospace" font-size="11" fill="#000000">Carol</text>
  <text x="240" y="190" font-family="monospace" font-size="11" fill="#000000">1.2 BTC</text>

<text x="60" y="220" font-family="monospace" font-size="11" fill="#565653">...</text>

<text x="180" y="295" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">to pay: update two rows</text>
  <text x="180" y="312" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">to read balance: look up the row</text>

<!-- UTXO model column -->
  <text x="540" y="30" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">UTXO model</text>
  <text x="540" y="50" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">a set of coins</text>

<rect x="400" y="70" width="280" height="200" fill="#e0deda" stroke="#000000" stroke-width="2"/>

<rect x="415" y="85" width="120" height="40" fill="#ed4937" stroke="#000000" stroke-width="1"/>
  <text x="475" y="102" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">coin: 1.0 BTC</text>
  <text x="475" y="118" text-anchor="middle" font-family="monospace" font-size="9" fill="#ffffff">owner: Alice</text>

<rect x="545" y="85" width="120" height="40" fill="#ed4937" stroke="#000000" stroke-width="1"/>
  <text x="605" y="102" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">coin: 1.5 BTC</text>
  <text x="605" y="118" text-anchor="middle" font-family="monospace" font-size="9" fill="#ffffff">owner: Alice</text>

<rect x="415" y="135" width="120" height="40" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <text x="475" y="152" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">coin: 0.3 BTC</text>
  <text x="475" y="168" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">owner: Bob</text>

<rect x="545" y="135" width="120" height="40" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <text x="605" y="152" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">coin: 0.5 BTC</text>
  <text x="605" y="168" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">owner: Bob</text>

<rect x="415" y="185" width="120" height="40" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <text x="475" y="202" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">coin: 1.2 BTC</text>
  <text x="475" y="218" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">owner: Carol</text>

<text x="605" y="210" font-family="monospace" font-size="11" fill="#565653">...</text>

<text x="540" y="295" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">to pay: destroy some, create new</text>
  <text x="540" y="312" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">to read balance: sum your coins</text>
</svg>

In the account model, "Alice has 2.5 BTC" is a fact stored in one place. In the UTXO model, Alice has 2.5 BTC because she happens to hold two unspent coins worth 1.0 and 1.5. Her balance is a derived quantity that the system computes by summing her coins. The chain doesn't know who Alice is or what her total is. It just knows about those two specific coins.

## How a payment actually works

The UTXO model produces a way of paying that feels strange the first time you see it. Here's the rule. A transaction takes one or more existing coins as inputs, destroys them, and creates one or more new coins as outputs. The total value of the new coins has to equal the total value of the old coins, minus a small fee that goes to whoever includes the transaction in a block.

Now imagine Alice wants to send 0.3 BTC to Bob. She holds the 1.0 BTC coin from the example above. There is no operation called "subtract 0.3 from Alice and add 0.3 to Bob." There is only "destroy old coins, create new coins." So Alice's payment has to be structured as follows.

Her transaction takes her 1.0 BTC coin as the input. That coin is destroyed in the process. She creates two new coins as outputs. One coin worth 0.3 BTC, owned by Bob. One coin worth a little less than 0.7 BTC, owned by Alice herself. The difference between the input value and the output value is the fee.

<svg role="img" viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>UTXO transaction: 1.0 BTC input splits into 0.3 BTC and 0.699 BTC outputs</title><desc>A 1.0 BTC coin owned by Alice is destroyed as the input and replaced by two new output coins: 0.3 BTC for Bob and 0.699 BTC change for Alice. The transaction box shows in: 1.0 BTC, out: 0.3 + 0.699 BTC, fee: 0.001 BTC, and the rule in = out + fee.</desc>
  <defs>
    <marker id="arr42" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
  </defs>

<!-- Input -->
  <text x="120" y="40" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Inputs (destroyed)</text>

<rect x="40" y="60" width="160" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="120" y="85" text-anchor="middle" font-family="monospace" font-size="12" fill="#ffffff">coin: 1.0 BTC</text>
  <text x="120" y="103" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">owner: Alice</text>

<!-- Transaction node -->
  <rect x="280" y="60" width="160" height="180" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="90" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Transaction</text>
  <text x="360" y="115" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">in: 1.0 BTC</text>
  <text x="360" y="133" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">out: 0.3 + 0.699 BTC</text>
  <text x="360" y="158" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">fee: 0.001 BTC</text>
  <line x1="295" y1="175" x2="425" y2="175" stroke="#565653" stroke-width="1" stroke-dasharray="3 2"/>
  <text x="360" y="200" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">in = out + fee</text>

<!-- Outputs -->
  <text x="600" y="40" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Outputs (created)</text>

<rect x="520" y="60" width="160" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="600" y="85" text-anchor="middle" font-family="monospace" font-size="12" fill="#000000">coin: 0.3 BTC</text>
  <text x="600" y="103" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">owner: Bob</text>

<rect x="520" y="140" width="160" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="600" y="165" text-anchor="middle" font-family="monospace" font-size="12" fill="#000000">coin: 0.699 BTC</text>
  <text x="600" y="183" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">owner: Alice (change)</text>

<!-- Arrows -->
  <line x1="200" y1="90" x2="280" y2="120" stroke="#565653" stroke-width="2" marker-end="url(#arr42)"/>
  <line x1="440" y1="120" x2="520" y2="90" stroke="#565653" stroke-width="2" marker-end="url(#arr42)"/>
  <line x1="440" y1="160" x2="520" y2="170" stroke="#565653" stroke-width="2" marker-end="url(#arr42)"/>
</svg>

That second output, the one going back to Alice, is called a **change output**. It exists for exactly the same reason change exists in cash. If you owe someone three dollars and you only have a ten in your wallet, you don't tear the ten in pieces. You hand over the ten and the other party hands you back seven. UTXOs are the same. You don't split a coin. You hand the whole coin to the transaction and the transaction hands you a new, smaller coin in return.

After this transaction settles, Alice no longer has her 1.0 BTC coin. That coin is destroyed forever. In its place she has a fresh 0.699 BTC coin she didn't have a moment ago, and Bob has a fresh 0.3 BTC coin he didn't have either. The chain's UTXO set has changed: one coin removed, two coins added.

## What's actually inside a transaction

The previous section was the conceptual picture. The structural picture is one level more concrete, and it's worth seeing because the structure is what gets stored on the chain and what wallet software actually reads.

A transaction is a small record with three significant parts: a list of inputs, a list of outputs, and some housekeeping fields like a version number and an optional time lock.

Each **input** has two essential pieces. The first is a pointer back to the specific UTXO being spent. That pointer is the **txid** of the past transaction that created the coin, plus an index into that transaction's output list (called **vout** for "output index"). Together those two values uniquely identify a coin anywhere in the chain's history. The second piece is the data that satisfies the coin's lock, called the **unlocking script** or **scriptSig**, typically a signature and the public key that the signature was produced from.

Each **output** has two essential pieces. The first is the value of the new coin, stored as an integer in **satoshis** (the smallest unit of Bitcoin, one hundred-millionth of one BTC). The second is the **locking script** or **scriptPubKey**, a small program defining the condition the future spender will have to satisfy. The typical condition is "prove ownership of the private key corresponding to this address."

The "small program" framing is worth pausing on. Each locking condition is written in a stack-based language called **Bitcoin Script**, deliberately limited so that every node can run it cheaply and deterministically. No loops. No external data. No shared state. The language only exists to answer one question per transaction: is this spend allowed, yes or no? The most common locking pattern, used in almost every routine payment, is called **Pay-to-Public-Key-Hash**. It locks the coin to a specific public key hash, and the spender unlocks it by providing a signature plus their public key. Other patterns exist, but the shape is always the same: the output sets a condition, the input satisfies it. Bitcoin's intentional choice to keep this language small is one of its defining design decisions, and we'll come back to it when we look at the trade-offs later.

<svg role="img" viewBox="0 0 720 240" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Transaction input referencing output 1 of an earlier transaction</title><desc>An earlier transaction has three outputs, and output 1 sends 1.0 BTC to Alice. Alice's new transaction has input 0, which points back to that output using prev_txid and prev_vout, and unlocks it with an unlocking script containing a signature and public key.</desc>
  <defs>
    <marker id="arr42b" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
  </defs>

<text x="180" y="25" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Earlier transaction</text>

<rect x="40" y="40" width="280" height="150" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="62" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">txid: d7a52b9c...</text>
  <line x1="55" y1="72" x2="305" y2="72" stroke="#565653" stroke-width="1"/>

<rect x="55" y="82" width="250" height="30" fill="#e0deda" stroke="#565653" stroke-width="1"/>
  <text x="65" y="102" font-family="monospace" font-size="10" fill="#565653">output 0: 0.5 BTC</text>

<rect x="55" y="117" width="250" height="30" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="65" y="137" font-family="monospace" font-size="10" fill="#ffffff" font-weight="bold">output 1: 1.0 BTC (Alice)</text>

<rect x="55" y="152" width="250" height="30" fill="#e0deda" stroke="#565653" stroke-width="1"/>
  <text x="65" y="172" font-family="monospace" font-size="10" fill="#565653">output 2: 0.3 BTC</text>

<text x="540" y="25" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Alice's new transaction</text>

<rect x="400" y="60" width="280" height="110" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="415" y="85" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">input 0:</text>
  <text x="430" y="105" font-family="monospace" font-size="10" fill="#565653">prev_txid: d7a52b9c...</text>
  <text x="430" y="125" font-family="monospace" font-size="10" fill="#565653">prev_vout: 1</text>
  <text x="430" y="145" font-family="monospace" font-size="10" fill="#565653">unlocking_script: &lt;sig&gt; &lt;pubkey&gt;</text>

<line x1="305" y1="132" x2="400" y2="125" stroke="#565653" stroke-width="2" marker-end="url(#arr42b)"/>
  <text x="352" y="115" text-anchor="middle" font-size="10" fill="#565653" font-style="italic">references</text>

<text x="360" y="220" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">An input points back at a specific output of a specific past transaction.</text>
</svg>

This pointer-based structure is why the chain is best thought of as a directed graph rather than a list of balances. Every coin in existence traces back through a chain of references all the way to a mining reward, and every transaction is a node in that graph that consumes some incoming edges and produces some outgoing ones.

One detail worth flagging: the fee paid to the miner is not its own field in the transaction. It's computed implicitly as the difference between the total input value and the total output value. If a transaction has 1.0 BTC of inputs and 0.999 BTC of outputs, the 0.001 BTC difference goes to whoever includes the transaction in a block. This is one place where developers writing transaction-construction code go wrong: forget to leave a fee, and your transaction either gets rejected or pays an enormous one to the miner.

## Why this is not as strange as it looks

If you're meeting the UTXO model for the first time, your reaction is probably "this is a complicated way to do something simple." Why not just update Alice's balance and Bob's balance directly, like every database in the world?

The answer goes back to the Bitcoin design philosophy lesson. Bitcoin is designed for a network where there's no operator, every node has to validate every transaction independently, and the validation has to be cheap and deterministic. UTXOs are a way of structuring state that makes that validation easy.

To validate a UTXO transaction, a node has to check three things. The inputs exist as unspent outputs in the current UTXO set. The signature on each input is valid against the coin's owner condition. The output values add up to no more than the input values. Three local checks. Each one is independent of everything else happening on the chain. There's no global table to consult, no balance to scan, no race condition to worry about. Just three lookups and some arithmetic.

The same validation in an account model has to look up balances, possibly across many accounts, and worry about ordering: if Alice tries to spend her balance in two different transactions at almost the same time, the system has to decide which one wins. The UTXO model can't have that problem because the coins themselves are the unit of state, and each coin can only be spent once.

This local-validation property pays off in three more ways.

**Parallelism.** Two transactions that touch different UTXOs are independent. The network can validate them in parallel without coordination. An account-model transaction touching Alice's balance and another transaction also touching Alice's balance cannot be processed in parallel without locking.

**Privacy.** Each coin has its own owner condition. The chain doesn't have a permanent identifier for a user. Alice can have a hundred different addresses, each holding different coins, and there's no on-chain record that they're all hers. In an account model, every transaction Alice makes is tied to the same account identifier, so her whole history is linked together. The UTXO model has no such per-user identifier, so it never forces that link.

**Audit clarity.** Every coin's history can be traced exactly. This specific coin was created by this transaction, which was funded by these earlier coins, which were created by these even earlier transactions, all the way back to a mining reward. The provenance is built into the data structure rather than bolted on as an audit log.

The trade is that the UTXO model is unfamiliar and uses more storage for the same number of users, because the chain has to track every unspent coin separately rather than a single balance per user. Bitcoin accepts that trade. Several other chains made the opposite trade and use account models instead. Both work. They optimise for different things.

## FAQ

### What is a UTXO?

An unspent transaction output. One coin created by an earlier transaction, with its own value and owner, waiting to be used as the input of a later one.

### Where is my Bitcoin balance actually stored?

Nowhere as a single number. Bitcoin tracks individual unspent coins, and your balance is the sum of the ones you can spend.

### What is a satoshi?

One hundred-millionth of a bitcoin. Output values are stored as whole satoshis.

### Why does my wallet send part of a payment back to me?

You cannot split a coin. Paying 0.3 BTC out of a 1.0 BTC coin destroys the 1.0 coin and creates 0.3 for the recipient plus a change coin of nearly 0.7 for you.

### How does the miner get paid if there is no fee field?

Inputs minus outputs. The gap goes to whoever includes the transaction in a block.

### Why does Bitcoin use the UTXO model instead of account balances?

Validation stays local. A node checks that the input coins are still unspent, that each unlocking script satisfies the coin's locking script, and that the outputs do not exceed the inputs. No balance table to scan, no ordering problem, and transactions touching different coins can be validated in parallel.

---

# Blocks and mining

Source: https://academy.redduck.io/courses/blockchain-basics/bitcoin/mining

A transaction sitting in a wallet is just a string of bytes. A transaction sitting in the Bitcoin network's mempool is just a string of bytes that many nodes happen to know about. Neither of those is settlement. The transaction is settled when it lands in a block, that block lands in the chain, and enough additional blocks are added on top of it that reversing the transaction would cost more than anyone would rationally spend. This lesson is about how that landing happens. Who builds the block. What's inside it. What the proof-of-work puzzle actually is. Why solving the puzzle is hard and checking the solution is trivial. And how new bitcoin gets minted into existence at the same moment each block is built.

## From transactions to blocks

A block is a batch of transactions, plus a small header that gives the batch an identity and connects it to the previous block in the chain. Anyone running mining software is competing to be the one who builds the next block. The competition is the proof-of-work puzzle, and only the winner gets to add to the chain.

The winner gets two things in return for their work. First, all of the transaction fees from the transactions they bundled into the block. Second, a fresh amount of newly-created BTC, called the **block subsidy**, which is currently 3.125 BTC and halves every four years on a fixed schedule. Together these two things are called the **block reward**, and they're the only reason mining happens at all. Without the reward, there would be no incentive to spend electricity on hashing.

The pattern of one new block roughly every ten minutes is a deliberate choice introduced in an earlier lesson. Slow block times leave room for new blocks to reach most of the network before the next one is produced, which keeps the network converging on the same chain. Faster blocks would mean more accidental forks. Ten minutes is conservative on purpose.

## What's inside a block

A block has two parts.

<svg role="img" viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Bitcoin block structure: 80-byte header and transaction list</title><desc>A Bitcoin block has two parts: a small block header on top and a much bigger transaction list below. The header is exactly 80 bytes and holds the version, previous block hash, merkle root, timestamp, bits, and nonce, while the transaction list is typically 1 MB to 4 MB, starting with the coinbase transaction and followed by thousands more.</desc>
  <rect x="120" y="20" width="480" height="40" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="45" text-anchor="middle" font-size="14" fill="#ffffff" font-weight="bold">A Bitcoin block</text>

<rect x="120" y="80" width="480" height="80" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="140" y="105" font-size="13" fill="#000000" font-weight="bold">Block header</text>
  <text x="280" y="105" font-family="monospace" font-size="11" fill="#565653">exactly 80 bytes</text>
  <text x="140" y="130" font-family="monospace" font-size="10" fill="#565653">version, prev_block_hash, merkle_root,</text>
  <text x="140" y="148" font-family="monospace" font-size="10" fill="#565653">timestamp, bits (difficulty target), nonce</text>

<rect x="120" y="170" width="480" height="170" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="140" y="195" font-size="13" fill="#000000" font-weight="bold">Transaction list</text>
  <text x="280" y="195" font-family="monospace" font-size="11" fill="#565653">typically 1 MB to 4 MB, thousands of txs</text>

<rect x="140" y="210" width="440" height="25" fill="#ed4937" stroke="#000000" stroke-width="1"/>
  <text x="155" y="227" font-family="monospace" font-size="10" fill="#ffffff" font-weight="bold">tx 0: coinbase (creates new BTC)</text>

<rect x="140" y="240" width="440" height="22" fill="#e0deda" stroke="#565653" stroke-width="1"/>
  <text x="155" y="256" font-family="monospace" font-size="10" fill="#565653">tx 1: ...</text>

<rect x="140" y="265" width="440" height="22" fill="#e0deda" stroke="#565653" stroke-width="1"/>
  <text x="155" y="281" font-family="monospace" font-size="10" fill="#565653">tx 2: ...</text>

<rect x="140" y="290" width="440" height="22" fill="#e0deda" stroke="#565653" stroke-width="1"/>
  <text x="155" y="306" font-family="monospace" font-size="10" fill="#565653">tx 3: ...</text>

<text x="155" y="328" font-family="monospace" font-size="11" fill="#565653">...thousands more...</text>

<text x="360" y="365" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">A tiny header sitting on top of a much bigger transaction list.</text>
</svg>

The disproportion in this diagram is the most important thing to notice. The block body is megabytes of transaction data. The block header is exactly 80 bytes, no matter what. This asymmetry is not an accident. A lightweight wallet running on a phone wants to verify that a particular transaction is in the chain without storing the entire chain. It can do this by storing only block headers and asking a full node for a Merkle proof when it needs to check a specific transaction. The headers add up to about 4 megabytes per year. The whole structure is shaped to make that lightweight verification work, which is one of the design choices that follows from the public-verifiability requirement introduced in an earlier lesson.

## The six fields in a block header

Each block header packs six fields into exactly 80 bytes.

<svg role="img" viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Six fields of a block header, totaling 80 bytes</title><desc>The header holds six fields: version, prev_block_hash, and merkle_root in the top row, then timestamp, bits, and nonce below, each labeled with its byte size and role. Five fields are set by existing data, while the nonce is the only field a miner chooses.</desc>
  <rect x="40" y="20" width="640" height="40" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="45" text-anchor="middle" font-size="14" fill="#ffffff" font-weight="bold">Block header (80 bytes total)</text>

<rect x="40" y="80" width="200" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="140" y="105" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">version</text>
  <text x="140" y="125" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">4 bytes</text>
  <text x="140" y="160" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">format flags</text>

<rect x="260" y="80" width="200" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="105" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">prev_block_hash</text>
  <text x="360" y="125" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">32 bytes</text>
  <text x="360" y="160" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">links to previous block</text>

<rect x="480" y="80" width="200" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="580" y="105" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">merkle_root</text>
  <text x="580" y="125" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">32 bytes</text>
  <text x="580" y="160" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">commits to all txs</text>

<rect x="40" y="210" width="200" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="140" y="235" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">timestamp</text>
  <text x="140" y="255" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">4 bytes</text>
  <text x="140" y="290" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">when mined</text>

<rect x="260" y="210" width="200" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="235" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">bits</text>
  <text x="360" y="255" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">4 bytes</text>
  <text x="360" y="290" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">difficulty target</text>

<rect x="480" y="210" width="200" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="580" y="235" text-anchor="middle" font-size="12" fill="#ffffff" font-weight="bold">nonce</text>
  <text x="580" y="255" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">4 bytes</text>
  <text x="580" y="290" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937" font-weight="bold">what miners search</text>

<text x="360" y="345" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">Five fields are determined by the world. The nonce is the only one a miner gets to choose.</text>
</svg>

Take a closer look at three of these because they carry most of the meaning.

**The prev_block_hash** is the hash of the previous block's header. This is what makes the chain a chain: every block points backwards to one specific predecessor by hash. Change anything in any past block, even one bit of one transaction, and the Merkle root in that block's header changes, which changes that block's hash, which means every block built on top of it has the wrong prev_block_hash and the entire later chain is invalidated. This is the structural source of permanence.

**The merkle_root** is a single 32-byte commitment to every transaction in this block. It's computed by building a Merkle tree over the transaction list and taking the root. This is why the header doesn't need to grow with the block: no matter how many thousands of transactions are in the block, the commitment to all of them fits in 32 bytes. A light client can ask for a Merkle proof that a specific transaction is in this block, and verify the proof against the merkle_root in the header, without ever downloading the rest of the block.

**The nonce** is the only field a miner gets to vary freely. It's a 4-byte integer the miner increments while searching for a header whose hash falls below the difficulty target. Every other field is fixed before the search begins.

## The mining puzzle

Now the puzzle itself. Bitcoin's proof-of-work puzzle has a clean statement.

> Given a block header that's almost complete, find a value for the nonce such that the double-SHA-256 hash of the entire header, treated as a number, is less than the difficulty target.

That's it. That's the whole puzzle.

The "treated as a number" framing matters. A SHA-256 hash is 32 bytes, which is 64 hex digits when written out. Treating that hex string as a single very large number, the puzzle is just to make that number small. And the easiest way to make a number small in hex notation is to have it start with zeros. A hash like `8a4f2e9c...` is huge. A hash like `0000abc...` is much smaller. A hash like `00000000000000abc...` is tiny.

So in practice, what miners are looking for is a hash that starts with a specific number of leading zero digits. Today, the target requires roughly 20 leading zero hex digits at the front of the hash before it qualifies. A miner who gets 18 leading zeros has missed and has to try a different nonce. A miner who gets 20 or more has found a winning block.

<svg role="img" viewBox="0 0 720 520" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Mining loop: pick nonce, hash header, compare to target, mine block</title><desc>A flowchart shows a miner picking a nonce, hashing the 80-byte header with double SHA-256, and checking if the hash is less than the target: if not, it tries the next nonce, and if yes, the block is mined and broadcast. Below, three example hashes show two failed attempts that are too big and a winning attempt with about 20 leading zero hex digits, smaller than the target.</desc>
  <defs>
    <marker id="arr43m" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
  </defs>

<text x="360" y="25" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">The mining loop</text>

<path d="M 600 80 Q 600 55 360 55 Q 120 55 120 80" fill="none" stroke="#565653" stroke-width="2" marker-end="url(#arr43m)"/>
  <text x="360" y="48" text-anchor="middle" font-family="monospace" font-size="11" fill="#ed4937" font-weight="bold">no, try the next nonce</text>

<rect x="40" y="80" width="160" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="120" y="105" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">pick a nonce</text>
  <text x="120" y="123" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">start at 0, increment</text>

<line x1="200" y1="110" x2="260" y2="110" stroke="#565653" stroke-width="2" marker-end="url(#arr43m)"/>

<rect x="260" y="80" width="200" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="105" text-anchor="middle" font-size="12" fill="#ffffff" font-weight="bold">double SHA-256</text>
  <text x="360" y="123" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">hash the 80-byte header</text>

<line x1="460" y1="110" x2="520" y2="110" stroke="#565653" stroke-width="2" marker-end="url(#arr43m)"/>

<rect x="520" y="80" width="160" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="600" y="105" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">hash &lt; target?</text>
  <text x="600" y="123" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">compare to bits</text>

<line x1="600" y1="140" x2="600" y2="190" stroke="#565653" stroke-width="2" marker-end="url(#arr43m)"/>
  <text x="615" y="168" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">yes</text>

<rect x="440" y="190" width="240" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="560" y="215" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">block is mined</text>

<text x="560" y="245" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">broadcast to the network</text>

<line x1="40" y1="275" x2="680" y2="275" stroke="#565653" stroke-width="1" stroke-dasharray="4 3"/>

<text x="360" y="305" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">What "hash &lt; target" looks like in practice</text>

<text x="40" y="335" font-family="monospace" font-size="11" fill="#565653">target:</text>
  <text x="120" y="335" font-family="monospace" font-size="11" fill="#000000">0000000000000000000000abc4f2e9c1b7d3a8...</text>

<text x="40" y="370" font-family="monospace" font-size="11" fill="#565653">attempt 1:</text>
  <text x="120" y="370" font-family="monospace" font-size="11" fill="#000000">8a4f2e9c1b7d3a8b2c5e9f0123456789abcdef...</text>
  <text x="480" y="370" font-family="monospace" font-size="11" fill="#ed4937" font-weight="bold">too big (fail)</text>

<text x="40" y="395" font-family="monospace" font-size="11" fill="#565653">attempt 2:</text>
  <text x="120" y="395" font-family="monospace" font-size="11" fill="#000000">00f2a91c4b7d3a8b2c5e9f0123456789abcdef...</text>
  <text x="480" y="395" font-family="monospace" font-size="11" fill="#ed4937" font-weight="bold">still too big (fail)</text>

<text x="40" y="420" font-family="monospace" font-size="11" fill="#565653">attempt N:</text>
  <text x="120" y="420" font-family="monospace" font-size="11" fill="#000000">00000000000000000000007a91c4b7d3a8b2c5...</text>
  <text x="480" y="420" font-family="monospace" font-size="11" fill="#ed4937" font-weight="bold">smaller than target, winner</text>

<text x="360" y="460" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">The hash must have around 20 leading zero hex digits today to qualify.</text>
  <text x="360" y="480" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">A miner runs this loop billions of times per second on dedicated hardware.</text>
  <text x="360" y="500" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">The whole network produces one winner roughly every ten minutes.</text>
</svg>

The diagram shows the structure. Mining is easiest to understand by doing it once yourself, even on a small scale. Try the puzzle below before reading on. Change the data, watch the hash flip wildly, then mine for a hash that starts with a few zeros and feel how the work scales with each extra zero you ask for.

[[block-mining]]

Three properties of the puzzle make the whole system work.

**The puzzle is hard.** SHA-256 has the avalanche property you've already seen, so there's no way to look at the target and calculate which nonce will produce a hash below it. You simply have to try different values until one works. The target is small enough that almost every attempt fails. Today the network as a whole performs roughly 10²¹ hash attempts per second. That's 1,000,000,000,000,000,000,000 attempts every second, across all the mining hardware on the planet. Even at that rate, finding one winning nonce takes about ten minutes.

**The puzzle is verifiable instantly.** Once a miner publishes a winning nonce, any node can hash the header once, compare to the target, and confirm in microseconds. Hard to solve, trivial to check. This asymmetry is what lets the network agree quickly on which miner won.

**The puzzle is tied to a specific block.** The hash inputs include the prev_block_hash and the merkle_root, so a winning nonce for one block doesn't transfer to any other block. The proof of work is bound to this exact block on this exact chain. Reorganising a winning nonce into a different chain is impossible.

## Mining pools

The puzzle's difficulty has an important consequence. A miner with a small share of global hashpower finds blocks rarely. With the network running at roughly 10²¹ hashes per second, an individual miner running modest hardware at a few terahashes per second would, on average, wait decades between finding a block. The reward when one finally lands is the same 3.125 BTC plus fees as anyone else's, but waiting decades for any income is not how a business runs. The standard response is **mining pools**. A pool operator builds a candidate block template and hands the header out to each connected miner along with the network's difficulty target. Each miner hashes nonces against that template exactly as they would solo. When a miner finds a hash that's below a much easier **share target**, easier than the real network target by some configurable factor, it submits that share to the operator as evidence of work done. The pool counts shares from everyone, and when one of the submitted hashes happens to also be below the real network target, the pool publishes the block and splits the reward in proportion to how many shares each miner contributed during the round. The participating miner's income changes from "3.125 BTC every several decades" to "a small payout every day," and practically every commercial miner today operates this way.

The cost is a centralization concern. A handful of large pools together control most of Bitcoin's hashpower at any given time. The pool operator is the one who decides which transactions go into the block template, so the operator effectively controls block contents on behalf of all the miners pointed at them. The underlying miners can switch pools if an operator misbehaves, and switching is fast with no lock-in, but the day-to-day power to censor or include transactions sits with a small number of pool operators rather than with the thousands of individual miners. This is one of the most-discussed structural tensions in Bitcoin and there's no clean fix to it.

## The coinbase transaction and the supply schedule

Every block has a special first transaction called the **coinbase transaction**. Unlike every other transaction in Bitcoin, the coinbase has no inputs from any prior UTXO. It creates value from nothing. The miner sets the value of its single output to whatever the protocol's current block subsidy is, plus the total fees from every other transaction in the block, and sends that to an address the miner controls.

This is the only mechanism that ever creates new BTC. Every bitcoin in circulation was originally minted as the output of some coinbase transaction by some miner at some point in the past.

The block subsidy starts at 50 BTC and halves every 210,000 blocks, which is roughly every four years. The halvings so far:

- Genesis (2009): subsidy was 50 BTC
- First halving (November 2012): dropped to 25 BTC
- Second halving (July 2016): dropped to 12.5 BTC
- Third halving (May 2020): dropped to 6.25 BTC
- Fourth halving (April 2024): dropped to 3.125 BTC, where it sits today
- Next halving (expected April 2028): will drop to 1.5625 BTC

<svg role="img" viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Bitcoin block subsidy halving, 50 BTC in 2009 down to 3.125 BTC by 2024</title><desc>A step chart shows the block subsidy in BTC on the y-axis and years from 2009 to 2028+ on the x-axis. The subsidy starts at 50 BTC and halves every 210,000 blocks, dropping to 25, 12.5, 6.25, and 3.125 BTC, approaching zero near year 2140 with total supply capped at 21 million.</desc>
  <line x1="60" y1="260" x2="680" y2="260" stroke="#000000" stroke-width="2"/>
  <line x1="60" y1="40" x2="60" y2="260" stroke="#000000" stroke-width="2"/>

<text x="40" y="265" text-anchor="end" font-family="monospace" font-size="10" fill="#565653">0</text>
  <text x="40" y="200" text-anchor="end" font-family="monospace" font-size="10" fill="#565653">12.5</text>
  <text x="40" y="140" text-anchor="end" font-family="monospace" font-size="10" fill="#565653">25</text>
  <text x="40" y="80" text-anchor="end" font-family="monospace" font-size="10" fill="#565653">50</text>

<text x="15" y="150" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653" transform="rotate(-90 15 150)">block subsidy (BTC)</text>

<line x1="60" y1="80" x2="180" y2="80" stroke="#ed4937" stroke-width="3"/>
  <line x1="180" y1="80" x2="180" y2="140" stroke="#ed4937" stroke-width="3"/>
  <line x1="180" y1="140" x2="300" y2="140" stroke="#ed4937" stroke-width="3"/>
  <line x1="300" y1="140" x2="300" y2="200" stroke="#ed4937" stroke-width="3"/>
  <line x1="300" y1="200" x2="420" y2="200" stroke="#ed4937" stroke-width="3"/>
  <line x1="420" y1="200" x2="420" y2="230" stroke="#ed4937" stroke-width="3"/>
  <line x1="420" y1="230" x2="540" y2="230" stroke="#ed4937" stroke-width="3"/>
  <line x1="540" y1="230" x2="540" y2="245" stroke="#ed4937" stroke-width="3"/>
  <line x1="540" y1="245" x2="640" y2="245" stroke="#ed4937" stroke-width="3"/>
  <line x1="640" y1="245" x2="640" y2="253" stroke="#ed4937" stroke-width="3"/>
  <line x1="640" y1="253" x2="675" y2="255" stroke="#ed4937" stroke-width="3" stroke-dasharray="4 2"/>

<text x="120" y="270" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">2009</text>
  <text x="180" y="280" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">2012</text>
  <text x="300" y="280" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">2016</text>
  <text x="420" y="280" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">2020</text>
  <text x="540" y="280" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">2024</text>
  <text x="640" y="290" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">2028+</text>

<text x="120" y="75" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">50</text>
  <text x="240" y="135" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">25</text>
  <text x="360" y="195" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">12.5</text>
  <text x="480" y="220" text-anchor="middle" font-family="monospace" font-size="9" fill="#000000">6.25</text>
  <text x="595" y="237" text-anchor="middle" font-family="monospace" font-size="9" fill="#000000">3.125</text>

<text x="360" y="20" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Block subsidy halves every 210,000 blocks</text>
  <text x="360" y="305" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">Asymptotically approaches zero around year 2140. Total supply caps at 21 million.</text>
</svg>

This schedule is enforced by the protocol. There's no governance body that can change it. A miner who tries to publish a block with an oversize coinbase output will have that block rejected by every honest node on the network. The constraint is structural.

Around the year 2140 the subsidy will drop to zero, and miners will be compensated only by transaction fees. The 21-million-coin cap follows from this halving schedule: each halving issues half as much as the previous, so the total approaches 21 million but never exceeds it. The cap isn't a separate rule. It's a consequence of the curve.

## Difficulty adjustment

One last mechanism keeps the system stable: the difficulty adjustment.

The proof-of-work target is what determines how hard it is to find a winning nonce. If global hashing power doubles overnight, and the target stays the same, blocks start coming faster than ten minutes apart. If global hashing power crashes, blocks come slower.

Either situation is bad for the network. So every 2,016 blocks (about every two weeks), every node recomputes the target. The recomputation is simple: if the last 2,016 blocks took longer than two weeks to produce, the target is loosened. If they took less, the target is tightened.

The effect is that long-run block times stay close to ten minutes regardless of how much hashing power is pointed at the network. The difficulty has risen by roughly a factor of 100 trillion since Bitcoin launched in 2009. The block time has stayed at ten minutes the whole way.

## FAQ

### What are Bitcoin miners searching for?

A four-byte nonce that makes the block header hash small enough. They change the nonce, hash the header twice with SHA-256, and check whether the result sits below the difficulty target, which today means roughly 20 leading zero hex digits. Nothing but trying trillions of nonces gets you there. Checking a winner takes one hash.

### How big is a Bitcoin block header?

Exactly 80 bytes, whatever the block holds. Version, prev_block_hash, merkle_root, timestamp, bits, and nonce, and only the nonce is the miner's to choose.

### Where does brand-new bitcoin come from?

The coinbase transaction, the first transaction in every block. It has no inputs and creates value from nothing. No other mechanism ever mints a bitcoin.

### What is the block subsidy now, and when does it halve again?

3.125 BTC since the April 2024 halving, paid to the winning miner along with every fee in the block. It started at 50 BTC in 2009 and halves every 210,000 blocks, roughly four years, through 25, 12.5 and 6.25. Next is 1.5625 BTC around April 2028. Near 2140 it reaches zero and only fees are left, and the 21 million cap is the sum of that curve.

### Why do miners join pools instead of mining alone?

A small miner can wait decades between blocks. In a pool everyone hashes the operator's block template and submits easier partial solutions called shares, and the reward is split by share count when one hash clears the real network target.

### Why does a block still take about ten minutes as more miners join?

Every 2,016 blocks, roughly two weeks, every node recomputes the difficulty target from how long the last batch took. Difficulty has risen by about a factor of 100 trillion since 2009 and the block time has not moved.

---

# The economics of proof of work

Source: https://academy.redduck.io/courses/blockchain-basics/bitcoin/the-economics-of-proof-of-work

The previous lesson explained what miners do. This one explains why they do it, and what their work gets the rest of the network. Both questions have economic answers. Mining is a competitive market where participants spend real electricity in the hope of winning block rewards, and the security of every transaction that has ever happened on Bitcoin rests on the fact that overwriting history is much more expensive than the alternative. By the end of this lesson, you should understand where Bitcoin's security guarantee actually comes from, what a 51% attack can and cannot do, and why "Bitcoin is secured by energy" is a literal statement rather than a metaphor.

## Mining is a market

A miner is in the business of turning electricity into bitcoin. There are two costs: the upfront cost of buying specialized hardware called **ASICs** (small machines built to do one thing, compute SHA-256 hashes as fast as possible) and the ongoing cost of the electricity to run them. The reward, when a miner wins a block, is the block subsidy plus the fees from every transaction in that block. Win a block, get the reward. Lose, get nothing.

Mining is competitive in two ways that matter for everything that follows.

First, every miner is fighting every other miner for the same fixed prize. The network produces one block every ten minutes, no matter how many people are mining. If you control 1% of the global hashing power, you'll win about 1% of the blocks over time. Doubling your hashing power doubles your expected revenue. But you're not creating new reward by doing this. You're taking a bigger share of the same fixed prize, leaving less for everyone else.

Second, electricity is the biggest cost, and electricity prices vary a lot. A miner running in a region with cheap hydroelectric power pays a fraction of what a miner running on retail grid power pays. So the miners who survive long-term are the ones with cheap electricity. Everyone else gets squeezed out when the bitcoin price dips.

Over time, this competition produces a predictable pattern. When the bitcoin price goes up or transaction fees rise, mining becomes more profitable, and more miners turn on their machines. Hashrate grows. The difficulty adjustment from the previous lesson happens every two weeks and tightens the puzzle so blocks still take ten minutes. When the price falls or fees shrink, less-efficient miners switch off, hashrate falls, and the difficulty loosens again. Block times stay at ten minutes through it all.

There's one consequence of this market structure that matters for the rest of the lesson. At every point in time, there is a miner just barely breaking even, one who would shut down tomorrow if their electricity bill went up by 5%. So the total amount of electricity being spent on mining is always close to the total reward being paid out, because anyone whose costs were much lower than their reward would attract competitors until prices equalised again.

In plain terms: the network spends roughly as much on mining as mining pays out. That sounds boring but it's the setup for the entire security argument.

<svg role="img" viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Mining as a market: a miner's costs and revenue, and network hashrate vs difficulty</title><desc>For one miner, hardware and electricity costs feed a mining loop that pays out block subsidy plus fees. Across all miners, total hashrate expands and contracts with miner economics, and difficulty retargets every 2,016 blocks to hold about a 10 minute block time.</desc>
  <defs>
    <marker id="arr44" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
  </defs>

<text x="360" y="25" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Mining as a market</text>

<text x="360" y="55" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653" font-style="italic">For one miner:</text>

<rect x="40" y="75" width="180" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="130" y="100" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Costs (in)</text>
  <text x="130" y="120" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">hardware + electricity</text>

<line x1="220" y1="105" x2="280" y2="105" stroke="#565653" stroke-width="2" marker-end="url(#arr44)"/>

<rect x="280" y="75" width="160" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="100" text-anchor="middle" font-size="12" fill="#ffffff" font-weight="bold">A miner</text>
  <text x="360" y="120" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">runs the mining loop</text>

<line x1="440" y1="105" x2="500" y2="105" stroke="#565653" stroke-width="2" marker-end="url(#arr44)"/>

<rect x="500" y="75" width="180" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="590" y="100" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Revenue (out)</text>
  <text x="590" y="120" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">block subsidy + fees</text>

<line x1="40" y1="180" x2="680" y2="180" stroke="#565653" stroke-width="1" stroke-dasharray="4 3"/>

<text x="360" y="210" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653" font-style="italic">Across all miners on the network:</text>

<rect x="40" y="230" width="280" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="255" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Total hashrate</text>
  <text x="180" y="275" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">expands and contracts with miner economics</text>

<line x1="320" y1="260" x2="400" y2="260" stroke="#565653" stroke-width="2" marker-end="url(#arr44)"/>
  <text x="360" y="252" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653" font-style="italic">retargets</text>

<rect x="400" y="230" width="280" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="255" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Difficulty</text>
  <text x="540" y="275" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">tunes every 2,016 blocks to hold ~10 min</text>

<text x="360" y="335" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">Cheap electricity wins. Total mining spending tracks total mining reward over time.</text>
</svg>

## Where Bitcoin's security comes from

Now the big question. Why is a transaction that's been confirmed in a Bitcoin block hard to reverse?

The short answer is that building the chain forward is cheap, but rewriting it is enormously expensive, and the gap between those two costs is where Bitcoin's security comes from.

When a transaction is included in block N, its security against being reversed depends on how many blocks have been built on top of it. Block N+1, then N+2, then N+3, and so on. Each of those later blocks required a successful proof-of-work search, which required on average the entire network's effort for ten minutes. To erase the transaction in N, an attacker has to publish an alternative chain that starts from block N's predecessor, doesn't include the transaction, and ends up longer than the chain everyone else is following.

This means two things have to happen at once. The attacker has to redo all the proof-of-work from the fork point forward. And while they're doing that, the honest network is still extending the current chain. So the attacker has to outpace the honest network's ongoing work while also catching up to it.

<svg role="img" viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Honest chain N to N+k versus attacker's alternative chain racing to catch up</title><desc>The diagram shows the honest chain growing block by block from N through N+1, N+2, N+3, up to N+k. Below it, an attacker's alternative chain from N' onward must reach N+k+1' before the honest chain reaches N+k+1, since each extra block adds another full network's worth of work to redo, so cost grows linearly with depth while the attacker's chance of success drops exponentially.</desc>
  <defs>
    <marker id="arr44b" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
  </defs>

<text x="360" y="25" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Why deeper blocks are harder to reverse</text>

<text x="40" y="65" font-size="12" fill="#000000" font-weight="bold">The honest chain:</text>

<rect x="40" y="80" width="60" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="70" y="105" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">N</text>

<line x1="100" y1="100" x2="120" y2="100" stroke="#565653" stroke-width="1.5" marker-end="url(#arr44b)"/>

<rect x="120" y="80" width="60" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="150" y="105" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">N+1</text>

<line x1="180" y1="100" x2="200" y2="100" stroke="#565653" stroke-width="1.5" marker-end="url(#arr44b)"/>

<rect x="200" y="80" width="60" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="230" y="105" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">N+2</text>

<line x1="260" y1="100" x2="280" y2="100" stroke="#565653" stroke-width="1.5" marker-end="url(#arr44b)"/>

<rect x="280" y="80" width="60" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="310" y="105" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">N+3</text>

<text x="365" y="105" font-family="monospace" font-size="13" fill="#565653">...</text>

<rect x="400" y="80" width="60" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="430" y="105" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">N+k</text>

<text x="40" y="170" font-size="12" fill="#ed4937" font-weight="bold">An attacker's alternative chain (must outpace the honest one):</text>

<rect x="40" y="190" width="60" height="40" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="70" y="215" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffffff">N'</text>

<line x1="100" y1="210" x2="120" y2="210" stroke="#ed4937" stroke-width="1.5" marker-end="url(#arr44b)"/>

<rect x="120" y="190" width="60" height="40" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="150" y="215" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffffff">N+1'</text>

<line x1="180" y1="210" x2="200" y2="210" stroke="#ed4937" stroke-width="1.5" marker-end="url(#arr44b)"/>

<rect x="200" y="190" width="60" height="40" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="230" y="215" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffffff">N+2'</text>

<text x="290" y="215" font-family="monospace" font-size="12" fill="#ed4937">...must reach N+k+1' before the honest chain reaches N+k+1</text>

<text x="40" y="270" font-family="monospace" font-size="11" fill="#000000">Each block deeper means another full network's worth of work to redo,</text>
  <text x="40" y="290" font-family="monospace" font-size="11" fill="#000000">on top of the work still being added to the honest chain. Cost grows linearly with depth,</text>
  <text x="40" y="310" font-family="monospace" font-size="11" fill="#000000">and probability of success drops exponentially as the attacker falls behind.</text>
</svg>

Concretely: to credibly reverse a transaction that's six blocks deep, an attacker would have to redo about an hour of the entire global network's effort, *while* matching the rest of the network's pace going forward. At Bitcoin's current scale, that requires assembling and running a fleet of specialized hardware comparable to the entire honest network. The hardware alone costs in the billions of dollars, much of it is already concentrated in the hands of large public mining companies, and the electricity to run it for the duration of the attack adds substantially to the bill.

This is what people mean when they say "Bitcoin is secured by energy." The security comes from cost rather than from cryptography being unbreakable. The cost of rewriting history is enormous, and that cost is paid in real-world resources an attacker would have to actually go out and buy. Forging the chain remains theoretically possible but practically *unprofitable*, and Bitcoin's whole security model rests on that gap.

## What a 51% attack can and cannot do

The textbook attack on a proof-of-work chain is the **51% attack**. The name comes from the idea that an attacker who controls more than half of the network's hashrate can, in expectation, produce blocks faster than the rest of the network, and therefore can win every fork-choice contest.

What this enables is narrower than the name suggests.

<svg role="img" viewBox="0 0 720 405" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Two-column list of what a 51% attacker can and cannot do</title><desc>The left column lists three things a 51% attacker can do: double-spend their own recent coins, censor transactions, and reorganise the recent chain. The right column lists four things it cannot do: steal coins from other addresses, create new BTC, cheaply rewrite ancient history, or change the protocol rules, each with a short reason.</desc>
  <text x="360" y="25" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">What a 51% attacker can and cannot do</text>

<rect x="40" y="60" width="320" height="305" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="200" y="90" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Can do</text>
  <line x1="50" y1="100" x2="350" y2="100" stroke="#565653" stroke-width="1"/>

<text x="55" y="125" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Double-spend their own coins</text>
  <text x="55" y="143" font-family="monospace" font-size="10" fill="#565653">in recent transactions, by reorganising</text>
  <text x="55" y="158" font-family="monospace" font-size="10" fill="#565653">the chain to exclude them</text>

<text x="55" y="190" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Censor transactions</text>
  <text x="55" y="208" font-family="monospace" font-size="10" fill="#565653">by refusing to include them in blocks</text>
  <text x="55" y="223" font-family="monospace" font-size="10" fill="#565653">the attacker mines</text>

<text x="55" y="255" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Reorganise the recent chain</text>
  <text x="55" y="273" font-family="monospace" font-size="10" fill="#565653">replace the last few blocks with</text>
  <text x="55" y="288" font-family="monospace" font-size="10" fill="#565653">a longer alternative they built</text>

<text x="55" y="320" font-family="monospace" font-size="10" fill="#565653" font-style="italic">All of the above degrades quickly as</text>
  <text x="55" y="335" font-family="monospace" font-size="10" fill="#565653" font-style="italic">the target transactions get older.</text>

<rect x="380" y="60" width="320" height="305" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="90" text-anchor="middle" font-size="13" fill="#ed4937" font-weight="bold">Cannot do</text>
  <line x1="390" y1="100" x2="690" y2="100" stroke="#565653" stroke-width="1"/>

<text x="395" y="125" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Steal coins from arbitrary addresses</text>
  <text x="395" y="143" font-family="monospace" font-size="10" fill="#565653">that would require forging signatures,</text>
  <text x="395" y="158" font-family="monospace" font-size="10" fill="#565653">which is a separate cryptographic problem</text>

<text x="395" y="190" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Create BTC out of thin air</text>
  <text x="395" y="208" font-family="monospace" font-size="10" fill="#565653">the coinbase subsidy is fixed by protocol</text>
  <text x="395" y="223" font-family="monospace" font-size="10" fill="#565653">and rejected by every honest node</text>

<text x="395" y="255" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Rewrite ancient history cheaply</text>
  <text x="395" y="273" font-family="monospace" font-size="10" fill="#565653">cost grows linearly with chain depth</text>
  <text x="395" y="288" font-family="monospace" font-size="10" fill="#565653">redoing years of work is prohibitive</text>

<text x="395" y="320" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Change the protocol rules</text>
  <text x="395" y="338" font-family="monospace" font-size="10" fill="#565653">block size, halving, etc. enforced by nodes</text>
</svg>

The "can do" side is real and worth taking seriously. An attacker who succeeds at a 51% attack can double-spend: deposit BTC at an exchange, wait for the deposit to be credited, withdraw that value, then rewrite history so the deposit transaction never happened. This is the most lucrative thing a 51% attacker can do, and it has happened on smaller proof-of-work chains where the cost of acquiring majority hashrate is low. It has not happened against Bitcoin itself, because acquiring majority Bitcoin hashrate would cost, depending on the moment, several billion dollars of hardware. Buying that much hardware would immediately drive prices up and signal to the entire industry that something was happening.

The "cannot do" side is what tends to surprise people. A 51% attack does not let the attacker steal coins from addresses they don't control, because moving coins requires a valid signature from the coin's owner. The attacker still has to satisfy the cryptographic locks on every output they want to spend. A 51% attack also doesn't change the protocol's economic rules. If the attacker mines a block with a coinbase reward of 100 BTC instead of the current 3.125, every other node on the network rejects that block as invalid. Hashrate doesn't override protocol rules. It just decides which valid blocks make it into the canonical chain.

The deepest reason 51% attacks against Bitcoin are rare is economic. An attacker who actually acquires majority Bitcoin hashrate has now invested billions in specialized hardware whose value depends entirely on Bitcoin's continued credibility. Successfully attacking Bitcoin would crash its price, devaluing the attacker's own hardware below scrap value. The attacker would have spent more on the attack than they could plausibly extract from it. This is the same dynamic that explains why the largest miners are typically the *most* invested in Bitcoin's health: they are the ones with the most to lose if the system fails.

## Why proof of work, fundamentally

The lesson on consensus made the argument abstractly. You need a way to prevent Sybil attacks without using identity, and the way to do that is to make participation costly. Proof of work is one specific instantiation of that principle: the cost is electricity, and the "vote" is hashrate. The energy is not a side-effect or a waste product. The energy *is* the security mechanism. To remove the energy expenditure would be to remove the very thing that makes attacks economically irrational.

Whether that tradeoff is the right one is a real question, and other approaches make different ones. A later lesson returns to that comparison. The narrow point of this lesson is that within Bitcoin's chosen design, the energy use is doing real work. It makes rewriting history vastly more expensive than leaving it alone, to the point where no rational actor attempts it.

## FAQ

### If someone controls 51% of Bitcoin's mining power, can they steal my coins?

No. Moving coins needs a signature from the owner, and hashrate does not break signatures. An attacker cannot mint BTC or change the rules either, since other nodes reject invalid blocks. They can double-spend their own recent coins, censor transactions, and reorganise the last few blocks.

### Why is a transaction harder to reverse the deeper it sits?

Each block above it cost the whole network about ten minutes of proof of work. Reversing a transaction six blocks deep means redoing roughly an hour of global effort while the honest network keeps extending the chain ahead of you.

### What does it mean to say Bitcoin is secured by energy?

Security comes from cost. Extending the chain forward is cheap. Rewriting it means redoing that proof of work with real electricity and hardware bought at market prices.

### What is an ASIC?

A machine built for one job, computing SHA-256 hashes as fast as possible.

### Why would a miner with enough hardware not just attack Bitcoin?

The economics do not work. Majority hashrate costs billions in ASICs whose value collapses along with Bitcoin's price, wrecking the attacker's own investment.

---

# Bitcoin-specific forks

Source: https://academy.redduck.io/courses/blockchain-basics/bitcoin/bitcoin-specific-forks

Bitcoin's rules can change, but the way they change is unusual. There's no CEO, no board, no committee that votes. The protocol moves when enough people running nodes voluntarily upgrade their software, and that's it. If consensus breaks, the network splits. This lesson walks through three real upgrade stories from Bitcoin's history to show what that looks like in practice.

## Soft fork, hard fork

Two kinds of changes matter for what comes next.

A **soft fork** is a backwards-compatible change. The new rules are stricter than the old ones, so anything that's valid under the new rules is still valid under the old ones. Old nodes don't notice the upgrade. The network stays one chain.

A **hard fork** is a change that the old rules don't accept. Blocks valid under the new rules look broken to old nodes. If both groups have miners, the chain splits into two.

The next three stories are: two successful soft forks, SegWit and Taproot, and one hard fork, Bitcoin Cash, that split the network in two.

<svg role="img" viewBox="0 0 720 260" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Bitcoin timeline: SegWit and Taproot soft forks, Bitcoin Cash hard fork</title><desc>A timeline runs from Bitcoin's launch in 2009 to 2021. Soft forks, SegWit (2017) and Taproot (2021), sit above the line, while the Bitcoin Cash hard fork (2017) sits below, showing where the chain split</desc>
<text x="360" y="30" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Three real Bitcoin upgrades</text>

<text x="180" y="80" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">soft forks (everyone stays on one chain) sit above</text>
<text x="180" y="200" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">hard forks (the chain can split) sit below</text>

<line x1="40" y1="140" x2="680" y2="140" stroke="#000000" stroke-width="2"/>

<circle cx="80" cy="140" r="6" fill="#000000"/>
  <text x="80" y="170" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">2009</text>
  <text x="80" y="185" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">Bitcoin launches</text>

<line x1="370" y1="140" x2="370" y2="115" stroke="#ed4937" stroke-width="2"/>
  <circle cx="370" cy="140" r="6" fill="#ed4937"/>
  <text x="370" y="105" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">SegWit</text>
  <text x="370" y="90" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">2017 · soft fork</text>

<line x1="445" y1="140" x2="445" y2="220" stroke="#ed4937" stroke-width="2"/>
  <circle cx="445" cy="140" r="6" fill="#ed4937"/>
  <text x="445" y="240" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Bitcoin Cash split</text>
  <text x="445" y="255" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">2017 · hard fork</text>

<line x1="600" y1="140" x2="600" y2="115" stroke="#ed4937" stroke-width="2"/>
  <circle cx="600" cy="140" r="6" fill="#ed4937"/>
  <text x="600" y="105" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Taproot</text>
  <text x="600" y="90" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">2021 · soft fork</text>
</svg>

## SegWit (2017): the upgrade that almost didn't happen

By 2016, Bitcoin had a known bug. Anyone watching the network could grab a signed transaction and slightly rewrite the signature in a way that kept it valid but changed the transaction's ID. The coins still went to the right place, but downstream software that tracked transactions by ID got confused. The Lightning Network, which was being designed around the same time, depended on stable transaction IDs and couldn't work until this bug was fixed.

The fix was an upgrade called **SegWit**. It restructured how signatures were stored so that they no longer affected the transaction ID. It also made room for more transactions per block as a side benefit. The change was technically a soft fork, meaning old nodes would keep working without trouble.

There was just one problem. Some of the biggest mining operations didn't want SegWit to activate, for reasons that had more to do with politics than technology. The activation mechanism required a supermajority of miners to signal support, and they refused. For most of 2017, SegWit was ready to activate and miners blocked it.

Then a movement of regular users acted on their own. They started running modified software that promised to reject any block that didn't signal support for SegWit, starting on a specific date. If miners kept blocking SegWit past that date, those miners' blocks would be ignored by a large part of the network, including most exchanges and big businesses. Their freshly mined bitcoin would be worth nothing to the people they wanted to sell to.

Within weeks of the deadline being announced, miner signaling jumped from a fraction to nearly all of them, and SegWit activated in August 2017.

Here is the lesson that matters most in this story. Miners produce blocks, but they don't decide what counts as a valid block. That's decided by the people running nodes, especially the ones connected to exchanges, custodians, and big merchants. When miners and nodes disagree, miners eventually have to give in, because they need someone to buy their bitcoin.

## Bitcoin Cash (2017): when consensus breaks

Not everyone wanted SegWit. A group of miners and developers thought Bitcoin should scale differently, by simply allowing bigger blocks. SegWit gave a modest capacity increase as a side effect. They wanted a much larger one as the main feature.

That disagreement was deep enough that no compromise emerged. So on the same day SegWit's user-imposed deadline was scheduled, the group that disagreed launched their own version of Bitcoin with bigger blocks. They called it **Bitcoin Cash**.

This is what a hard fork looks like in practice.

<svg role="img" viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Bitcoin hard fork splitting into Bitcoin (BTC) and Bitcoin Cash (BCH)</title><desc>A shared history box leads to a last shared point, which then splits into two paths: Bitcoin (BTC), where old rules continue, and Bitcoin Cash (BCH), a new chain with new rules. Notes explain that both chains share the same history up to the split and diverge forever after, and anyone holding 1 BTC at the split also held 1 BCH on the new chain.</desc>
<defs>
<marker id="arr45" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
</marker>
</defs>

<text x="360" y="25" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">A chain splits in two</text>

<rect x="40" y="100" width="120" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="100" y="125" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">shared history</text>

<line x1="160" y1="120" x2="200" y2="120" stroke="#565653" stroke-width="2" marker-end="url(#arr45)"/>

<rect x="200" y="100" width="100" height="40" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="250" y="125" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000">last shared</text>

<line x1="300" y1="115" x2="340" y2="85" stroke="#565653" stroke-width="2" marker-end="url(#arr45)"/>
  <line x1="300" y1="125" x2="340" y2="170" stroke="#565653" stroke-width="2" marker-end="url(#arr45)"/>

<rect x="340" y="60" width="240" height="50" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="460" y="80" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Bitcoin (BTC)</text>
  <text x="460" y="98" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">old rules continue</text>

<line x1="580" y1="85" x2="660" y2="85" stroke="#565653" stroke-width="2" stroke-dasharray="4 2"/>

<rect x="340" y="160" width="240" height="50" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="460" y="180" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffffff" font-weight="bold">Bitcoin Cash (BCH)</text>
  <text x="460" y="198" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">new chain, new rules</text>

<line x1="580" y1="185" x2="660" y2="185" stroke="#ed4937" stroke-width="2" stroke-dasharray="4 2"/>

<text x="360" y="245" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">Up to the split, both chains share the same history. After, they diverge forever.</text>
<text x="360" y="263" text-anchor="middle" font-size="12" fill="#565653" font-style="italic">Anyone holding 1 BTC at the split moment also held 1 BCH on the new chain.</text>
</svg>

The shared history is the key idea. Up to the split moment, both chains are literally the same chain. The same blocks, the same balances, the same transaction history. After the split, the two networks operate independently and never reconnect. Both still produce blocks today.

A side effect worth knowing: when a hard fork creates a new chain, everyone who owned coins on the original chain at the split moment also owns coins on the new chain. The same private key that controlled your BTC now also controls an equal amount of BCH, because both chains inherited the same state. This is what "fork airdrops" actually are. They aren't gifts from anyone. They're the automatic consequence of two chains sharing history up to the split.

Years later, Bitcoin's market value is roughly a hundred times bigger than Bitcoin Cash's. The market made its choice. But the BCH chain is still running and still has users, which is the point. In a decentralized system, you can't force people to agree. If they don't, they can leave with a copy.

## Taproot (2021): a smooth upgrade

After the SegWit fight, many people predicted Bitcoin would never be able to upgrade itself again without another community split. Taproot proved them wrong.

Taproot is a technical upgrade that improved Bitcoin's privacy and made certain advanced spending conditions cheaper. The technical details aren't the interesting part. The interesting part is that the activation was almost boring. The community broadly agreed it was a good idea. Miners signaled support without a fight. It locked in within months of being proposed for activation. It went live in November 2021 with no drama.

What Taproot shows is that Bitcoin's upgrade process is slow, but it works. When a change is well-designed and the community broadly agrees, the protocol can still evolve. The slow pace doesn't mean Bitcoin is frozen, just that it doesn't change unless there's strong agreement.

That slowness, though, is real. The ideas behind Taproot were proposed years before activation. From the first proposal to deployment took roughly four years. Other software platforms release comparable features in months. Bitcoin's slowness is a deliberate trade. The protocol settles hundreds of billions of dollars of value. If it could be changed quickly, it could be broken quickly too.

## Three takeaways

**Bitcoin upgrades through voluntary agreement.** No one is in charge. A change happens when enough miners, nodes, and users adopt it on their own. When that doesn't happen, the change fails or the network splits.

**Nodes are the real backstop.** Miners build blocks, but nodes decide which blocks count as valid. When miners and nodes disagree, nodes eventually win, because miners need their work to be accepted by the people who buy bitcoin.

**Soft forks are the safe path.** Most Bitcoin changes are soft forks because they don't risk a chain split. Hard forks are reserved for cases where backwards compatibility just isn't possible, and they only succeed if the community is unanimous. When it isn't unanimous, you get two chains.

## FAQ

### What is the difference between a soft fork and a hard fork?

A soft fork only tightens the rules, so old nodes keep accepting new blocks and the chain stays whole. A hard fork produces blocks old nodes reject, and the chain splits if both sides keep mining.

### What did SegWit fix?

Transaction malleability. Anyone could rewrite a signed transaction's signature so it stayed valid but carried a different transaction ID, which broke software that tracked transactions by ID and blocked the Lightning Network. SegWit moved signatures out of the ID calculation and made room for more transactions per block, activating in August 2017.

### Do miners decide Bitcoin's rules?

No. Miners produce blocks, but node operators decide which blocks count as valid, and exchanges, custodians, and large merchants run the ones that matter. In 2017 users ran software set to reject blocks that did not signal for SegWit, and miner support jumped within weeks.

### Where do fork airdrops like Bitcoin Cash come from?

Shared history. Both chains hold every block up to the split, so the key that controlled your BTC controls the same amount of BCH on the new chain.

### Has Bitcoin upgraded successfully since the SegWit fight?

Yes. Taproot activated in November 2021 with broad agreement and no drama, roughly four years after the ideas behind it were proposed.

---

# What Bitcoin gave up and what it gained

Source: https://academy.redduck.io/courses/blockchain-basics/bitcoin/what-bitcoin-gave-up-and-what-it-gained

Every design choice is a trade. Bitcoin made specific choices about what to optimise for, and those choices closed certain doors permanently while opening others. To finish the Bitcoin story honestly, we need to name both halves of the trade. What did Bitcoin sacrifice to become what it is? What did it get in return? And what does the rest of the blockchain world look like when you sit at a different point in the same design space? This lesson is the synthesis. By the end you'll have a working framework for thinking about any chain you meet later in the course, including the ones that chose almost the opposite of what Bitcoin chose.

## What Bitcoin gave up

The honest list is short and consequential.

**Throughput.** Bitcoin handles roughly seven transactions per second on the base layer. A small bank handles thousands. A credit card network handles tens of thousands. The 10-minute block time and the conservative block size limit are deliberate, but they cap how much business the base layer can do. Bitcoin is slow on purpose, and it will stay slow on purpose. Faster throughput on Bitcoin happens off the base layer, via Layer 2 systems like Lightning that settle to Bitcoin periodically rather than living on it.

**Expressiveness.** Script is intentionally limited. There are no loops, no recursion, no persistent state, no general computation. The kinds of applications most developers think of as "programming" cannot be written in Script. You can't build a lending market, an automated exchange, or a complex multi-party agreement in Bitcoin Script. You can write conditions for spending coins. That's it.

**Feature velocity.** Bitcoin upgrades on a four-year cadence at best. The discussion that produced Taproot started in 2018, and the upgrade activated in late 2021. Other chains release comparable features in months. Bitcoin's slowness is the source of its stability, but the cost is that ideas which could improve the system either wait years to be released or end up released on other chains first.

**Programmability of state.** Bitcoin tracks coins rather than arbitrary state. There's no concept of a contract that has its own balance and code that other transactions can call. The decentralised-finance applications already mentioned above exist on chains with a different state model, because Bitcoin's design intentionally rules out contracts that hold their own state.

**Privacy by default.** Every Bitcoin transaction is permanently visible to anyone with internet access. Tools and patterns (CoinJoin, Lightning, fresh addresses) can improve privacy, but the base layer is public. Some chains made privacy a first-class design goal. Bitcoin did not.

None of these are accidents. Each one is the direct consequence of a design choice from earlier lessons. Throughput is bounded by the conservative block time. Expressiveness is bounded by Script's deliberate limits. Feature velocity is bounded by Bitcoin's social conservatism around upgrades. And so on. To get the things Bitcoin lacks, you have to make different choices than the ones Bitcoin made.

## What Bitcoin gained

The other half of the trade.

**The deepest security record of any blockchain.** Bitcoin has run continuously since January 2009 without a successful attack on its consensus layer. The hash-linked chain has not been rewritten. The 21-million cap has not been violated. The signature scheme has not been broken. No other chain has anything close to that record, because no other chain has been around as long under that much economic pressure. Security is partly a measurement that only time can produce, and Bitcoin has the most of it.

**Credible neutrality of monetary policy.** The supply schedule (50 BTC subsidy halving every 210,000 blocks, asymptotically capping at 21 million) is enforced by every node and effectively impossible to change. No one can vote to issue more. No central party can decide to inflate. Whether or not the specific schedule is the right one is a separate debate. What is not debatable is that the schedule will continue to do exactly what it says. A monetary asset whose supply policy is fixed and verifiable is a rare thing.

**Operational simplicity.** A full Bitcoin node can be run by one person on consumer hardware. The whole protocol is small enough for one developer to understand fully. The validation rules are stable. The data structures are simple. This matters more than it sounds, because every additional complexity is somewhere a bug can hide. Bitcoin's narrow design surface is what makes it auditable by a global community of developers, and it's what makes node operation accessible to ordinary users.

**Conservatism as a feature.** A protocol that's hard to change is a protocol that's hard to break. Bitcoin's deliberate slowness around upgrades is the source of the predictability that makes long-term contracts and trust possible on the chain. If the rules could change rapidly, the asset's reliability as a store of value would erode along with the rules.

**Permissionless participation.** Anyone can run a node, mine a block, send a transaction, receive a payment, all without asking anyone's permission, anywhere on the planet, at any time. There's no KYC at the protocol level. There's no allowlist. The system runs on the same terms for everyone. This property has been preserved through more than fifteen years of regulatory pressure, technical change, and contentious internal debate. That preservation is itself evidence of how seriously the community treats it.

<svg role="img" viewBox="0 0 720 405" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Bitcoin's trade-off: what it gave up vs. what it gained</title><desc>A two-column chart titled "The trade in one picture." The left column, Gave up, lists throughput, expressiveness, feature velocity, stateful applications, and privacy by default, each with a short note explaining the limit. The right column, Gained, lists deepest security record, credible monetary policy, operational simplicity, predictability, and permissionless participation, each with a short note explaining the benefit.</desc>
  <text x="360" y="25" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">The trade in one picture</text>

<rect x="40" y="60" width="320" height="305" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="200" y="90" text-anchor="middle" font-size="13" fill="#ed4937" font-weight="bold">Gave up</text>
  <line x1="50" y1="100" x2="350" y2="100" stroke="#565653" stroke-width="1"/>

<text x="55" y="130" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Throughput</text>
  <text x="55" y="148" font-family="monospace" font-size="10" fill="#565653">~7 base-layer transactions per second</text>

<text x="55" y="180" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Expressiveness</text>
  <text x="55" y="198" font-family="monospace" font-size="10" fill="#565653">no general-purpose programmability</text>

<text x="55" y="230" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Feature velocity</text>
  <text x="55" y="248" font-family="monospace" font-size="10" fill="#565653">years between major upgrades</text>

<text x="55" y="280" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Stateful applications</text>
  <text x="55" y="298" font-family="monospace" font-size="10" fill="#565653">no contracts that hold balances or run code</text>

<text x="55" y="325" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Privacy by default</text>
  <text x="55" y="343" font-family="monospace" font-size="10" fill="#565653">every transaction is public</text>

<rect x="380" y="60" width="320" height="305" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="90" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Gained</text>
  <line x1="390" y1="100" x2="690" y2="100" stroke="#565653" stroke-width="1"/>

<text x="395" y="130" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Deepest security record</text>
  <text x="395" y="148" font-family="monospace" font-size="10" fill="#565653">15+ years of continuous operation</text>

<text x="395" y="180" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Credible monetary policy</text>
  <text x="395" y="198" font-family="monospace" font-size="10" fill="#565653">21M cap, enforced structurally</text>

<text x="395" y="230" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Operational simplicity</text>
  <text x="395" y="248" font-family="monospace" font-size="10" fill="#565653">runs on consumer hardware, auditable</text>

<text x="395" y="280" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Predictability</text>
  <text x="395" y="298" font-family="monospace" font-size="10" fill="#565653">rules are slow to change and well understood</text>

<text x="395" y="325" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Permissionless participation</text>
  <text x="395" y="343" font-family="monospace" font-size="10" fill="#565653">anyone can run a node, mine, or transact</text>
</svg>

The two columns are connected. Bitcoin couldn't have the security record without the conservatism. Couldn't have the operational simplicity without the limited Script. Couldn't have the credible monetary policy without the unchangeable supply schedule. You cannot keep the strengths while discarding the limitations. The strengths and the limitations are different views of the same set of choices.

## The design space

Bitcoin sits at one specific point in a design space with several axes. Other chains sit at different points. Once you've internalised this, you can read any blockchain's design as a series of explicit choices about where on each axis to land.

**Throughput vs decentralisation.** The 10-minute block time and small blocks keep Bitcoin verifiable by ordinary hardware. Chains that increased block sizes or reduced block times to handle more transactions usually had to accept that fewer participants could run full nodes. Throughput and decentralisation pull in opposite directions, and every chain picks a point on this axis.

**Expressiveness vs validation simplicity.** Bitcoin Script is intentionally weak. Chains with Turing-complete smart-contract languages can express richer programs but at the cost of more complex validation rules, more attack surface, and more cases where the protocol's behaviour is hard to reason about. The two values trade off.

**Feature velocity vs predictability.** Bitcoin moves slowly and changes very little. Other chains release new features regularly and accept that their rules will evolve. Both approaches are defensible. They optimise for different users.

**Issuance schedule.** Bitcoin's fixed supply is a choice rather than a law of nature. Other chains have continuous low inflation, deflationary mechanisms, dynamic issuance keyed to network activity, or no native token at all. Each model implies different assumptions about who should be compensated for securing the chain and how.

**Consensus mechanism.** Bitcoin chose proof of work and bought security through energy expenditure. Proof-of-stake chains buy similar properties (often less of them, often more efficiently) through bonded capital. The argument over which mechanism is "better" is really an argument about which costs and risks you'd rather pay.

These axes are the framework you'll take with you into the rest of the course. When you meet a new chain later, ask: where does it sit on each of these? What did it gain by choosing as it did? What did it accept losing?

## Why Bitcoin still matters

Even after the rest of the blockchain world built things Bitcoin can't do, Bitcoin remains the largest, most valuable, most decentralised, and most thoroughly tested blockchain in existence. Its market capitalisation has been larger than the next chain by a wide margin for almost its entire history. Its hashrate is orders of magnitude larger than any competing proof-of-work chain. Its node count is the highest. Its developer community is the most distributed.

These facts aren't decorative. They're evidence that Bitcoin's specific set of choices has produced something genuinely valuable in the world, even as other chains have produced different valuable things by making different choices. A complete blockchain education starts here because Bitcoin is the first chain to have worked, and it's the chain every other chain measures itself against. Every later track in this course assumes you've done this one first.

## FAQ

### How many transactions per second does Bitcoin handle?

Roughly seven on the base layer, capped by the ten-minute block time and a conservative block size. Lightning and other Layer 2 systems carry more volume and settle back to Bitcoin periodically.

### Why can't you build a lending market or an exchange on Bitcoin?

Script has no loops, no recursion, no persistent state, and no general computation. It expresses conditions for spending a coin and nothing more. There is no contract that holds its own balance and code, so those applications live on chains with a different state model.

### Is Bitcoin private?

No. Every transaction is permanently public. CoinJoin, Lightning, and fresh addresses help, but the base layer hides nothing.

### What did Bitcoin get in return for all those limits?

Continuous operation since January 2009 with no successful attack on its consensus layer. A supply schedule of 50 BTC halving every 210,000 blocks toward a 21 million cap, enforced by every node and beyond anyone's vote. A full node small enough for consumer hardware. And participation open to anyone, with no allowlist and no identity check at the protocol level.

### What should I compare when a new chain claims to beat Bitcoin?

Five axes. Throughput against decentralisation, expressiveness against validation simplicity, feature velocity against predictability, the issuance schedule, and the consensus mechanism. Bitcoin sits at one specific point on each. For any other chain, ask where it lands and what it accepted losing to get there.

---

# Bitcoin internals - Test

Source: https://academy.redduck.io/courses/blockchain-basics/bitcoin/bitcoin-internals

This test checks whether you understood the reasoning behind Bitcoin's specific design choices. The wrong answers often treat one of those choices as arbitrary, when it actually follows from another constraint.

---

# Public and private chains

Source: https://academy.redduck.io/courses/blockchain-basics/beyond-bitcoin/public-and-private-chains

> Bitcoin is one specific blockchain. There are many others. They differ in dozens of ways, but two questions capture the differences that matter most and let you place any chain you meet. First: who is allowed to run a node, validate transactions, and submit data to the chain? Second: what is the chain designed to do? This lesson walks through those two questions and where the major chains land on each.

## The first question: who can participate?

Every blockchain has rules about who is allowed to take part. Those rules sort chains into two broad camps.

A **public** blockchain lets anyone participate without permission. You can download the node software, run it on any computer, and start validating blocks. You can submit transactions without registering an identity. There is no operator who can ban you or revoke your access. Bitcoin is public. Ethereum is public. Most of the chains you'll encounter as a web3 developer are public.

A **private** blockchain restricts participation. Some operator controls who is allowed to run a node, who can validate, and who can submit transactions. The operator might be a company, a government, or a consortium. If you want in, you ask the operator. If they don't approve, you don't get in. Private chains are used inside companies and industry groups for shared record-keeping where the participants are known to each other but want the data structure of a blockchain.

The reason this distinction matters is bigger than it sounds. Almost everything that makes blockchains interesting comes from being public. Private chains keep the data structure of a blockchain but give up most of the guarantees that come from open participation. They are essentially shared databases with cryptographic auditing, run by a small group that trusts each other.

A subtype worth knowing exists in the middle: **consortium** or **permissioned** chains. These are private chains where the operator is a group rather than a single company. Banks running a shared settlement network, for instance, or a group of supply-chain participants tracking shipments together. The mechanics are the same as private chains. The governance is shared.

Two private-chain platforms are worth knowing by name because you'll encounter them in industry contexts. **Hyperledger Fabric** is a modular framework for building permissioned chains where participants run validating nodes and a separate ordering service decides transaction order. It was originally contributed by IBM and is now hosted by the Linux Foundation. It's used in supply chains, food traceability, and a number of inter-bank pilots. **R3 Corda** is built specifically for financial institutions and works a bit differently from most blockchains: instead of every node holding the full chain, each transaction is shared only between the parties involved and the regulators who need to see it. Both are mature, both have real production deployments, and both live in a world that almost never overlaps with the public chains the rest of this course teaches. If you end up at a bank or a large enterprise doing "blockchain," it'll usually be one of these.

<svg role="img" viewBox="0 0 720 340" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Public vs private chains: coin, smart-contract, and enterprise uses</title><desc>A 2x2 grid splits public chains, where anyone can join, from private chains, where an operator decides who joins. Public chains cover coin chains like Bitcoin and smart-contract chains like Ethereum and Solana, while private chains cover industry settlement (Hyperledger, Corda) and internal record-keeping for company enterprise deployments.</desc>
  <text x="360" y="25" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Public and private chains, and what they're for</text>

<text x="180" y="60" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Public chains</text>
  <text x="180" y="78" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">anyone can join</text>

<text x="540" y="60" text-anchor="middle" font-size="13" fill="#ed4937" font-weight="bold">Private chains</text>
  <text x="540" y="78" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">an operator decides who joins</text>

<line x1="360" y1="100" x2="360" y2="310" stroke="#000000" stroke-width="2" stroke-dasharray="4 3"/>

<rect x="40" y="100" width="280" height="90" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="125" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Coin chains</text>
  <text x="180" y="143" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">designed for tracking money</text>
  <text x="180" y="170" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Bitcoin</text>

<rect x="400" y="100" width="280" height="90" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="125" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Industry settlement</text>
  <text x="540" y="143" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">banks, supply chains, trade finance</text>
  <text x="540" y="170" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">Hyperledger, Corda</text>

<rect x="40" y="210" width="280" height="90" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="235" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Smart-contract chains</text>
  <text x="180" y="253" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">designed for running programs</text>
  <text x="180" y="280" text-anchor="middle" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Ethereum, Solana, and others</text>

<rect x="400" y="210" width="280" height="90" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="235" text-anchor="middle" font-size="12" fill="#000000" font-weight="bold">Internal record-keeping</text>
  <text x="540" y="253" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">company-internal chains</text>
  <text x="540" y="280" text-anchor="middle" font-family="monospace" font-size="11" fill="#565653">enterprise deployments</text>
</svg>

The rest of this course assumes public chains. The chain-specific tracks that follow teach development on public chains. If you end up working in enterprise blockchain (banks, logistics companies, government tracking systems), the patterns transfer but the deployment model is different.

## The second question: what is the chain for?

Within the public side of the map, there's a second distinction that matters more for developers than the first.

A **coin chain** is designed to track money. The transactions on it move value from one address to another. The chain's protocol cares about preventing double-spending, enforcing a supply schedule, and verifying signatures. It doesn't care about anything else, because there isn't anything else. Bitcoin is the canonical coin chain. Everything Bitcoin's protocol enforces is in service of "is this spend allowed, yes or no."

A **smart-contract chain** is designed to run programs. Transactions on a smart-contract chain can do more than move money. They can invoke contracts that read shared state, perform computations, conditionally move money, and write new state back. The chain's protocol cares about all the same things a coin chain cares about, plus a whole layer of execution: which contracts get called, what they're allowed to do, how much computation costs, and how the resulting state changes get committed. Ethereum is the canonical smart-contract chain. Solana is another. Most of the public chains active today fall on this side.

A **smart contract** is a program that lives at its own address on the chain, has its own storage, and can hold funds. You'll see the term constantly from here on. Anyone can call it by submitting a transaction that points at its address. When called, it runs whatever code its creator deployed and updates its own state accordingly. The chain enforces the contract's rules: whatever the code says happens, happens, and nobody can override that. Tokens, NFTs, decentralised exchanges, and lending markets are all smart contracts, or collections of them working together. These are the familiar things you've heard about on smart-contract chains. 

The distinction matters because it determines what's possible on the chain. On Bitcoin you can build payment systems. That's it. The locking conditions on UTXOs are deliberately limited. They can express payment rules and little else. On a smart-contract chain you can build payment systems. You can also build lending markets, exchanges, identity systems, prediction markets, in-game economies, ownership records, and arbitrary applications that combine those. The range of what you can build is far larger.

It also matters because it determines what kind of developer you'll be. Bitcoin development is a specialty focused on payment infrastructure and protocols built on top of Bitcoin. Smart-contract development is general-purpose application development on shared state, and it's where the bulk of web3 jobs are. The Solidity track this course leads into is smart-contract development on Ethereum. The Solana track is smart-contract development on Solana.

## Where to put any chain you meet

Whenever you come across a new chain, you now have the vocabulary to ask the first two questions about it.

**Is it public or private?** If public, it's part of the open web3 ecosystem and the patterns you've learned apply. If private, treat it as enterprise software with a blockchain data structure underneath.

**If public, is it a coin chain or a smart-contract chain?** Coin chains are payment-focused and intentionally limited. Smart-contract chains are general-purpose and run programs.

Those two questions categorize 95% of what you'll encounter. There are more axes but they sit underneath these two. The next lesson takes the smart-contract chain side of this map and asks a harder question: if smart-contract chains can do so much more than coin chains, why are they slower, more expensive, and harder to scale? That question has a name. It's called the blockchain trilemma.

## FAQ

### What is the real difference between a public and a private blockchain?

Permission. On a public chain anyone can run a node, validate, and submit transactions, and no operator can ban them. A private chain hands all three decisions to an operator, which makes it closer to a shared database with cryptographic auditing.

### Which private chain platforms will I meet in enterprise work?

Hyperledger Fabric and R3 Corda. Fabric is a modular permissioned framework from IBM, now hosted by the Linux Foundation, with a separate ordering service that decides transaction order. Corda targets financial institutions and shares each transaction only with the parties involved and their regulators.

### What is the difference between a coin chain and a smart-contract chain?

What a transaction is allowed to do. Bitcoin, a coin chain, only moves value, so its rules cover double-spends, supply, and signatures. Ethereum and Solana also run programs that read shared state, compute, and write new state back.

### What exactly is a smart contract?

A program with its own address on the chain, its own storage, and the ability to hold funds. Tokens, NFTs, decentralized exchanges, and lending markets are all smart contracts.

---

# The blockchain trilemma

Source: https://academy.redduck.io/courses/blockchain-basics/beyond-bitcoin/the-blockchain-trilemma

> Every blockchain has to make a trade-off between three things it would ideally have all of: security, decentralization, and scalability. Improving any one of them tends to weaken at least one of the others. This trade-off is so consistent across designs that it has a name: the blockchain trilemma. Once you have this framework, every chain you meet becomes a point in the same triangle. You can read its design choices as a specific bet about which two corners to prioritize. This lesson walks through the trilemma, places the major chains inside it, then looks at one of the ways modern designs try to escape it. That way out is called Layer 2.

## The three things every chain wants

Three properties matter for any blockchain. Briefly:

**Security** is how expensive it is for an attacker to overturn confirmed transactions or trick the network into accepting invalid ones. A chain with strong security has a long track record, a large pool of validators or miners with real economic stake, and rules conservative enough that attacks would cost more than they could plausibly earn. Bitcoin's security is the strongest of any chain by this measure.

**Decentralization** is how widely distributed the set of validating parties is. A decentralized chain has many independent nodes run by people in different jurisdictions, on hardware ordinary users can afford, with no single party able to influence the chain's behavior on their own. The classic test is: can a motivated hobbyist run a full node on a laptop? If yes, the chain is decentralized. If no, the chain is at best partially decentralized.

**Scalability** is how many transactions per second the chain can process while preserving the other two properties. A scalable chain can handle the throughput of a real consumer payment system without breaking. By 2025, the major credit card networks handle tens of thousands of transactions per second at peak. Most L1 blockchains handle far fewer.

## Why you can't easily have all three

The trilemma is the empirical observation that improving any of these three tends to weaken at least one of the others. The reasons aren't mysterious. They fall out of the design choices each property requires.

**Wanting more scalability** typically means bigger blocks, faster block times, or both. But bigger blocks take more storage and bandwidth to relay. This raises the cost of running a node, which thins out the set of people willing to run one. Fewer node operators means weaker decentralization. Faster block times mean less time for blocks to propagate across a global network before the next block is produced, which increases the chance of accidental forks and makes consensus less reliable, which weakens security.

**Wanting more decentralization** means keeping hardware requirements low enough that ordinary people can participate. But low hardware requirements cap how much computation, storage, and bandwidth the network can collectively handle, which caps throughput. Decentralization and scalability pull against each other on the base layer.

**Wanting more security** means long confirmation times, conservative upgrades, and heavy economic stake required to be a validator. All of those make the chain feel slow and inflexible. That's the trade-off Bitcoin has been making for more than fifteen years. Security comes from caution, and caution costs speed.

These aren't strict laws. They're observations about how the design choices tend to interact. A clever design might push the trade-off in one direction without hurting the others as much as the typical trade would, but no design has eliminated the trade-off entirely.

<svg role="img" viewBox="0 0 720 480" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Blockchain trilemma triangle plotting Bitcoin, Ethereum, and Solana</title><desc>A triangle has Security, Decentralization, and Scalability at its three corners. Bitcoin is placed near security and decentralization with a slow base layer, Ethereum sits balanced on L1 and pushes scaling to L2, and Solana sits near scalability with high throughput but heavier hardware needs.</desc>
  <text x="360" y="30" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">The blockchain trilemma</text>

<polygon points="360,80 140,420 580,420" fill="none" stroke="#000000" stroke-width="2"/>

<text x="360" y="65" text-anchor="middle" font-size="13" fill="#ed4937" font-weight="bold">Security</text>
  <text x="100" y="435" text-anchor="middle" font-size="13" fill="#ed4937" font-weight="bold">Decentralization</text>
  <text x="620" y="435" text-anchor="middle" font-size="13" fill="#ed4937" font-weight="bold">Scalability</text>

<circle cx="290" cy="220" r="6" fill="#000000"/>
  <text x="305" y="217" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Bitcoin</text>
  <text x="305" y="232" font-family="monospace" font-size="9" fill="#565653">strong security and</text>
  <text x="305" y="244" font-family="monospace" font-size="9" fill="#565653">decentralization,</text>
  <text x="305" y="256" font-family="monospace" font-size="9" fill="#565653">slow base layer</text>

<circle cx="370" cy="290" r="6" fill="#000000"/>
  <text x="385" y="287" font-family="monospace" font-size="11" fill="#000000" font-weight="bold">Ethereum</text>
  <text x="385" y="302" font-family="monospace" font-size="9" fill="#565653">balanced on L1,</text>
  <text x="385" y="314" font-family="monospace" font-size="9" fill="#565653">pushes scaling to L2</text>

<circle cx="450" cy="370" r="6" fill="#000000"/>
  <text x="435" y="367" font-family="monospace" font-size="11" fill="#000000" font-weight="bold" text-anchor="end">Solana</text>
  <text x="435" y="382" font-family="monospace" font-size="9" fill="#565653" text-anchor="end">high throughput,</text>
  <text x="435" y="394" font-family="monospace" font-size="9" fill="#565653" text-anchor="end">heavier hardware</text>

<text x="360" y="465" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">Closer to a corner means a chain prioritizes that property more.</text>
</svg>

The diagram is qualitative. There are no scores. Bitcoin sits up near the security/decentralization edge because that's what its design optimizes for, with the well-known cost of base-layer throughput. Solana sits closer to the scalability/security edge, with higher hardware requirements that cap how many people can run a full node. Ethereum tries to balance all three on L1 by being conservative about base-layer throughput. It pushes the scaling work off the base layer entirely, to a category of systems called Layer 2.

Both terms come up in every discussion of blockchain scaling. A **Layer 1** or **L1** is a base blockchain that runs by itself and doesn't depend on any other chain for security. Bitcoin is an L1. Ethereum is an L1. Solana is an L1. When you hear the phrase "base layer," it means the L1. A **Layer 2** or **L2** is a system built on top of an L1 that handles transactions separately but periodically writes its state back down to the L1, inheriting the L1's security in the process. An L2 is not a standalone chain. It relies on an L1 as its foundation for security. Bitcoin has Lightning as its main L2. Ethereum has several. Solana, designed for high base-layer throughput, has fewer.

## Layer 2 as a way out

If the trilemma forces every L1 to compromise on one of the three properties, the natural question is whether you have to do everything on the L1. The answer, increasingly, is no.

The idea is simple. The L1 is slow, expensive, and very secure. So use the L1 for what it's best at: providing strong final settlement that nobody can roll back. Above it, run a faster system that handles the actual transaction volume. That faster system regularly anchors its state back to the L1, inheriting the L1's security guarantees without being constrained by the L1's throughput.

There are three styles of L2 worth knowing by name. Each one approaches the same problem differently.

<svg role="img" viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Layer 2 on top of Layer 1, with periodic summaries settling to L1</title><desc>A top box labeled Layer 2 is fast, cheap, and handles everyday activity, sitting above a bottom box labeled Layer 1 that is slow, expensive, and provides final settlement. An arrow points down from Layer 2 to Layer 1, labeled periodic summaries written down to L1.</desc>
  <defs>
    <marker id="arr52" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
  </defs>

<text x="360" y="30" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">How Layer 2 sits on top of Layer 1</text>

<rect x="40" y="60" width="640" height="90" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="360" y="90" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Layer 2</text>
  <text x="360" y="110" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">fast, cheap, lots of transactions</text>
  <text x="360" y="135" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">handles the everyday activity</text>

<line x1="360" y1="160" x2="360" y2="200" stroke="#565653" stroke-width="2" marker-end="url(#arr52)"/>
  <text x="370" y="183" font-family="monospace" font-size="10" fill="#565653" font-style="italic">periodic summaries written down to L1</text>

<rect x="40" y="210" width="640" height="90" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="360" y="240" text-anchor="middle" font-size="13" fill="#000000" font-weight="bold">Layer 1</text>
  <text x="360" y="260" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">slow, expensive, very secure</text>
  <text x="360" y="285" text-anchor="middle" font-family="monospace" font-size="10" fill="#565653">provides final settlement</text>
</svg>

**Payment channels** are Bitcoin's primary L2 approach. Two parties open a "channel" by jointly locking up some bitcoin on the L1. Once the channel is open, they can exchange any number of transactions privately between themselves, with each transaction updating the balance inside the channel. None of these intermediate transactions touch the L1. When the parties are done, they close the channel by writing the final balance back to Bitcoin. The L1 sees two transactions, open and close, regardless of how many transactions happened inside. The Lightning Network is the largest deployed example of this pattern.

**Optimistic rollups** run on Ethereum and similar smart-contract chains. They execute many transactions off the L1, batch them together, and post the batch to L1 with a summary of the resulting state. The L1 accepts the summary as valid unless someone challenges it within a fixed time window, typically about a week. If a challenge succeeds, the disputed batch is rolled back. The "optimistic" name comes from this assumption that batches are valid unless proven otherwise. They're cheap and fast to operate, but withdrawals from the L2 back to the L1 take the length of the challenge window to finalize.

**ZK rollups** do the same thing but instead of "trust unless challenged," they post a cryptographic proof along with each batch that mathematically demonstrates the off-chain execution was correct. The L1 verifies the proof and either accepts or rejects the batch immediately. ZK rollups are more complex to operate but withdrawals back to L1 are fast because there's no challenge window. The math behind ZK proofs is one of the deepest topics in modern cryptography and gets a proper treatment in the Ethereum track.

The broader idea behind these L2s, sometimes called the **modular blockchain thesis**, is that a chain doesn't have to do everything itself. It can specialize in one job, typically settlement and data storage, and let other layers handle execution, ordering, and fast finality. Each layer trades off the trilemma differently, but when you stack them together, the combined system can be stronger along all three axes than any single chain could be alone.

This is a deliberate escape from the trilemma's hardest limitation. It doesn't break the trade-off, but it lets different layers in the stack make different bets, with the most security-sensitive operations anchored to the slowest and most decentralized layer.

## FAQ

### What is the blockchain trilemma?

A chain trades security, decentralization, and scalability against each other, and pushing one usually costs at least one of the others. Bigger blocks raise throughput and also raise the cost of running a node, which reduces the number of node operators.

### What is the difference between Layer 1 and Layer 2 in blockchain?

An L1 runs by itself and secures itself. Bitcoin, Ethereum, and Solana are L1s. An L2 sits on top of an L1, handles transactions separately, and periodically writes its state back down to inherit the L1's security. Lightning is Bitcoin's main L2.

### What is the difference between optimistic rollups and ZK rollups?

How they prove a batch is correct. An optimistic rollup treats each batch as valid unless someone challenges it inside a window of about a week, so a withdrawal to L1 takes that long to finalize. A ZK rollup posts a cryptographic proof that the L1 verifies immediately, so withdrawals are fast and the system is harder to operate.

### Does Layer 2 break the trilemma?

No. It lets each layer trade the three properties differently, with the most security-sensitive operations anchored to the slowest and most decentralized layer.

### How do I tell whether a chain is really decentralized?

Ask whether a motivated hobbyist can run a full node on a laptop. If the hardware rules that out, the chain is at best partially decentralized.

---

# What you know now

Source: https://academy.redduck.io/courses/blockchain-basics/beyond-bitcoin/what-you-know-now

You started this course not knowing what a hash was. Now you can explain what makes SHA-256 a cryptographic hash function and why blockchains need that property. You can describe how a Merkle tree commits to thousands of items in 32 bytes. You can walk through how a signature works, how a wallet derives keys from a seed phrase, and what gets exposed when a phrase leaks. You understand consensus as the problem of agreeing on transaction ordering across a network of mutually untrusted nodes. You understand why naive voting can't solve it. You know what Bitcoin actually does, why its design choices were made the way they were, and what it gave up in exchange for what it gained. You can place any chain you encounter on the trilemma and read its design as a set of tradeoffs.

That's not a small thing. Most of what circulates about blockchains lives as disconnected fragments, phrases like "blockchains are immutable," "Bitcoin uses proof of work," or "smart contracts," passed around without the mechanics underneath. You now have the underlying machine in your head, and from here you can read any whitepaper, evaluate any new protocol's claims, and recognize when someone is making things up.

## What's next

The basics are done. From here, the path branches. The Solidity track teaches you to write the contracts that run on Ethereum and the chains that copied its design. The Solana track teaches you to write programs in Rust against a different account model and execution environment. You can take either, both, or neither, and use what you learned here to follow the field on your own. The foundation you built does not expire.

## FAQ

### After finishing the blockchain basics course, what should I be able to do?

Explain what makes SHA-256 a cryptographic hash function, how a Merkle tree commits to thousands of items in 32 bytes, and how signatures and seed-phrase wallets work. Describe consensus as agreeing on transaction ordering across untrusted nodes and what Bitcoin gave up to get it. Place any chain on the trilemma and read its design as a set of trade-offs.

### Should I learn Solidity or Solana development after the basics course?

Either, both, or neither. Solidity means writing the contracts that run on Ethereum and the chains that copied its design. Solana development means writing Rust programs against a different account model and execution environment.

---

# What Ethereum course is about

Source: https://academy.redduck.io/courses/development-on-ethereum/welcome-to-ethereum/what-ethereum-course-is-about

This is the part of the course where you stop reading about blockchains and start building on one. The basics module taught you what Ethereum is and how it works. This track teaches you how to write the contracts that run on it. It is a separate track because there is a real gap between understanding a blockchain and being able to build something on one that holds funds without losing them. Closing that gap is what these lessons are for.

## What's in the course

This track starts with the language itself. Syntax first, then types, then the pieces that hold a contract together: functions, errors, events, modifiers, inheritance. Once the language is in place, the course moves to the patterns that production contracts are built from. Tokens, access control, upgradeability, oracles, AMMs, lending, governance. Each topic builds on the previous ones, so by the time you reach the harder material you already have the vocabulary to read it.

## How it works

Testing runs alongside every project. Simple unit tests early, fuzz and invariant tests later. Security runs through the whole course rather than appearing as one lecture at the end. You'll build vulnerable versions of real patterns, watch them get drained, and fix them with the test that would have caught the original bug. The point is not to memorize Solidity syntax. The point is to develop the mental models that let you reason about what your contract does when an attacker runs it on a chain you do not control.

## FAQ

### What does the Ethereum track cover?

Solidity first, from syntax and types through functions, errors, events, modifiers, and inheritance. Then the patterns production contracts are built from: tokens, access control, upgradeability, oracles, AMMs, lending, governance.

### Is security a separate module or part of every topic?

Part of every topic. You build vulnerable versions of real patterns, watch them get drained, and fix them with the test that would have caught the bug. Testing runs alongside every project, unit tests early, fuzz and invariant tests later.

---

# What Ethereum is

Source: https://academy.redduck.io/courses/development-on-ethereum/welcome-to-ethereum/what-ethereum-is

> Bitcoin is a network for moving one specific asset. Ethereum is a network for running arbitrary programs that touch any asset. Think of Ethereum as a single computer that the entire world shares. Every program written for it runs forever, and no single party can switch the network off.

## The shape of the thing

You already know what a blockchain is. A network of nodes that hold the same data, agree on what gets added next, and chain blocks together with hashes so nothing can be quietly changed. Bitcoin is one chain built on that idea, optimised for one job: moving its native coin around securely.

Ethereum starts from the same architecture and asks for more: arbitrary state, and arbitrary programs that touch that state. Same gossip network, same consensus mechanism conceptually, same hash-linked blocks. The protocol just makes room for a lot more to live on the chain.

The way to picture this: imagine a single global computer. It has memory, storage, and a CPU. Anyone can read its state. Anyone can submit a request to change its state, as long as they pay for the work. The computer runs the request, updates its state, and the change is permanent. There is no operator, no admin, no off-switch. The whole computer is replicated across thousands of machines worldwide that all agree on what the state currently is.

That picture is what people mean when they call Ethereum the "world computer." It's a useful shorthand, even if it leaves out a lot of detail.

## What lives on it

Two kinds of things have addresses on Ethereum and can hold state.

The first is a regular user account, called an **externally owned account** or EOA. It's controlled by a private key, exactly like a Bitcoin address. You sign transactions with the key to spend your ETH or call a smart contract (the second account type, described next). EOAs hold a balance of ETH and a counter called a nonce. Nothing else.

The second is a **smart contract** account. It's controlled by code rather than a private key. When a contract is created, its code gets deployed to a new address on the chain. That code stays at that address for as long as the chain runs, and no one else can change it or take it down. The one historical exception is selfdestruct: for years a contract could erase its own code with that operation, though a later change to the protocol has since removed almost all of its effect. Anyone can send a transaction to a contract's address. When they do, the contract's code runs. It can read and write its own storage, send ETH around, and call other contracts. The result of the code's execution becomes part of the global state.

The state of Ethereum at any moment is the combined state of every account, EOA and contract, that has ever existed on the chain. Balances, nonces, contract code, contract storage. Every node holds this state, updates it as new blocks arrive, and rejects any block whose transactions don't transition the state correctly.

## What you pay for and why

Running code on a global shared computer is not free. Every operation a contract performs costs something. Adding two numbers is cheap. Writing to storage is expensive. Reading from storage is somewhere in the middle.

The unit of cost is called **gas**. Every transaction declares a maximum amount of gas it's willing to consume, and a price it's willing to pay per unit. The fee paid is gas-used × gas-price, denominated in ETH. The fee goes to whoever produces the block that includes the transaction, with a portion burned out of circulation since 2021.

Gas exists for two reasons. First, computation on every full node has to be paid for or the network gets spammed. Second, gas bounds execution. A transaction cannot run forever because it cannot consume more gas than it brought with it. When the gas runs out, the transaction is reverted, the state changes it made are undone, and the gas spent so far is kept by the producer as the cost of the attempt.

ETH is also what most values on Ethereum are denominated in. New ETH enters circulation as block rewards to validators. Combined with the fee burn mentioned above, this means the total supply can rise or fall rather than only growing.

## What you can do that you couldn't do with Bitcoin

The point of the smart-contract layer is that you can put logic on the chain that anyone can interact with and that runs by its own rules forever.

Some examples of what's been built:

- Tokens that aren't ETH, with their own balances and rules
- Stablecoins pegged to fiat
- Decentralised exchanges where users swap tokens without an exchange company holding their funds
- Lending markets where deposits earn yield and borrows post collateral
- Governance systems where token holders vote on parameter changes that the chain applies automatically
- Identity records, ownership records, on-chain games, prediction markets

The common thread: anything that can be expressed as a program whose rules are public and whose execution cannot be censored.

None of this is possible on Bitcoin's protocol, by design. Bitcoin keeps its scripting minimal so the protocol stays small, the security argument stays simple, and the surface area for bugs stays narrow. Ethereum made the opposite trade. It accepted a much larger protocol and a much bigger bug surface in exchange for being able to host programs that look like real applications. You will see the cost of this trade as you learn what can go wrong in smart contracts, but you will also see what it unlocks.

## Where this fits

Ethereum is one specific design for a smart-contract chain among many. Solana, Aptos, Sui, Sei, Avalanche, BNB Chain, and many others are also smart-contract chains, each with different choices about account models, consensus, gas pricing, throughput, and developer experience.

Ethereum matters disproportionately for two reasons. It got there first as a general-purpose smart-contract chain, which is why the design vocabulary the whole industry uses is mostly Ethereum's. And it's where the most valuable applications live by a wide margin: at the time of writing, hundreds of billions of dollars sit in contracts on Ethereum or on chains that copied Ethereum's execution model. Those copies are what people mean when they say "EVM-compatible." When you learn Ethereum, you learn a model that runs on dozens of other chains too, with small variations.

The chain you'll actually write code for in this course is Ethereum. The patterns you learn will transfer, with some adjustment, to the rest of the EVM world. The deeper differences with non-EVM chains like Solana belong to a separate track.

## FAQ

### What is gas on Ethereum and why do I pay it?

The unit of cost for running an operation. Adding numbers is cheap, writing storage is expensive. Your fee is gas used times gas price, paid in ETH. Gas pays every node for the work and keeps a transaction from running forever.

### How does a contract account differ from a user account?

An externally owned account, the user kind, is controlled by a private key and holds an ETH balance and a nonce. A contract account is controlled by code deployed at its address and has storage of its own.

### If my transaction runs out of gas, do I get that gas back?

No. Every state change it made is undone and the block producer keeps what you spent.

---

# Where Ethereum came from

Source: https://academy.redduck.io/courses/development-on-ethereum/welcome-to-ethereum/where-ethereum-came-from

> Ethereum exists because someone looked at Bitcoin in 2013 and got frustrated. The frustration was specific: Bitcoin's scripting language was deliberately too limited to build real applications on top of, and the Bitcoin community wanted to keep it that way. The someone was a nineteen-year-old named Vitalik Buterin, who had been writing for Bitcoin Magazine and trying to convince the Bitcoin developers to add the features he wanted. When that didn't work, he wrote a whitepaper proposing his own chain.

## The problem Vitalik wanted to solve

In 2012 and 2013, a small group of people had started trying to build things on top of Bitcoin. Coloured coins, which used tiny amounts of bitcoin to represent other assets. Counterparty, which embedded its own protocol inside Bitcoin transactions. Mastercoin, an early attempt at a tokenisation layer. All of these existed because Bitcoin was the only chain anyone trusted, but Bitcoin's scripting language couldn't really express what they wanted to do.

Bitcoin Script, as you learned in the previous module, is deliberately minimal. No loops, no persistent state, no way for one script to call another. The Bitcoin developers had made this choice on purpose, to keep the protocol small and the security argument simple. They were not going to add features just because some app developers wanted them.

Vitalik concluded that the answer was not to keep pushing Bitcoin to add features, but to start over with a new chain designed to run arbitrary programs. He drafted a whitepaper in November 2013 describing what such a chain would look like, sent it to a few people for feedback, and within weeks had a small group of co-founders willing to build it with him.

The whitepaper called it Ethereum. The name comes from luminiferous ether, a medium that nineteenth-century physicists thought light travelled through. Vitalik liked the connotation of an invisible substrate that everything else runs on top of.

## The launch

A team formed quickly. Gavin Wood, a British software engineer, joined and wrote what's now called the Yellow Paper. The Yellow Paper is the formal technical specification of Ethereum, and it turned the whitepaper's ideas into something implementable. Joe Lubin, a Canadian who had been working in finance, put in early funding and would later go on to found ConsenSys. Charles Hoskinson, briefly the CEO, would later leave to start Cardano. There were others. It was a chaotic founding period with a lot of disagreement about whether Ethereum should be a non-profit foundation or a for-profit company. The non-profit camp won.

In mid-2014, the foundation ran a public crowdsale. Anyone could send bitcoin in exchange for ether at a fixed rate. The sale raised about 31,000 BTC (worth around eighteen million dollars at the time) and distributed sixty million ETH to participants. This was unusual and somewhat controversial. Some saw it as a clean way to fund development without venture capital. Others saw it as the start of a model where every new chain would launch its own token and sell it to early backers, which is roughly what happened.

The network went live on July 30, 2015, in a release called Frontier. It was deliberately rough. The user-facing tooling barely existed. The first version of Solidity had been released a few months earlier. The Ethereum Virtual Machine ran the contracts, mining secured the chain, and people started deploying programs. Within a year there were thousands of contracts on the network, most of them experiments.

## The DAO and the fork

The most consequential event of Ethereum's early years happened in 2016. A group of developers had built a contract called The DAO, which stood for "decentralised autonomous organisation." It was an on-chain investment fund. Anyone could deposit ETH and receive DAO tokens that voted on what investments the fund should make. The DAO became enormously popular. By the time it stopped accepting deposits, it held about 11.5 million ETH, roughly fifteen percent of all the ETH in existence at the time, worth around 150 million dollars.

The DAO had a bug. It was reentrant. When a user requested to withdraw their ETH, the contract sent them the ETH before updating its internal records of who owned what. An attacker noticed and exploited it. They called the withdrawal function recursively, draining ETH faster than the contract could update its bookkeeping. Over the course of a few days, they moved about 3.6 million ETH out of the DAO and into a separate contract.

This put the Ethereum community in an impossible position. The DAO was a contract, and the attacker had followed its rules. There was no protocol violation. By one principle, smart contracts are autonomous and their rules are the rules, so the ETH should stay with the attacker. By another, fifteen percent of all ETH being stolen would destroy the network's credibility, so the community needed to do something.

After weeks of debate, the Ethereum developers proposed a hard fork: a one-time, deliberate change to the protocol that would reverse the attacker's transactions and return the ETH to its original owners. Most of the community supported this. A minority did not, on the grounds that doing so violated the principle of immutability that made Ethereum worth using in the first place. The fork happened in July 2016. The minority who refused to follow the fork kept running the original chain, which is now called Ethereum Classic. The majority went on with the new chain, which kept the name Ethereum.

This event shaped Ethereum culturally for years. It established that the community could and would intervene in extraordinary circumstances, which is a comforting property for some users and a worrying one for others. It also forced the developers to take smart contract security seriously, because The DAO bug had been visible in the code if anyone had known what to look for.

## The road to proof of stake

Ethereum launched with proof of work, the same consensus mechanism Bitcoin uses, where miners burn electricity to compete for the right to produce blocks. The founders had said from the beginning that they wanted to move to proof of stake, where validators put up ETH as collateral and lose it if they misbehave. Proof of stake would use a fraction of the energy.

The transition took years. Various intermediate designs were proposed and revised. A separate chain called the Beacon Chain launched in December 2020 to test the proof-of-stake mechanism in isolation, without yet running real transactions. For nearly two years, the two chains ran in parallel: the original Ethereum chain processing transactions with mining, and the Beacon Chain finalising blocks with staking but not actually running any applications.

In September 2022, the two were combined. The event was called the Merge. The original Ethereum chain stopped using mining and started taking its consensus from the Beacon Chain instead. From one block to the next, Ethereum stopped being a proof-of-work network and became a proof-of-stake network. No contract was interrupted. It was one of the most complex software upgrades ever attempted on a live system holding hundreds of billions of dollars, and it worked.

Since the Merge, Ethereum has continued releasing upgrades on a regular schedule. The Shanghai upgrade in 2023 let stakers withdraw their ETH. The Dencun upgrade in 2024 introduced cheaper data storage for layer-2 rollups, which we'll cover later. The Pectra upgrade in 2025 brought changes to staking and to how regular accounts can temporarily act like smart contracts.

## Where things stand

Ethereum today is the dominant smart-contract platform by every measure that counts. Hundreds of billions of dollars sit in contracts on the network. Tens of thousands of developers build on it. Most of the other smart-contract chains in existence either copy its execution environment directly or position themselves explicitly as alternatives to it.

The original team has scattered. Vitalik still leads research at the Ethereum Foundation. Gavin Wood went on to found Polkadot. Joe Lubin runs ConsenSys, which builds MetaMask and other Ethereum-adjacent infrastructure. Charles Hoskinson runs Cardano. The network itself is governed by no one. There is no company in charge. In practice it's shaped by the Foundation's research, the major client teams, the application developers, and the validators who run the network.

Most of what makes Ethereum interesting from here is not its history but what gets built on it next. Which is the rest of this course.

## FAQ

### Why did Vitalik Buterin start a new chain instead of extending Bitcoin?

Bitcoin's scripting language was deliberately limited and its developers wanted the protocol to stay small. Buterin, then nineteen, wrote a whitepaper in November 2013 for a chain designed to run arbitrary programs instead.

### Where does the name Ethereum come from?

Luminiferous ether, the medium nineteenth-century physicists thought light travelled through. Buterin wanted the image of an invisible substrate everything else runs on.

### When did Ethereum launch and how was it funded?

July 30, 2015, in a release called Frontier. The 2014 crowdsale raised about 31,000 BTC, around eighteen million dollars then, and distributed sixty million ETH.

### What was the DAO hack and why did it split the chain?

An on-chain investment fund holding about 11.5 million ETH, roughly fifteen percent of the supply, sent users their ETH before updating its records. An attacker called the withdrawal repeatedly and drained about 3.6 million ETH. A hard fork in July 2016 reversed the theft, and the minority who refused it kept running the original chain, now called Ethereum Classic.

### When did Ethereum switch to proof of stake?

September 2022, in an upgrade called the Merge. The Beacon Chain had run proof of stake in parallel since December 2020, and the main chain switched to it from one block to the next.

---

# The account model

Source: https://academy.redduck.io/courses/development-on-ethereum/how-ethereum-works/the-account-model

> Bitcoin tracks money as discrete coins that get consumed and recreated. Ethereum tracks money as account balances that get modified in place. This change cascades into everything else: how transactions look, what state means, how contracts hold funds, why nonces exist. If you only take one new idea from this module, take this one.

## Two ways to track who has what

In the previous module you learned how Bitcoin tracks state with UTXOs: unspent transaction outputs. Every coin in existence is a UTXO sitting in the chain's database, locked to some address by a script. To spend it, you reference it as the input to a new transaction, prove you can satisfy its lock, and create new UTXOs as outputs. The old UTXO is consumed. The new ones are created. The chain's state at any moment is the full set of currently-unspent outputs across all addresses.

Ethereum uses a different model called the **account model**. Instead of tracking individual coins, the chain tracks accounts. Each account has an address, a balance of ETH, and possibly some additional state. When Alice sends 3 ETH to Bob, the chain subtracts 3 from Alice's balance and adds 3 to Bob's balance. Nothing gets consumed or created. The balances just change.

This sounds simpler. It is, conceptually. The implications take longer to absorb.

## What an Ethereum account contains

An account on Ethereum has four fields:

- A **balance** of ETH, stored as an integer in wei, where one ETH is 10^18 wei
- A **nonce**, which is a counter that increases by one with every transaction the account sends
- A **code hash**, which is empty for regular user accounts and points to the deployed bytecode for contract accounts
- A **storage root**, which is empty for regular user accounts and points to the contract's storage tree for contract accounts

Two kinds of accounts exist. **Externally owned accounts**, or EOAs, are controlled by a private key. Their code hash and storage root are empty. They behave like a Bitcoin address: you sign transactions with the matching key to spend their balance or trigger something else on the chain. **Contract accounts** are controlled by code. They have a balance like any other account, but they also have code that runs when called and storage that persists between calls. They have no private key. Nobody can directly sign transactions from a contract account. The only way to move a contract's funds or change its state is to call its code from another account, and the code decides what happens.

The chain's state at any moment is the combined state of every account that exists. Every EOA and every contract. Their balances, their nonces, and, for contract accounts, their code and their storage. When a block is produced, every full node applies the block's transactions to this state, computes the new state, and rejects the block if its claimed result doesn't match.

## Comparing the two models

The same payment looks completely different in the two models.

In Bitcoin, if Alice wants to send 3 BTC to Bob and she has a single 10 BTC UTXO, her transaction consumes that UTXO as its input and creates two outputs: 3 BTC locked to Bob's address, and 7 BTC locked back to one of her own addresses as change. After the transaction, her old UTXO no longer exists. In its place are two new UTXOs, one belonging to Bob and one belonging to her.

In Ethereum, if Alice wants to send 3 ETH to Bob and she has a balance of 10 ETH, her transaction just says: send 3 ETH to Bob. After the transaction, her balance is 7 and Bob's balance has increased by 3. There are no outputs, no change addresses, no UTXOs being consumed.

<svg role="img" viewBox="0 0 720 460" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Bitcoin UTXO model vs Ethereum account model for Alice's payment to Bob</title><desc>On the left, Bitcoin's UTXO model spends Alice's 10 BTC UTXO and creates two new UTXOs: 3 BTC for Bob and 7 BTC change back to Alice. On the right, Ethereum's account model just updates balances in place, from Alice 10 ETH and Bob 0 ETH before, to Alice 7 ETH and Bob 3 ETH after.</desc>
  <defs>
    <marker id="arr21u" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
  </defs>

<text x="180" y="30" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">UTXO model (Bitcoin)</text>
  <text x="540" y="30" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Account model (Ethereum)</text>

<line x1="360" y1="20" x2="360" y2="440" stroke="#565653" stroke-width="1" stroke-dasharray="4 4"/>

<text x="20" y="65" font-size="11" fill="#565653" font-style="italic">Before</text>

<rect x="40" y="75" width="280" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="98" text-anchor="middle" font-size="11" fill="#000000">Alice's UTXO</text>
  <text x="180" y="120" text-anchor="middle" font-family="monospace" font-size="13" fill="#ed4937">10 BTC</text>

<text x="380" y="65" font-size="11" fill="#565653" font-style="italic">Before</text>

<rect x="400" y="75" width="280" height="60" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="430" y="100" font-family="monospace" font-size="11" fill="#000000">Alice:</text>
  <text x="660" y="100" text-anchor="end" font-family="monospace" font-size="11" fill="#ed4937">10 ETH</text>
  <text x="430" y="122" font-family="monospace" font-size="11" fill="#000000">Bob:</text>
  <text x="660" y="122" text-anchor="end" font-family="monospace" font-size="11" fill="#000000">0 ETH</text>

<text x="20" y="170" font-size="11" fill="#565653" font-style="italic">Transaction</text>

<rect x="40" y="180" width="280" height="100" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="180" y="205" text-anchor="middle" font-size="11" fill="#000000" font-weight="bold">Alice sends 3 BTC to Bob</text>
  <line x1="60" y1="218" x2="300" y2="218" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="60" y="238" font-family="monospace" font-size="10" fill="#565653">input:  Alice's 10 BTC UTXO</text>
  <text x="60" y="256" font-family="monospace" font-size="10" fill="#565653">output: 3 BTC to Bob</text>
  <text x="60" y="272" font-family="monospace" font-size="10" fill="#565653">output: 7 BTC back to Alice</text>

<text x="380" y="170" font-size="11" fill="#565653" font-style="italic">Transaction</text>

<rect x="400" y="180" width="280" height="100" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="540" y="205" text-anchor="middle" font-size="11" fill="#000000" font-weight="bold">Alice sends 3 ETH to Bob</text>
  <line x1="420" y1="218" x2="660" y2="218" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="420" y="238" font-family="monospace" font-size="10" fill="#565653">from:   Alice</text>
  <text x="420" y="256" font-family="monospace" font-size="10" fill="#565653">to:     Bob</text>
  <text x="420" y="272" font-family="monospace" font-size="10" fill="#565653">value:  3 ETH</text>

<line x1="180" y1="280" x2="180" y2="320" stroke="#565653" stroke-width="2" marker-end="url(#arr21u)"/>
  <line x1="540" y1="280" x2="540" y2="320" stroke="#565653" stroke-width="2" marker-end="url(#arr21u)"/>

<text x="20" y="335" font-size="11" fill="#565653" font-style="italic">After</text>

<rect x="40" y="345" width="135" height="80" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="107" y="368" text-anchor="middle" font-size="10" fill="#000000">Bob's UTXO</text>
  <text x="107" y="392" text-anchor="middle" font-family="monospace" font-size="13" fill="#ed4937">3 BTC</text>
  <text x="107" y="412" text-anchor="middle" font-size="9" fill="#565653" font-style="italic">(new)</text>

<rect x="185" y="345" width="135" height="80" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="252" y="368" text-anchor="middle" font-size="10" fill="#000000">Alice's UTXO</text>
  <text x="252" y="392" text-anchor="middle" font-family="monospace" font-size="13" fill="#ed4937">7 BTC</text>
  <text x="252" y="412" text-anchor="middle" font-size="9" fill="#565653" font-style="italic">(new, change)</text>

<text x="380" y="335" font-size="11" fill="#565653" font-style="italic">After</text>

<rect x="400" y="345" width="280" height="80" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <text x="430" y="370" font-family="monospace" font-size="11" fill="#000000">Alice:</text>
  <text x="660" y="370" text-anchor="end" font-family="monospace" font-size="11" fill="#ed4937">7 ETH</text>
  <text x="430" y="392" font-family="monospace" font-size="11" fill="#000000">Bob:</text>
  <text x="660" y="392" text-anchor="end" font-family="monospace" font-size="11" fill="#ed4937">3 ETH</text>
  <text x="540" y="416" text-anchor="middle" font-size="9" fill="#565653" font-style="italic">(balances updated in place)</text>

<text x="360" y="450" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">Same payment. Completely different state representation.</text>
</svg>

The Bitcoin transaction is a graph operation: it removes one node from the UTXO set and adds two new ones. The Ethereum transaction is a mutation: it changes two existing entries in the account state table. The Bitcoin version preserves nothing. The inputs and outputs are entirely new objects. The Ethereum version preserves the accounts. Only their balances move.

## What the account model gives you

Three things follow from this choice that matter for the rest of this course.

**Contracts can have their own state.** A contract account stores key-value pairs that persist across calls. The contract's code reads and writes this storage as transactions come in. This is what makes Ethereum programmable. In the UTXO model, there is nowhere for a program's data to live between transactions. The chain's state is just a set of locked coins. In the account model, the chain's state has structure: each account has its own slice of storage that the protocol tracks. A contract can have a million users with a million different balances stored in it. This is impossible to express cleanly in UTXOs.

**State changes are simpler to reason about.** In Bitcoin, "what is Alice's balance" requires scanning the entire UTXO set for outputs locked to her addresses and summing them. In Ethereum, "what is Alice's balance" is one lookup in the state table. This makes contract code dramatically simpler. A token contract just keeps a mapping from address to balance and updates it on every transfer. The protocol doesn't need to know what the contract is doing. The contract manages its own state.

**Parallelism is harder.** Two Bitcoin transactions that touch different UTXOs are independent and can be processed in parallel. Two Ethereum transactions that touch the same account may conflict. The Ethereum protocol processes transactions sequentially within a block to handle this. Most newer chains, including Solana, build elaborate machinery to recover the parallelism Ethereum gave up.

## Why nonces exist

There's one more piece of the account that needs explaining.

Recall the **nonce** from the account fields. It starts at zero, and Alice includes her current value in every transaction she signs. The chain only accepts the transaction if the nonce matches what it expects from Alice's account.

The nonce exists because the account model has a problem the UTXO model doesn't. In Bitcoin, a transaction is uniquely identified by the UTXOs it consumes. If Alice broadcasts the same transaction twice, the second one fails because the first one already consumed the input. Replay is impossible by construction.

In Ethereum, a transaction is "send 3 ETH from Alice to Bob." If someone rebroadcasts that same signed transaction, the network needs a way to tell it's the same one. The nonce solves this. The first transaction Alice sends has nonce 0. If the network sees a second transaction from Alice with nonce 0, it rejects it as a duplicate. Alice's next transaction must use nonce 1. Then nonce 2. And so on.

The nonce also enforces ordering. If Alice broadcasts transactions with nonces 5, 6, and 7, the network will include them in that order regardless of when they arrive. Transaction 6 can't be processed until transaction 5 is in a block. This matters for contracts that depend on a specific sequence of calls.

## Where the account model shows up in your code

Most of the code you write in this course will assume the account model without naming it. When a token contract does `balances[alice] -= 3` and `balances[bob] += 3`, that's the account model at work, inside a contract. When a transaction reverts halfway through and the chain rolls back the state, that's the account model at work, at the protocol level. When you debug why a transaction "stuck" with the wrong nonce, that's the account model at work, at the wallet level.

The UTXO model produces a kind of cryptocurrency that's good at being money. The account model produces a kind of cryptocurrency that's good at being a platform for running arbitrary programs. Ethereum picked the second one. Every contract you build sits on top of that choice.

## FAQ

### What does an Ethereum account store?

Four fields. A balance in wei, where one ETH is 10^18 wei. A nonce that counts the transactions it has sent. A code hash and a storage root, empty for user accounts and pointing at the deployed bytecode and the storage tree for contract accounts.

### Why does every transaction need a nonce?

To stop replays and fix ordering. The chain accepts a transaction only when its nonce matches what it expects from that account. Nonce 6 waits until nonce 5 is in a block.

### Can anyone sign a transaction from a contract account?

No. A contract account has no private key. Its code runs only when another account calls it, and that code decides what happens to the balance and the storage.

### Do the transactions in a block run in parallel?

No. Two of them can touch the same account, so the protocol applies them one after another in block order.

---

# Contracts and gas

Source: https://academy.redduck.io/courses/development-on-ethereum/how-ethereum-works/contracts-and-gas

> A smart contract is a piece of code that lives at an address on the chain, has its own storage, and runs whenever someone calls it. Running it is not free. Every operation the code performs costs a small amount of a metering unit called gas, and gas is paid in ETH. These two facts, contracts and gas, are inseparable: gas exists because contracts exist, and contracts only work because gas keeps their execution bounded.

## What a contract is at runtime

A contract is a kind of account. Like any account on Ethereum, it has a balance of ETH and a nonce. Unlike an EOA, it also has code and storage. The code was deployed once when the contract was created, and from that point onward it lives at the contract's address. The storage is a mapping from 256-bit keys to 256-bit values that the contract's code reads and writes as it runs.

A contract is created by sending a special transaction whose `to` field is empty and whose `data` field contains creation code. When the transaction is mined, the EVM runs that creation code, which returns the contract's runtime bytecode. The protocol computes the contract's address as the lowest 20 bytes of `keccak256(sender, sender_nonce)`, places the bytecode there, and from then on the contract exists. There's a second deployment opcode called `CREATE2` that lets you choose a salt instead of using the sender's nonce, which is how you compute a contract's address in advance for things like factory patterns.

**A contract does nothing on its own.** It has no scheduler, no listener, no background process. It only runs when called, either by a transaction from an EOA or by another contract that decides to call it mid-execution. Between calls it sits dormant in the chain's state.

**Each execution is atomic.** Either the entire call succeeds and its state changes are committed, or the call reverts and all its state changes are rolled back, including changes made earlier in the same call. There is no partial commit.

**A contract can call other contracts.** Each nested call has its own arguments, its own gas budget allocated from the caller's remaining gas, and its own opportunity to revert. This property is called **composability**, and it's why a token contract you write today can be called next year by a lending protocol you've never heard of. The interface lives on chain. Any contract can use it.

<svg role="img" viewBox="0 0 720 360" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Alice calls Exchange contract, which calls Token A contract</title><desc>Alice signs a transaction that calls Exchange's swap function, which reads pool reserves and calls Token A's transferFrom to subtract from Alice and add to Exchange, then updates reserves and returns. All these nested calls share one transaction's atomic scope, so a revert anywhere rolls back every state change.</desc>
  <defs>
    <marker id="arr22" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
    <marker id="arr22r" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#ed4937"/>
    </marker>
  </defs>

<rect x="20" y="100" width="120" height="60" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="80" y="125" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">EOA: Alice</text>
  <text x="80" y="145" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffffff">signs tx</text>

<line x1="140" y1="130" x2="208" y2="130" stroke="#ed4937" stroke-width="2" marker-end="url(#arr22r)"/>
  <text x="174" y="120" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">call</text>

<rect x="210" y="60" width="220" height="140" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <rect x="210" y="60" width="220" height="30" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="320" y="80" text-anchor="middle" font-size="12" fill="#ffffff" font-weight="bold">Contract A: Exchange</text>
  <text x="220" y="108" font-family="monospace" font-size="9" fill="#565653">code: swap(tokenA, tokenB, amount)</text>
  <text x="220" y="126" font-family="monospace" font-size="9" fill="#565653">storage: pool_reserves</text>
  <line x1="225" y1="138" x2="425" y2="138" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="220" y="156" font-family="monospace" font-size="9" fill="#000000">1. read reserves</text>
  <text x="220" y="172" font-family="monospace" font-size="9" fill="#000000">2. call Token A transferFrom</text>
  <text x="220" y="190" font-family="monospace" font-size="9" fill="#000000">3. update reserves</text>

<line x1="432" y1="110" x2="500" y2="110" stroke="#565653" stroke-width="2" marker-end="url(#arr22)"/>
  <text x="466" y="100" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">call</text>

<line x1="500" y1="170" x2="432" y2="170" stroke="#565653" stroke-width="2" stroke-dasharray="5 3" marker-end="url(#arr22)"/>
  <text x="466" y="185" text-anchor="middle" font-family="monospace" font-size="9" fill="#565653">return</text>

<rect x="502" y="60" width="200" height="140" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <rect x="502" y="60" width="200" height="30" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="602" y="80" text-anchor="middle" font-size="12" fill="#ffffff" font-weight="bold">Contract B: Token A</text>
  <text x="512" y="108" font-family="monospace" font-size="9" fill="#565653">code: transferFrom(...)</text>
  <text x="512" y="126" font-family="monospace" font-size="9" fill="#565653">storage: balances[address]</text>
  <line x1="517" y1="138" x2="697" y2="138" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="512" y="156" font-family="monospace" font-size="9" fill="#000000">subtract from Alice</text>
  <text x="512" y="172" font-family="monospace" font-size="9" fill="#000000">add to Exchange</text>

<rect x="20" y="230" width="680" height="60" fill="#e0deda" stroke="#565653" stroke-width="1" stroke-dasharray="4 3"/>
  <text x="360" y="253" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">All of this runs inside one transaction's execution scope.</text>
  <text x="360" y="273" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">If anything reverts, every state change made above is rolled back atomically.</text>

<text x="360" y="320" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">One transaction can touch many contracts. They run in sequence, sharing one atomic scope.</text>
</svg>

This composition is the source of nearly all the interesting applications on Ethereum, and the source of nearly all the spectacular failures. A simple-looking call to contract A can quietly reach into contracts B, C, and D, and a bug in any of them can break the whole chain of operations. You'll see this in detail when you write code that calls external contracts, and again when you study security.

## Why gas exists

Every operation a contract performs costs **gas**. Adding two numbers costs 3 gas. Multiplying them costs 5. Reading a word from a contract's storage costs 100 or 2,100 depending on whether the slot has been touched recently in the transaction. Writing a new word to storage costs 22,100. The exact numbers don't matter yet. What matters is the shape: cheap operations cost a few gas, expensive ones cost thousands or tens of thousands.

Notice that storage operations dominate the numbers above. That gap between writing a word and adding two numbers isn't arbitrary. Storage has to be held by every full node on the network, forever, until something explicitly overwrites it. Adding two numbers is a one-time computation that disappears as soon as the transaction ends. The protocol prices storage to reflect that asymmetry. A side effect of this is that clearing a storage slot, setting it from non-zero back to zero, gives the sender a partial gas refund, because the network no longer has to hold that value.

Gas exists for two reasons.

**Computation has to be paid for.** Every full node on Ethereum runs every transaction in every block. If running a transaction could cost the network thousands of dollars in electricity and CPU time without the sender paying anything, the network would get spammed into uselessness within hours. Gas is the protocol's way of charging the sender for the work their transaction makes every node do, with pricing that roughly tracks how much work each operation actually takes.

**Execution has to be bounded.** Without a limit, a contract could run forever. The protocol can't ask each node to detect infinite loops, because deciding whether a program halts is undecidable in general. The solution is blunt. Every transaction declares the maximum amount of gas it's willing to spend, called the **gas limit**. Execution halts when that limit is reached. If the transaction was going to complete legitimately within the limit, it completes. If it was going to loop forever, it gets cut off and its state changes are reverted.

Pricing keeps the cost of computation fair, and limits stop execution from running forever. Together they're what makes Ethereum work as a general-purpose execution platform.

## What you actually pay

Every transaction includes two numbers related to gas. The first is the gas limit you already met, the maximum amount of gas the transaction may use. The second is the **gas price**, how much they're willing to pay per unit. The actual fee is `gas used × gas price`, paid in ETH.

Gas prices are usually denominated in **gwei**, where one gwei is `10^-9` ETH, or one billionth of an ETH. A typical mainnet transaction in 2025 might pay something like 10 to 50 gwei per unit of gas. At current ETH prices, that translates to a fraction of a cent per gas unit. A simple transfer costs about 21,000 gas, so the total fee is small. A complex DeFi interaction might use 200,000 to 500,000 gas, which is where transaction costs start being something the user notices.

Since August 2021, Ethereum has used a fee market called **EIP-1559**. It splits the gas price into two parts.

The **base fee** is determined by the protocol. Each block carries a base fee, and the next block's base fee is adjusted based on how full the current block was. The protocol targets a fullness of 50%. If a block goes above that, the next block's base fee rises by up to 12.5%. If it goes below, the base fee falls by up to 12.5%. So the base fee tracks demand in near-real time. When the network is congested it climbs quickly. When it's quiet it falls quickly. The base fee is **burned**, meaning it's destroyed and removed from circulation. The validator who included the transaction does not receive it.

The **priority fee**, sometimes called the tip, is what the sender adds on top of the base fee to incentivize validators to include their transaction faster. The priority fee goes to the validator who produced the block. In quiet times the priority fee can be a fraction of a gwei. In congested times it climbs into the tens.

This design has two effects worth naming. First, it makes fees predictable. Before EIP-1559, fees were a blind auction and users routinely overpaid. Now the base fee is set by the protocol and only the priority fee is negotiable, which lets wallets give accurate estimates. Second, it burns ETH over time. Combined with PoS issuance, this means the supply of ETH can rise or fall depending on network usage.

## Where transactions wait

A signed transaction doesn't go straight into a block. It enters a holding area called the **mempool**, where it waits to be picked up by whoever is building the next block.

Every node on the network keeps its own mempool of pending transactions. When your wallet sends a transaction, it goes to one node, typically a hosted RPC provider, which adds it to its mempool and gossips it to peers. Within a second or two, most of the network has the transaction in their mempools. When a validator's turn comes to propose a block, the builder assembling that block picks transactions from the mempool, generally ordered by priority fee, until the block is full.

A few things follow from this that matter for builders.

**The mempool is public.** Anyone running a node, or paying for access to one, can read pending transactions before they're included. This is the foundation of MEV: bots watch the mempool for profitable opportunities and submit their own transactions to capture them, sometimes by paying higher priority fees to get ordered ahead of the original. We'll come back to this in the security module.

**Transactions can be replaced.** If you submit a transaction with too low a priority fee and it sits in the mempool unconfirmed, you can resubmit a new transaction with the same nonce and a higher fee. Validators pick the more profitable one. Wallets expose this as a "speed up" button. You can also "cancel" a stuck transaction by submitting a no-op transaction with the same nonce and higher fee.

**Nonces enforce ordering.** A transaction with nonce 5 cannot be included until the transaction with nonce 4 from the same account has been. If you have transactions sitting at nonce 4 stuck on low fees, every later transaction from that account is also stuck until you bump the nonce-4 fee.

## What happens when gas runs out

If a transaction's gas limit is too low, execution halts partway through. The transaction reverts. Every state change it made is rolled back. The sender does not get a refund of the gas they spent. The validator still keeps the priority fee for the work it did, and the base fee is still burned. From the chain's perspective, the transaction happened: it occupied a slot in a block, it consumed gas, but its intended effect did not take place. Setting the gas limit too low means you fail and pay anyway. Setting it too high is mostly harmless: the unused portion is refunded. Wallets estimate the gas needed and add a safety margin.

When contract A calls contract B, A passes some of its remaining gas to B. If B runs out of gas, B reverts. A can choose to catch this revert and continue, or let it propagate and revert itself. This is composability at the gas level: one part of a transaction can fail without bringing the whole thing down, if the calling code chose to handle that case.

## How gas shapes the code you write

The first is that **storage is precious**. The most expensive opcode by a wide margin is the one that writes to a new storage slot. Every byte you store on Ethereum costs gas at deployment, and every subsequent change costs gas again. This shapes how Solidity code is written. Variables get packed into structs. State that doesn't need to be on chain stays off chain. Patterns like emitting events instead of storing values get used everywhere. You will spend a measurable share of your development time thinking about storage layout.

The second is that **transactions cost users money**. A user calling your contract is paying for every operation it performs. If your contract does too much work per call, it becomes too expensive to use. The cheaper your contract is to interact with, the wider the audience that can afford to use it. This is the single biggest constraint on Ethereum application design, and it's why so much of advanced Solidity is about gas optimization.

## FAQ

### Why do I pay gas for a transaction that failed?

Every node already did the work. The validator keeps the priority fee, the base fee is burned, and the reverted transaction still took a slot in a block.

### What does a transaction cost in ETH?

Gas used times gas price. Prices are quoted in gwei, one billionth of an ETH. A plain transfer uses about 21,000 gas and a complex DeFi call 200,000 to 500,000, at 10 to 50 gwei per unit on mainnet in 2025.

### What is the difference between the base fee and the priority fee?

The base fee is set by the protocol and burned, so nobody receives it. It targets blocks that are 50% full and moves up or down by at most 12.5% per block. The priority fee is your tip and goes to the validator who produced the block. EIP-1559 introduced the split in August 2021.

### My transaction is stuck pending. Can I cancel it?

Yes. Send a new transaction with the same nonce and a higher fee, and validators take the more profitable one. A do-nothing transaction cancels the original. Until that nonce clears, everything behind it from the same account is stuck.

### Why does storing a value cost so much more than adding two numbers?

Every full node holds stored data forever until something overwrites it, while a computation disappears when the transaction ends. Adding two numbers costs 3 gas, multiplying 5, writing a new word to storage 22,100. Clearing a slot back to zero earns a partial refund.

### How is a contract's address decided?

The lowest 20 bytes of keccak256 over the sender and the sender's nonce. CREATE2 substitutes a salt you choose, so the address can be computed before deploying.

---

# The EVM

Source: https://academy.redduck.io/courses/development-on-ethereum/how-ethereum-works/the-evm

> Every Ethereum contract you'll ever write runs on the same virtual machine, the Ethereum Virtual Machine, or EVM. It's a deterministic, stack-based computer that exists only as a specification. Every Ethereum node implements it, and every node implementation must produce the same output on the same input, or the network would split into incompatible chains. Understanding the shape of this machine, even at a high level, makes everything else in Solidity easier to reason about.

## What "virtual machine" means here

The EVM is not a physical chip. It's a specification of how a particular kind of computer behaves. Every Ethereum client includes its own implementation of the EVM in its source code. The major clients are Geth, Reth, Erigon, Besu, and Nethermind. When a transaction arrives, the client feeds the transaction's input and the relevant contract's bytecode into its EVM implementation, runs the execution, and produces a result. That result must be identical to what every other client produces for the same inputs. If two clients disagree about the output of an EVM execution, one of them has a bug, and that bug will cause a chain split.

This is the core property that everything else depends on: **the EVM is deterministic**. Same code, same input, same state, same result. Every time. On every machine.

A consequence: the EVM cannot do things that would produce non-deterministic results. It can't make HTTP requests. It can't read the system clock. It can't generate true randomness. It can't read files. It has access only to data the protocol explicitly provides: the transaction's input, the contract's storage, the current block's metadata (`block.timestamp`, `block.number`, `block.coinbase`, etc.), and the chain's state at the time the block is being executed. Everything else is off-limits, by design.

The EVM is also not unique to Ethereum. The specification is open and has been adopted by dozens of other chains. Polygon, BNB Chain, Avalanche's C-Chain, Arbitrum, Optimism, Base, zkSync Era's later versions, and many more all run EVM implementations. When people say a chain is "EVM-compatible," this is what they mean: the same bytecode you deploy on Ethereum will run on these other chains, sometimes with small differences in opcode behavior or gas pricing. The skills you build for Ethereum carry directly to most major smart contract chains.

## How it runs code

The EVM is **stack-based**. It has a stack that holds values, and most operations work by pushing values onto the stack, popping them off, and pushing results back on. There are no general-purpose registers. The stack is the working surface.

Each stack slot holds a **256-bit word**, or 32 bytes. This is the EVM's native size for everything. A boolean is a 32-byte word with 1 or 0 in the last byte. An address is a 32-byte word with the 20-byte address padded with zeros. Math on smaller integers still computes with all 256 bits. The high bits are thrown away if they're not needed. The 256-bit size was chosen to match what cryptographic operations like Keccak hashing produce.

The code itself is **bytecode**: a sequence of single-byte instructions called **opcodes**. There are about 140 of them. Each one tells the EVM to do one specific thing: `ADD` pops two values from the stack and pushes their sum, `MSTORE` writes a word to memory, `SLOAD` reads a value from contract storage, `CALL` invokes another contract. The Solidity compiler turns your source code into a sequence of these opcodes, which the EVM then walks one at a time.

To make this concrete, consider one line of Solidity:

```solidity
uint256 c = a + b;
```

If `a` and `b` are local variables, the compiler emits something close to this bytecode sequence:

```
PUSH1 0x02  // push the value of a (say, 2) onto the stack
PUSH1 0x03  // push the value of b (say, 3) onto the stack
ADD         // pop the top two values, push their sum (5)
            // c now holds 5 in whatever slot the compiler chose
```

Three opcodes for one line of source. Total gas cost: about 9. The stack started empty, ended with one value on it. This is the rhythm of all EVM execution. Every Solidity statement decomposes into a small handful of stack operations that push, pop, and combine values.

Every opcode has a gas cost, and the EVM itself increments a running counter as it walks through them. When the next opcode would push the counter past the transaction's gas limit, execution halts and the transaction reverts. There is no external supervisor checking gas. The EVM does its own accounting.

## The four places data lives

During execution, data lives in one of four places. Each has different cost, lifetime, and access rules.

<svg role="img" viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>EVM data locations: Stack, Memory, Storage, Calldata compared</title><desc>Four columns show where data lives during EVM execution: Stack, Memory, Storage, and Calldata. Each column lists its cost, size, word size, and lifetime, showing that Storage is expensive and lasts forever while the other three are cheap and last only one call.</desc>
  <text x="360" y="30" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">Where data lives during EVM execution</text>

<rect x="40" y="55" width="150" height="290" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <rect x="40" y="55" width="150" height="35" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="115" y="78" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Stack</text>
  <text x="115" y="110" text-anchor="middle" font-size="10" fill="#565653" font-style="italic">working surface</text>
  <line x1="55" y1="125" x2="175" y2="125" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="115" y="145" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">cost: free-ish</text>
  <text x="115" y="163" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">size: 1024 slots</text>
  <text x="115" y="181" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">word: 32 bytes</text>
  <text x="115" y="199" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">lifetime: one call</text>
  <line x1="55" y1="215" x2="175" y2="215" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="115" y="240" text-anchor="middle" font-size="10" fill="#565653">Where opcodes do</text>
  <text x="115" y="256" text-anchor="middle" font-size="10" fill="#565653">their actual work.</text>
  <text x="115" y="272" text-anchor="middle" font-size="10" fill="#565653">Push, pop, math.</text>

<rect x="210" y="55" width="150" height="290" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <rect x="210" y="55" width="150" height="35" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="285" y="78" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Memory</text>
  <text x="285" y="110" text-anchor="middle" font-size="10" fill="#565653" font-style="italic">scratch pad</text>
  <line x1="225" y1="125" x2="345" y2="125" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="285" y="145" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">cost: cheap</text>
  <text x="285" y="163" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">size: unlimited*</text>
  <text x="285" y="181" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">word: byte-addressable</text>
  <text x="285" y="199" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">lifetime: one call</text>
  <line x1="225" y1="215" x2="345" y2="215" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="285" y="240" text-anchor="middle" font-size="10" fill="#565653">Temporary buffer for</text>
  <text x="285" y="256" text-anchor="middle" font-size="10" fill="#565653">arrays, strings, structs.</text>
  <text x="285" y="272" text-anchor="middle" font-size="10" fill="#565653">Wiped between calls.</text>
  <text x="285" y="295" text-anchor="middle" font-size="9" fill="#565653" font-style="italic">*priced quadratically</text>

<rect x="380" y="55" width="150" height="290" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <rect x="380" y="55" width="150" height="35" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="455" y="78" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Storage</text>
  <text x="455" y="110" text-anchor="middle" font-size="10" fill="#565653" font-style="italic">contract's persistent state</text>
  <line x1="395" y1="125" x2="515" y2="125" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="455" y="145" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937">cost: expensive</text>
  <text x="455" y="163" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">size: 2^256 slots</text>
  <text x="455" y="181" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">word: 32 bytes</text>
  <text x="455" y="199" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937">lifetime: forever</text>
  <line x1="395" y1="215" x2="515" y2="215" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="455" y="240" text-anchor="middle" font-size="10" fill="#565653">Persistent state of the</text>
  <text x="455" y="256" text-anchor="middle" font-size="10" fill="#565653">contract. Survives forever.</text>
  <text x="455" y="272" text-anchor="middle" font-size="10" fill="#565653">Costs you for every write.</text>

<rect x="550" y="55" width="150" height="290" fill="#e0deda" stroke="#000000" stroke-width="2"/>
  <rect x="550" y="55" width="150" height="35" fill="#ed4937" stroke="#000000" stroke-width="2"/>
  <text x="625" y="78" text-anchor="middle" font-size="13" fill="#ffffff" font-weight="bold">Calldata</text>
  <text x="625" y="110" text-anchor="middle" font-size="10" fill="#565653" font-style="italic">incoming arguments</text>
  <line x1="565" y1="125" x2="685" y2="125" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="625" y="145" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">cost: cheap</text>
  <text x="625" y="163" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">size: tx-defined</text>
  <text x="625" y="181" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">word: byte-addressable</text>
  <text x="625" y="199" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">lifetime: one call</text>
  <line x1="565" y1="215" x2="685" y2="215" stroke="#565653" stroke-width="1" stroke-dasharray="3 3"/>
  <text x="625" y="240" text-anchor="middle" font-size="10" fill="#565653">The transaction's input.</text>
  <text x="625" y="256" text-anchor="middle" font-size="10" fill="#565653">Read-only from inside</text>
  <text x="625" y="272" text-anchor="middle" font-size="10" fill="#565653">the contract.</text>

<text x="360" y="370" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">Solidity asks you to declare which one your variables live in. Each choice costs different gas.</text>
</svg>

The **stack** is where the EVM does its work. Every arithmetic operation, every comparison, every branch happens by pushing values onto the stack and popping them off. The stack has a maximum depth of 1024 slots. Reaching that depth halts execution. Solidity manages the stack for you implicitly. You don't write stack operations by hand unless you drop into inline assembly.

**Memory** is a contiguous, byte-addressable buffer that lives only for the duration of a single call. When a transaction triggers a contract, memory starts empty. As the code runs, it can write data to memory and read it back. When the call ends, memory is gone. Solidity uses memory to hold the runtime representations of structs, arrays, and strings that don't need to persist. Memory is cheap for small amounts and gets expensive as you use more of it, because the protocol charges quadratically as it grows.

**Storage** is the contract's persistent state. It survives between calls. It survives between blocks. It survives forever, unless someone explicitly overwrites it or the contract is destroyed. Storage is a mapping from 256-bit keys to 256-bit values, owned by the contract. Every state variable you declare in Solidity goes here unless you say otherwise. Storage is by a wide margin the most expensive place to put data. Writing a new value to storage costs 22,100 gas. Reading from storage costs 100 or 2,100 gas depending on whether the slot was already accessed in this transaction. These numbers are why so much of advanced Solidity is about avoiding storage when you can.

**Calldata** is the input data sent with the transaction. It's read-only from inside the contract: the contract can look at it but can't modify it. Calldata is the cheapest place to read function arguments from, which is why Solidity functions that take large inputs like long arrays usually take them as `calldata` rather than copying them into memory first.

## Call context and reverts

Every time a contract is called, the EVM creates a fresh **execution context**. A new empty stack. A new empty memory buffer. A fresh copy of the call's arguments in calldata. The context also includes a small set of values that describe the call itself. Solidity exposes three of them: `msg.sender` is the address that made this call, `msg.value` is the ETH attached to the call, and `msg.data` is the raw calldata bytes. These exist for the lifetime of one call. If contract A calls contract B, B sees A as `msg.sender`. The original EOA that started the transaction is invisible from inside B. If B then calls C, C sees B as its `msg.sender`. The chain of `msg.sender` values mirrors the call stack.

Storage works differently from the other three data areas in one important way. Each contract has its own storage, and the EVM only lets the currently executing code read or write *that contract's* storage. When A calls B, the executing code is B's, so storage operations touch B's slots. When B returns and execution resumes in A, storage operations touch A's slots again. The other three areas, stack and memory and calldata, are local to a single call and get destroyed when the call ends.

Reverts work at this same call-boundary level. When a contract starts executing, the EVM takes a logical snapshot of the chain state. If the call completes normally, the state changes are committed. If anything in the call reverts, the EVM restores the snapshot, discarding every state change made during that call. This is what makes the atomicity property from the previous lesson actually work. The snapshot-and-restore mechanism is built into the EVM itself, part of the protocol rather than a layer on top.

## How the EVM ties to consensus

Every full node on Ethereum runs every transaction through its EVM. When a new block arrives, the node takes each transaction in order, finds the contract being called, loads that contract's bytecode and storage, and executes the bytecode in its EVM. The result is a set of state changes: balances moved, storage slots written, events emitted, possibly new contracts created. The node applies those changes to its copy of the state.

After applying every transaction in the block, the node has a new state. It computes a hash of that state, called the **state root**, and checks whether that root matches the one the block producer included in the block header. If it matches, the node accepts the block. If not, the node rejects it.

This is what makes the EVM's determinism so important. If your node and my node disagree about what the EVM did with a particular transaction, our state roots after the block will be different, and we'll end up on different chains. The whole system rests on every node's EVM producing exactly the same answer on exactly the same input. A bug in one client's EVM implementation that diverges from the others can fork the network, which has happened in the past and is treated as a critical incident by the client teams.

## Three reflexes to carry into your Solidity

You won't write EVM bytecode directly in this course. You'll write Solidity. But understanding the machine your code compiles to changes how you write that code.

Three reflexes carry forward. Variables get assigned to one of four data areas, and you'll need to know which is which when Solidity asks. Storage is expensive, which means contract design starts with the question of what actually has to live on chain versus what can be reconstructed off chain. And every line of Solidity becomes some number of EVM opcodes, which means the cost of your code is real, measurable, and the topic of an entire subfield of Solidity expertise.

## FAQ

### Why can't a contract call an API or generate a random number?

The EVM has to be deterministic. Same input, same result on every node, or the network splits into incompatible chains. So no HTTP requests, no system clock, no true randomness, no file reads. A contract sees the transaction input, its own storage, the chain state, and block metadata like block.timestamp and block.number.

### Where does data live while a contract runs?

Stack, memory, storage, calldata. The stack is the arithmetic surface, 1024 slots of 32 bytes. Memory is a byte-addressable buffer wiped when the call ends. Calldata is the read-only transaction input. Storage is permanent and costs 22,100 gas to write a new word, 100 or 2,100 gas to read one.

### Why is everything in the EVM 32 bytes?

Each stack slot holds one 256-bit word, sized to match what Keccak hashing produces. A bool is a word with 1 or 0 in the last byte, and an address is 20 bytes padded with zeros.

### When contract A calls contract B, what is msg.sender inside B?

A. msg.sender is whoever made the current call, so the wallet that started the transaction is invisible inside B. If B calls C, C sees B.

### What happens to storage writes when a call reverts?

Discarded. The EVM snapshots the state when the call starts and restores it on a revert.

---

# Consensus on Ethereum

Source: https://academy.redduck.io/courses/development-on-ethereum/how-ethereum-works/consensus-on-ethereum

> Ethereum doesn't use proof of work anymore. Since September 2022, it has used proof of stake. Validators put up 32 ETH as collateral, take turns proposing blocks, and lose part of their stake if they cheat. The mechanics are different from Bitcoin in almost every detail, but the goal is the same. Get a network of independent nodes to agree on which blocks count, without anyone in charge.

## What changed at the Merge

Bitcoin's consensus you already know. Miners burn electricity hashing block headers. Whoever finds a winning hash first publishes the next block. Rewriting history requires redoing all that work, which is prohibitively expensive.

Ethereum used the same model until September 2022. Then, in an upgrade called the Merge, it switched to proof of stake. The block producers stopped being miners with hardware and started being **validators** with staked ETH. The protocol's security argument changed from "attacking the chain costs more electricity than it could possibly earn" to "attacking the chain forfeits more staked ETH than it could possibly earn."

Three things drove the change. The first was energy. PoW Ethereum was using roughly the same electricity as Switzerland, and PoS dropped that by about 99.95% overnight. The second was issuance. PoW required generous block rewards to keep miners buying hardware and burning power, so the network minted a lot of new ETH each year. PoS can secure the network on a much smaller subsidy. The third was scalability. Ethereum's roadmap for scaling, including rollups, depends on finality properties that PoW cannot deliver.

## Who runs Ethereum now

A **validator** is an entity with 32 ETH locked into a special contract on the Ethereum chain. The 32 ETH minimum exists to keep the validator set small enough to coordinate but large enough to be decentralized. At the time of writing there are over a million active validators on the network. They aren't a million different people. Many of them are run by staking services, exchanges, or pools where users deposit smaller amounts of ETH that get bundled into validator-sized chunks. Each validator is still a distinct identity with its own signing key, and each one acts independently in the protocol.

Running a validator means running two pieces of software side by side. The **execution client**, such as Geth or Reth, processes transactions and maintains the EVM state. The **consensus client**, such as Lighthouse or Prysm, handles attestations, block proposals, and the proof-of-stake mechanics. The two communicate over a local API. Before the Merge, a single client did everything. The split exists because PoS consensus is a fundamentally different concern from transaction execution, and separating them lets each evolve independently.

Validators have two main jobs. First, when their turn comes, they propose a block. Second, every chance they get, they vote on which block they think should be the canonical one. These votes are called **attestations**. The protocol bundles validators into committees, with each slot getting one committee of around 128 validators tasked with attesting to that slot's block. Across an entire epoch, every active validator attests exactly once. The protocol aggregates all those attestations to decide what the chain looks like.

Selection of the proposer for each slot is pseudo-random but not arbitrary. The protocol uses a mechanism called **RANDAO**, which mixes in entropy contributed by validators themselves over many slots. The next proposer for slot N is determined by RANDAO output from earlier slots, plus the validator's stake weight. Nobody can predict the exact proposer schedule far in advance, but it is fully deterministic given the chain's state, which means every node agrees on whose turn it is.

A validator that does its job correctly earns small rewards on roughly every slot it attests in and a larger reward when it proposes a block. A validator that misbehaves loses some or all of its stake, an event called **slashing**. Slashing punishes specific provable bad behavior: signing two conflicting attestations, or proposing two different blocks for the same slot. The minimum slashing penalty is 1 ETH, but the protocol also applies a **correlation penalty** that scales with how many other validators were slashed around the same time. A lone bug that slashes one validator costs that validator 1 ETH. A coordinated attack that slashes thousands of validators at once costs each of them their entire 32 ETH stake. This is the math that makes attacking Ethereum economically catastrophic.

## How time works on Ethereum

Bitcoin's block timing is loose, because mining is a random process. Ethereum's PoS clock is rigid. The chain divides time into fixed units.

A **slot** is 12 seconds. Every 12 seconds, the protocol expects a new block. One validator is selected as the proposer for that slot. If they propose a valid block on time, the chain advances. If they miss their slot, that slot is skipped, the chain moves on to the next slot's proposer, and the missing validator earns no reward and pays a small penalty. Missed slots happen for ordinary reasons: an offline validator, an unreliable internet connection, a software bug. At the time of writing, the missed-slot rate hovers around 1%.

An **epoch** is 32 slots. So one epoch is 32 × 12 = 384 seconds, about 6.4 minutes. Epochs are where higher-level consensus events happen. At the end of each epoch, validators vote on a **checkpoint**: a specific block at an epoch boundary that they agree should be locked in.

<svg role="img" viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" style="background:#e0deda; font-family: system-ui, sans-serif;"><title>Ethereum epochs of 32 slots, checkpoints, and finalization</title><desc>Each epoch has 32 slots of 12 seconds, about 6.4 minutes, and ends with a checkpoint block, shown for epochs N, N+1, and N+2. When two epochs in a row have justified checkpoints, the chain finalizes, roughly 12.8 minutes after the block was first proposed.</desc>
  <defs>
    <marker id="arr24" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#565653"/>
    </marker>
  </defs>

<text x="360" y="30" text-anchor="middle" font-size="14" fill="#000000" font-weight="bold">How time is structured on Ethereum</text>

<text x="40" y="70" font-size="11" fill="#565653" font-style="italic">Slots (12 seconds each)</text>

<rect x="40" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <rect x="64" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <rect x="88" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <rect x="112" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <text x="146" y="100" font-family="monospace" font-size="11" fill="#565653">...</text>
  <rect x="170" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <rect x="194" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <rect x="218" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <text x="51" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">1</text>
  <text x="75" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">2</text>
  <text x="99" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">3</text>
  <text x="123" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">4</text>
  <text x="181" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">30</text>
  <text x="205" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">31</text>
  <text x="229" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">32</text>

<text x="140" y="135" text-anchor="middle" font-size="11" fill="#565653">32 slots = 1 epoch ≈ 6.4 minutes</text>

<rect x="280" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <rect x="304" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <text x="338" y="100" font-family="monospace" font-size="11" fill="#565653">...</text>
  <rect x="362" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <rect x="386" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <text x="291" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">1</text>
  <text x="315" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">2</text>
  <text x="373" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">31</text>
  <text x="397" y="100" text-anchor="middle" font-family="monospace" font-size="10" fill="#000000">32</text>

<rect x="450" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <rect x="474" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <text x="508" y="100" font-family="monospace" font-size="11" fill="#565653">...</text>
  <rect x="532" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>
  <rect x="556" y="80" width="22" height="32" fill="#e0deda" stroke="#000000" stroke-width="1"/>

<text x="640" y="100" font-family="monospace" font-size="11" fill="#565653">...</text>

<line x1="245" y1="80" x2="245" y2="125" stroke="#ed4937" stroke-width="2"/>
  <line x1="415" y1="80" x2="415" y2="125" stroke="#ed4937" stroke-width="2"/>
  <line x1="585" y1="80" x2="585" y2="125" stroke="#ed4937" stroke-width="2"/>

<text x="245" y="155" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937">checkpoint</text>
  <text x="415" y="155" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937">checkpoint</text>
  <text x="585" y="155" text-anchor="middle" font-family="monospace" font-size="10" fill="#ed4937">checkpoint</text>

<text x="140" y="180" text-anchor="middle" font-size="10" fill="#565653">Epoch N</text>
  <text x="330" y="180" text-anchor="middle" font-size="10" fill="#565653">Epoch N+1</text>
  <text x="500" y="180" text-anchor="middle" font-size="10" fill="#565653">Epoch N+2</text>

<rect x="40" y="210" width="450" height="55" fill="#e0deda" stroke="#ed4937" stroke-width="2"/>
  <text x="265" y="232" text-anchor="middle" font-size="11" fill="#000000" font-weight="bold">Two consecutive justified checkpoints → finalization</text>
  <text x="265" y="252" text-anchor="middle" font-size="10" fill="#565653">Roughly 12.8 minutes after the block was first proposed.</text>

<line x1="245" y1="155" x2="245" y2="208" stroke="#565653" stroke-width="1.5" stroke-dasharray="3 3"/>
  <line x1="415" y1="155" x2="415" y2="208" stroke="#565653" stroke-width="1.5" stroke-dasharray="3 3"/>

<text x="360" y="305" text-anchor="middle" font-size="11" fill="#565653" font-style="italic">Every 12 seconds, one validator proposes a block. Every 6.4 minutes, the network finalizes a checkpoint.</text>
</svg>

This regular rhythm is one of the biggest practical differences from Bitcoin. A Bitcoin block might arrive in 30 seconds or in 30 minutes, while an Ethereum slot is always 12 seconds. Wallets, applications, indexers, and bridges all rely on this predictability.

## How a block becomes final

Ethereum's finality model has two stages.

The first stage is **justification**. At the end of each epoch, the validator set looks at the checkpoint block, which is the first block of that epoch, and votes on whether to justify it. If a supermajority of validators, defined as two-thirds of the total active stake, attests to that checkpoint, it becomes justified.

The second stage is **finalization**. A justified checkpoint becomes finalized when the *next* checkpoint after it is also justified. So two consecutive justified epochs lock the earlier one in. A block proposed at the start of epoch N becomes finalized at the end of epoch N+1, about 12.8 minutes later.

This two-thirds threshold is what makes the whole security model work. It means an attacker needs to control more than one-third of the total stake to prevent finalization, by withholding their attestations. The same one-third threshold also makes finalized blocks essentially permanent: reverting a finalized block requires at least one-third of all staked ETH to attest to a conflicting checkpoint, which the protocol detects as a slashable offense. Slashing one-third of all stake is tens of billions of dollars at current stake levels. So in practice, finalized blocks are as close to permanent as any computer system gets.

This is genuinely different from Bitcoin. Bitcoin has **probabilistic finality**. The probability that a block gets reverted decreases as more blocks are built on top of it, but it never reaches zero. Ethereum has **economic finality**. After about 12.8 minutes, the protocol mathematically guarantees the block stays unless attackers willingly destroy a third of all staked ETH. The first kind of finality is statistical. The second kind is contractual.

## How conflicting blocks get resolved

What happens if a network split or a misbehaving proposer produces two valid blocks at the same slot, or two competing chains? Ethereum uses a fork-choice rule called **LMD-GHOST**, short for Latest Message Driven Greediest Heaviest Observed Sub-Tree.

Every validator's most recent attestation counts as a vote for a particular block. When choosing the canonical chain, each node looks at all the votes it knows about, weights them by the validator's stake, and picks the chain branch with the most weight behind it. Validators are required to only vote on the head of the chain they think is canonical, so as long as a supermajority is honest, the votes converge.

This is similar in spirit to Bitcoin's "longest chain wins" rule, but the unit of weight is different. Bitcoin counts cumulative proof of work. Ethereum counts cumulative stake-weighted attestations. Both produce a single canonical chain that the rest of the protocol can rely on.

## Who actually builds the block

In current Ethereum, the validator selected as proposer almost never builds the block themselves. The block-building process is split between two actors through a system called **proposer-builder separation**, or PBS. Specialized **builders** assemble blocks, typically optimizing them to extract maximum value from the transactions they include. The proposer, when their slot arrives, picks the highest-paying block from the builders and signs it. The proposer earns the priority fees plus a payment from the chosen builder.

This split exists because building an optimal block is a hard problem. It means simulating thousands of transactions, finding the most profitable ordering of them, and offering a higher price than other builders. That most profitable ordering is known as MEV, or maximal extractable value. Hobbyist validators can't compete with professional builder operations. Rather than let block-building centralize into a few large validators, the protocol effectively lets validators stay decentralized while specialized builders handle the optimization. The mechanism that makes this work in practice is software called **MEV-Boost**, run by most validators, which relays builder blocks to the proposer for selection.

## What this means in practice

Three implications matter for the rest of this course.

Block times are short. Twelve seconds between blocks means transactions confirm fast. A user who submits a transaction with sufficient priority fee can usually expect it to appear in the next block or the one after.

Finality takes minutes. Bridges, exchanges, and high-value applications wait for finalization at around 12.8 minutes before treating a transaction as settled. Smaller applications often treat one or two confirmations as enough, depending on what's at stake.

Reorgs happen rarely and only at the tip. A block can be reorged out if the network briefly disagrees on the head, which usually resolves within a slot or two. Once justified, reorgs become very unlikely. Once finalized, reorgs become economically impossible without a catastrophic attack.

The PoS machinery has more moving parts than PoW, but the practical effect is the same: a network of independent nodes converges on a single shared history, and you can write code that relies on that convergence.

## FAQ

### How long is a slot on Ethereum?

12 seconds. An epoch is 32 slots, about 6.4 minutes.

### Is Ethereum still mined?

No. Since the Merge in September 2022 Ethereum reaches consensus through proof of stake. Validators lock 32 ETH as collateral, take turns proposing blocks, and lose stake if they cheat. Energy use dropped by about 99.95%.

### How long until a transaction is truly final?

About 12.8 minutes. A transaction with a decent tip lands within a slot or two, but finality waits for two consecutive epoch checkpoints, each justified by two-thirds of the staked ETH. Bridges and exchanges wait for it.

### What happens to a validator that goes offline or double-signs?

Going offline costs it a missed slot, no reward, and a small penalty, and around 1% of slots are missed for ordinary reasons. Double-signing is slashable: at least 1 ETH, plus a correlation penalty that can reach the whole 32 ETH when many validators are slashed together.

### Does the validator chosen for a slot build the block?

Usually not. Specialized builders assemble blocks and compete on how much they pay, and the proposer signs the highest-paying one. Most validators run MEV-Boost.

---

# Booleans and integers

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/booleans-and-integers

> Solidity's value types are the simple types that hold their data inline, and booleans and integers are where every contract begins. The surprise for newcomers is that Solidity integers are fixed-size rather than the arbitrary-precision numbers Python or JavaScript give you, which means arithmetic can overflow or silently truncate. Get the file structure and these types right, and everything else in the type system builds on them.

## How a Solidity file is structured

Solidity source code lives in files with the `.sol` extension. A minimal Solidity file has three things before any contract code: a license declaration, a pragma directive, and at least one contract.

```solidity
// SPDX-License-Identifier: MIT
// Solidity 0.8.24, Ethereum mainnet
pragma solidity 0.8.24;

contract Token {
    // state variables, events, and functions go here
}
```

The **SPDX license identifier** is a machine-readable license tag the Solidity compiler expects at the top of every file. The compiler emits a warning if it's missing. Etherscan reads this line when displaying verified contracts. The most common choice is `MIT`. For proprietary code use `UNLICENSED`. Other valid identifiers include `Apache-2.0`, `GPL-3.0`, and `BUSL-1.1`.

The **pragma directive** tells the compiler which Solidity version this file expects. `pragma solidity 0.8.24;` pins exactly that version. `pragma solidity ^0.8.24;` allows that version or any later patch in the 0.8 series. Production code usually pins exactly. Tutorial code often uses the caret to stay forward-compatible.

The **contract block** opens with the `contract` keyword followed by a name and curly braces. Everything that defines what the contract does goes inside: state variables, events, errors, modifiers, the constructor, and functions. A single file can contain multiple contracts, but a deployed contract is one contract block from one file. The on-chain contract lives at an address. The name is purely a source-code convenience.

One Solidity file can also import others. `import "./Token.sol";` brings in everything declared in `Token.sol`. `import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";` brings in just one named symbol. Imports get more interesting once we cover inheritance and libraries.

## How bool works

The `bool` type holds one of two values: `true` or `false`. Nothing else. No `null`, no `undefined`, and no treating other values as true or false the way JavaScript does. A variable typed `bool` has exactly two possible runtime states.

There are two ways to declare one. As a state variable, stored on chain and persisting across transactions:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Vault {
    bool public paused;
}
```

Or as a local variable inside a function, which lives only for the duration of that call:

```solidity
function process() external {
    bool ready = true;
    // ...
}
```

The naming convention is `mixedCase`: first word lowercase, subsequent words capitalized, no underscores. The compiler does not enforce this. Every Solidity codebase you read will follow it anyway.

Boolean values support the standard logical operators. Logical AND is `&&`, logical OR is `||`, negation is `!`. Equality is `==` and inequality is `!=`. Both `&&` and `||` **short-circuit**: if the left operand of `&&` is `false`, the right operand is never evaluated. This matters when the right operand has side effects or could revert.

```solidity
function safeWithdraw() external {
    // checkOwner() is never called if paused is true
    require(!paused && checkOwner(msg.sender), "locked");
}
```

Short-circuiting is the same as in most languages. It's worth pointing out because Solidity has no implicit type coercion to bool. You cannot write `if (someUint)` to test for "non-zero". You write `if (someUint != 0)`. The compiler will reject anything that isn't already typed `bool`.

One quirk: a `bool` takes up one byte on the stack but always occupies a full 32-byte word when stored in a struct or array slot. So a standalone `bool` state variable costs the same to store as a `uint256`. The compiler can pack multiple booleans together into one storage slot if you declare them consecutively as state variables, which becomes relevant once you're thinking about storage layout for gas optimization.

## Why integers are fixed-size

Two ideas before any syntax.

First, **every variable in Solidity has a definite value the moment it is declared**. There is no `null`, no `undefined`, no "uninitialized" state. If you write `uint256 public count;` and never assign to it, reading it returns `0`. Booleans default to `false`, addresses to `0x0...`, strings to empty, structs to all-zero. This is a consequence of how storage on a chain works. Every storage slot is a 32-byte word, and an unwritten slot reads as zeros, which the type system interprets as the zero value of that type.

Second, **Solidity integers are fixed-size machine integers rather than arbitrary-precision math**. When you write `uint256 x = 5`, you have actually allocated 256 bits to hold that 5. Languages you've used hide this from you. Python's `int` grows as needed. JavaScript's `Number` trades away precision silently above 2^53. Ruby's `Integer` auto-promotes to bignum. Solidity does none of that. You choose the bit budget up front, and any arithmetic that exceeds it is treated as a real error.

Those two ideas underlie everything in this section.

## The integer families

Solidity has two integer families: **unsigned** (`uint`) for non-negative values, and **signed** (`int`) for values that can go negative. Inside each family there are several sizes, expressed in bits, from 8 up to 256 in steps of 8. So `uint8`, `uint16`, `uint24`, all the way up to `uint256`. Same for `int`.

A bare `uint` is shorthand for `uint256`. A bare `int` is shorthand for `int256`. The 256-bit width is the default because the EVM itself operates on 256-bit words. Native arithmetic happens on whole words. Smaller integer types still get fetched as full words and then masked. Using `uint8` in a local variable doesn't save you gas. It often costs slightly more because of the masking.

The bit width determines the range. For an unsigned integer of width N, the value can be anywhere from `0` to `2^N - 1` inclusive. So:

- `uint8` ranges from 0 to 255, which is `2^8 - 1`
- `uint16` ranges from 0 to 65,535
- `uint256` ranges from 0 to a number with 78 digits

You can ask the compiler for these bounds directly:

```solidity
uint256 maxUint8 = type(uint8).max;     // 255
uint256 maxUint256 = type(uint256).max; // 2^256 - 1
```

When should you reach for a smaller size? Less often than you'd think. The default `uint256` is the cheapest to operate on. The reason to use smaller types is **storage packing**: if you have several small values that fit together inside one 32-byte slot, declaring them as `uint8` or `uint16` lets the compiler pack them. You pay for one storage slot instead of several. For loop counters, function arguments, and temporary values, the answer is almost always just `uint256`.

The motivation for unsigned types is straightforward. Many on-chain quantities cannot be negative: a token balance, a block number, an amount of gas, the length of an array. Modeling these as `uint256` lets the type system catch a class of bugs at compile time. If a function takes `uint256 amount`, you don't need a runtime check that the caller passed a non-negative number. The type already says so.

## Signed integers and the two's-complement asymmetry

Signed integers sacrifice one bit of value range to remember the sign. So an `int8`, instead of holding 0 to 255, holds `-128` to `127`. The top bit separates negative values from non-negative ones. That gives 128 non-negative values (0 to 127) and 128 negative values (-1 to -128).

```solidity
int256 minInt256 = type(int256).min; // -2^255
int256 maxInt256 = type(int256).max; //  2^255 - 1
```

Notice the asymmetry: the negative side reaches one further from zero than the positive side. That is two's-complement representation, which is what every general-purpose CPU on Earth uses. You don't need to internalize the bit patterns, but you should remember that `type(intN).min` is `-2^(N-1)` and `type(intN).max` is `2^(N-1) - 1`.

When do you need signed integers? Rarely. In typical contract logic, everything is expressed as a non-negative balance plus a direction. Deposits and withdrawals both work in `uint256`, with the operation deciding whether to add or subtract. Signed math creeps in only for things like price deltas, fixed-point arithmetic, or tick math in concentrated-liquidity AMMs. If you're writing a token, an escrow, a vault, or a registry, you almost certainly want unsigned.

## Arithmetic and the truncating division rule

The arithmetic operators are conventional: `+`, `-`, `*`, `/`, `%` for modulo, `**` for exponentiation. Comparisons are `==`, `!=`, `<`, `<=`, `>`, `>=`, and they return `bool`. There's also a unary minus for sign flip.

```solidity
uint256 a = 10 + 3;   // 13
uint256 b = 10 - 3;   // 7
uint256 c = 10 * 3;   // 30
uint256 d = 2 ** 8;   // 256
uint256 e = 10 % 3;   // 1
```

The twist that catches developers from dynamically-typed languages: **integer division truncates toward zero**. No floats, no rounding. Whatever was after the decimal point is discarded.

```solidity
uint256 half = 1 / 2;          // 0, not 0.5
uint256 third = 10 / 3;        // 3, not 3.33...
uint256 percent = 1 * 100 / 200; // 0, not 0.5
```

Solidity has no native floating-point type for production use, and truncating division is the predictable behavior across every fixed-precision language. But it is the source of an entire category of accounting bugs in beginner contracts. A fee calculation that "should be 0.5%" silently becomes 0% for any input that isn't a multiple of 200. The fix in production code is always the same. Scale your numerators up by some factor, typically `1e18`, before dividing, then scale results back down at the boundary where you need a human-readable number. This is the foundation of how every DeFi protocol handles fractional math.

Division by zero reverts the transaction. So does modulo by zero.

## What happens on overflow

Before Solidity 0.8.0, integer overflow wrapped silently. If you incremented a `uint8` past 255, it became 0 with no warning and no revert. This caused real exploits in production contracts. The BatchOverflow incident in 2018 created vast token balances out of nothing in multiple ERC-20 contracts by exploiting unchecked multiplication. That incident was the canonical case study that pushed the language toward fixing this by default.

From Solidity 0.8.0 onward, arithmetic that would overflow or underflow **reverts the transaction by default**. No silent wraparound. The transaction is rolled back as if it never happened, any ETH sent with it is returned to the sender, and the contract state is unchanged. Here is the canonical demonstration:

```solidity
contract OverflowDemo {
    uint8 public value = 254;

    function increment() external {
        value += 1; // 255 the first time, reverts the second
    }
}
```

Call `increment()` once, `value` becomes 255. Call it a second time and the call reverts. `value` stays at 255. The sender still pays gas for the failed transaction, since you cannot get free reverts on Ethereum, but the bad state change does not happen.

This guarantee matters. In a financial contract, "the operation silently does the wrong thing" is almost never what you want. "The transaction fails and the user is told" is almost always what you want.

## The `unchecked` escape hatch

There are cases where you genuinely don't want the overflow check. Two common ones:

1. You've proved by the surrounding logic, or a `require` upstream, that overflow cannot happen, and you want to skip the redundant check to save gas.
2. You actually want modular arithmetic, for example in a hash combiner or a pseudorandom step.

For these, Solidity gives you the `unchecked` block, which suspends overflow protection inside its braces:

```solidity
function unsafeIncrement(uint256 x) external pure returns (uint256) {
    unchecked {
        return x + 1; // wraps to 0 if x == type(uint256).max
    }
}
```

Inside `unchecked`, an overflow wraps around to the bottom of the type's range, and an underflow wraps to the top. Subtracting 1 from a `uint8` whose value is 0 produces 255. Solidity 0.8 just stopped letting you see this by accident.

A practical rule for your first year of Solidity: **do not use ****`unchecked`**** unless you can write down, in one sentence, why the bounds it bypasses cannot be exceeded**. The gas savings are real but small, around 30 to 40 gas per arithmetic op. The cost of being wrong is a security bug auditors will absolutely find.

A pattern you'll see in production code is `unchecked` on loop counter increments:

```solidity
for (uint256 i = 0; i < items.length;) {
    // ... do something with items[i] ...
    unchecked { ++i; }
}
```

This is safe because `i` is provably less than `items.length` at the point of increment, and `items.length` cannot exceed `type(uint256).max`. The EVM doesn't have enough memory to hold an array that long. The pattern saves a small amount of gas per iteration, which matters for loops over thousands of items.

The increment forms `x = x + 1`, `x += 1`, and `x++` are all equivalent and all subject to the same overflow check outside an `unchecked` block. Pick whichever reads best for the code you're writing.

## Things that catch developers

A few patterns to internalize early.

**Defaulting to ****`int`**** because that's what other languages use.** In Solidity, `uint256` is the right default for almost every quantity. Reach for `int` only when you actually need negative values.

**Picking ****`uint8`**** to "save gas" on a single variable.** A standalone `uint8` storage variable costs the same as a `uint256`. Smaller types only pay off when they're packed together in one slot.

**Assuming integer division rounds.** It truncates. `1 / 2` is `0`, not `1`. Scale numerators or use a fixed-point library for percentages and rates.

**Reaching for ****`unchecked`**** to optimize without justification.** The gas savings are small and the worst-case is a real vulnerability. The bar should be: you can articulate the bound you're relying on in one sentence.

**Treating ****`unchecked`**** as "old Solidity behavior, faster."** It's faster, but the reason 0.8 made checking the default is that the unchecked behavior was the cause of major historical exploits.

## FAQ

### Why does 1 / 2 give 0 in Solidity instead of 0.5?

Integer division truncates toward zero. Solidity has no floating-point type, so 10 / 3 is 3. Scale by 1e18 before dividing, then scale back where a human reads the number.

### What happens when a uint8 goes past 255?

Since Solidity 0.8.0 the transaction reverts. State is unchanged, any ETH sent is returned, and the sender still pays gas. Before 0.8.0 it wrapped silently to 0, which the 2018 BatchOverflow exploits used to create token balances out of nothing.

### Should I use uint8 instead of uint256 to save gas?

Usually no. The EVM operates on 256-bit words, so a standalone uint8 costs the same to store as a uint256 and a little more gas to mask. Packing pays off only when several small fields are declared consecutively and share one 32-byte slot.

### What does an unassigned bool read as?

false. Booleans have no null state in Solidity, and every unwritten 32-byte storage slot reads as zeros, so an untouched uint256 reads 0 for the same reason.

### How much gas does an unchecked block save?

Around 30 to 40 gas per arithmetic operation. The bar for using it is being able to write down in one sentence why the bound it bypasses cannot be exceeded.

---

# Strings and addresses

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/strings-and-addresses

Two types you'll meet in the first hour of any non-trivial contract. They handle the two kinds of external data a contract sees. Strings carry text from humans. Addresses carry identifiers from the chain itself. Both behave in ways that catch developers coming from other languages. Strings have limitations: operations you'd expect to work simply don't. Addresses come in two forms, and you have to pick the right one to move money.

## What strings actually are

A string in Solidity is not an array of characters the way it is in higher-level languages. It's a sequence of UTF-8 bytes. When you write a Latin letter, you get one byte. A Cyrillic letter takes two. An emoji takes three or four. The string type stores the bytes faithfully and refuses to commit to any single answer for "how long is this."

This single fact explains every limitation that follows. Solidity did not decide to make strings weak. It decided not to pick a wrong answer for length, equality, or concatenation when the right answer depends on what you mean by "character."

Strings are reference types: the variable holds a reference to data that lives somewhere, and that somewhere has to be named. State variables declared at the contract level live in `storage` automatically. Function arguments and local variables require an explicit data-location keyword:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Example {
    string public name = "MyToken";   // storage, by default

    function setName(string memory newName) public {
        name = newName;               // memory string copied into storage
    }
}
```

`calldata` is more efficient for arguments you only read from, since it avoids the copy into memory. `memory` works in any situation and is the safe default if you're not sure which to use.

## What strings can't do

The following all fail to compile, despite being routine in JavaScript or Python:

```solidity
string memory a = "hello";
uint len = a.length;             // no .length on string
string memory b = a + " world";  // no + concatenation
if (a == "hello") { ... }        // no == on string
bytes1 first = a[0];             // no index access
```

The reason traces back to the byte-array nature. `string.length` would be ambiguous: bytes? Unicode code points? Grapheme clusters? Different answers depending on what you mean, all of them defensible. Solidity exposes none of these operations rather than commit to a wrong one.

The workarounds, when you actually need them:

```solidity
// Compare strings by hashing their underlying bytes.
bool equal = keccak256(bytes(a)) == keccak256(bytes(b));

// Read individual bytes by casting the string to bytes.
bytes1 firstByte = bytes(a)[0];

// Concatenate with string.concat, available since 0.8.12.
string memory greeting = string.concat(a, " world");

// Get length in bytes (not characters) by casting first.
uint byteLength = bytes(a).length;
```

All of these are expensive in gas. Hashing a long string is O(n) in its byte length. Concatenation allocates fresh memory. For anything string-heavy, ask whether the work belongs off-chain.

## What strings are good for

Token names, token symbols, URIs pointing to off-chain metadata, event payloads, and human-readable error messages. The pattern is: store the string, emit it, return it, but do not manipulate it on-chain. NFT projects almost never store full metadata on-chain. They store an IPFS or HTTPS URI as a string, and the actual data lives off-chain at that URI.

If you find yourself wanting to slice, search, lowercase, or compare strings on-chain, step back. Either restructure so the comparison happens off-chain and the result is passed in, or use `bytes32` for short fixed-length identifiers, which gives you a value type with proper comparison operators.

## What addresses identify

An address in Solidity is a 20-byte value that identifies an account on Ethereum. Both externally owned accounts and contracts have addresses. Externally owned accounts are the wallets people use. From the type system's perspective they're identical. You can call into either, transfer ETH to either, query the balance of either.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Example {
    address public owner = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;

    function ownerBalance() public view returns (uint256) {
        return owner.balance;
    }

    function balanceOf(address target) public view returns (uint256) {
        return target.balance;
    }
}
```

Addresses are written as hex literals without quotes. They are not strings, and they are not interchangeable with `bytes20` despite being the same size in bytes. The `.balance` property reads the current balance of any address in wei, the smallest ETH denomination. One ETH equals `10**18` wei. ETH amounts are stored in `uint256`, the EVM's native 256-bit word.

All balance data on Ethereum is public, which is why you can query any address's balance, including ones your contract does not own. The `view` keyword on these functions is a promise that they don't modify state, which lets them be called for free without sending a transaction.

One implementation detail worth knowing. Even though an address is 20 bytes logically, on the EVM stack it occupies a full 32-byte word with the top 12 bytes zeroed. This is why you'll occasionally see code cast between `address` and `uint160`. The cast is well-defined because `uint160` is exactly the bit width an address fits into.

## The payable capability

Here's where the type system steps in. Sending ETH from your contract to an address used to be done with a method called `.transfer()`. But `.transfer()` exists only on a specialized version of the address type called `address payable`. A plain `address` does not have it.

Why two address types? Sending ETH is a state-changing action with real consequences. Solidity makes you opt into that capability explicitly. Every time you touch a payable address, the type system reminds you that money can flow this way.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Wallet {
    // Option 1: declare the field as address payable from the start.
    address payable public recipient;

    constructor(address payable initialRecipient) {
        recipient = initialRecipient;
    }

    function sendOne() public {
        recipient.transfer(1 ether);
    }

    // Option 2: keep a plain address, cast when sending.
    address public otherRecipient;

    function sendTwo() public {
        payable(otherRecipient).transfer(1 ether);
    }
}
```

Both patterns appear in production code. Declaring the field as `address payable` makes the intent clear at the storage level. You can't always do that, since some sources of addresses give you a plain `address`, like function arguments from external callers or return values from certain operations. The `payable(addr)` cast lets you opt into the capability at the call site.

A word on `.transfer()` itself. The method forwards a fixed 2300 gas stipend to the recipient. That used to be enough for the recipient's `receive()` function to log an event and return. Since EIP-1884 raised the cost of certain opcodes in 2019, the 2300 stipend has become unreliable, and `.transfer()` to a contract recipient can fail when the recipient is a multisig or a proxy that runs extra logic on receipt. Modern Solidity style is to send ETH using a low-level `call` instead:

```solidity
(bool ok, ) = payable(target).call{value: amount}("");
require(ok, "send failed");
```

Low-level calls have meaningful depth beyond what this lesson covers. The takeaway here is just that `.transfer()` is no longer the default recommendation, despite still appearing in older tutorials.

## Receiving ETH

The other side. For your contract to receive ETH at all, it needs at least one function marked `payable`. The keyword is the signal to both the compiler and the EVM that this function accepts attached value.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Vault {
    function deposit() public payable {
        // body can be empty; the ETH is credited automatically
    }

    function balance() public view returns (uint256) {
        return address(this).balance;
    }
}
```

A contract with no payable function rejects incoming ETH transfers. Solidity also has two special functions called `receive()` and `fallback()` that handle ETH sent without naming a specific function to call. Both have quirks of their own. For now, if you want a contract to be able to receive ETH at a named function, expose one marked `payable`.

Notice `address(this).balance` in the second function. `address(this)` is the current contract's own address. `.balance` works on it the same way it works on any other address, returning the ETH balance in wei. This is the standard way for a contract to ask "how much ETH am I holding right now?"

## Where the two meet: `msg.sender` and `msg.value`

Every function call has access to a global `msg` object that describes the incoming call. Two of its fields tie this lecture together.

`msg.sender` is the address that called the current function. For an EOA-initiated transaction, that's the wallet. For a contract-to-contract call, it's the calling contract. Either way, it's typed as plain `address`, not `address payable`. If you want to send ETH back to `msg.sender`, you have to cast: `payable(msg.sender)`.

`msg.value` is the amount of ETH, in wei, sent with the call. It's typed as `uint256` and is only non-zero inside `payable` functions, since non-payable functions reject value transfers.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Logger {
    address public lastSender;
    uint256 public lastValue;

    function log() public payable {
        lastSender = msg.sender;
        lastValue = msg.value;
    }
}
```

Inside `log`, `msg.sender` is whoever called this transaction. `msg.value` is whatever ETH they attached. The state writes commit if the function returns normally. They roll back if anything in the function reverts.

## A small contract using both

A near-minimal demonstration. This contract receives ETH from anyone, records who deployed it, and lets the deployer forward funds elsewhere.

```solidity
// SPDX-License-Identifier: MIT
// Solidity 0.8.24, Ethereum mainnet
pragma solidity 0.8.24;

contract ForwardingWallet {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    function deposit() external payable {}

    function forward(address target, uint256 amount) external {
        // Production code needs access control and balance checks here.
        (bool ok, ) = payable(target).call{value: amount}("");
        require(ok, "send failed");
    }

    function balance() external view returns (uint256) {
        return address(this).balance;
    }
}
```

The constructor captures the deployer's address as `msg.sender`. The `deposit` function is external `payable` with an empty body. The ETH is credited automatically when the function is called with attached value. The `forward` function casts the target to `payable` and sends the requested amount via a low-level `call`. Production code would add access control on `forward` and check that the requested amount is actually available before sending, but the skeleton you see here is the minimum that compiles and runs.

## Things that catch developers

A few patterns to internalize.

**Forgetting the data-location keyword on a string argument.** `function set(string newName)` fails to compile. You need `string memory newName` or `string calldata newName`. The reflex from other languages is to omit the keyword entirely.

**Trying to compare strings with ****`==`****.** Compile error. Use `keccak256(bytes(a)) == keccak256(bytes(b))` when comparison is genuinely needed, but first ask whether the comparison should happen off-chain.

**Storing large strings on-chain when an off-chain pointer would do.** Storage is expensive. NFT metadata, large configs, anything text-heavy: store a URI to off-chain data rather than the data itself.

**Calling ****`.transfer()`**** on a plain ****`address`****.** Compile error. Either declare the field as `address payable` or cast at the call site with `payable(addr)`.

**Sending ETH to a contract that has no payable function.** The transaction reverts. If you control the receiving contract, expose a payable function. If you don't, the contract cannot accept the transfer.

**Treating ****`address`**** and ****`address payable`**** as interchangeable.** They aren't. Every cast between them is a place where money can move. Auditors look at exactly those points.

**Defaulting to ****`.transfer()`**** because every old tutorial uses it.** The 2300 gas stipend is unreliable post-EIP-1884. The modern pattern is `(bool ok, ) = payable(target).call{value: amount}(""); require(ok);`. Older Solidity code in production uses `.transfer()` and mostly works fine, but new code should default to the call form.

## FAQ

### Why can't I compare two strings with == in Solidity?

Solidity defines no == for strings. Hash the bytes, keccak256(bytes(a)) == keccak256(bytes(b)). A string is a sequence of UTF-8 bytes, so .length, indexing and + are left out too. To join two strings, use string.concat, added in 0.8.12.

### When do I need payable(addr) instead of a plain address?

Whenever you call .transfer() on it. Only address payable carries that method. msg.sender and function arguments from external callers arrive as plain addresses, so the cast at the call site is common.

### Why is .transfer() no longer the recommended way to send ETH?

It forwards a fixed 2300 gas stipend, and EIP-1884 raised opcode costs in 2019. A multisig or proxy that runs logic on receipt now needs more, so the send fails. Use a low-level call, (bool ok, ) = payable(target).call{value: amount}(""), then require ok.

### Why does sending ETH to my contract revert?

The contract has no function marked payable. Without one, every incoming ETH transfer is rejected.

---

# Mappings

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/mappings

> A key-value store, by design constrained. Solidity's mappings look like dictionaries from other languages until you try to do anything with them. No length, no iteration, no equality, storage-only. These limitations aren't arbitrary. They follow directly from how the EVM stores data, and once you see the storage model the design choices become inevitable.

## What you'd expect

Every mainstream language has some flavor of dictionary. Python's `dict`, JavaScript's `Map`, Java's `HashMap`. The interface is consistent across them: put a value under a key, get it back by key, list the keys, ask for the size, iterate.

Solidity has the same data structure. It's called a `mapping`. The syntax even looks the same:

```solidity
mapping(address => uint256) balances;
balances[someAddress] = 100;
uint256 b = balances[someAddress];
```

And then everything else you'd expect to work doesn't. You can't call `.length`. You can't loop over the keys. You can't return a mapping from a function. You can't even put one in memory. Reading a key that was never written doesn't throw an error, it just gives you zero.

To understand mappings, start from why these limitations exist.

## The constraint: EVM storage

A contract's persistent state lives in what the EVM calls storage. Storage has a specific shape that drives the entire design of mappings.

Storage is a flat array of slots. Each slot holds exactly 32 bytes. There are `2^256` of them, numbered from 0 upward. That's an astronomically large number. Every slot exists in principle. Every slot is initialized to zero. Every slot can be read or written by the contract that owns it.

Three properties matter for what follows:

- The keyspace is fixed and enormous. Every possible 256-bit number is a valid slot address.
- Every slot starts as zero. There's no concept of "this slot hasn't been written." Reading a fresh slot returns 32 bytes of zero.
- The EVM does not track which slots a contract has touched. There's no metadata, no key set, no list of "slots in use." You write to a slot, the value is there. You read another slot, you get zero. The EVM doesn't distinguish written from unwritten.

These properties are not Solidity-specific. They're the EVM. Solidity's design choices for state variables, arrays, and mappings all have to live within these rules.

## What this forces

Suppose you want a data structure that lets a contract store unbounded key-value pairs. Keys can be addresses or integers or fixed bytes. Values can be anything. You want O(1) lookups, the way every dictionary in every other language works. Walk through what you have to do, given the storage shape above.

**Step one: deriving slots from keys.** You have keys, and you have a flat numbered slot space. To get O(1) lookup, the slot for a given key needs to be computable from the key alone, with no intermediate table lookup. The standard way to do this is hashing. Hash your key with a cryptographic hash function, take the result as a 256-bit number, use that as the slot address.

Solidity does exactly this. For a mapping declared at position `p` in the contract's storage layout, the slot for key `k` is `keccak256(k, p)`. No table lookup, no key list, no metadata. The slot comes straight from the hash.

**Step two: handling the "key not present" case.** Every key hashes to some slot. That slot was zero before anyone wrote to it. That slot is still zero unless someone wrote to it.

This means there's no way to "ask if a key exists" that's distinct from "read the value at that key." If you ask, the EVM gives you whatever is currently in the slot. If that's zero, you can't tell whether the value zero was stored deliberately or whether nothing was ever stored.

So Solidity makes a choice: reading a key always succeeds. There is no concept of a missing key. Conceptually, every possible key already maps to the zero value of the value type. Writing replaces the zero. Writing zero is indistinguishable from never writing at all.

This is the mental picture. Not a table with a small finite set of entries, but an infinite table with one entry for every possible key, all pre-initialized to zero, sitting there waiting for you to overwrite some of them.

**Step three: confronting what you can't do.** Now ask what's missing.

**Length** is gone. The EVM doesn't track which slots you've touched, and there's no key set being maintained anywhere. There's literally no number that represents "how many entries are in this mapping," because conceptually every key is an entry. There are `2^256` of them. Reporting that is useless.

**Iteration** is gone. To iterate, you need to enumerate keys. To enumerate keys, you need a key list. There is no key list.

**Equality** is gone in two senses. You can't ask whether two mappings hold the same set of entries. You can't ask whether a specific key was set.

**Returning from a function** is gone. To return a mapping, you'd have to return its contents, which means enumerating the keys, which is impossible. Solidity refuses to compile a function that tries to return a mapping by value.

**Memory and calldata** are gone. The slot-derivation trick only works in storage. Memory has its own model and there's no equivalent mechanism. Solidity rejects any attempt to declare a `mapping` as a memory or calldata variable.

**Bulk deletion** is gone. `delete myMapping` doesn't compile, because the operation would require enumerating every key the contract has touched, which the EVM can't tell you. You can `delete myMapping[someKey]` because that just sets one slot back to zero, but you can't wipe the whole structure in one operation.

Every one of these limitations falls out of the same starting constraint. Storage is flat, slots are derived by hashing, the EVM tracks nothing on its own. Once you've accepted that, you've accepted everything.

## Declaring and using mappings

Pulling it together. The syntax for declaring a mapping:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Storage {
    mapping(address => uint256) public balances;
}
```

The slot for `balances[someAddr]` is `keccak256(someAddr, 1)` if `balances` is declared at storage position 1. You don't compute this yourself. The compiler emits the SLOAD and SSTORE opcodes that do it. But knowing how it works explains everything else.

Reading and writing use bracket syntax:

```solidity
balances[msg.sender] = 100;             // SSTORE to keccak256(msg.sender, slot)
uint256 b = balances[msg.sender];       // SLOAD from the same slot
balances[msg.sender] += 50;             // SLOAD + add + SSTORE
```

Reading a never-written key returns the zero value of the value type. For `uint256`, that's 0. For `bool`, `false`. For `address`, `0x0`. For `string`, the empty string. No revert, no error, just the type's default. The default-value rule that holds for every Solidity variable generalizes here: every key has a definite value at all times, which is zero until you write something else.

**Key type restrictions** are tighter than value type restrictions. Keys must be types the EVM can deterministically hash: integers, addresses, `bool`, fixed-width bytes. Dynamic types like `string` and `bytes` are also legal as keys, and the compiler hashes them by their full byte content. Structs, mappings, arrays, and other compound reference types aren't allowed as keys. They don't have a canonical fixed representation.

**Value types are unrestricted.** Any type works, including nested mappings, dynamic arrays, structs, anything. Nested mappings compose by recursive hashing: the slot for `outer[k1][k2]` is `keccak256(k2, keccak256(k1, p))`. All the way down.

```solidity
// Token allowance pattern: owner => (spender => amount)
mapping(address => mapping(address => uint256)) public allowance;

allowance[msg.sender][spender] = amount;
```

**Public mappings auto-generate a getter.** A `public` mapping creates an external function with the same name that takes the key and returns the value. For `mapping(address => uint256) public balances`, the auto-getter is `function balances(address) external view returns (uint256)`. For a nested mapping, the getter takes all the keys in order. The getter has no equivalent of "give me the whole mapping" because that operation doesn't exist.

## A canonical contract

The pattern you'll write more often than any other. A mapping keyed by sender, accumulating the values they've sent:

```solidity
// SPDX-License-Identifier: MIT
// Solidity 0.8.24, Ethereum mainnet
pragma solidity 0.8.24;

contract PaymentLog {
    mapping(address => uint256) public totalSentBy;

    function pay() external payable {
        totalSentBy[msg.sender] += msg.value;
    }
}
```

Three lines of logic. Anyone can call `pay()` with ETH attached. The contract reads that sender's running total from the mapping, adds the incoming amount, writes the new total back. The auto-generated `totalSentBy(addr)` getter lets external callers query any sender's running total.

Everything from the previous lesson shows up here. `msg.sender`, typed `address`, is the mapping key. `msg.value`, typed `uint256`, is the value being accumulated. The `payable` keyword admits ETH. The mapping pattern records per-user state without needing to track who has paid before.

This three-line pattern, with small variations, is the basis for crowdfunding contracts, donation pools, balance tracking in token contracts, vote counting, and many other accounting-style patterns. Recognize the shape. You'll see it everywhere.

## Workarounds for what mappings can't do

Each impossible operation has a standard workaround. None of them are free.

**Wanting to iterate.** Maintain a parallel array of keys yourself. Every time you insert a key for the first time, push it onto the array. When you need to iterate, loop over the array.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract IterableBalances {
    mapping(address => uint256) public balanceOf;
    mapping(address => bool) public hasEverPaid;
    address[] public allPayers;

    function pay() external payable {
        if (!hasEverPaid[msg.sender]) {
            hasEverPaid[msg.sender] = true;
            allPayers.push(msg.sender);
        }
        balanceOf[msg.sender] += msg.value;
    }
}
```

The cost is real. Every first-time payer triggers two extra storage writes: one to flip the seen flag, one to append to the array. The gain is that you can now enumerate every payer off-chain by reading `allPayers`.

**Wanting to distinguish "stored zero" from "never stored."** Maintain a separate flag mapping, exactly like `hasEverPaid` does above. The contract now has two pieces of state: the value, and whether anything was ever set. If the value is zero and the flag is false, the key was never written. If the value is zero and the flag is true, the key was deliberately set to zero.

This is the right pattern whenever zero is a legitimate distinct value rather than "absence." Auctions where a zero bid is a real action. Voting where a vote weighted zero is a deliberate abstention. Allowance mappings where setting the allowance to zero is a deliberate revocation.

**Wanting bulk operations or removal with renumbering.** The parallel-array pattern handles insertion and iteration well, but not deletion. Removing a key from a parallel array means either leaving a gap or shifting later entries. Both have problems. The standard answer is to reach for OpenZeppelin's `EnumerableSet` or `EnumerableMap`, which implement the swap-and-pop technique to keep the parallel array dense. If you're maintaining your own iteration support, look at how they do it before reinventing.

## Where mappings are the wrong answer

Mappings are the right answer for unbounded, sparse, key-addressable data. They're the wrong answer in several specific cases.

For small known key sets, say the four possible states of a contract lifecycle, a fixed-size array indexed by an enum value is simpler and gives you trivial iteration.

For ordered traversal, for example payers from largest to smallest, mappings give you nothing useful. Even the parallel-array trick only helps if the array is maintained in sorted order, which costs extra on every insert.

For "is this key present" checks that need to be fast and frequent, the separate flag mapping has the right semantics but doubles your storage cost. Sometimes the right answer is to use a sentinel value that can't appear naturally in the value type, so absence is detectable from the value alone.

The deeper point is this. Mappings optimize hard for O(1) random access at the cost of every other access pattern. Use them when that tradeoff matches your problem. When it doesn't, an array, a struct, or a more specialized data structure is the right call.

## Things that catch developers

A few patterns to internalize.

**Reading a key returns zero, never an error.** No "key not found" exception, no null. If your logic needs to distinguish "user has zero balance" from "user has never interacted," you need a separate flag or a sentinel value. Building auth checks on `balance > 0` is a real bug class because a legitimate user who just spent everything looks identical to a non-user.

**`delete mapping[key]`**** sets the value back to zero, it doesn't remove the entry.** There is no removal because there was never an entry. If you're maintaining a parallel array for iteration, `delete` doesn't update the array. You have to do that work yourself.

**Trying to put a mapping in memory or in a struct returned to memory.** Mappings live in storage only. A struct containing a mapping cannot be passed around or returned by value. It can only exist as a storage-located variable, which is a real constraint on how you structure your contracts.

**Forgetting that public mappings expose every key.** The auto-generated getter takes the key as input. There's no privacy in mapping data. If you don't want every value queryable by anyone, mark the mapping `private`. Note that "private" only means the auto-getter is suppressed. The data is still readable directly from storage by anyone who knows the slot derivation, which on a public chain is everyone.

**Using a mapping for ordered data.** Mappings are unordered by design. If insertion order or rank order matters, you need a different structure. As shown earlier, a parallel array can impose order, but maintaining it becomes expensive.

## FAQ

### Why can't I loop over a mapping's keys?

There is no key set to iterate. A mapping computes each key's slot by hashing the key, and the EVM keeps no record of which slots a contract touched. Keep your own array of keys if you need to enumerate them.

### What does reading an unset key return?

The zero value of the value type. 0 for a uint, false for a bool, the zero address for an address. A deliberately stored zero and an untouched key are indistinguishable.

### How do I tell an unset key from one deliberately set to zero?

There is no built-in check. Keep a second mapping to bool and set it true the first time a key is written. Zero with the flag false means never set. Zero with the flag true means a deliberate zero.

### Which storage slot does balances[addr] live in?

keccak256(addr, p), where p is the mapping's position in the contract's storage layout. Nested mappings hash recursively, so outer[k1][k2] sits at keccak256(k2, keccak256(k1, p)).

### Can anyone read a private mapping?

Yes. private only hides the auto-generated getter, and all contract storage is publicly readable.

---

# Enums, arrays, and byte arrays

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/enums-arrays-and-byte-arrays

> Three composite types that handle discrete states, indexed sequences, and raw byte data. You'll touch all three in nearly every non-trivial contract. They share a single unifying idea: all three are positional. Enums map names to integer indices. Arrays are accessed by integer index. Byte arrays are sequences of bytes accessed by index. That positional nature is what makes them feel related, and it's why their operations all look similar.

The contrast with the previous lesson is worth holding in mind. Mappings are accessed by key, with no notion of a "first" or "last" element. The types in this lesson are accessed by position, with length, ordering, and iteration as natural operations. Storage cost scales with the number of positions used. Iteration is possible for all three.

## How `enum` works

An enum declares a finite set of named values. Solidity stores them internally as small integers, but in your code you reference them by name.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Order {
    enum Status { Pending, Paid, Shipped, Delivered }

    Status public currentStatus;

    function pay() external {
        currentStatus = Status.Paid;
    }

    function ship() external {
        currentStatus = Status.Shipped;
    }
}
```

Three things to notice. First, you declare an enum at the contract level, the same way you'd declare a state variable. Second, you reference values with dot syntax: `Status.Paid`, never just `Paid`. Third, when you read `currentStatus` from the public getter, you get back a small integer because that's how the value is actually stored. The first declared name gets 0, the second gets 1, and so on.

The default value of an enum variable is always the first declared value. In the example above, a freshly declared `Status` variable equals `Status.Pending` because `Pending` is index 0. This matters. Order your enum values so that the most natural starting state comes first. Putting `Delivered` first by accident would mean every newly created order starts as delivered, which is exactly the kind of bug that reaches production.

You can convert between enum values and their underlying integer with an explicit cast:

```solidity
uint8 statusIndex = uint8(currentStatus);  // explicit downcast to integer
Status reconstructed = Status(2);          // build a Status from an integer
```

The cast in the second line will revert if the integer is out of range for the enum. So `Status(7)` on a four-value enum reverts at runtime. You don't get an undefined value the way you might in C.

The storage cost is small. Solidity stores enum values in a `uint8`, the smallest unsigned integer type. An enum can have at most 256 members, so one `uint8` always holds the value. This makes enums much cheaper than the string-typed status fields you'd use in a database schema.

Enums also make excellent mapping keys. The previous lesson's mapping pattern composes naturally:

```solidity
mapping(Status => uint256) public ordersInState;
ordersInState[Status.Pending] += 1;
```

When should you use an enum? Whenever you have a small, fixed set of mutually exclusive states. Order lifecycles, governance proposal states, vault modes, escrow phases. If you ever find yourself comparing strings to magic constants like `"pending"` or `"paid"`, an enum is the right answer.

## Fixed-length arrays

Arrays come in two flavors. The fixed-length version specifies its size at declaration, and that size never changes:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract FixedArray {
    uint256[10] public scores;     // exactly 10 slots, all initialized to 0

    function setScore(uint256 index, uint256 value) external {
        scores[index] = value;     // index >= 10 reverts
    }

    function getScore(uint256 index) external view returns (uint256) {
        return scores[index];
    }
}
```

The size goes inside the brackets after the element type. Indexing is zero-based, the same as most mainstream languages. Reading or writing an index that's out of bounds reverts the transaction. This is a runtime check the EVM performs automatically.

The unfilled slots have the zero value of the element type. So in the example above, every position from 0 to 9 reads as 0 immediately after deployment, until you write to it. This is consistent with how all storage works in Solidity, but it's worth saying out loud because some languages return `null` or undefined for unset array slots.

Solidity arrays are homogeneous. Every element has to be the same type. There's no `[uint256, address, bool]` triple of mixed types. That's what structs are for.

You can initialize a fixed array with literal values, but the syntax is finicky:

```solidity
uint256[3] memory firstThree = [uint256(1), 2, 3];
```

The cast on the first element forces the literal type. Without it, the compiler infers `uint8` for the literal `1` and then refuses to assign a `uint8[3]` to a `uint256[3]`. This is one of those small Solidity annoyances that seems unfair until you understand the type inference rules. For state-variable declarations it usually doesn't come up. You'll hit it in memory arrays.

## Dynamic arrays

The other flavor has no fixed size and grows or shrinks at runtime:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract DynamicArray {
    uint256[] public items;

    function add(uint256 value) external {
        items.push(value);          // append
    }

    function removeLast() external {
        items.pop();                // remove the last element
    }

    function count() external view returns (uint256) {
        return items.length;
    }
}
```

Three operations to know. `push(value)` appends to the end of the array, growing it by one. `pop()` removes the last element and shrinks the array by one. `length` returns the current number of elements. Index access works the same as for fixed arrays, with the same runtime bounds check.

Note that `push` and `pop` do not exist on fixed-length arrays. Trying `scores.push(1)` on the `uint256[10]` from earlier is a compile error. The reasoning is in the name: a fixed array's length is fixed.

`delete` works on dynamic arrays in two ways. `delete arr[i]` sets the element at index `i` to the zero value of the element type, without changing the array's length. `delete arr` clears the entire array, setting its length to zero. The latter is the bulk-clear operation that mappings don't have.

Dynamic arrays in storage are gas-friendly for appends and individual reads, but expensive for operations that touch the whole array. Iterating over a thousand-element dynamic array inside a function can easily exceed the block gas limit. The general guidance: if you might iterate, keep the array small, or keep iteration off-chain.

## Nested arrays and the right-to-left reading rule

Solidity supports nested arrays, but the dimension order is the opposite of what most languages use. This is genuinely confusing the first time you see it:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Matrix {
    // OUTER length is 2, INNER length is 3.
    // Read RIGHT to LEFT: "two arrays of three uints"
    uint256[3][2] public grid;

    function setCell(uint256 outer, uint256 inner, uint256 value) external {
        grid[outer][inner] = value;
    }
}
```

The trick is that `uint256[3][2]` should be read right-to-left as "an array of length 2, where each element is `uint256[3]`." So the outer index is the second number in the type, and the inner index is the first.

The brackets in the access expression go in the opposite order from the type declaration. Declaring `[3][2]` and then accessing `grid[outer][inner]` looks contradictory until you remember that the access reads left-to-right, with the outer index first and the inner second, while the type reads right-to-left, with the innermost type first.

The same right-to-left logic applies to higher dimensions. Until you are used to it, double-check every nested array declaration. Real bugs have reached production because someone declared `uint256[5][10]` thinking they were getting 5 rows of 10 columns when they actually got 10 rows of 5 columns.

## Memory arrays

Local arrays inside a function live in memory, the same way local strings do. The declaration syntax for a memory array is different from storage:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract MemoryArrays {
    function buildArray() external pure returns (uint256[] memory) {
        uint256[] memory temp = new uint256[](10);  // size required at allocation
        temp[0] = 100;
        temp[1] = 200;
        return temp;
    }
}
```

Two things to flag. First, the `new T[](size)` syntax is mandatory for memory arrays, and the size must be specified at allocation. There's no equivalent of `push()` for memory arrays. Their length is fixed once they're allocated. If you need to "grow" a memory array, you allocate a new one of the larger size and copy.

Second, the size argument can be a variable rather than only a constant. So `new uint256[](msg.value / 100)` is valid and will allocate an array sized at runtime based on the incoming ETH. This is one of the few places in Solidity where you allocate memory whose size depends on runtime inputs.

Returning a memory array from a function uses the type `T[] memory`, as shown in the example. The function caller receives a copy. This is how you produce dynamic-sized result data, for example a list of token IDs owned by an address.

## Byte arrays

Byte arrays come in two forms. Fixed-width sizes are `bytes1`, `bytes2`, and so on up to `bytes32`. The dynamic version is just `bytes`.

The fixed-width forms are exactly what they sound like. `bytes1` is one byte. `bytes32` is 32 bytes, which is exactly the size of an EVM word and the natural size for storing a hash. The reason to use a smaller width is when packing several into one slot together with other small types.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract ByteArrays {
    bytes32 public hash = keccak256("hello");
    bytes4 public selector = 0xa9059cbb;       // ERC-20 transfer() selector
    bytes public data;                          // dynamic, grows as needed

    function appendByte(bytes1 b) external {
        data.push(b);                           // bytes supports push, like dynamic arrays
    }
}
```

Dynamic `bytes` is essentially `bytes1[]` but with a more efficient storage layout. It supports `push`, `pop`, `length`, and index access in storage. It's the right type whenever you're handling arbitrary-length raw byte data: hashed pre-images, ABI-encoded payloads, signatures, and similar.

A note on `bytes` versus `string`: they have the same underlying storage layout. The difference is purely semantic. `string` carries the convention that the bytes are valid UTF-8 text. `bytes` carries no such convention. Use `string` when the data is meant to be displayed as text, `bytes` for everything else.

## Why bytes look like a fix for string length, but aren't

The previous lesson noted that you can cast a string to bytes to get its byte count. The natural follow-up question is whether that byte count is the same as the character count. For ASCII it is. For anything else, it isn't.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract StringLength {
    function asciiLength() external pure returns (uint256) {
        bytes memory b = bytes("hello");
        return b.length;  // returns 5
    }

    function unicodeLength() external pure returns (uint256) {
        bytes memory b = bytes(unicode"привіт");  // 6 Cyrillic letters
        return b.length;  // returns 12, not 6
    }
}
```

The Cyrillic example has 6 visible characters but takes 12 bytes because each Cyrillic letter is 2 bytes in UTF-8. An emoji can take 4 bytes. A flag emoji can take 8.

The takeaway: `bytes(s).length` is the byte count rather than the character count. Use it knowingly. If you need character counts on internationalized text, the answer is to do that work off-chain.

The `unicode""` literal in the example is required when the string contains non-ASCII characters. Plain `"..."` literals reject them at compile time. This is Solidity's way of making you opt into the encoding question.

## Reading individual bytes

You can index into a byte array to pull out a single byte:

```solidity
function firstByte(bytes memory b) external pure returns (bytes1) {
    require(b.length > 0, "empty");
    return b[0];
}
```

The return type is `bytes1`, a single byte. You can compare it, convert it to a small integer, or use it in switch-style logic. This is how you'd parse a custom binary format on-chain, though if you find yourself doing that frequently it's usually a sign the format should be redesigned.

## FAQ

### What does a freshly declared enum variable equal?

Its first declared value. Enums are stored as integers starting at 0, so for enum Status { Pending, Paid, Shipped } a fresh Status is Status.Pending. Order the members so the natural starting state comes first.

### How many values can a Solidity enum hold?

256. The value is stored in a uint8, and casting an out-of-range integer such as Status(7) on a four-member enum reverts at runtime.

### Why is push a compile error on my array?

push and pop exist only on dynamic storage arrays. A fixed-size uint256[10] fixes its length at declaration, and a memory array fixes it at allocation with new uint256[](n).

### Why does bytes(myString).length not match the character count?

It counts UTF-8 bytes. A Latin letter is one byte, a Cyrillic letter two, an emoji up to four. The six-letter word привіт reports a length of 12.

### How do I read a nested array type like uint256[3][2]?

Right to left. uint256[3][2] is an array of length 2 whose elements are each a uint256[3]. The access expression reads the other way, so grid[outer][inner] takes the outer index first. Declaring uint256[5][10] gives you 10 rows of 5.

---

# Structs

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/structs

If you've used Go, Rust, TypeScript, or C, the basic model is the same. Two things make Solidity structs distinctive. First, they have rules about what can go inside them, including no recursion and no storage qualifiers on fields. Second, they interact with storage and memory in ways that affect both correctness and gas cost. The reference-vs-copy distinction in particular is the single most common source of bugs for developers new to the language.

## The model

A struct is a named bundle of fields. Two things to internalize before any syntax.

First, field access is by name rather than by position. This is the main difference from arrays, which use positions, and from mappings, which use keys. When you have a `Payment` struct with an `amount` field, you read it as `payment.amount`. The compiler maps the name to the field's location and reads it for you. You never work with positions directly. Structs are the right choice when the things you're bundling are different in kind rather than in position.

Second, structs are templates that only become values once instantiated. Declaring `struct Payment { ... }` only defines the shape. To actually have a payment, you need to create an instance, either as a state variable, a local variable, or as a value sitting inside a mapping or array. The instance is where the data lives. The struct definition is just the recipe.

These two ideas explain why some operations don't exist. You can't compare two structs with `==`, because there's no general definition of equality across all field types. You can't have a struct as a mapping key, because keys have to be hashable value types and structs aren't. You can't return a struct that contains a mapping, because the mapping has no enumerable content to copy out.

## Defining a struct

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Marketplace {
    struct Order {
        uint256 amount;
        address buyer;
        uint64 placedAt;
        bool fulfilled;
    }

    Order public lastOrder;
}
```

A few rules.

**Field types can be almost anything.** Primitives, arrays, mappings, other structs. The one restriction is that a struct can't contain a field of its own type directly. More on that below.

**No storage qualifiers inside the definition.** When you have a `string` field inside a struct, you write `string message;`, not `string memory message;`. Storage location is determined by where the struct instance lives rather than by where each field is declared. A struct instance in storage stores its strings in storage. A struct instance in memory stores them in memory. The fields don't get a separate choice.

**No recursion.** A struct cannot contain a field of its own type directly. This is a compile error:

```solidity
struct TreeNode {
    uint256 value;
    TreeNode left;   // compile error: recursive struct
    TreeNode right;
}
```

The reason is mechanical. A struct's storage layout is computed at compile time by summing the sizes of its fields, and a recursive type would have an infinite size. The workaround is indirection. Hold an array or a mapping of the type instead of the type itself.

```solidity
struct TreeNode {
    uint256 value;
    TreeNode[] children;   // ok: array introduces indirection
}
```

An array breaks the cycle because it's just a length and a pointer to a separate region of storage. The struct itself remains finite-sized.

## Storage packing

Solidity packs struct fields into 32-byte storage slots whenever consecutive fields fit together. Field order matters for gas. Consider these two definitions:

```solidity
// 3 storage slots: each field uses its own slot
struct Bad {
    address owner;   // 20 bytes, slot 0 (12 unused trailing bytes)
    uint256 amount;  // 32 bytes, slot 1
    bool active;     // 1 byte, slot 2 (31 unused trailing bytes)
}

// 2 storage slots: owner + active pack together
struct Good {
    address owner;   // 20 bytes, slot 0
    bool active;     // 1 byte, slot 0 (packed)
    uint256 amount;  // 32 bytes, slot 1
}
```

The `Good` version uses one less storage slot per instance, which translates into thousands of gas saved on every fresh write and every cold read. A simple guideline: declare larger fields together, and group smaller fields so they can share a slot. This becomes a real optimization for contracts that create many struct instances, like NFT contracts, vaults, and order books.

The full rules of storage layout have more depth than this section covers, but knowing that field order matters is enough to write reasonable structs today.

## Creating instances

Two ways to construct a struct value. Both create the same thing. They differ in readability.

**Positional:**

```solidity
Order memory o = Order(100, msg.sender, uint64(block.timestamp), false);
```

You pass values in the order the fields were declared. Concise but fragile. If you ever reorder or insert fields in the struct definition, every positional construction silently becomes wrong. Don't use this form except for very small structs that won't change.

**Named:**

```solidity
Order memory o = Order({
    amount: 100,
    buyer: msg.sender,
    placedAt: uint64(block.timestamp),
    fulfilled: false
});
```

You name each field explicitly. Verbose, but safe against reordering and self-documenting. Use this form by default.

In both cases, the storage location keyword is required when declaring a local struct variable. `Order o = Order(...)` does not compile. You must write `Order memory o` or `Order storage o`. The `storage` form is only valid when assigning from an existing storage location, which is covered next.

## The reference vs copy distinction

A struct retrieved from a storage location with the `storage` keyword is a reference. With `memory`, it's a copy.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Bug {
    struct Profile {
        bool verified;
        uint256 score;
    }

    mapping(address => Profile) public profiles;

    function brokenVerify(address user) external {
        Profile memory p = profiles[user];  // COPY
        p.verified = true;                  // mutates the copy
        // copy is discarded at function exit. profiles[user] unchanged.
    }

    function workingVerify(address user) external {
        Profile storage p = profiles[user]; // REFERENCE
        p.verified = true;                  // mutates the underlying storage
    }
}
```

The `storage` keyword declares `p` as a pointer into the same storage location as `profiles[user]`. Mutations through `p` affect the underlying state. SSTORE happens.

The `memory` keyword allocates a fresh memory region and copies every field of the struct into it. Mutations to that copy are local. When the function returns, the copy is discarded. The original storage is untouched.

This is identical to the reference-vs-value distinction in many languages, but Solidity makes you pick explicitly every time you bind a name. The choice has both correctness implications, since the changes only persist if you used `storage`, and gas implications, since memory copies cost gas to allocate and write. When in doubt, write tests that read state back after each function and check that the values actually changed.

You can also assign one struct value to a storage slot directly:

```solidity
profiles[user] = Profile({verified: true, score: 100});
```

This copies every field from the right-hand side into storage, which costs an SSTORE per field. For large structs, batching writes this way is sometimes cleaner than mutating fields one at a time.

## Modifying fields

Field access uses dot syntax. The semantics depend on where the struct lives.

```solidity
Order memory order = Order({amount: 100, buyer: msg.sender, placedAt: 0, fulfilled: false});
order.amount = 150;        // local change to memory copy

lastOrder.amount = 200;    // SSTORE: writes directly to storage
lastOrder.fulfilled = true; // another SSTORE
```

Each field write to a storage struct is a separate SSTORE, which is one of the most expensive EVM operations. If you're updating multiple fields, the gas adds up. To keep this cost down, design your structs so the common write paths modify only a small number of fields.

The `delete` keyword clears a struct back to all-zero values:

```solidity
delete lastOrder;  // every field reset to its type's zero value
```

`delete` on a struct works the same way `delete` works on every other type: it sets the memory or storage region to zero. For a struct that contains a mapping, the mapping isn't cleared, since the EVM can't enumerate its keys, but every non-mapping field is reset.

## Structs in mappings

The most common use of structs is as the value type of a mapping. This combines the per-key access of mappings with the multi-field structure of structs.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Registry {
    struct Profile {
        string name;
        bool verified;
        uint256 reputation;
    }

    mapping(address => Profile) public profiles;

    function register(string calldata name) external {
        profiles[msg.sender] = Profile({
            name: name,
            verified: false,
            reputation: 0
        });
    }

    function verify(address user) external {
        Profile storage p = profiles[user];
        p.verified = true;
    }
}
```

The `register` function builds a fresh `Profile` in memory using the named-args form, then assigns it into the mapping. The assignment copies all fields into storage. The `verify` function takes a storage reference to the existing profile and modifies it in place, which is the only way the change persists.

If `verify` had used `Profile memory p = profiles[user]; p.verified = true;`, it would have read a copy, set the copy's `verified` to true, and exited the function with that copy thrown away. The actual stored profile would have remained unverified.

## Capstone: a payments ledger pattern

The pattern below is the structural template for crowdfunding contracts, donation pools, vesting schedules, voting records, and most other accounting-style contracts. It shows the key idea of using structs as both mapping values and as a way to compose a nested data shape.

```solidity
// SPDX-License-Identifier: MIT
// Solidity 0.8.24, Ethereum mainnet
pragma solidity 0.8.24;

contract PaymentsLedger {
    struct Payment {
        uint256 amount;
        uint64 timestamp;
        bytes32 ref;
    }

    struct Account {
        uint256 paymentCount;
        mapping(uint256 => Payment) byIndex;
    }

    mapping(address => Account) internal accounts;

    function pay(bytes32 ref) external payable {
        Account storage acc = accounts[msg.sender];
        acc.byIndex[acc.paymentCount] = Payment({
            amount: msg.value,
            timestamp: uint64(block.timestamp),
            ref: ref
        });
        acc.paymentCount += 1;
    }

    function getPayment(address user, uint256 i) external view returns (Payment memory) {
        return accounts[user].byIndex[i];
    }
}
```

Three things to notice here.

The `Account` struct holds a counter and a nested mapping. Mappings inside structs are allowed, but the struct can only ever live in storage. There is no such thing as a memory mapping, so the compiler will not let you declare a memory `Account`. This is why `accounts` is `internal` rather than `public`: the auto-generated public getter would need to return an `Account`, which the compiler refuses to do because of the embedded mapping.

The `pay` function takes a `storage` reference to the caller's account. Every subsequent write goes to the actual storage location. If `Account memory acc = accounts[msg.sender]` had been used, the counter increment would have happened on a local copy that gets discarded, and the payment would have been written to slot 0 every time.

`getPayment` returns a `Payment` by value, typed `Payment memory`. The function caller receives a copy of the data. References can't escape the contract boundary in either direction. Everything that crosses is copied into the caller's space.

## FAQ

### Why didn't my change to a struct get saved?

You bound it with memory. Profile memory p = profiles[user] copies every field, and the copy is discarded when the function returns. Profile storage p = profiles[user] is a pointer into storage, so writes through it are SSTOREs against real state.

### Does the order of fields in a struct change gas cost?

Yes. A 20-byte address declared next to a 1-byte bool shares one 32-byte slot. Move a uint256 between them and the same struct costs three slots instead of two.

### Why won't the compiler let me make my struct public?

A public state variable generates a getter that returns the whole value, and a struct holding a mapping cannot be returned. It has no memory form either, so it lives only in storage. Mark the variable internal and write your own view function.

### Can a struct contain a field of its own type?

No, that is a compile error. A struct's size is the sum of its field sizes, and a self-containing type would be infinite. Hold TreeNode[] children instead.

### Can I use a struct as a mapping key?

No. Keys have to be hashable value types.

---

# Solidity types - Test

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/solidity-types-test



---

# Functions

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/functions

A Solidity function signature is dense. Every keyword in it, from visibility to mutability to data location, decides who can call the function, whether it can touch state, and what it costs to run. Once you can read those keywords, you can predict any function's behavior from its declaration alone.

## The shape of a function

A function declaration has the form:

```solidity
function name(parameters) visibility [mutability] [modifiers] [returns (types)] {
    // body
}
```

Visibility is required. The brackets denote optional pieces. The mutability annotation is one of them: add `view` if the function only reads state, `pure` if it reads nothing, and leave it off if the function modifies state. Names follow `mixedCase` convention: first word lowercase, subsequent words capitalized, no underscores.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Counter {
    uint256 public count;

    function increment() external {
        count += 1;
    }

    function getCount() external view returns (uint256) {
        return count;
    }
}
```

`increment` is an external state-mutating function. `getCount` is an external view function. Both have no parameters and no modifiers. The difference between them, and what each piece of the signature actually means, is what the rest of this lesson covers.

## Visibility: who can call this

Every function declares one of four visibility levels. The choice affects what code can call the function and how the arguments are passed internally.

**`public`** functions can be called from anywhere. Off-chain callers can invoke them via transactions. Other contracts can call them. The contract's own code can call them. This is the most permissive option. Production code rarely needs it.

**`external`** functions can only be called from outside the contract. An off-chain transaction or a call from another contract works. The contract calling its own `external` function does not, unless it goes through `this.functionName()` which is itself an external call and costs extra gas. The key reason `external` exists is gas efficiency. It lets the compiler read arguments directly from calldata. This is significantly cheaper than the `public` case, where arguments have to be copied into memory in case an internal caller passes them. For functions with large reference-type parameters like `bytes calldata` or `uint256[] calldata`, this difference can be hundreds of gas per call.

**`internal`** functions can be called from inside the contract and from contracts that inherit from it. They cannot be called from outside. This is the right visibility for helper functions, shared logic that subclasses extend, and any code you want exposed to the contract family but not to external callers.

**`private`** functions are even more restrictive. They can only be called from inside the same contract. Inheriting contracts cannot reach them. Use `private` when you want to be explicit that no subclass should call this function, but note that `private` is not a security boundary. The bytecode is still on chain. Anyone reading the contract's bytecode can see what private functions do.

For state variables, the same four keywords apply with slightly different effects. A `public` state variable auto-generates a getter function. An `internal` one is accessible to inheriting contracts. A `private` one is not. There is no `external` for state variables.

If you don't specify a visibility for a function, the compiler refuses to compile. There is no implicit default.

## State mutability: what this can touch

A function that doesn't modify state has options for advertising that fact, which unlocks cheaper invocation patterns. The two relevant modifiers are `view` and `pure`.

**`view`** declares that the function reads state but doesn't modify it. Reading state variables is fine. Reading `block.number`, `block.timestamp`, the contract's own balance, or another contract's storage via a view function call is fine. Writing anything, emitting events, calling non-view functions on other contracts, or sending ETH is forbidden. The compiler enforces this.

**`pure`** is stricter. A pure function reads neither state nor the chain. It can compute results from its arguments and local variables only. Pure functions are essentially regular language functions that happen to be written in Solidity. They take inputs, return outputs, touch nothing.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Tokens {
    uint256 public totalSupply;
    uint256 public price;

    function balanceLeft() external view returns (uint256) {
        return totalSupply - 1000;   // reads state, allowed in view
    }

    function quote(uint256 tokens) external view returns (uint256) {
        return tokens * price;       // reads state, allowed in view
    }

    function tokensFor(uint256 amount, uint256 rate) external pure returns (uint256) {
        return amount * rate;        // no state, pure is fine
    }
}
```

`balanceLeft` and `quote` both read state, so they need `view`. `tokensFor` is a calculation over its arguments only, so it can be `pure`.

The mutability annotation is checked at compile time. A `view` function that tries to write state, or a `pure` function that tries to read state, is a compile error.

If a function modifies state, you don't write any mutability modifier at all. The absence is the marker.

## Why view and pure functions are free to call

A function annotated `view` or `pure` doesn't change anything on chain. That means a node can answer the question "what does this function return?" by running it locally, in memory, without making it part of any block. There's no transaction, no consensus, no gas paid by anyone. The RPC method `eth_call` does exactly this.

A function that modifies state can't work that way. To change the chain's state, the operation has to go through a transaction, get mined into a block, and be applied by every full node. That's where the gas cost comes from: every node runs the function as part of validating the block.

The practical consequence: calling `getCount` from your frontend is free, instant, and doesn't need a connected wallet. Calling `increment` costs gas, requires a signed transaction, and takes a few seconds to be mined. The mutability annotation `view` or `pure` is the signal that switches between these two worlds.

The same function can be called either way from contract code. When contract A calls `B.getCount()` from inside its own state-mutating function, it costs gas as part of A's transaction. When the same function is called from a frontend's `eth_call`, it's free. The cost depends on the calling context rather than the function itself.

## Return values

A function that returns something declares the return type after the keyword `returns`:

```solidity
function getCount() external view returns (uint256) {
    return count;
}
```

The return value is computed and passed back to the caller. For off-chain callers of a view function, this is the value that arrives in your frontend. For internal callers, it's the value of the function call expression.

Solidity supports multiple return values with the same syntax:

```solidity
function getPair() external view returns (address token, uint256 amount) {
    return (tokenAddress, balance);
}
```

The caller can destructure the result:

```solidity
(address t, uint256 a) = getPair();
```

There's also a less common form called **named returns**. You declare the return values with names in the `returns` clause, then assign to those names inside the function. An implicit `return` happens at the end of the function.

```solidity
function getCount() external view returns (uint256 result) {
    result = count;
    // no explicit return needed
}
```

The two forms produce the same compiled output. Named returns can be useful when the function has multiple exit points and you want to set the return value in one place. Explicit `return` statements are clearer for short functions. Both are used in production code.

One subtle but important point about state-mutating functions. They can declare return values, and other contracts calling them can read those return values. But **off-chain callers cannot read the return value of a state-mutating transaction**. This is a property of how Ethereum transactions work rather than of Solidity. At the moment a transaction is signed and broadcast, its eventual return value is unknown. By the time the transaction is mined, the network only records whether it succeeded or reverted, never what it returned. To communicate results from state-mutating functions to off-chain code, contracts emit events, which are written to the transaction receipt and are readable by frontends and indexers.

## Function arguments

Function parameters are declared in parentheses, with a type for each:

```solidity
function transfer(address to, uint256 amount) external returns (bool) {
    // ...
}
```

For value type parameters, that's all you need. For reference type parameters, you must specify a data location. `string`, `bytes`, dynamic arrays, and structs all need `memory` or `calldata`:

```solidity
function process(uint256[] calldata items, string memory note) external {
    // ...
}
```

`calldata` is the cheapest option for read-only inputs in `external` functions. The data is read directly from the transaction's input bytes without being copied. `memory` works in any context and is required if you intend to modify the parameter inside the function. For `internal` and `public` functions, `calldata` is not always allowed and `memory` is the safe default.

Function parameters are positional. Solidity does not support named arguments at the call site. The order in the declaration is the order callers must use.

## Constructors

A constructor is a special function that runs exactly once, when the contract is deployed. It's the right place to set immutable values, initial state, and the owner address.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Owned {
    address public immutable owner;
    uint256 public createdAt;

    constructor() {
        owner = msg.sender;
        createdAt = block.timestamp;
    }
}
```

The constructor uses the keyword `constructor`, not `function`. It has no name, no visibility annotation, and no return type. It can take arguments, which are supplied at deployment time. It can be marked `payable` to accept ETH on deployment, but it's rarely necessary.

After the constructor finishes, it's gone. The bytecode of the deployed contract does not include the constructor. This is why a constructor's code is sometimes called the "creation code" and the post-constructor bytecode is called the "runtime code." The two are distinct.

## Payable: accepting ETH

By default, a function rejects any ETH attached to the call. If a transaction with non-zero value calls a non-payable function, the EVM reverts.

To accept ETH, mark the function `payable`:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Tipping {
    mapping(address => uint256) public totalTipped;

    function tip() external payable {
        totalTipped[msg.sender] += msg.value;
    }
}
```

Inside a `payable` function, `msg.value` is the amount of ETH in wei that the caller sent. Outside a `payable` function, `msg.value` is always zero. The ETH is automatically credited to the contract's balance the moment the function is entered. There's no explicit transfer.

A `payable` function can also be marked with other modifiers. `external payable` is the most common combination. `public payable` is also valid. You can't combine `payable` with `view` or `pure`, since accepting ETH is itself a state change.

## Receive and fallback: handling untargeted calls

Two special functions handle cases where a transaction arrives at the contract without targeting a specific function.

**`receive()`** runs when ETH is sent to the contract with no data, like a plain transfer from a wallet. It must be marked `external payable` and takes no arguments. It returns nothing.

```solidity
contract Vault {
    receive() external payable {
        // optional: emit an event, update state, etc.
    }
}
```

If a contract has no `receive` function, plain ETH transfers to it revert. Most contracts that hold ETH define `receive` even if its body is empty, just to permit deposits via wallet UIs.

**`fallback()`** runs when a transaction calls a function the contract doesn't define, or when ETH is sent with data that doesn't match any function. It can be marked `external` or `external payable`. If marked `payable`, it also handles ETH sent with arbitrary data.

```solidity
contract Proxy {
    fallback() external payable {
        // typically used in proxy patterns to forward calls
    }
}
```

The two functions exist because the EVM doesn't have a built-in notion of "method not found." Without a fallback, any call to an unknown function selector reverts. With a fallback, the contract gets a chance to handle it. This is what proxy contracts use to forward calls to an implementation contract.

The relationship: if a transaction arrives with no data, `receive` is preferred over `fallback`. If it arrives with data but no matching function, `fallback` is used. If neither exists, the call reverts.

A note on older code: in early Solidity, both behaviors were handled by a single unnamed function. The split into `receive` and `fallback` happened in 0.6.0. You'll occasionally see legacy contracts that use the older form, and modernization just means splitting them into the two new ones.

## FAQ

### What is the difference between a public and an external function?

Both are callable from outside. An external function reads its arguments straight from calldata, while a public one may copy them into memory for internal callers, costing hundreds of gas on a large array. The contract's own code reaches an external function only through this.functionName().

### Why can I call a view function for free?

It changes nothing, so any node runs it locally through eth_call. No transaction, no block, no gas. Writing state needs a transaction that every full node re-executes, and that is what gas pays for.

### Why can't my frontend read the value a state-changing function returned?

The network records only whether the transaction succeeded or reverted. Emit an event instead, which lands in the transaction receipt where wallets and indexers read it.

### What is the difference between receive and fallback?

receive() runs when ETH arrives with no calldata and must be external payable. fallback() runs when a call names no function the contract defines, or when ETH arrives with data. With neither, both revert. The two were split apart in Solidity 0.6.0.

### Can a payable function also be view?

No. Accepting ETH is itself a state change.

---

# Errors, modifiers, and events

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/errors-modifiers-and-events

> Three mechanisms that together control what a contract permits and what the outside world observes. A revert is how a contract says "no" to a call. State rolls back, the caller sees an error, and any ETH sent is returned. A modifier is how you reuse the same "no" conditions across many functions without duplicating the check. An event is how a successful call publishes what happened to off-chain observers, since state-mutating functions can't return values to wallets and frontends directly. The three handle the lifecycle of every interesting transaction. Should this call happen? What conditions does it need to satisfy? How do we tell the world it did?

## What a revert does

When a contract reverts, three things happen in order. The current function stops executing. Every state change made during the call is rolled back. An error is returned to the caller.

The state rollback covers everything the EVM did since the call began. Storage writes are undone. Memory and stack are discarded. Events emitted before the revert are dropped from the transaction log as if they never happened. ETH that was forwarded to nested calls returns up the call stack.

If the reverting call was the top-level transaction, the user pays gas for the work done up to the revert. The validator keeps the priority fee, but no state on chain has changed. The transaction occupies a slot in a block and consumes gas, but its intended effects do not take place. This is the EVM atomicity guarantee, viewed from the contract's side.

If the reverting call was a nested one made by another contract, the revert propagates up by default. The outer contract's call expression throws, which usually causes the outer call to revert too, all the way up to the top-level transaction.

Solidity gives you four ways to trigger a revert: `require`, `revert`, `assert`, and custom errors.

## `require`: the everyday check

`require` is the most common way to revert. Use it for input validation, authorization checks, and any precondition that callers might violate.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Vault {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    function withdraw(address to, uint256 amount) external {
        require(msg.sender == owner, "not owner");
        require(amount <= address(this).balance, "insufficient balance");
        require(to != address(0), "zero address");
        // ... actual transfer logic ...
    }
}
```

`require(condition, "message")` takes a boolean condition and a string. If the condition is true, execution continues. If it's false, the call reverts with the provided message as the error reason.

The string form has a cost. The message has to be stored in the contract's bytecode and emitted as part of the revert data. A 32-byte message adds roughly 50 gas of bytecode to the deployed contract, plus encoding cost on every revert. For frequently-called functions with many require checks, this adds up.

## `revert`: the manual form

`revert` is the lower-level primitive that `require` is built on. It takes a single string argument and always reverts. To use it conditionally, wrap it in an `if`.

```solidity
function withdraw(address to, uint256 amount) external {
    if (msg.sender != owner) {
        revert("not owner");
    }
    // ...
}
```

This is functionally identical to `require(msg.sender == owner, "not owner")`. The `revert` form reads better when the condition for reverting is complex enough that rewriting it as a positive `require` would make it hard to read:

```solidity
function settle(uint256 amount) external {
    if (paused || (amount == 0 && msg.sender != owner)) {
        revert("invalid call");
    }
    // ...
}
```

The two forms compile to the same bytecode. The choice is stylistic.

## Custom errors: the modern form

In Solidity 0.8.4, the language gained **custom errors**. They're the preferred way to revert in production code today, for three reasons.

First, they're dramatically cheaper. A custom error is identified by a 4-byte selector, the first 4 bytes of `keccak256(errorName(types))`, the same way functions are identified in calldata. A typical string revert reason costs around 50 gas of bytecode plus the cost of the revert opcode. A custom error costs around 4 gas to encode and is shorter in deployed bytecode.

Second, they can carry typed data. A string reason is just a string. A custom error can include the values that triggered it: the caller's address, the amount that was short, the deadline that was missed.

Third, they're integrated with off-chain tooling. Block explorers, frontends, and analysis libraries recognize custom error selectors and decode them automatically when the contract's ABI is known.

Here's the same Vault written with custom errors:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Vault {
    address public owner;

    error NotOwner(address caller);
    error InsufficientBalance(uint256 requested, uint256 available);
    error ZeroAddress();

    constructor() {
        owner = msg.sender;
    }

    function withdraw(address to, uint256 amount) external {
        if (msg.sender != owner) revert NotOwner(msg.sender);
        if (amount > address(this).balance) {
            revert InsufficientBalance(amount, address(this).balance);
        }
        if (to == address(0)) revert ZeroAddress();
        // ...
    }
}
```

Each error declaration looks like a function signature without a body. To trigger one, you write `revert ErrorName(args)`. The error and its arguments are encoded into the transaction's revert data and propagated up.

A frontend reading the revert from a failed transaction sees a structured object rather than a raw string. For example, an `InsufficientBalance(1000, 500)` revert tells the frontend exactly how short the user was, which is far more useful than the string `"insufficient balance"`.

`require` was also overloaded to accept a custom error as its second argument starting in 0.8.26 via the via-IR pipeline, with full legacy-pipeline support from 0.8.27:

```solidity
error NotOwner();
require(msg.sender == owner, NotOwner());
```

This combines the readability of `require` with the gas efficiency of custom errors.

OpenZeppelin, Solady, and other production-quality libraries have moved to custom errors throughout. New code should follow.

## `assert`: invariants only

`assert(condition)` reverts if the condition is false, but it produces a different kind of revert from `require`. Where `require` and `revert` produce an `Error(string)` revert, `assert` produces a `Panic(uint256)` revert.

The intent is also different. `assert` is for invariants that should never fail under any circumstances. If `assert(x > 0)` ever fires, it means there is a bug in the contract rather than a mistake by the user. A panic indicates the contract reached a state the developer believed to be unreachable.

In practice, you'll write `assert` rarely. Solidity 0.8 automatically generates `Panic` reverts for arithmetic overflow, division by zero, out-of-bounds array access, and other common conditions that previously needed explicit `assert`. The compiler handles what `assert` used to do.

When you do reach for it, the use case is an arithmetic invariant the compiler can't infer from the surrounding code. `assert` takes no message. The panic code is the only signal.

## When to use which

- **Custom errors** for any revert in code that will go to production. They're the cheapest, the most informative, and the modern standard.
- **`require`**** with a string message** when you're writing a small contract, a test, or a prototype, and gas optimization doesn't matter yet.
- **`revert`**** with a string message** when the predicate is awkward to express as a positive `require`.
- **`assert`** for invariants you genuinely believe cannot fail. Rare in 0.8+ code.

## Modifiers: reusing a check across many functions

Look at the Vault again. In an owner-controlled contract, many state-mutating functions start with the same line: `require(msg.sender == owner, "not owner")`. Across ten functions, that's ten copies of the same check. If the check ever needs to change, you have to remember to update all ten.

Solidity has a feature for exactly this: **modifiers**. A modifier is a named, reusable block of code that wraps a function. You write the check once, give it a name, and apply it to as many functions as you want.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Vault {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    function withdraw(address to) external onlyOwner {
        payable(to).transfer(address(this).balance);
    }

    function setOwner(address newOwner) external onlyOwner {
        owner = newOwner;
    }
}
```

The `modifier onlyOwner() { ... }` block defines the check. The `_;` placeholder is the spot where the function body gets inserted at compile time. When you write `function withdraw(address to) external onlyOwner`, the compiled function is equivalent to:

```solidity
function withdraw(address to) external {
    require(msg.sender == owner, "not owner");  // from the modifier
    payable(to).transfer(address(this).balance); // the function body
}
```

The check is now declared once and applied wherever needed. If you ever change `onlyOwner` to use a custom error or a different condition, every function carrying the modifier updates automatically.

## Modifiers with arguments

A modifier can take parameters, just like a function:

```solidity
modifier onlyRole(bytes32 role) {
    require(hasRole(role, msg.sender), "missing role");
    _;
}

function adminAction() external onlyRole(ADMIN_ROLE) {
    // ...
}

function pauserAction() external onlyRole(PAUSER_ROLE) {
    // ...
}
```

This is the foundation of role-based access control. One modifier, many uses, each with a different role identifier.

## Code before and after the body

The `_;` doesn't have to be the last statement in the modifier. You can put code after it as well, which runs after the function body executes:

```solidity
modifier logged() {
    emit CallStarted(msg.sender);
    _;
    emit CallFinished(msg.sender);
}
```

The pre-check pattern uses code before `_;`. The post-check pattern uses code after. The combined pattern, with code on both sides, shows up in production for things like reentrancy guards:

```solidity
modifier nonReentrant() {
    require(!locked, "reentrant call");
    locked = true;
    _;
    locked = false;
}
```

The guard sets a flag, executes the function, and clears the flag. If the function tries to re-enter through an external call, the second entry hits the `require` and reverts.

## Multiple modifiers, and modifier inheritance

A function can have several modifiers. They're applied left to right:

```solidity
function emergencyWithdraw() external onlyOwner whenPaused nonReentrant {
    // ...
}
```

The compiled function checks `onlyOwner`, then `whenPaused`, then `nonReentrant`, then runs the body. After the body, whatever comes after `_;` in each modifier runs in reverse order. Order matters when the modifiers interact: putting `nonReentrant` before `onlyOwner` would lock the contract before checking authorization, which is wasteful when most failed calls are unauthorized.

Modifiers can also be `virtual` and `override`, the same way functions can. This lets a child contract change the behavior of a modifier without duplicating it.

The three canonical production modifier patterns to recognize:

- `onlyOwner` for single-admin contracts
- `whenNotPaused` and `whenPaused` for pausable contracts
- `nonReentrant` for protection against reentrancy via external calls

OpenZeppelin provides all three as ready-to-inherit contracts (`Ownable`, `Pausable`, `ReentrancyGuard`).

## Events: telling the world what happened

When a contract's state changes, the chain itself knows what happened, but applications don't read storage slot by slot. Frontends, indexers, and bots watch for **events**, which are structured records the contract emits as a side effect of execution.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Bank {
    mapping(address => uint256) public balances;

    event Deposited(address indexed from, uint256 amount, uint256 timestamp);

    function deposit() external payable {
        balances[msg.sender] += msg.value;
        emit Deposited(msg.sender, msg.value, block.timestamp);
    }
}
```

An `event` declaration looks like a function signature with no body. To fire one, you write `emit EventName(args)`. The event and its arguments get appended to the transaction's log entries. Once the transaction is mined, anyone who knows the contract's address and the event signature can find and decode the log entry.

This is the primary way state-mutating functions report results to off-chain code. A state-mutating function like `deposit()` can't return a value to its caller off-chain, so it emits an event instead. A frontend listens for `Deposited(address,uint256,uint256)` events from the contract and updates its UI when one arrives.

## Indexed parameters and the topic limit

Up to three of an event's parameters can be marked `indexed`. Indexed parameters are stored in special slots called **topics**, which are the indices that off-chain tools use to filter events.

```solidity
event Deposited(address indexed from, uint256 amount, uint256 indexed timestamp);
```

A frontend can ask "give me every `Deposited` event where `from == 0x1234...`" and get an efficient lookup, because `from` is indexed. The same query for `amount` would require scanning every event, since `amount` is not indexed.

The "three indexed fields" limit comes from how Ethereum stores logs. Each log entry has up to four topics. Topic 0 is automatically the keccak256 hash of the event signature, like `keccak256("Deposited(address,uint256,uint256)")`. That leaves three topics free for indexed parameters.

For value types like `address`, `uint256`, and `bytes32`, the topic stores the value directly. For dynamic types like `string` and `bytes`, the topic stores the keccak256 hash of the value. The original value is not recoverable from the topic alone. This means you can filter by exact match on a string but you can't recover the original string from the log unless it's also passed as a non-indexed parameter.

The non-indexed parameters of an event get packed into the log's `data` field, which is essentially unstructured bytes. Decoding `data` requires knowing the event ABI.

## Why events exist as a separate facility

Events live in transaction receipts, which are part of the chain but distinct from contract storage. Reading an event costs nothing if you're off-chain. The trade-off: contracts cannot read their own events. There's no Solidity opcode that retrieves past log entries. Events are a write-only output channel from contracts to off-chain observers.

This separation is intentional and useful. Storage is expensive because every full node maintains it forever. Logs are cheap because nodes can prune them if they want, though most don't. A contract that recorded every deposit as a storage entry would pay an SSTORE for each one. The same contract emitting a `Deposited` event pays a few hundred gas instead, and an off-chain indexer can reconstruct the full history by reading the logs.

The standard pattern: store what the contract itself needs to read on chain, emit events for everything else. A token contract stores balances, since it needs to check them on every transfer, but emits `Transfer` events for the historical record, since the chain itself doesn't need to remember individual transfers but frontends do.

## Standard events you'll recognize

A handful of events are so common that every Ethereum developer reads them by sight:

```solidity
event Transfer(address indexed from, address indexed to, uint256 value);     // ERC-20
event Approval(address indexed owner, address indexed spender, uint256 value); // ERC-20
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); // ERC-721
```

The ERC-20 `Transfer` event fires on every token movement. Wallets read it to display balance changes. Indexers like The Graph, Dune, and Etherscan read it to build token databases. ERC-721 reuses the same name but indexes `tokenId` instead of value, since NFTs are unique. Recognizing these signatures lets you understand most token contracts quickly.

## FAQ

### What happens to my state changes when a transaction reverts?

All of them are undone. Storage writes roll back, events emitted before the revert are dropped from the log, and ETH forwarded to nested calls returns up the call stack. The caller still pays gas for the work done up to that point.

### require with a string message, or a custom error?

Custom errors for production. A string reason costs roughly 50 gas of bytecode plus encoding on every revert. A custom error is a 4-byte selector from keccak256 of errorName(types), around 4 gas to encode, and it can carry typed data such as the amount a caller was short. They arrived in 0.8.4, and require accepts one since 0.8.27.

### How do I apply the same owner check to ten functions?

Write a modifier. The check goes in its body, and _; marks where the function body is inserted at compile time.

### Why can only three event parameters be indexed?

A log entry holds four topics and topic 0 is the keccak256 hash of the event signature, which leaves three. An indexed value type stores the value itself. An indexed string or bytes stores a keccak256 hash, so the original cannot be recovered from the topic.

---

# Donor Tiers Vault

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/donor-tiers-vault

### Donor Tiers Vault

**The Scenario** You're writing the contract layer for a charity platform. Anyone can send ETH along with a short message; the contract classifies each donor by their total contributed amount and lets the frontend query each donor's full history.

**What You'll Build** A single contract called `DonorVault` that records every donation as a struct, classifies donors into a tier enum based on cumulative amount, tracks the unique donor count, and exposes the view functions the frontend needs.

**Requirements**

1. **Tier Enum:** The contract MUST define an enum `Tier` with these values, in this exact order: `None`, `Bronze`, `Silver`, `Gold`, `Platinum`.
2. **Tier Boundaries:** A donor's tier MUST be derived from their cumulative total in wei according to these strict boundaries:
  - **None:** total = 0
  - **Bronze:** 0 < total < 0.1 ether
  - **Silver:** 0.1 ether ≤ total < 1 ether
  - **Gold:** 1 ether ≤ total < 10 ether
  - **Platinum:** 10 ether ≤ total *(Test cases to check your math: 0 is None. 1 wei is Bronze. Exactly 0.1 ether is Silver. Exactly 10 ether is Platinum.)*
3. **The Donation Struct:** You MUST define a `Donation` struct containing `amount` , `timestamp` , and `message`  **in that exact order**.
4. **The Donate Function:** The function `donate(string calldata message)` MUST be `external payable`.
  - It MUST revert with a custom error `ZeroDonation()` if `msg.value` is 0.
  - It MUST record the `Donation` in that donor's history.
  - It MUST update the donor's cumulative total correctly.
5. **Unique Donors:** The contract MUST track the count of unique donors. Subsequent donations from the same address MUST NOT increment that count.
6. **Storage:** Donor history MUST be stored exactly as `mapping(address => Donation[])`. Do not use a flat array, and do not wrap the array inside another struct.
7. **View Functions:** The view functions `tierOf`, `donationCount`, `getDonation`, `uniqueDonorCount`, and `totalDonatedBy` MUST behave as named and match the signatures shown in the starter code.

---

# Inheritance

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/inheritance

> Solidity contracts can extend other contracts the same way classes extend in object-oriented languages. A child contract gets all the state variables, modifiers, functions, and events declared in its parents. Properly used, inheritance lets you write small, focused contracts that compose into larger ones. Improperly used, it produces multi-level hierarchies that are hard to follow and audit. This lesson covers the mechanics of inheritance, the rules around overriding, and the patterns most often seen in production code.

The classic motivation is repetition. A handful of state variables and modifiers appear in nearly every nontrivial contract: an owner address, an `onlyOwner` modifier, a constructor that captures `msg.sender` as the owner, a `withdraw` function that the owner alone can call. Writing these out by hand in every contract is wasteful and error-prone. Extracting them into a base contract and inheriting from it lets you share the implementation across many contracts.

## Single inheritance

The syntax for one contract inheriting from another uses the keyword `is`.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Ownable {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }
}

contract Vault is Ownable {
    function withdraw(address payable to) external onlyOwner {
        to.transfer(address(this).balance);
    }
}
```

`Vault is Ownable` means everything declared in `Ownable` is part of `Vault`. The state variable `owner`, the constructor, and the modifier `onlyOwner` are all available inside `Vault`. When `Vault` is deployed, the `Ownable` constructor runs first, then any code in `Vault`'s own constructor. You don't deploy `Ownable` separately. There's only one contract on chain, and it contains both the inherited and the original code.

The four visibility levels from the functions lesson interact with inheritance directly. `public` and `internal` members are accessible from child contracts. `private` members are not. `external` functions can be called by child contracts, but only via `this.functionName()`. That's the same restriction as calling them from the same contract.

A child contract cannot declare a state variable with the same name as one in a parent. This is a compile error rather than silent shadowing. If `Ownable` declares `address public owner`, then `Vault` cannot also declare `address public owner` even if it intends to. The variable exists in the inheritance chain exactly once.

## Passing arguments to a parent constructor

When a parent contract's constructor takes arguments, the child must supply them. There are two ways to do it.

The first is to specify the arguments directly in the inheritance list, with literal or compile-time-known values:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Ownable {
    address public owner;

    constructor(address initialOwner) {
        owner = initialOwner;
    }
}

contract HardcodedVault is Ownable(0x1234567890123456789012345678901234567890) {
    // owner is set to the hardcoded address when this contract is deployed
}
```

This form is useful when the parent's constructor argument is known at the time you write the child contract. It's static, but compact.

The second is to call the parent constructor from inside the child's own constructor:

```solidity
contract DynamicVault is Ownable {
    constructor(address initialOwner) Ownable(initialOwner) {
        // additional initialization can happen here
    }
}
```

The syntax after the parameter list, `Ownable(initialOwner)`, looks like a function call attached to the constructor declaration. It tells Solidity to pass `initialOwner` to the parent's constructor when `DynamicVault` is deployed. The parent constructor runs first, then `DynamicVault`'s body runs.

Use the second form when the value comes from the deployment transaction and isn't known at compile time. Use the first when the value is fixed.

If a parent has a constructor with required arguments and the child supplies none, the child cannot be deployed and the compiler will either reject it or require it to be marked `abstract`. We'll see this next.

## Abstract contracts

A contract that doesn't fully satisfy all the requirements for deployment is **abstract**. Most commonly, that means it inherits from a parent with a constructor argument it doesn't provide.

```solidity
// Solidity 0.8.24, Ethereum mainnet
abstract contract Balances is Ownable {
    // Ownable's constructor takes (address initialOwner)
    // Balances does not provide it, so Balances cannot be deployed.

    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

contract Wallet is Balances {
    constructor(address initialOwner) Ownable(initialOwner) {
        // now the constructor chain is complete; Wallet is deployable
    }
}
```

An abstract contract can still be inherited from. It just cannot be deployed by itself. Trying to deploy one is a compile-time error. Abstract contracts are a structural device for organizing code that's only complete when something else extends it.

Abstract contracts can also have function declarations without bodies, similar to interface methods:

```solidity
abstract contract PriceFeed {
    function latestPrice() public view virtual returns (uint256);
}
```

A child that inherits from this must override `latestPrice` with a concrete implementation, or remain abstract itself.

## Function overriding: `virtual` and `override`

Solidity requires explicit opt-in for function overriding. A function in a parent contract is not overridable by default. You have to declare it `virtual` to allow children to provide a replacement:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Ownable {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    function transferOwnership(address newOwner) external virtual {
        require(msg.sender == owner, "not owner");
        owner = newOwner;
    }
}

contract NoTransfer is Ownable {
    function transferOwnership(address) external pure override {
        revert("ownership transfer disabled");
    }
}
```

The child writes `override` after the parameter list to declare that it is replacing the parent's function. Both keywords are required. Forgetting `virtual` on the parent or `override` on the child is a compile error. This explicitness is intentional. It makes it impossible to accidentally override a parent function, and it makes the inheritance behavior obvious to anyone reading the code.

If you want the child to remain overridable in turn, mark it both `virtual` and `override`:

```solidity
contract Variant is Ownable {
    function transferOwnership(address newOwner) external virtual override {
        // do something extra, then call the parent
        require(newOwner != address(0), "no zero address");
        super.transferOwnership(newOwner);
    }
}
```

This becomes important in deep inheritance chains where multiple layers add behavior.

Visibility on an override has to be the same or more permissive than the parent. A `public` function can be overridden as `public`. An `external` function can be overridden as `external` or `public`. You cannot tighten visibility, since callers of the parent's contract expect the function to remain callable from where the parent allowed.

Modifiers can also be virtual and overridden using the same `virtual`/`override` keywords. The same rules apply.

## Multiple inheritance and linearization

Solidity supports inheriting from multiple parents. The syntax is just a comma-separated list:

```solidity
contract Vault is Ownable, Balances {
    // inherits members from both Ownable and Balances
}
```

The order matters. Parents must be listed from **most base to most derived**. That is, most general first, most specific last. If `Balances is Ownable`, then `Ownable` is more base than `Balances`, so `Ownable` must come first in any list that mentions both:

```solidity
contract Vault is Ownable, Balances { ... }   // correct order
contract Bad is Balances, Ownable { ... }     // compile error
```

The reverse order produces an error about "linearization" that is hard to interpret at first. The compiler is using a specific algorithm called C3 linearization to compute a single ordering of all ancestor contracts. C3 needs the inheritance lists to be consistent with the topology of the inheritance graph. If you list a more-derived parent before a more-base one, the algorithm fails because the order you wrote contradicts the order implied by the rest of the graph.

In practice: list the most general library contracts first, then progressively specialized ones, with the most specific behavior last.

When a function exists in multiple parents and a child needs to override it, the override must name every parent that declares the function:

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract A {
    function action() public virtual {
        // ...
    }
}

contract B {
    function action() public virtual {
        // ...
    }
}

contract C is A, B {
    function action() public override(A, B) {
        // explicitly resolving the ambiguity
    }
}
```

The `override(A, B)` declares that `C` is overriding the `action` from both `A` and `B`. Without the explicit list, the compiler cannot tell which one or both the override applies to.

## `super` and explicit parent calls

Inside an override, you often want to call the parent's version of the function rather than reimplementing it from scratch. There are two ways.

The first names the parent explicitly:

```solidity
function transferOwnership(address newOwner) external override {
    require(newOwner != address(0), "no zero address");
    Ownable.transferOwnership(newOwner);   // call Ownable's version specifically
}
```

`Ownable.transferOwnership(newOwner)` is unambiguous. It calls the implementation declared in `Ownable`, regardless of the linearization.

The second uses `super`:

```solidity
function transferOwnership(address newOwner) external override {
    require(newOwner != address(0), "no zero address");
    super.transferOwnership(newOwner);
}
```

`super` calls the **next** function in the linearization order, which is not necessarily the immediate parent of the contract you're writing. This distinction matters in diamond inheritance, where a contract has multiple parents that themselves share a common ancestor.

Consider:

```solidity
contract A {
    function f() public virtual { /* A's logic */ }
}

contract B is A {
    function f() public virtual override { /* B's logic */ super.f(); }
}

contract C is A {
    function f() public virtual override { /* C's logic */ super.f(); }
}

contract D is B, C {
    function f() public override(B, C) { super.f(); }
}
```

When `D.f()` runs, `super.f()` calls `C.f()` because `C` comes after `D` in the linearization. `C.f()`'s `super.f()` then calls `B.f()`. `B.f()`'s `super.f()` finally calls `A.f()`. The call chain is `D → C → B → A` rather than the visually-suggested `D → B → A` followed by `D → C → A`. The C3 linearization guarantees each ancestor runs at most once.

This is the pattern behind composable extensions: each layer adds its behavior, calls `super.f()` to chain to the next, and the linearization ensures all layers run in a well-defined order. OpenZeppelin's ERC-20 and ERC-721 implementations rely on this pattern so each extension module can add behavior to the same function.

If you want the immediate-parent semantics, use the named form. If you want the "next layer in the chain" semantics, use `super`. They're different operations, even though in single-inheritance code they look identical.

## Production patterns

A few patterns to recognize when reading real contracts.

**OpenZeppelin's ****`Ownable`****.** The `Ownable` contract in OpenZeppelin Contracts is the canonical example of the pattern shown at the start of this lesson. Many production contracts inherit from it directly. Newer versions accept the initial owner as a constructor argument, which addresses a real pitfall where the deployer accidentally became the owner of a contract intended for someone else.

**OpenZeppelin's ****`AccessControl`****.** Generalizes `Ownable` to a role-based system. Inherits the same patterns but adds a mapping of role identifiers to address sets.

## FAQ

### Do I have to deploy the parent contract separately?

No. One contract goes on chain, holding both. The parent's constructor runs first at deployment.

### Why won't Solidity let me override a parent function?

Overriding is opt-in. The parent function needs virtual, the child needs override, and missing either is a compile error. An override can widen visibility but never tighten it.

### In what order do I list parent contracts?

Most base first, most derived last, left to right. If B inherits from A, then A comes before B in any list naming both. The reverse fails with a linearization error. Solidity orders every ancestor with the C3 algorithm, and your written order has to agree with the inheritance graph.

### What is the difference between super.foo() and Parent.foo()?

Parent.foo() always runs the implementation declared in that parent. super.foo() runs the next contract in the linearization, which under multiple inheritance is not always the immediate parent. In contract D is B, C, super.f() inside D reaches C, then B, then A, and each ancestor runs once.

---

# Loops and hashing

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/loops-and-hashing

> Three language features that show up across nearly every Solidity contract. Loops iterate over data and run code repeatedly, with a constraint that doesn't exist outside Solidity: every iteration costs gas, and the total gas available in a transaction is bounded by the block. Data locations control where values live during execution, which determines what they cost and whether changes to them persist. Hashing produces a fixed-length hash of arbitrary data, and `keccak256` is the primary one used for storage slot derivation, function selectors, event topics, commitment schemes, and signature verification. All three are simple in their basic form and trickier than they look in production.

## Loops in Solidity

Solidity has three loop forms, all of which work the way you'd expect from C, Java, or JavaScript.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract Loops {
    function forLoop(uint256 n) external pure returns (uint256 sum) {
        for (uint256 i = 0; i < n; i++) {
            sum += i;
        }
    }

    function whileLoop(uint256 n) external pure returns (uint256 sum) {
        uint256 i = 0;
        while (i < n) {
            sum += i;
            i++;
        }
    }
}
```

`break` exits the current loop. `continue` skips to the next iteration. There's also `do-while` which works identically to other languages. Nothing here is Solidity-specific.

What is Solidity-specific is the cost. Every operation inside a loop costs gas. Reading from storage costs around 2,100 gas the first time and 100 gas thereafter in the same transaction. Writing to storage costs 22,100 gas for a fresh slot and 5,000 gas to update an existing one. A loop that reads or writes storage on every iteration multiplies these costs by the number of iterations.

## The block gas limit

Every Ethereum block has a maximum total amount of gas that all transactions in that block combined can consume. The current block gas limit is around 30 million gas. Individual transactions can use anywhere from a small fixed amount, like the 21,000 gas a simple ETH transfer costs, up to the full block limit if no other transactions share the block.

The block gas limit creates a hard ceiling on what a single transaction can do. If your function tries to execute more work than fits in 30 million gas, the transaction reverts with an out-of-gas error. The state changes the function made up to that point are rolled back, and the caller still pays for the gas it consumed.

This matters most for loops over storage arrays whose length the contract doesn't control.

```solidity
// Solidity 0.8.24, Ethereum mainnet
contract BadDistribution {
    address[] public recipients;

    function register() external {
        recipients.push(msg.sender);
    }

    function distribute() external payable {
        uint256 amountEach = msg.value / recipients.length;
        for (uint256 i = 0; i < recipients.length; i++) {
            payable(recipients[i]).transfer(amountEach);
        }
    }
}
```

This contract looks fine on a small scale. A few accounts register, someone calls `distribute()`, and everyone gets their share.

But `recipients` can grow without bound. An attacker can register thousands of addresses at very low cost. Each registration adds one storage slot. Each call to `distribute` then has to iterate over every address and make an external ETH transfer to each one, which costs around 9,000 gas per transfer. At a few thousand recipients, the distribute function exceeds the block gas limit and reverts every time. The contract is bricked. The ETH inside it can never be distributed, because the only function that distributes it cannot complete.

This is called a denial-of-service attack via unbounded loops, and it's one of the most common production bugs in early Solidity code.

## Safe iteration patterns

Never write a function that iterates over data of unbounded size in a single transaction. Three patterns let you avoid this.

**Pagination.** Add `start` and `count` parameters, as in the code below. The function processes a slice of the data per call. The caller paginates by submitting multiple transactions.

```solidity
function distributeBatch(uint256 start, uint256 count) external payable {
    uint256 end = start + count;
    if (end > recipients.length) end = recipients.length;
    uint256 amountEach = msg.value / count;
    for (uint256 i = start; i < end; i++) {
        payable(recipients[i]).transfer(amountEach);
    }
}
```

The contract still iterates, but each call is bounded by `count` instead of by `recipients.length`. The caller chunks the work across many transactions.

**Pull over push.** Instead of the contract pushing tokens or ETH to many recipients, let each recipient pull their own share. The contract records what each recipient is owed, and `claim()` is a fixed-size operation with no loop. The contract works correctly no matter how many recipients exist.

**Off-chain iteration.** Compute the result off-chain, submit it on-chain as a single transaction. This is how Merkle-proof airdrops work: the contract verifies one proof per transaction, which is bounded.

Each pattern shifts the work somewhere different. Pagination spreads it across multiple transactions, pull-over-push moves it to the recipients, off-chain iteration moves it off the chain entirely. All three avoid the block-gas-limit pitfall.

## Data locations: storage, memory, calldata

Solidity has three places where values can live during execution. You've already seen all three in earlier lessons, written as data-location keywords on function parameters. Now we'll be explicit about what they mean.

**Storage** is the contract's permanent state. Every state variable lives in storage. Anything written to storage persists across transactions. Storage is expensive. The same per-slot read and write costs from the loops section apply: a fresh write costs far more than an update, and the first read of a slot costs more than later reads in the same transaction.

**Memory** is per-transaction scratch space. It's allocated when a function starts, used during execution, and discarded when the transaction ends. Memory is cheap: a typical read or write costs a few gas, with memory expansion costing more as you allocate larger regions.

**Calldata** is the raw inbound transaction data. When you call an external function, the arguments arrive as a sequence of bytes in calldata. Calldata is read-only and even cheaper than memory because the bytes are already there. You can't modify calldata, only read from it.

For value types like `uint256`, `bool`, and `address`, the location is implicit and you don't write it anywhere. State variables live in storage automatically. Local variables of value types live on the stack rather than in memory, and the compiler manages them for you. Function parameters of value types are read from calldata.

For reference types like arrays, structs, strings, and bytes, you must specify the location explicitly when they appear as function parameters or local variables.

```solidity
contract Users {
    struct User {
        uint256 age;
        string name;
    }

    mapping(address => User) public users;

    // calldata: the caller's bytes are read directly from the transaction
    function setName(string calldata newName) external {
        users[msg.sender].name = newName;
    }

    // memory: a copy is made; the function can mutate it freely
    function buildGreeting(string memory prefix) external view returns (string memory) {
        return string.concat(prefix, users[msg.sender].name);
    }
}
```

The choice between `calldata` and `memory` for an input parameter has a real cost difference. `calldata` parameters are read in place from the transaction bytes, no copy needed. `memory` parameters require copying the calldata into memory first. For any input you only need to read, prefer `calldata`. Use `memory` when you need to modify the value or pass it to another function that requires memory.

## The aliasing trap

Inside a function, when you assign a struct or array from storage to a local variable, the location of that local variable determines whether you've made a reference or a copy.

```solidity
function increaseAge(address who) external {
    User storage u = users[who];   // u is a REFERENCE to storage
    u.age += 1;                    // modifies users[who].age directly
}

function broken(address who) external {
    User memory u = users[who];    // u is a COPY in memory
    u.age += 1;                    // modifies the copy; storage unchanged
}
```

Both functions compile and run. The first one increases the user's age. The second one increases a local copy and throws it away when the function ends. This is one of the most common bugs in Solidity code written by developers coming from languages where assignment is always a reference.

The rule is simple. When you want to read AND write a complex value in storage, declare your local as `storage`. When you want a local working copy that doesn't affect the contract's state, declare it as `memory`. The compiler will tell you if you pick the wrong one in cases it can detect, but plenty of cases compile silently and behave incorrectly.

## `keccak256` and hashing

Solidity has one hash function you'll use constantly: `keccak256`. It takes a `bytes` value of any length and returns a `bytes32`. The bytes32 is the cryptographic hash of the input.

```solidity
bytes32 h = keccak256(abi.encode("hello"));
```

`keccak256` is the same algorithm as SHA-3 in its design, though with a slightly different parameter that makes it incompatible with the NIST SHA-3 standard. Ethereum uses keccak256 throughout: every address is derived from keccak256 of a public key, every function selector is the first 4 bytes of keccak256 of the function signature, an event's signature topic is keccak256 of the event signature, and every storage slot in a mapping is derived from keccak256.

It's deterministic, collision-resistant in practice, and irreversible. Given the same input, it always produces the same output. Given the output, finding any input that produces it would require enumerating an effectively infinite search space.

## What you can hash

You can hash any byte sequence. The trick is getting your Solidity values into a byte sequence in the first place. Solidity has two functions for this: `abi.encode` and `abi.encodePacked`.

```solidity
bytes memory encoded = abi.encode("hello", uint256(42), address(this));
bytes32 h = keccak256(encoded);
```

Note that the result of `abi.encode` is `bytes memory`. The encoding is built in a fresh memory region. You can't encode into storage directly, and you don't need to. The hash is what gets stored or compared.

`abi.encode` produces standard ABI-encoded bytes: every value padded to 32 bytes, dynamic types prefixed with offset and length. This is the same encoding the EVM uses for function calls and return values.

`abi.encodePacked` produces the same bytes but without the padding. Each value takes only as many bytes as it actually needs. Strings and bytes are concatenated raw.

```solidity
bytes memory padded = abi.encode("hello");        // 96 bytes
bytes memory packed = abi.encodePacked("hello");  // 5 bytes
```

`encodePacked` produces much shorter output, which means cheaper hashing. For single-argument hashing, this is fine and is the more common idiom.

For multi-argument hashing, `encodePacked` has a trap.

## The `encodePacked` collision risk

Because `encodePacked` removes the separator between dynamic values, two distinct sets of inputs can produce identical encoded bytes.

```solidity
bytes32 a = keccak256(abi.encodePacked("hello", "world"));
bytes32 b = keccak256(abi.encodePacked("hellow", "orld"));
// a == b
```

Both inputs produce the same byte sequence `helloworld`, so they hash to the same value. This is a real collision rather than a cryptographic accident. It happens because `encodePacked` doesn't insert any delimiter or length prefix between adjacent dynamic values.

If your contract uses `keccak256(abi.encodePacked(...))` as an identifier or as part of a signature scheme, an attacker who can choose the inputs may be able to construct two different argument sets that hash to the same value. Real exploits have been published against contracts that used this pattern naively.

The rule: when hashing multiple dynamic-type values like strings, bytes, or dynamic arrays, always use `abi.encode` rather than `abi.encodePacked`. The padding `encode` adds removes the collision risk because each value gets a length prefix.

When hashing only static-type values like uint, address, or bytes32, `abi.encodePacked` is safe because static types have fixed sizes that cannot be reinterpreted as different boundaries.

When hashing a single value of any kind, either form works, but `encodePacked` is cheaper.

## Common hashing patterns

A few places `keccak256` shows up in real contracts.

**Identifier generation.** Building a unique ID from a set of inputs.

```solidity
bytes32 orderId = keccak256(abi.encode(msg.sender, tokenAddress, amount, nonce));
```

The order ID is deterministic from the inputs. Two callers with the same inputs and different nonces get different IDs.

**Commitment schemes.** Committing to a value without revealing it.

```solidity
// Phase 1: commit
bytes32 commitment = keccak256(abi.encode(secret, salt));
commitments[msg.sender] = commitment;

// Phase 2: reveal
require(keccak256(abi.encode(revealedSecret, revealedSalt)) == commitments[msg.sender]);
```

The committer publishes the hash early, then later reveals the inputs. Anyone can verify that the revealed inputs match the commitment, but no one can determine the inputs from the commitment alone. The salt prevents brute-force attacks against the secret space.

**Signature verification.** A signer hashes a message off-chain and signs it. The contract reconstructs the same hash and uses `ecrecover` to recover the signing address. If the recovered address matches the expected signer, the signature is valid. This pattern underlies EIP-2612 `permit`, meta-transactions, and most signature-based authorization in DeFi.

**Storage slot derivation.** Mapping slots are computed as `keccak256(abi.encode(key, slotIndex))`. The compiler handles this automatically. You'll only invoke `keccak256` directly for mappings if you're writing assembly or computing slots for proxy storage patterns.

## Function selectors

Every external function in Solidity is identified by a 4-byte selector. The selector is the first 4 bytes of the keccak256 hash of the function signature:

```solidity
bytes4 selector = bytes4(keccak256("transfer(address,uint256)"));
// selector == 0xa9059cbb, the canonical ERC-20 transfer selector
```

The function signature is the function name followed by parenthesized argument types, no spaces, no return type. When a transaction calls a function, the first 4 bytes of the calldata are the selector. The contract uses the selector to dispatch to the right function.

This is why interface functions are commonly referred to by their `bytes4` selectors, and why every interface function has a deterministic identifier on chain. Two functions with the same signature in different contracts produce the same selector. ERC-20's `transfer(address,uint256)` is `0xa9059cbb` on every ERC-20 token, which is what lets standard wallets call any ERC-20 without per-token configuration.

## FAQ

### Why does my loop run out of gas once the array grows?

The block gas limit, around 30 million, caps a single transaction, and storage dominates a loop at 22,100 gas per fresh slot write and 5,000 per update. An array anyone can extend eventually needs more gas than a block holds, so the call reverts every time and the ETH inside is stuck. Paginate, let recipients pull their own funds, or move the work off chain.

### abi.encode or abi.encodePacked for keccak256 hashing?

abi.encode whenever two or more dynamic values go in. encodePacked drops the separators between them, so "hello" plus "world" and "hellow" plus "orld" both encode to helloworld and hash the same. encodePacked is safe for a single value, or for fixed-size types like uint256 and address.

### Why is the ERC-20 transfer selector always 0xa9059cbb?

It is the first 4 bytes of keccak256 of "transfer(address,uint256)". A selector hashes the function name plus parenthesized argument types with no spaces, so the same signature yields the same 4 bytes in every contract.

---

# Academy token

Source: https://academy.redduck.io/courses/development-on-ethereum/solidity-basics/academy-token

You have the toolkit. The last module walked you through every piece of Solidity you need to write a real, useful contract: mappings keyed by address, structs, events, custom errors, modifiers, inheritance, payable functions. You've built two contracts already, but both were exercises designed to drill specific lessons. This one is different. The contract you write here is the contract future lessons in this course will actually use.

## The story

The academy is launching its own token. Future lessons will deploy it in worked examples: a staking contract that pays rewards in this token, a vesting schedule for the team, a DEX where it trades against ETH. Before any of that can happen, the token itself has to exist. That's what you're building now.

Call it whatever you want. The name and symbol are constructor parameters.

## What to do

Fork the repository and read `TASK.md` for the full setup. The starter contract is there with the function signatures, the interface it inherits from, and the test suite. Your job is to fill in the bodies so every test passes. The tests double as the spec: every behavior the contract must exhibit is encoded in a test.

The contract is small. Around 80-120 lines depending on how concise your code is.

## The one rule

**Don't look up someone else's implementation.** Not OpenZeppelin, not Solmate, not Solady, not a tutorial blog. Not "just to understand the structure." Not for five seconds while you're stuck.

The reason isn't moral. It's that every line you copy from an existing implementation is a line you don't understand. When you later have to debug a token contract in production, modify one for a specific use case, or audit one written by someone else, you'll need to actually know how every piece works. The OpenZeppelin implementation is excellent code. It's also written by people who already understood the standard before they wrote it. You're trying to become one of those people. Copying skips the only step that matters.

Everything you need is in the tests and in this course's prior lessons. The Solidity documentation is fine. The academy chat is fine when you're truly stuck. Someone else's contract is not.

## What you've actually built

When you finish, you'll have written ERC-20, the most widely used smart contract standard on Ethereum. Every fungible token on Ethereum implements the same interface you just implemented: USDC, DAI, UNI, LINK, all of them. The next lesson covers what ERC-20 is, why it's structured the way it is, and how the rest of the ecosystem builds on top of it. By then, you'll already know the answer from the inside.

Good luck.

---

# What is ERC-20

Source: https://academy.redduck.io/courses/development-on-ethereum/token-security-and-evm-internals/what-is-erc-20

> You just built one. Now the conceptual frame. ERC-20 is the standard that defines what a fungible token contract looks like on Ethereum. Every common token you've heard of (USDC, DAI, UNI, LINK, WETH) implements the same six functions and two events you just wrote. This lesson covers what makes ERC-20 the standard, why standardization mattered for Ethereum's growth, what the `decimals` system actually means, what production implementations add beyond the bare spec, and the one security quirk in the standard you should know about before deploying anything to mainnet.

## What a token actually is

Tokens existed long before Ethereum. Casino chips. Arcade tokens you bought at the door to spend on machines. Loyalty points on a coffee shop card. Concert tickets. Theater tickets. Gift certificates. All of these are tokens. They represent value, they can be exchanged, and they're issued by some entity that defines what they're good for.

A token is not quite money. Its value depends on what someone is willing to accept it for. Casino chips have value because the casino will exchange them for currency. Arcade tokens have value because the machines accept them. Fan tokens for a sports club have value because the club uses them for voting on minor decisions, or because other fans want them.

The pattern works because the issuer commits to a meaning for the token, and other parties accept that meaning. A token without an issuer who stands behind it is worth nothing. A token whose issuer has a credible commitment to redeem it for something is worth roughly what it can be redeemed for.

On Ethereum, the issuer is a smart contract. The contract decides who has how many tokens, who can transfer them, and what they represent. The contract you wrote in the last task is exactly this: an issuer that tracks balances and lets holders move tokens around.

## Fungible and non-fungible

Two casino chips of the same denomination are interchangeable. If you have one and your friend has one, and you swap them, nothing has changed. They have the same purchasing power, the same physical form, the same value. This property is called **fungibility**. Two units of a fungible thing are equivalent.

Now consider rare coins. Most one-euro coins are fungible: each is worth one euro, you swap them, nothing changes. But a rare collector's edition one-euro coin from a limited mint run might be worth fifty euros because of its rarity. The face value is one euro, but the coin itself has an identity that makes it different from other one-euro coins. This is **non-fungible**: each unit has its own identity and can't be substituted for another.

ERC-20 is the standard for **fungible** tokens. Every unit is identical to every other unit. If you hold 100 ACAD tokens, those 100 units are interchangeable with any other 100 units anywhere else. Token #47 and token #48 don't exist as concepts. There are just balances.

The non-fungible counterpart is ERC-721. ERC-721 is what NFTs use: each token has a unique ID, and a contract tracks who owns which specific ID. The two standards have different shapes for fundamentally different use cases.

## Why a standard exists at all

Imagine Ethereum without ERC-20. Someone deploys a token contract. It has functions to track balances and transfer tokens, but the function names and parameter orders are whatever the developer chose. Maybe their `transfer` function takes the amount before the recipient address. Maybe their balance lookup is called `getBalance` instead of `balanceOf`. Maybe they don't emit events at all.

Now you want to build a wallet that displays token balances. Every token contract works differently, so your wallet needs custom code for each one. Every time a new token launches, your wallet needs an update to support it. A new exchange listing a new token needs new integration work for every wallet that wants to display it. Building anything on top of tokens is very hard because there's no shared vocabulary.

ERC-20 fixes this by defining an interface. Any contract that implements these six functions and emits these two events is an ERC-20 token. Any wallet that knows ERC-20 can interact with any ERC-20 token without further work. Any exchange that knows ERC-20 can list any ERC-20 token without writing token-specific code. The standard turned tokens from custom integrations into a commodity, which is exactly what made Ethereum's token ecosystem possible.

The number 20 comes from EIP-20, which is the formal specification document on Ethereum's improvement proposal site. ERC stands for "Ethereum Request for Comments," historically the section of EIPs dealing with application-layer standards. Today the names are used interchangeably. The token standard is EIP-20 to the spec writers and ERC-20 to everyone else.

## The interface

You implemented this in the last task. As a quick reference:

```solidity
// Solidity 0.8.24, Ethereum mainnet
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}
```

Three of these handle direct holder actions: `transfer` moves tokens from the caller, `approve` authorizes a third party to spend on the caller's behalf, `transferFrom` is how that third party then spends. Two are view functions for querying state: `balanceOf` and `allowance`. One reports total supply.

The two events tell off-chain observers what happened. `Transfer` fires whenever tokens move. `Approval` fires whenever someone changes an allowance. Wallets and indexers read these events to build their view of the world without needing to query storage directly.

Three things about this interface are worth noting.

First, every state-mutating function returns `bool`. The spec inherits this from a time when reverting was less common in Solidity. A modern implementation reverts on failure and returns `true` on success, but the return type stays for backward compatibility.

Second, `transferFrom` exists because contracts can't initiate transactions, only respond to them. If a DEX wants to swap your tokens for ETH, it can't reach into your wallet and take them. Instead, you call `approve` to authorize the DEX, the DEX then calls `transferFrom` during the swap. The two-step process is the only way to let one contract move tokens that belong to another address.

Third, the `Transfer` event from `address(0)` represents minting. By convention, when a token contract creates new tokens, it emits a transfer from the zero address. Block explorers and indexers use this to display the initial supply distribution. Forget this transfer when minting, and block explorers will show no initial supply at all.

## Decimals and the integer-only world

The EVM has no floating-point arithmetic. Everything is integers. A contract storing balances can't represent "0.5 tokens" the way Python would. It can only store integer values.

ERC-20 handles this with a convention: tokens have a `decimals` value, and the displayed amount is the stored amount divided by `10**decimals`. By tradition, this value is 18, which matches the wei-to-ether ratio on Ethereum itself. One whole token is `1 * 10**18` raw units. Half a token is `0.5 * 10**18 = 5 * 10**17` raw units.

The contract only ever sees the raw integer. When a wallet displays "100 ACAD," it's showing you `100_000_000_000_000_000_000` divided by `10**18`. When you type "0.5" in a transfer field, the wallet multiplies by `10**18` before sending the transaction. The contract works in the raw units the whole time, oblivious to the display layer.

This is why your contract returns `1000 * 10**18` from `totalSupply` even though you'd think of it as "1000 tokens." The raw value in storage is one followed by 21 zeros. The display value is `1000`. Both are correct. They describe the same balance at different layers.

Some tokens use different decimals values. USDC uses 6, so one USDC is `10**6` raw units. WBTC uses 8, matching Bitcoin's satoshi denomination. The 18-decimals convention isn't universal, but it's what wallets default to and what almost all new tokens follow. Picking a non-standard value means every interface needs to handle your token specifically, which is exactly what the standard was meant to avoid.

A consequence worth internalizing: when you write code that interacts with arbitrary ERC-20 tokens, you cannot assume 18 decimals. You read the `decimals()` function and scale accordingly. Production contracts that hardcode 18 will misbehave with USDC.

## What OpenZeppelin adds beyond the bare standard

The contract you wrote implements EIP-20 correctly. It's deployable. It would work in any wallet that understands ERC-20. So what does OpenZeppelin's implementation do that yours doesn't?

A few things, and they're worth understanding even if you never use OpenZeppelin directly.

**Custom errors with structured data.** Where your contract probably uses `require` strings or simple custom errors, OpenZeppelin's errors carry the values that caused the failure: `ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed)`. A frontend reading the revert data sees exactly how much was short, which makes debugging dramatically easier.

**A unified ****`_update`**** internal function.** OpenZeppelin factors transfer, mint, and burn into a single internal function that handles balance changes, total supply tracking, and event emission. Mint is "transfer from `address(0)`," burn is "transfer to `address(0)`," and regular transfers are both addresses being non-zero. The single helper means there's only one place to audit and only one place to add functionality.

**Hooks for extension.** Contracts that inherit from OpenZeppelin's `ERC20` can override `_update` to add behavior before or after every balance change: pausing, blacklisting, fee-on-transfer, tax mechanisms. The base contract calls these hooks at well-defined points, and extension contracts plug into those points without rewriting the core logic.

**The infinite-allowance optimization.** When a holder sets allowance to `type(uint256).max`, OpenZeppelin's `transferFrom` recognizes this as "approve forever" and skips the allowance decrement, saving gas on every transfer. This is the standard pattern that most wallets use for "approve" buttons today.

**Defensive checks against zero addresses.** OpenZeppelin reverts on transfers to or from `address(0)` in places the bare standard doesn't strictly require. This prevents accidental token burns and helps users avoid losing tokens to typos.

None of this is required by EIP-20. Your bare implementation is conformant. But these additions are what production token contracts include, and they're the reason most projects inherit from OpenZeppelin rather than writing their own.

## The approval race condition

ERC-20 has one well-known design flaw, and it's worth understanding before you deploy anything.

Suppose Alice has approved Bob to spend 100 ACAD. She changes her mind and wants to reduce his allowance to 50. She calls `approve(bob, 50)`. The transaction enters the mempool.

Before the transaction is mined, Bob sees it pending. He front-runs Alice by calling `transferFrom(alice, bob, 100)` with a higher gas price. His transaction mines first. He receives 100 tokens. Then Alice's `approve(bob, 50)` mines, setting his allowance to 50. He then calls `transferFrom(alice, bob, 50)` and receives 50 more.

Bob ended up with 150 tokens when Alice intended to allow only 50 in total.

The flaw is structural. The `approve` function overwrites the allowance, but there's no way to atomically transition from "old value" to "new value" while ensuring the old value isn't used in between. The standard pattern for working around this is to first approve zero, then approve the new amount in a second transaction. This forces the spender to either use the original allowance fully before the zero, or be left with nothing. But it requires two transactions and two gas payments for every allowance change.

EIP-2612 (`permit`) provides a more elegant solution by replacing approvals with cryptographic signatures. It's a separate standard layered on top of ERC-20 and is its own topic.

For now: be aware that increasing an allowance is safe, decreasing it is not, and any frontend that lets users change allowances should either zero-out first or use `permit` if the token supports it.

## Where ERC-20 fits in the wider ecosystem

ERC-20 is the foundation of Ethereum's token economy, but it's not the only standard. A brief preview of what builds on or around it.

**ERC-721**, the non-fungible standard introduced earlier, has a different interface shape: instead of `balanceOf(owner)` returning a token count, you have `ownerOf(tokenId)` returning the address that owns a specific ID. The spirit is the same as ERC-20, though, in that the standardization is what lets wallets and marketplaces integrate any compliant token.

**ERC-1155** is a hybrid standard that supports both fungible and non-fungible tokens in a single contract. Game economies use it heavily: gold pieces are fungible, but each rare sword has its own ID. ERC-1155 is more gas-efficient for these mixed cases than running separate ERC-20 and ERC-721 contracts.

**EIP-2612 (****`permit`****)** adds a function to ERC-20 tokens that lets users approve spending via a signed message instead of an on-chain transaction. USDC, DAI, and most modern tokens implement it. Older tokens like WETH do not.

**ERC-4626** is the standard for tokenized vaults. A vault that takes ERC-20 deposits and issues shares as ERC-20 tokens itself, used by DeFi protocols like Aave, Yearn, and Morpho. Built on top of ERC-20 rather than replacing it.

Each of these is a substantial topic in its own right. The point for now is that ERC-20 is the foundation. Understanding it is the prerequisite for everything else.

## Further reading

The official [EIP-20 specification](https://eips.ethereum.org/EIPS/eip-20) is short and worth reading in full once you've implemented a token. The [OpenZeppelin ERC20 source](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) is the production reference implementation. Compare it to your own to see the patterns described in this lesson. For the approval race condition history, the original [issue thread](https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729) on the EIP-20 repository documents the discovery and the failed attempts to fix it within the standard itself.

## FAQ

### My wallet shows 1000 tokens but totalSupply returns a huge number. Why?

Balances are stored as integers, and the ERC-20 `decimals` value says where the display point sits. 1000 tokens at 18 decimals is stored as 1000 * 10**18. Most tokens use 18, USDC uses 6, WBTC uses 8. Code that handles arbitrary tokens reads `decimals()` instead of assuming 18.

### Why can't a DEX just take my tokens directly?

Contracts cannot start their own transactions, so a DEX cannot reach into your wallet. You `approve` a spender for an amount, and the spender calls `transferFrom` to move the tokens when the swap runs.

### Is it safe to lower an allowance I already gave?

Not directly. This is the approval race condition. A spender who sees your `approve` pending can spend the old, larger allowance first, then the new amount once it lands. Zero the allowance first, then set the new value, or use EIP-2612 `permit`.

### Why does my token emit a Transfer from address(0)?

Minting. Creating tokens is emitted as a `Transfer` from `address(0)`, and explorers read that to show a token's initial supply. Skip it and the supply exists but no explorer shows it.

### What does approving type(uint256).max do?

It sets an unlimited allowance. OpenZeppelin's `transferFrom` treats `type(uint256).max` as approve forever and skips the allowance write, which saves gas on every transfer.

---

# Vault robbing

Source: https://academy.redduck.io/courses/development-on-ethereum/token-security-and-evm-internals/vault-robbing



---

# Reentrancy and denial-of-service

Source: https://academy.redduck.io/courses/development-on-ethereum/token-security-and-evm-internals/reentrancy-and-denial-of-service



---

# Low-level calls

Source: https://academy.redduck.io/courses/development-on-ethereum/token-security-and-evm-internals/low-level-calls



---

# Solidity gotchas - Test

Source: https://academy.redduck.io/courses/development-on-ethereum/token-security-and-evm-internals/solidity-gotchas-test



---

# Front-running and MEV

Source: https://academy.redduck.io/courses/development-on-ethereum/token-security-and-evm-internals/front-running-and-mev



---

# Commit-reveal

Source: https://academy.redduck.io/courses/development-on-ethereum/token-security-and-evm-internals/commit-reveal



---

# Storage layout

Source: https://academy.redduck.io/courses/development-on-ethereum/token-security-and-evm-internals/storage-layout



---

# Upgradeable contracts and proxies

Source: https://academy.redduck.io/courses/development-on-ethereum/token-security-and-evm-internals/upgradeable-contracts-and-proxies



---

# Proxies - Test

Source: https://academy.redduck.io/courses/development-on-ethereum/token-security-and-evm-internals/proxies-test



---

# Testing

Source: https://academy.redduck.io/courses/development-on-ethereum/defi-tasks-and-patterns/testing



---

# Voting on token price

Source: https://academy.redduck.io/courses/development-on-ethereum/defi-tasks-and-patterns/voting-on-token-price



---

# Off-chain computation, on-chain verification

Source: https://academy.redduck.io/courses/development-on-ethereum/defi-tasks-and-patterns/off-chain-computation-on-chain-verification



---

# Voting on token price, with withdrawal

Source: https://academy.redduck.io/courses/development-on-ethereum/defi-tasks-and-patterns/voting-on-token-price-with-withdrawal



---

# AMM

Source: https://academy.redduck.io/courses/development-on-ethereum/defi-tasks-and-patterns/amm



---

# AMM Pair

Source: https://academy.redduck.io/courses/development-on-ethereum/defi-tasks-and-patterns/amm-pair



---

# Merkle airdrop

Source: https://academy.redduck.io/courses/development-on-ethereum/defi-tasks-and-patterns/merkle-airdrop



---

# ERC-721 and ERC-1155

Source: https://academy.redduck.io/courses/development-on-ethereum/tokens-beyond-erc-20/erc-721-and-erc-1155



---

# ERC-4626 tokenized vaults

Source: https://academy.redduck.io/courses/development-on-ethereum/tokens-beyond-erc-20/erc-4626-tokenized-vaults



---

# Oracles and Chainlink Price Feeds

Source: https://academy.redduck.io/courses/development-on-ethereum/oracles/oracles-and-chainlink-price-feeds



---

# Randomness on chain

Source: https://academy.redduck.io/courses/development-on-ethereum/oracles/randomness-on-chain



---

# Raffle

Source: https://academy.redduck.io/courses/development-on-ethereum/oracles/raffle



---

# Flash loans

Source: https://academy.redduck.io/courses/development-on-ethereum/oracles/flash-loans



---

# TWAP oracles

Source: https://academy.redduck.io/courses/development-on-ethereum/oracles/twap-oracles



---

# Gasless approvals

Source: https://academy.redduck.io/courses/development-on-ethereum/advanced-defi/gasless-approvals



---

# Uniswap V3

Source: https://academy.redduck.io/courses/development-on-ethereum/advanced-defi/uniswap-v3



---

# Account abstraction

Source: https://academy.redduck.io/courses/development-on-ethereum/advanced-defi/account-abstraction



---

# DeFi - Test

Source: https://academy.redduck.io/courses/development-on-ethereum/advanced-defi/defi-test



---

# Keepers and Chainlink Automation

Source: https://academy.redduck.io/courses/development-on-ethereum/advanced-defi/keepers-and-chainlink-automation



---

# Lending and borrowing on-chain

Source: https://academy.redduck.io/courses/development-on-ethereum/advanced-defi/lending-and-borrowing-on-chain



---

# Blind signing and the Bybit hack

Source: https://academy.redduck.io/courses/development-on-ethereum/clear-signing/blind-signing-and-the-bybit-hack



---

# ERC-8009: sign the outcome

Source: https://academy.redduck.io/courses/development-on-ethereum/clear-signing/erc-8009-sign-the-outcome



---

# The core and its routers

Source: https://academy.redduck.io/courses/development-on-ethereum/clear-signing/the-core-and-its-routers



---

# Congratulations

Source: https://academy.redduck.io/courses/development-on-ethereum/conclusion/congratulations



---

# What the Solana course is about

Source: https://academy.redduck.io/courses/development-on-solana/welcome-to-solana/what-the-solana-course-is-about



---

# What Solana is

Source: https://academy.redduck.io/courses/development-on-solana/welcome-to-solana/what-solana-is



---

# Where Solana came from

Source: https://academy.redduck.io/courses/development-on-solana/welcome-to-solana/where-solana-came-from



---

# Why Solana was built differently

Source: https://academy.redduck.io/courses/development-on-solana/the-solana-mental-model/why-solana-built-differently



---

# Accounts

Source: https://academy.redduck.io/courses/development-on-solana/the-solana-mental-model/accounts



---

# Instructions and transactions

Source: https://academy.redduck.io/courses/development-on-solana/the-solana-mental-model/instructions-and-transactions



---

# Lamports, rent, and account lifecycle

Source: https://academy.redduck.io/courses/development-on-solana/the-solana-mental-model/lamports-rent-and-account-lifecycle



---

# Transaction fees and compute units

Source: https://academy.redduck.io/courses/development-on-solana/the-solana-mental-model/transaction-fees-and-compute-units



---

# Proof of History

Source: https://academy.redduck.io/courses/development-on-solana/the-solana-mental-model/proof-of-history



---

# Solana essentials - Test

Source: https://academy.redduck.io/courses/development-on-solana/the-solana-mental-model/solana-essentials-test



---

# Rust for reading Anchor programs

Source: https://academy.redduck.io/courses/development-on-solana/rust-essentials/rust-for-reading-anchor-programs



---

# Ownership, borrowing, and the borrow checker

Source: https://academy.redduck.io/courses/development-on-solana/rust-essentials/ownership-borrowing-and-the-borrow-checker



---

# Anchor and program structure

Source: https://academy.redduck.io/courses/development-on-solana/writing-your-first-program/anchor-and-program-structure



---

# The IDL

Source: https://academy.redduck.io/courses/development-on-solana/writing-your-first-program/the-idl



---

# Account types in Anchor

Source: https://academy.redduck.io/courses/development-on-solana/writing-your-first-program/account-types-in-anchor



---

# Account constraints

Source: https://academy.redduck.io/courses/development-on-solana/writing-your-first-program/account-constraints



---

# Client side

Source: https://academy.redduck.io/courses/development-on-solana/writing-your-first-program/client-side



---

# PDAs: addresses with no private key

Source: https://academy.redduck.io/courses/development-on-solana/writing-your-first-program/pdas-addresses-with-no-private-key



---

# Account space and layout

Source: https://academy.redduck.io/courses/development-on-solana/writing-your-first-program/account-space-and-layout



---

# Errors and events

Source: https://academy.redduck.io/courses/development-on-solana/writing-your-first-program/errors-and-events



---

# Tests with solana-bankrun

Source: https://academy.redduck.io/courses/development-on-solana/writing-your-first-program/tests-with-solana-bankrun



---

# Your First Solana Program

Source: https://academy.redduck.io/courses/development-on-solana/writing-your-first-program/donor-tier-vaults



---

# Cross-Program Invocation (CPI)

Source: https://academy.redduck.io/courses/development-on-solana/working-with-other-programs/cross-program-invocation-cpi



---

# PDAs as signers

Source: https://academy.redduck.io/courses/development-on-solana/working-with-other-programs/pdas-as-signers



---

# The SPL Token program in practice

Source: https://academy.redduck.io/courses/development-on-solana/working-with-other-programs/the-spl-token-program-in-practice



---

# Associated Token Accounts (ATAs)

Source: https://academy.redduck.io/courses/development-on-solana/working-with-other-programs/associated-token-accounts-atas



---

# Token-2022 and token extensions

Source: https://academy.redduck.io/courses/development-on-solana/working-with-other-programs/token-2022-and-token-extensions



---

# Composing programs in practice

Source: https://academy.redduck.io/courses/development-on-solana/working-with-other-programs/composing-programs-in-practice



---

# Versioned transactions and address lookup tables

Source: https://academy.redduck.io/courses/development-on-solana/working-with-other-programs/versioned-transactions-and-address-lookup-tables



---

# Program upgrades

Source: https://academy.redduck.io/courses/development-on-solana/working-with-other-programs/program-upgrades



---

# Solana basics - Test

Source: https://academy.redduck.io/courses/development-on-solana/working-with-other-programs/solana-basics-test



---

# Zero-copy accounts

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/zero-copy-accounts



---

# Account closure and realloc

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/account-closure-and-realloc



---

# Staking

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/staking



---

# Off-chain computation, on-chain verification

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/off-chain-computation-on-chain-verification



---

# AMM

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/amm



---

# Pyth price feeds

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/pyth-price-feeds



---

# Merkle airdrop

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/merkle-airdrop



---

# Solana NFTs

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/solana-nfts



---

# CLMM

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/clmm



---

# Randomness on chain

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/randomness-on-chain



---

# Raffle

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/raffle



---

# Off-chain state: events, logs, and indexers

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/off-chain-state-events-logs-and-indexers



---

# The classic Solana exploits

Source: https://academy.redduck.io/courses/development-on-solana/production-state-design/the-classic-solana-exploits



---

# Detailed Solana architecture

Source: https://academy.redduck.io/courses/development-on-solana/architecture-details/detailed-solana-architecture



---

# The leader schedule and block production

Source: https://academy.redduck.io/courses/development-on-solana/architecture-details/the-leader-schedule-and-block-production



---

# Outages, lessons, and resilience

Source: https://academy.redduck.io/courses/development-on-solana/architecture-details/outages-lessons-and-resilience



---

# What Solana traded for what

Source: https://academy.redduck.io/courses/development-on-solana/architecture-details/what-solana-traded-for-what



---

# Jito, MEV, Bundles

Source: https://academy.redduck.io/courses/development-on-solana/architecture-details/jito-mev-bundles



---

# Congratulations

Source: https://academy.redduck.io/courses/development-on-solana/conclusion/congratulations



