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.
'Immutable' is marketing shorthand for something more precise: tampering is cheaply detectable, not physically prevented. Any node with a copy of the chain can compute hashes in milliseconds and catch a rewrite instantly, so changing history requires convincing the whole network to accept your rewrite — an economic problem, not a cryptographic one.
Take any Ethereum block on Etherscan — say block 19000000 (hash 0x8a62…). Its header includes parentHash pointing to block 18999999. If an attacker flips one byte in block 18999999, its hash changes, so block 19000000's parentHash is now wrong, so block 19000001's parentHash-chain is now wrong, and so on for the next 5 million blocks. To make the tampered history stick, the attacker has to re-mine (or re-validate) and out-propose the honest chain from that point forward — which is why 'immutability' is really 'finality backed by the cost of rewriting'.
Parent Hash. Click it — confirm it matches the hash of block 18999999.Use these three in order. Each builds on the one before.
Define 'immutable' as blockchain people use it. How is it different from 'can never be changed'?
If I mutate block N in an Ethereum-style chain, exactly which fields in block N+1 are now cryptographically invalid, and why does a full node reject the mutated block without needing to trust anyone?
Given that immutability is economic, not cryptographic: what does it cost in dollars to rewrite the last hour of Bitcoin today? Walk through hashrate, electricity, and hardware. Contrast with the cost of rewriting an hour on a small PoS chain with $10M staked.
// main.go
// Take the first block of our toy chain and mutate it.
// Show that every subsequent block's prev-hash becomes invalid.
package main
import (
"crypto/sha256"
"encoding/json"
"fmt"
)
type Block struct {
I int `json:"i"`
Data string `json:"data"`
Prev string `json:"prev"`
}
func h(b Block) string {
bytes, _ := json.Marshal(b)
sum := sha256.Sum256(bytes)
return fmt.Sprintf("%x", sum)
}
type ValidationResult struct {
Ok bool
BrokenAt int
}
// Validator: walk the chain and flag any broken link.
func validate(chain []Block) ValidationResult {
for i := 1; i < len(chain); i++ {
if chain[i].Prev != h(chain[i-1]) {
return ValidationResult{Ok: false, BrokenAt: i}
}
}
return ValidationResult{Ok: true}
}
func main() {
chain := []Block{{I: 0, Data: "genesis", Prev: fmt.Sprintf("%064d", 0)}}
for i := 1; i < 4; i++ {
chain = append(chain, Block{I: i, Data: fmt.Sprintf("tx-%d", i), Prev: h(chain[i-1])})
}
r := validate(chain)
fmt.Printf("{ok: %v}\n", r.Ok) // {ok: true}
chain[1].Data = "tx-1-TAMPERED"
r = validate(chain)
fmt.Printf("{ok: %v, brokenAt: %d}\n", r.Ok, r.BrokenAt) // {ok: false, brokenAt: 2}
}
go run main.go