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
+}