@cryptotaxi247 / kubo / commits / b838cc061

cleaner KeySpace abstraction.

Juan Batiz-Benet committed Sep 17, 2014 at 01:39 UTC b838cc061902f8298a947687a59f307e61f331a7
3 files changed +308
routing/dht/keyspace/keyspace.go new
+95
@@ -0,0 +1,95 @@
1 +package keyspace
2 +
3 +import (
4 + "sort"
5 +
6 + "math/big"
7 +)
8 +
9 +// Key represents an identifier in a KeySpace. It holds a reference to the
10 +// associated KeySpace, as well references to both the Original identifier,
11 +// as well as the new, KeySpace Adjusted one.
12 +type Key struct {
13 +
14 + // Space is the KeySpace this Key is related to.
15 + Space KeySpace
16 +
17 + // Original is the original value of the identifier
18 + Original []byte
19 +
20 + // Adjusted is the new value of the identifier, in the KeySpace.
21 + Adjusted []byte
22 +}
23 +
24 +// Equal returns whether this key is equal to another.
25 +func (k1 Key) Equal(k2 Key) bool {
26 + if k1.Space != k2.Space {
27 + panic("k1 and k2 not in same key space.")
28 + }
29 + return k1.Space.Equal(k1, k2)
30 +}
31 +
32 +// Less returns whether this key comes before another.
33 +func (k1 Key) Less(k2 Key) bool {
34 + if k1.Space != k2.Space {
35 + panic("k1 and k2 not in same key space.")
36 + }
37 + return k1.Space.Less(k1, k2)
38 +}
39 +
40 +// Distance returns this key's distance to another
41 +func (k1 Key) Distance(k2 Key) *big.Int {
42 + if k1.Space != k2.Space {
43 + panic("k1 and k2 not in same key space.")
44 + }
45 + return k1.Space.Distance(k1, k2)
46 +}
47 +
48 +// KeySpace is an object used to do math on identifiers. Each keyspace has its
49 +// own properties and rules. See XorKeySpace.
50 +type KeySpace interface {
51 +
52 + // Key converts an identifier into a Key in this space.
53 + Key([]byte) Key
54 +
55 + // Equal returns whether keys are equal in this key space
56 + Equal(Key, Key) bool
57 +
58 + // Distance returns the distance metric in this key space
59 + Distance(Key, Key) *big.Int
60 +
61 + // Less returns whether the first key is smaller than the second.
62 + Less(Key, Key) bool
63 +}
64 +
65 +// byDistanceToCenter is a type used to sort Keys by proximity to a center.
66 +type byDistanceToCenter struct {
67 + Center Key
68 + Keys []Key
69 +}
70 +
71 +func (s byDistanceToCenter) Len() int {
72 + return len(s.Keys)
73 +}
74 +
75 +func (s byDistanceToCenter) Swap(i, j int) {
76 + s.Keys[i], s.Keys[j] = s.Keys[j], s.Keys[i]
77 +}
78 +
79 +func (s byDistanceToCenter) Less(i, j int) bool {
80 + a := s.Center.Distance(s.Keys[i])
81 + b := s.Center.Distance(s.Keys[j])
82 + return a.Cmp(b) == -1
83 +}
84 +
85 +// SortByDistance takes a KeySpace, a center Key, and a list of Keys toSort.
86 +// It returns a new list, where the Keys toSort have been sorted by their
87 +// distance to the center Key.
88 +func SortByDistance(sp KeySpace, center Key, toSort []Key) []Key {
89 + bdtc := &byDistanceToCenter{
90 + Center: center,
91 + Keys: toSort[:], // copy
92 + }
93 + sort.Sort(bdtc)
94 + return bdtc.Keys
95 +}
routing/dht/keyspace/xor.go new
+74
@@ -0,0 +1,74 @@
1 +package keyspace
2 +
3 +import (
4 + "bytes"
5 + "crypto/sha256"
6 + "math/big"
7 +)
8 +
9 +// XORKeySpace is a KeySpace which:
10 +// - normalizes identifiers using a cryptographic hash (sha256)
11 +// - measures distance by XORing keys together
12 +var XORKeySpace = &xorKeySpace{}
13 +var _ KeySpace = XORKeySpace // ensure it conforms
14 +
15 +type xorKeySpace struct{}
16 +
17 +// Key converts an identifier into a Key in this space.
18 +func (s *xorKeySpace) Key(id []byte) Key {
19 + hash := sha256.Sum256(id)
20 + key := hash[:]
21 + return Key{
22 + Space: s,
23 + Original: id,
24 + Adjusted: key,
25 + }
26 +}
27 +
28 +// Equal returns whether keys are equal in this key space
29 +func (s *xorKeySpace) Equal(k1, k2 Key) bool {
30 + return bytes.Equal(k1.Adjusted, k2.Adjusted)
31 +}
32 +
33 +// Distance returns the distance metric in this key space
34 +func (s *xorKeySpace) Distance(k1, k2 Key) *big.Int {
35 + // XOR the keys
36 + k3 := XOR(k1.Adjusted, k2.Adjusted)
37 +
38 + // interpret it as an integer
39 + dist := big.NewInt(0).SetBytes(k3)
40 + return dist
41 +}
42 +
43 +// Less returns whether the first key is smaller than the second.
44 +func (s *xorKeySpace) Less(k1, k2 Key) bool {
45 + a := k1.Adjusted
46 + b := k2.Adjusted
47 + for i := 0; i < len(a); i++ {
48 + if a[i] != b[i] {
49 + return a[i] < b[i]
50 + }
51 + }
52 + return true
53 +}
54 +
55 +// XOR takes two byte slices, XORs them together, returns the resulting slice.
56 +func XOR(a, b []byte) []byte {
57 + c := make([]byte, len(a))
58 + for i := 0; i < len(a); i++ {
59 + c[i] = a[i] ^ b[i]
60 + }
61 + return c
62 +}
63 +
64 +// ZeroPrefixLen returns the number of consecutive zeroes in a byte slice.
65 +func ZeroPrefixLen(id []byte) int {
66 + for i := 0; i < len(id); i++ {
67 + for j := 0; j < 8; j++ {
68 + if (id[i]>>uint8(7-j))&0x1 != 0 {
69 + return i*8 + j
70 + }
71 + }
72 + }
73 + return len(id) * 8
74 +}
routing/dht/keyspace/xor_test.go new
+139
@@ -0,0 +1,139 @@
1 +package keyspace
2 +
3 +import (
4 + "bytes"
5 + "math/big"
6 + "testing"
7 +)
8 +
9 +func TestXOR(t *testing.T) {
10 + cases := [][3][]byte{
11 + [3][]byte{
12 + []byte{0xFF, 0xFF, 0xFF},
13 + []byte{0xFF, 0xFF, 0xFF},
14 + []byte{0x00, 0x00, 0x00},
15 + },
16 + [3][]byte{
17 + []byte{0x00, 0xFF, 0x00},
18 + []byte{0xFF, 0xFF, 0xFF},
19 + []byte{0xFF, 0x00, 0xFF},
20 + },
21 + [3][]byte{
22 + []byte{0x55, 0x55, 0x55},
23 + []byte{0x55, 0xFF, 0xAA},
24 + []byte{0x00, 0xAA, 0xFF},
25 + },
26 + }
27 +
28 + for _, c := range cases {
29 + r := XOR(c[0], c[1])
30 + if !bytes.Equal(r, c[2]) {
31 + t.Error("XOR failed")
32 + }
33 + }
34 +}
35 +
36 +func TestPrefixLen(t *testing.T) {
37 + cases := [][]byte{
38 + []byte{0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00},
39 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
40 + []byte{0x00, 0x58, 0xFF, 0x80, 0x00, 0x00, 0xF0},
41 + }
42 + lens := []int{24, 56, 9}
43 +
44 + for i, c := range cases {
45 + r := ZeroPrefixLen(c)
46 + if r != lens[i] {
47 + t.Errorf("ZeroPrefixLen failed: %v != %v", r, lens[i])
48 + }
49 + }
50 +
51 +}
52 +
53 +func TestXorKeySpace(t *testing.T) {
54 +
55 + ids := [][]byte{
56 + []byte{0xFF, 0xFF, 0xFF, 0xFF},
57 + []byte{0x00, 0x00, 0x00, 0x00},
58 + []byte{0xFF, 0xFF, 0xFF, 0xF0},
59 + }
60 +
61 + ks := [][2]Key{
62 + [2]Key{XORKeySpace.Key(ids[0]), XORKeySpace.Key(ids[0])},
63 + [2]Key{XORKeySpace.Key(ids[1]), XORKeySpace.Key(ids[1])},
64 + [2]Key{XORKeySpace.Key(ids[2]), XORKeySpace.Key(ids[2])},
65 + }
66 +
67 + for i, set := range ks {
68 + if !set[0].Equal(set[1]) {
69 + t.Errorf("Key not eq. %v != %v", set[0], set[1])
70 + }
71 +
72 + if !bytes.Equal(set[0].Adjusted, set[1].Adjusted) {
73 + t.Errorf("Key gen failed. %v != %v", set[0].Adjusted, set[1].Adjusted)
74 + }
75 +
76 + if !bytes.Equal(set[0].Original, ids[i]) {
77 + t.Errorf("ptrs to original. %v != %v", set[0].Original, ids[i])
78 + }
79 +
80 + if len(set[0].Adjusted) != 32 {
81 + t.Errorf("key length incorrect. 32 != %d", len(set[0].Adjusted))
82 + }
83 + }
84 +
85 + for i := 1; i < len(ks); i++ {
86 + if ks[i][0].Less(ks[i-1][0]) == ks[i-1][0].Less(ks[i][0]) {
87 + t.Errorf("less should be different.")
88 + }
89 +
90 + if ks[i][0].Distance(ks[i-1][0]).Cmp(ks[i-1][0].Distance(ks[i][0])) != 0 {
91 + t.Errorf("distance should be the same.")
92 + }
93 +
94 + if ks[i][0].Equal(ks[i-1][0]) {
95 + t.Errorf("Keys should not be eq. %v != %v", ks[i][0], ks[i-1][0])
96 + }
97 + }
98 +}
99 +
100 +func TestCenterSorting(t *testing.T) {
101 +
102 + adjs := [][]byte{
103 + []byte{173, 149, 19, 27, 192, 183, 153, 192, 177, 175, 71, 127, 177, 79, 207, 38, 166, 169, 247, 96, 121, 228, 139, 240, 144, 172, 183, 232, 54, 123, 253, 14},
104 + []byte{223, 63, 97, 152, 4, 169, 47, 219, 64, 87, 25, 45, 196, 61, 215, 72, 234, 119, 138, 220, 82, 188, 73, 140, 232, 5, 36, 192, 20, 184, 17, 25},
105 + []byte{73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127},
106 + []byte{73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127},
107 + []byte{73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 126},
108 + []byte{73, 0, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127},
109 + }
110 +
111 + keys := make([]Key, len(adjs))
112 + for i, a := range adjs {
113 + keys[i] = Key{Space: XORKeySpace, Adjusted: a}
114 + }
115 +
116 + cmp := func(a int, b *big.Int) int {
117 + return big.NewInt(int64(a)).Cmp(b)
118 + }
119 +
120 + if 0 != cmp(0, keys[2].Distance(keys[3])) {
121 + t.Errorf("distance calculation wrong: %v", keys[2].Distance(keys[3]))
122 + }
123 +
124 + if 0 != cmp(1, keys[2].Distance(keys[4])) {
125 + t.Errorf("distance calculation wrong: %v", keys[2].Distance(keys[4]))
126 + }
127 +
128 + d1 := keys[2].Distance(keys[5])
129 + d2 := XOR(keys[2].Adjusted, keys[5].Adjusted)
130 + d2 = d2[len(keys[2].Adjusted)-len(d1.Bytes()):] // skip empty space for big
131 + if !bytes.Equal(d1.Bytes(), d2) {
132 + t.Errorf("bytes should be the same. %v == %v", d1.Bytes(), d2)
133 + }
134 +
135 + if -1 != cmp(2<<32, keys[2].Distance(keys[5])) {
136 + t.Errorf("2<<32 should be smaller")
137 + }
138 +
139 +}