Advanced Cryptography
Now the specific primitives blockchain depends on: Keccak-256, ECDSA over secp256k1, and the Merkle tree that makes "give me a proof that this tx is in the block" a 32-byte problem instead of a 1MB problem.
1. SHA-256 vs Keccak-256
Bitcoin uses SHA-256. Ethereum uses Keccak-256 — the original NIST SHA-3 submission, before NIST tweaked the padding to produce SHA-3. Ethereum forked before the tweak; now everyone in EVM-land is stuck with Keccak.
hashlib.sha3_256 in Python is not Keccak-256. Use pysha3, eth_hash, or from Crypto.Hash import keccak.from Crypto.Hash import keccak
k = keccak.new(digest_bits=256); k.update(b"hello")
print(k.hexdigest())
# 1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
2. RSA vs ECC (and why Ethereum picked ECC)
| RSA-2048 | ECDSA secp256k1 | |
|---|---|---|
| Public key | 256 bytes | 33 bytes (compressed) |
| Signature | 256 bytes | 65 bytes |
| Speed | Slow keygen | Fast everything |
| Security (~bits) | 112 | 128 |
3. Ethereum addresses — it's just hashing
from eth_keys import keys
pk = keys.PrivateKey(b"\x01"*32)
print(pk.public_key.to_checksum_address())
# 0x1a642f0E3c3aF545E7AcBD38b07251B3990914F1
This means an address is not random — it's a deterministic function of a private key. Lose the key → lose the account. There is no password reset; you are the math.
4. Merkle trees
A Merkle tree is a binary tree of hashes. Leaves are H(data_i); internal nodes are H(left || right); the root is a single 32-byte commitment to all the data.
log₂(N) sibling hashes, not every file.Show: 30-line Merkle tree + proof in Python
import hashlib
H = lambda b: hashlib.sha256(b).digest()
def build(leaves):
layer = [H(x) for x in leaves]
tree = [layer]
while len(layer) > 1:
if len(layer) % 2: layer.append(layer[-1]) # duplicate last
layer = [H(layer[i] + layer[i+1]) for i in range(0, len(layer), 2)]
tree.append(layer)
return tree # tree[0]=leaves, tree[-1]=[root]
def proof(tree, idx):
p = []
for layer in tree[:-1]:
sib = idx ^ 1
if sib >= len(layer): sib = idx # duplicated
p.append((layer[sib], idx & 1)) # (hash, is_right_child_of_parent)
idx //= 2
return p
def verify(leaf, proof_, root):
h = H(leaf)
for sib, side in proof_:
h = H(sib + h) if side else H(h + sib)
return h == root
leaves = [b"A", b"B", b"C", b"D"]
t = build(leaves)
root = t[-1][0]
assert verify(b"C", proof(t, 2), root)5. Where Merkle trees actually live in Ethereum
- Transactions root in each block → lets light clients prove a tx is in a block.
- State root → Merkle-Patricia Trie of every account balance. When you query
eth_getBalance, a full node essentially walks this trie. - Receipt root → logs/events integrity.
6. Commit–reveal & HMAC (sidebar)
Useful patterns you'll reach for later:
- Commit: publish
H(value || salt). Reveal: later publishvalue+salt. Used for fair randomness, sealed-bid auctions. - HMAC: authenticated hash using a secret. Not common on-chain (can't hide secrets), but used off-chain (e.g., signed webhook payloads from your indexer).
Project
merkle build <dir> and merkle prove <file> command. You should be able to prove any file's inclusion in ~32 × log₂(N) bytes.Quiz
- Ethereum would just pick one
- Collision resistance of Keccak → the entire Merkle-tree argument for block integrity collapses
- Only the specific block would be invalid
- The gas fee calculation