Light Clients and Stateless Verification TODO
Concept
A light client is a node that follows only the chain of block headers, without storing or executing full blocks and state, and verifies specific facts using Merkle proofs. It treats a header's state root as its anchor of trust: given an inclusion proof that some account balance or storage value belongs under that root, it recomputes the hashes locally to confirm it. The legitimacy of the header itself has to come from the consensus layer — on a PoS chain, the light client follows headers via lightweight proofs over the validator signature set. Stateless verification goes a step further: the block arrives together with a witness — the state fragments needed to execute it, plus their proofs — so the block can be re-executed and verified without a state database at all. The bottleneck of this approach is witness size; because Merkle Patricia tries have a wide branching factor, their proofs are large, which is why smaller commitment structures such as Verkle trees or STARK-based proofs are being researched and adopted.
In environments that can't run a full node — mobile, browser, cross-chain bridges — light client verification is the only realistic way to minimize trust assumptions.
Code & Formula
# 라이트 클라이언트와 상태 없는(stateless) 검증 — Merkle 증명 하나로 전체 데이터 없이 포함 여부를 검증한다.
# 전체 트리를 갖지 않고도 leaf + 형제 해시 경로(proof) + 루트만으로 위조 여부를 탐지한다.
import hashlib
def h(*parts):
return hashlib.sha256(b"".join(parts)).digest()
def merkle_root(leaves):
layer = leaves
while len(layer) > 1:
layer = [h(layer[i], layer[i + 1]) for i in range(0, len(layer), 2)]
return layer[0]
def merkle_proof(leaves, index):
proof = []
layer, idx = leaves, index
while len(layer) > 1:
sibling_idx = idx ^ 1
is_left = sibling_idx < idx # 형제가 왼쪽에 있는지
proof.append((layer[sibling_idx], is_left))
layer = [h(layer[i], layer[i + 1]) for i in range(0, len(layer), 2)]
idx //= 2
return proof
def verify(leaf, proof, root):
computed = leaf
for sibling, is_left in proof:
computed = h(sibling, computed) if is_left else h(computed, sibling)
return computed == root
accounts = [f"account-{i}:balance={i * 100}".encode() for i in range(8)]
leaves = [h(a) for a in accounts]
root = merkle_root(leaves)
target_index = 5
proof = merkle_proof(leaves, target_index)
# 라이트 클라이언트: 전체 accounts 리스트 없이, leaf 하나 + proof + root만으로 검증
print("헤더의 상태 루트:", root.hex())
print("account-5 포함 증명 검증:", verify(leaves[target_index], proof, root))
# 공격자가 값을 조작한 leaf를 제시하면 검증에 실패해야 한다
forged_leaf = h(b"account-5:balance=999999")
print("조작된 leaf 검증(실패해야 정상):", verify(forged_leaf, proof, root))
docs/code/algorithms/algorithms-62.py
Exercise
Fetch a proof for a specific account via eth_getProof, then write a script that walks the hashes yourself — without trusting the node — to verify they match the header's stateRoot.
Practical Connection
If Verex's frontend or settlement watcher verifies positions and settlement results with state proofs instead of trusting RPC responses at face value, the RPC provider can be removed from the trust set entirely.
If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/dev-100-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.