Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
The architectural leap isn't cryptographic — it's that there is no api.ethereum.com. Every full node holds the entire ledger, re-executes every transaction, and enforces identical rules. You don't ask a server 'is this transaction valid' — you run the rules yourself, or trust one of thousands of peers who does.
At this moment there are roughly 8,000 Ethereum execution-layer full nodes and 1M+ validators running consensus clients, plus tens of thousands of Bitcoin nodes. None of them is authoritative. If you run geth/reth/erigon locally and sync, your laptop is as canonical as Vitalik's — same rules, same state root. Contrast this with api.stripe.com, where Stripe's DB is the only truth.
Use these three in order. Each builds on the one before.
Explain the difference between a full node, a light node, and an archive node on Ethereum. What does each store, roughly?
When my `eth_call` hits Infura, what does Infura's infrastructure actually do to answer it? Where does the ledger data live, and why is it replaceable with any other provider or my own node?
If I'm building a DeFi app and I depend on Alchemy for RPC: what failure modes does that dependency reintroduce, and how would 'running your own node' actually help vs. multi-provider fallback? Be specific about latency, censorship, and data consistency.
// main.go — query eth_blockNumber from two public RPCs and compare
// Run: go run main.go (stdlib only)
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strconv"
)
func blockNumber(rpc string) (int64, error) {
body := []byte(`{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}`)
resp, err := http.Post(rpc, "application/json", bytes.NewReader(body))
if err != nil {
return 0, err
}
defer resp.Body.Close()
var out struct {
Result string `json:"result"`
}
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return 0, err
}
return strconv.ParseInt(out.Result[2:], 16, 64)
}
func main() {
for _, rpc := range []string{"https://cloudflare-eth.com", "https://eth.llamarpc.com"} {
n, err := blockNumber(rpc)
if err != nil {
fmt.Println(rpc, "-> error:", err)
continue
}
fmt.Println(rpc, "->", n)
}
}
go run main.go