Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5ff173b

Browse files
committedNov 7, 2014
Post about IBLTs.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent 3fcfb29 commit 5ff173b

7 files changed

+2920
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
---
2+
layout: post
3+
commentIssueId: 38
4+
---
5+
6+
Gavin Andresen of bitcoin fame posted
7+
[a github gist](https://gist.github.com/gavinandresen/e20c3b5a1d4b97f79ac2)
8+
a while ago, about using IBLTs to send block transaction summaries across
9+
the bitcoin network.
10+
11+
## About IBLT ##
12+
13+
The IBLT structure is fairly simple: create a counting bloom filter,
14+
and attach to each bucket the contents of the thing you're filtering
15+
(xoring in each time). If the IBLT is fairly empty, you'll find
16+
buckets which only have 1 thing in them, and thus recover the things
17+
which were put in (this is the "invertible" part). When you remove
18+
those, you might find more buckets now have 1 thing in them, etc. The
19+
[paper](http://arxiv.org/pdf/1101.2245.pdf) is worth reading, but this
20+
should give you the idea.
21+
22+
Here's a simplified example. Consider the following buckets:
23+
24+
count: 1
25+
id: 0xf00
26+
index: 0
27+
frag: Feed Me
28+
29+
count: 1
30+
id: 0xf00
31+
index: 1
32+
frag: Seymore!
33+
34+
count: 2
35+
id: 0xf0f
36+
index: 0
37+
frag: 0x70x170x100xB0x90x1B0x10x53
38+
39+
count: 1
40+
id: 0x00f
41+
index: 0
42+
frag: I love
43+
44+
Here each "transaction" has 2 parts (index 0 and index 1), we can see
45+
both parts of id 0xf00 exposed in the first two buckets. The result
46+
is "Feed Me Seymore!". Now we've recovered that, we remove it, and it
47+
turns out that fragment 1 is also in that third bucket, so we subtract
48+
1 from the counter and XOR out the other values, giving:
49+
50+
count: 0
51+
id: 0
52+
index: 0
53+
frag:
54+
55+
count: 0
56+
id: 0
57+
index: 0
58+
frag:
59+
60+
count: 1
61+
id: 0x00f
62+
index: 1
63+
frag: Triffids
64+
65+
count: 1
66+
id: 0x00f
67+
index: 0
68+
frag: I love
69+
70+
Now we can extract id 0x00f, which is "I love Triffids".
71+
72+
### Subtracting IBLTs ###
73+
74+
The clever bit comes by realizing that if I create an IBLT with
75+
everything in my set, and you create an IBLT with everything in your
76+
set, it's trivial to subtract my IBLT from your IBLT and get an IBLT
77+
of the set differences. Which is great, because all bloom filters
78+
become useless when they clog up, but if our sets are very similar,
79+
the subtraction result gives a nice sparse IBLT. And
80+
[here's the paper](http://conferences.sigcomm.org/sigcomm/2011/papers/sigcomm/p218.pdf).
81+
82+
## Using IBLTs for Bitcoin ##
83+
84+
Gavin suggested some ways to adapt an IBLT to the blockchain problem:
85+
86+
1. Set a size of 1M for the whole thing.
87+
2. Make each IBLT bucket look like:
88+
struct entry {
89+
u32 count;
90+
struct keySum {
91+
u8 id[6];
92+
u16 index;
93+
u8 frag[8];
94+
} key;
95+
}
96+
This gives 52428 buckets for a 1MB table.
97+
98+
3. Generate a 48-bit id for each transaction and fragment it into
99+
64-bits at a time, with index incremented for each fragment. This
100+
means the average transaction (263 bytes) uses about 32 of these.
101+
102+
## Testing It ##
103+
104+
For my experiment, I used 4 hash functions for each bloom entry, and
105+
just make the 48-bit id the first 48 bits of the SHA. I used a simple
106+
shell script to pull out the last 10000 bitcoin transactions.
107+
108+
### Recovering Transactions ###
109+
110+
Let's first look at how many transactions we can put in such table and
111+
pull them out again. This reflects the simple case where the miner
112+
has N transactions we don't have (and we have none they don't have).
113+
For each transaction, we just have to find a standalone copy of each
114+
fragment; since each fragment is in 4 separate places, removing a
115+
transaction as we find it may leave other fragments exposed so we can
116+
make more progress.
117+
118+
The result turns out to be a cliff: with 300 transactions we can
119+
extract all the transactions 100% of the time, with 320 it's down to
120+
0% (this is only 10 runs though). We only care about full recovery,
121+
because if we can't get one transaction, we fall back to getting the
122+
whole block:
123+
124+
![images/recovery-stats-simple.svg](Graph of recovery success)
125+
126+
### Eliminating not-present Transactions ###
127+
128+
Now, reality is a bit more complex. We will have some transactions
129+
which are *not* in the block (I'll call these "our" transactions), as
130+
well as missing some which are ("their" transactions). Indeed, if the
131+
network is working perfectly, we should have all the transactions in
132+
the block, and also any which have reached us while the block was
133+
being relayed to us, so we expect "our" transactions to dominate.
134+
135+
And it turns out that it's much easier to detect transactions which
136+
aren't in the block than those that are; we only need to find a single
137+
standalone fragment with count -1, and we can be fairly confident that
138+
it means we've found one of our transactions and should remove the
139+
whole thing. Finding a single exposed fragment for a transaction like
140+
this is far more likely than finding all fragments of a transaction
141+
exposed, and indeed our cliff for these is far higher, around 3300:
142+
143+
![images/removal-stats-simple.svg](Graph of recovery success)
144+
145+
Finally, here's a heat map of interaction between the two, as the
146+
number of "their" transactions we need to recover and the number of
147+
"our" transactions we should remove varies. It really needs more data
148+
points, but you get the idea:
149+
150+
![images/heatmap-8byte.svg](Recovery success with variable ours/theirs)
151+
152+
## Testing Some Variants ##
153+
154+
There are three obvious things to try:
155+
1. Increase the size of the slice in each IBLT bucket.
156+
2. Reduce the size of the IBLT from 1M.
157+
3. Add a hashSum field to each bucket as suggested by the paper.
158+
159+
### Larger slices ###
160+
161+
Increasing the data per bucket from 8 to 64 bytes seems like a no-brainer:
162+
instead of 50% overhead for IBLT data, we're down to 12%. Obviously we
163+
have fewer buckets, but it seems like a net win:
164+
165+
![images/heatmap-64byte.svg](Recovery success with 64 byte slices)
166+
167+
If we make the buckets *too* large (eg. 256 bytes), we lose as expected,
168+
as most space is wasted:
169+
170+
![images/heatmap-256byte.svg](Recovery success with 256 byte slices)
171+
172+
### Smaller IBLTs ###
173+
174+
As expected it's not linear, but it's close enough: with 102k, we can
175+
recover 30-35 transactions, and with 2M it's about 500-520 transactions.
176+
177+
Here's the heatmap with 30k (and 64-byte slices); this will almost
178+
certainly enough for the immediate future:
179+
180+
![images/heatmap-64-byte-30k.svg](Recovery success with 64 byte slices, 30k map)
181+
182+
### A Byte of HashSum ###
183+
184+
This is suggested in the original paper to provide more resilience
185+
against false positives, but I only saw this once, even with the very
186+
limited checking my program does that a transaction is well-formed.
187+
(I found it because my code initially didn't handle it, and got stuck
188+
on one run).
189+
190+
Once we check the 48-bit id field, which is based on a cryptographic
191+
hash of the transaction, we're already extremely robust without an
192+
additional hashSum.
193+
194+
## Results and Suggestions ##
195+
196+
8 bytes per slice is too small, larger sizes should be explored.
197+
198+
1M is vast overkill for the current network; as well as making block
199+
propagation *slower* than the current scheme, it's unnecessary for
200+
now. I suggest a variable scheme.
201+
202+
Nodes should probably include discarded doublespend transactions in
203+
their IBLT since it's cheaper to include a transaction for
204+
consideration than to extract one which we didn't consider.
205+
206+
## Flaws and Future Work ##
207+
208+
Obviously my code could use optimization. Also, my selection of
209+
transactions was "the first N", which means it's of limited accuracy.
210+
For example, transaction 613 is a huge 50k transaction, so if it's
211+
marginal the failure rate jumps there. A more serious analysis would
212+
try to create standard profiles of transactions.
213+
214+
[Feedback](mailto:rusty@rustcorp.com.au) welcome, as always.

‎images/heatmap-256byte.svg

+550
Loading

‎images/heatmap-64-byte-30k.svg

+528
Loading

‎images/heatmap-64byte.svg

+550
Loading

‎images/heatmap-8byte.svg

+550
Loading

‎images/recovery-stats-simple.svg

+267
Loading

‎images/removal-stats-simple.svg

+261
Loading

0 commit comments

Comments
 (0)
Please sign in to comment.