By @quakewang
Light client (or light node) help users access and interact with a blockchain in a secure and decentralized manner without having to sync the full blockchain
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
◄──┤parent_hash: H() │◄──┤parent_hash: H() │◄──┤parent_hash: H() │
│transactions_root: H()│ │transactions_root: H()│ │transactions_root: H()│
└──────────────────────┘ └──────────▲───────────┘ └──────────────────────┘
│
│ ◄── merkle tree
┌───┬───┴────┬───┐
│H()│ │H()│
└───┘ └───┘
┌──┐ ┌──┐ ┌──┐ ┌──┐
│tx│ │tx│ │tx│ │tx│
└──┘ └──┘ └──┘ └──┘
Simple payment verification allows a lightweight client to verify that a transaction is included in the Bitcoin blockchain, without downloading the entire blockchain.
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
◄──┤parent_hash: H() │◄──┤parent_hash: H() │◄──┤parent_hash: H() │
│transactions_root: H()│ │transactions_root: H()│ │transactions_root: H()│
└──────────────────────┘ └──────────▲───────────┘ └──────────────────────┘
│
│
┌───┬───┴────┬───┐
│ │ │P │ ◄── merkle tree proof
└───┘ └───┘
┌──┐ ┌──┐
│P │ │tx│
└──┘ └──┘
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
◄──┤parent_hash: H() │◄──┤parent_hash: H() │◄──┤parent_hash: H() │
│transactions_root: H()│ │transactions_root: H()│ │transactions_root: H()│
└──────────────────────┘ └──────────────────────┘ └──────────────────────┘
Merkle Mountain Ranges
┌─────┐
│Root1│
└─────┘
/ \
┌──────┐ ┌──────┐
│Block0│ │Block1│ Contains Block0 Hash
└──────┘ └──────┘
Merkle Mountain Ranges Append
┌─────┐
│Root2│
└─────┘
/ \
┌─────┐ \
│ │ \
└─────┘ \
/ \ \
┌──────┐ ┌──────┐ ┌──────┐
│Block0│ │Block1│ │Block2│ Contains Root1
└──────┘ └──────┘ └──────┘
Merkle Mountain Ranges Append
┌─────┐
│Root3│
└─────┘
/ \
┌─────┐ ┌─────┐
│ │ │ │
└─────┘ └─────┘
/ \ / \
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│Block0│ │Block1│ │Block2│ │Block3│ Contains Root2
└──────┘ └──────┘ └──────┘ └──────┘
Merkle Mountain Ranges Append
┌─────┐
│Root4│
└─────┘
/ \
┌─────┐ \
│ │ \
└─────┘ \
/ \ \
┌─────┐ ┌─────┐ \
│ │ │ │ \
└─────┘ └─────┘ \
/ \ / \ \
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│Block0│ │Block1│ │Block2│ │Block3│ │Block4│ Contains Root3
└──────┘ └──────┘ └──────┘ └──────┘ └──────┘
/\ Time complexity of append: O(log n)
/ \ Time complexity of proof generation: O(log n)
/ \
/\ \
/ \ /\
/\ /\ /\ \
/\/\/\/\/\/\/\
Merkle Mountain Ranges: list of perfectly balance binary trees
log2(N)
┌──────mmr proof─────┐
┌──────────────────────┐ ┌──────────────────────┐
│root: H() │ |root: H() │ ◄── tip mmr root
│transactions_root: H()│ │transactions_root: H()│ ◄── tip header
└───────────▲──────────┘ └──────────────────────┘
│
│
┌───┬───┴────┬───┐
│ │ │P │ ◄── merkle tree proof
└───┘ └───┘
┌──┐ ┌──┐
│P │ │tx│
└──┘ └──┘
Tip 1 is a honest chain tip
Tip 2 is a malicious chain with 1/3 hashrate
┌────┐
──────────────────────────────────────────►│Tip1│
└────┘
┌────┐
──────────────────────────────────────────►│Tip2│
└────┘
Sampling constant with binary distribution, 41 times
\[\begin{aligned} ({\frac13})^{41} < 2^{-64} \end{aligned} \]
┌────┐
─┬──────────────────┬───────────┬────┬──┬─►│Tip1│
│ | | | | └────┘
└──────────────────┴───────────┴────┴──┴─
┌────┐
─┬──────────────────┬───────────┬────┬──┬─►│Tip2│
│ | | | | └────┘
└──────────────────┴───────────┴────┴──┴─
┌────┐
─┬──────────────────┬───────────┬────┬──┬─►│Tip1│
│ | | | | └────┘
└──────────────────┴───────────┴──┬─┴──┴─
│
└──────── malicious fork
│ ┌────┐
─┬──────────────────┬───────────┬──┴─┬──┬─►│Tip2│
│ | | | | └────┘
└──────────────────┴───────────┴────┴──┴─
┌────┐
─┬──────────────────┬───────────┬────┬──┬─►│Tip1│
│ | | | | └────┘
┬└──────────────────┴───────────┴────┴──┴─
│
└──────── malicious fork
│ ┌────┐
┴┬──────────────────┬───────────┬────┬──┬─►│Tip2│
│ | | | | └────┘
└──────────────────┴───────────┴────┴──┴─
FlyClient: Super-Light Clients for Cryptocurrencies
How the light client gets the transactions it is interested in?
┌──────────────────────┐
│parent_hash: H() │
│transactions_root: H()│
└─────────▲────────────┘
│
│
┌───┬───┴────┬───┐
│H()│ │H()│
└───┘ └───┘
┌──┐ ┌──┐ ┌──┐ ┌──┐
│tx│ │tx│ │tx│ │tx│ ◄── input: alice
└──┘ └──┘ └──┘ └──┘ output: bob
┌────────────┐
│Light Client├───┬──────────────────────────────────────────
└────────────┘ | Send filter (bob) ▲
│ │
┌────────────┐ ▼ │ Send merkle proof
│Full Node ├──────────────────────────────────────────────
└────────────┘ │ │ │
│ │ │
┌──────┐ ┌──────┐ ┌──────┐
│Block1| │Block2| │Block3|
└──────┘ └──────┘ └──────┘
|
|
tx4 (alice send to bob)
┌────────────┐
│Light Client├────────────────────────────────────────────────────┬────────
└────────────┘ ▲ Send filter (all txs) ▲ Send filter |
│ │ | Get Block2
┌────────────┐ | | ▼
│Full Node ├─────────────────────────────────────────────────────────────
└────────────┘ │ │
│ │
┌──────┐ ┌──────┐
│Block1| │Block2|
└──────┘ └──────┘
N is the number of elements, P is the false-positive probability, bloom filter size:
\[\begin{aligned} bits = N \times \log_2e \times \log_2P \end{aligned} \]CKB 1000 transactions in one block, N = 1000, P = 2 ** 13 => 1000 * 1.44 * 13 bits = 2.34 KB
Theoretical minimum for a probabilistic data structure
\[\begin{aligned} bits = N \times \log_2P \end{aligned} \]BIP158 vs BIP37 bloom filter -44%: Golomb coded sets
hash -> Digest::MD5.hexdigest(s)[-8..].hex % (N * P)
N -> 24
P -> 2 ** 6 = 64
| String | Hash |
|---|---|
| Alpha | 408 |
| ... | ... |
| Omicron | 1001 |
| ... | ... |
| Omega | 812 |
Uniformly distributed hash result
alphabetum = %w(Alpha Beta Gamma Delta Epsilon Zeta Eta Theta Iota Kappa Lambda Mu Nu Xi Omicron Pi Rho Sigma Tau Upsilon Phi Chi Psi Omega)
alphabetum.map{|s| Digest::MD5.hexdigest(s)[-8..].hex % 1536}.sort
[181, 354, 359, 395, 407, 408, 662, 740, 771, 794, 804, 807, 812, 1001, 1021, 1026, 1090, 1092, 1126, 1126, 1132, 1243, 1315, 1367]
Convert uniformly distribution to geometric distribution
sorted.each_cons(2).map{|a| a[1] - a[0]}.sort
[0, 1, 2, 3, 5, 5, 5, 6, 10, 12, 20, 23, 31, 34, 36, 52, 64, 72, 78, 111, 173, 189, 254]
P = 2 ** 6
| 0 | 0 |
| 1 | 10 |
| 2 | 110 |
| 3 | 1110 |
| 4 | 11110 |
| 5 | 111110 |
[181, 354, 359, 395, 407, 408, 662, 740, 771, 794, 804, 807, 812, 1001, 1021, 1026, 1090, 1092, 1126, 1126, 1132, 1243, 1315, 1367]
sorted.each_cons(2).map{|a| ((a[1] - a[0]).divmod 64)}
[[2, 45], [0, 5], [0, 36], [0, 12], [0, 1], [3, 62], [1, 14], [0, 31], [0, 23], [0, 10], [0, 3], [0, 5], [2, 61], [0, 20], [0, 5], [1, 0], [0, 2], [0, 34], [0, 0], [0, 6], [1, 47], [1, 8], [0, 52]]
| Number | Quotient | Modulus | Golomb encoding |
|---|---|---|---|
| 181 | 2 | 53 | 110 | 110101 |
| 173 | 2 | 45 | 110 | 101101 |
| 5 | 0 | 5 | 0 | 000101 |
| 36 | 0 | 36 | 0 | 100100 |
| 12 | 0 | 12 | 0 | 001100 |
| 1 | 0 | 1 | 0 | 000001 |
Theoretical minimum for a probabilistic data structure
\[\begin{aligned} bits = N \times \log_2P \end{aligned} \]110 110101 110 101101 0 000101 010010000011000000001111011111010001110001111100101110001010000001100001011101111010010100000010110000000000001001000100000000000011010101111100010000110100
181 / (24 * 6) =~ 1.25