forked from ElementsProject/lightning
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ccan: update (for htable_getfirst/getnext)
Signed-off-by: Rusty Russell <[email protected]>
- Loading branch information
1 parent
7efc0ef
commit ab09a42
Showing
22 changed files
with
447 additions
and
235 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
CCAN imported from http://ccodearchive.net. | ||
|
||
CCAN version: init-2136-g64e9e71 | ||
CCAN version: init-2181-g2953f51 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,99 +16,107 @@ A simple system is a hash chain: we select a random seed value, the | |
hash it 1,000,000 times. This gives the first "random" number. | ||
Hashed 999,999 times gives the second number, etc. ie: | ||
|
||
R(1,000,000) = seed | ||
R(N-1) = SHA256(R(N)) | ||
R(0) = seed | ||
R(N+1) = SHA256(R(N)) | ||
|
||
This way the remote node needs only to remember the last R(N) it was | ||
given, and it can calculate any R for N-1 or below. | ||
given, and it can calculate any R for N+1 or above. | ||
|
||
However, this means we need to generate 1 million hashes up front, and | ||
then do almost as many hashes to derive the next number. That's slow. | ||
|
||
A More Complex Solution | ||
----------------------- | ||
|
||
Instead of a one-dimensional chain, we can use two dimensions: 1000 | ||
chains of 1000 values each. Indeed, we can set generate the "top" of | ||
each chain much like we generated a single chain: | ||
|
||
Chain 1000 Chain 999 Chain 998 ...........Chain 1 | ||
seed SHA256(C1000) SHA256(C999) ....... SHA256(C2) | ||
|
||
Now, deriving chain 1000 from seed doesn't quite work, because it'll | ||
look like this chain, so we flip the lower bit to generate the chain: | ||
|
||
Chain 1000 Chain 999 Chain 998 ...........Chain 1 | ||
1000 seed^1 SHA256(C1000)^1 SHA256(C999)^1...... SHA256(C2)^1 | ||
999 SHA256(above) SHA256(above) SHA256(above) ..... SHA256(above) | ||
998 SHA256(above) SHA256(above) SHA256(above) ..... SHA256(above) | ||
... | ||
|
||
Now, we can get the first value to give out (chain 1, position 1) with | ||
999 hashes to get to chain 1, and 999 hashes to get to the end of the | ||
chain. 2000 hashes is much better than the 999,999 hashes it would | ||
have taken previously. | ||
|
||
Why Stop at 2 Dimensions? | ||
------------------------- | ||
|
||
Indeed, the implementation uses 64 dimensions rather than 2, and a | ||
chain length of 2 rather than 1000, giving a worst-case of 63 hashes | ||
to derive any of 2^64 values. Each dimension flips a different bit of | ||
the hash, to ensure the chains are distinct. | ||
|
||
For simplicity, I'll explain what this looks like using 8 dimensions, | ||
ie. 8 bits. The seed value always sits the maximum possible index, in | ||
this case index 0xFF (b11111111). | ||
|
||
To generate the hash for 0xFE (b11111110), we need to move down | ||
dimension 0, so we flip bit 0 of the seed value, then hash it. To | ||
generate the hash for 0xFD (b11111101) we need to move down dimension | ||
1, so we flip bit 1 of the seed value, then hash it. | ||
|
||
To reach 0xFC (b11111100) we need to move down dimension 1 then | ||
dimension 0, in that order. | ||
|
||
Spotting the pattern, it becomes easy to derive how to reach any value: | ||
|
||
hash = seed | ||
for bit in 7 6 5 4 3 2 1 0: | ||
if bit not set in index: | ||
flip(bit) in hash | ||
hash = SHA256(hash) | ||
|
||
Handling Partial Knowledge | ||
-------------------------- | ||
|
||
How does the remote node, which doesn't know the seed value, derive | ||
subvalues? | ||
|
||
Once it knows the value for index 1, it can derive the value for index | ||
0 by flipping bit 0 of the value and hashing it. In effect, it can | ||
always derive a value for any index where it only needs to clear bits. | ||
|
||
So, index 1 gives index 0, but index 2 doesn't yield index 1. When | ||
index 3 comes along, it yields 2, 1, and 0. | ||
|
||
How many hash values will we have to remember at once? The answer is | ||
equal to the number of dimensions. It turns out that the worst case | ||
for 8 dimensions is 254 (0b11111110), for which we will have to | ||
remember the following indices: | ||
|
||
127 0b01111111 | ||
191 0b10111111 | ||
223 0b11011111 | ||
239 0b11101111 | ||
247 0b11110111 | ||
251 0b11111011 | ||
253 0b11111101 | ||
254 0b11111110 | ||
|
||
127 lets us derive any hash value for index <= 127. Similarly, 191 | ||
lets us derive anything > 127 but <= 191. 254 lets us derive only | ||
itself. | ||
|
||
When we get index 255 this collapses, and we only need to remember | ||
that one index to derive everything. | ||
A Tree Solution | ||
--------------- | ||
|
||
A better solution is to use a binary tree, with the seed at the root. | ||
The left child is the same as the parent, the right child is the | ||
SHA256() of the parent with one bit flipped (corresponding to the | ||
height). | ||
|
||
This gives a tree like so: | ||
|
||
seed | ||
/ \ | ||
/ \ | ||
/ \ | ||
/ \ | ||
seed SHA256(seed^1) | ||
/ \ / \ | ||
seed SHA256(seed^2) SHA256(seed^1) SHA256(SHA256(seed^1)^2) | ||
Index: 0 1 2 3 | ||
|
||
Clearly, giving R(2) allows you to derive R(3), giving R(1) allows you | ||
to derive nothing new (you still have to remember R(2)), and giving | ||
R(0) allows you to derive everything. | ||
|
||
In pseudocode, this looks like the following for a 64 bit tree: | ||
|
||
generate_from_seed(index): | ||
value = seed | ||
for bit in 0 to 63: | ||
if bit set in index: | ||
flip(bit) in value | ||
value = SHA256(value) | ||
return value | ||
|
||
|
||
The Receiver's Tree | ||
------------------- | ||
|
||
To derive the value for a index N, you need to have the root of a tree | ||
which contains it. That is the same as needing an index I which is N | ||
rounded down in binary: eg. if N is 0b001100001, you need 0b001100000, | ||
0b001000000 or 0b000000000. | ||
|
||
Pseudocode: | ||
|
||
# Can we derive the value for to_index from from_index? | ||
can_derive(from_index, to_index): | ||
# to_index must be a subtree under from_index; this is the same as | ||
# saying that to_index must be the same as from_index up to the | ||
# trailing zeros in from_index. | ||
for bit in count_trailing_zeroes(from_index)..63: | ||
if bit set in from_index != bit set in to_index: | ||
return false | ||
return true | ||
|
||
# Derive a value from a lesser index: generalization of generate_from_seed() | ||
derive(from_index, to_index, from_value): | ||
assert(can_derive(from_index, to_index)) | ||
value = from_value | ||
for bit in 0..63: | ||
if bit set in to_index and not bit set in from_index: | ||
flip bit in value | ||
value = SHA256(value) | ||
return value | ||
|
||
If you are receiving values (in reverse order), you need to remember | ||
up to 64 of them to derive all previous values. The simplest method | ||
is to keep an array, indexed by the number of trailing zeroes in the | ||
received index: | ||
|
||
# Receive a new value (assumes we receive them in order) | ||
receive_value(index, value): | ||
pos = count_trailing_zeroes(index) | ||
# We should be able to generate every lesser value, otherwise invalid | ||
for i in 0..pos-1: | ||
if derive(index, value, known[i].index) != known[i].value: | ||
return false | ||
known[pos].index = index | ||
known[pos].value = value | ||
return true | ||
|
||
To derive a previous value, find an element in that array from which | ||
you can derive the value you want, eg: | ||
|
||
# Find an old value | ||
regenerate_value(index): | ||
for i in known: | ||
if can_derive(i.index, index): | ||
return derive(i.index, i.value, index) | ||
fail | ||
|
||
You can see the implementation for more optimized variants of the | ||
above code. | ||
|
||
Rusty Russell <[email protected]> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.