rough kbucket implementation, tests and cleanup to follow
Jeromy committed
Aug 3, 2014 at 13:37 UTC
35a4086e066b2602f9c8d3ae28e6ec957e606242
3 files changed
+170
-8
peer/peer.go
+2
-2
@@ -12,8 +12,8 @@ import (
12
type ID mh.Multihash
13
14
// Utililty function for comparing two peer ID's
15
-func (id *ID) Equal(other *ID) bool {
16
- return bytes.Equal(*id, *other)
15
+func (id ID) Equal(other ID) bool {
16
+ return bytes.Equal(id, other)
17
}
18
19
// Map maps Key (string) : *Peer (slices are not comparable).
routing/dht/routing.go
+2
-2
@@ -21,7 +21,7 @@ func GenerateMessageID() uint64 {
21
// PutValue adds value corresponding to given Key.
22
func (s *IpfsDHT) PutValue(key u.Key, value []byte) error {
23
var p *peer.Peer
24
- p = s.routes.NearestNode(key)
24
+ p = s.routes.NearestPeer(convertKey(key))
25
26
pmes_type := DHTMessage_PUT_VALUE
27
str_key := string(key)
@@ -44,7 +44,7 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error {
44
// GetValue searches for the value corresponding to given Key.
45
func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) {
46
var p *peer.Peer
47
- p = s.routes.NearestNode(key)
47
+ p = s.routes.NearestPeer(convertKey(key))
48
49
str_key := string(key)
50
mes_type := DHTMessage_GET_VALUE
routing/dht/table.go
+166
-4
@@ -3,6 +3,9 @@ package dht
3
import (
4
"bytes"
5
"container/list"
6
+ "sort"
7
+
8
+ "crypto/sha256"
9
10
peer "github.com/jbenet/go-ipfs/peer"
11
u "github.com/jbenet/go-ipfs/util"
@@ -16,18 +19,177 @@ import (
19
type ID []byte
20
21
// Bucket holds a list of peers.
19
-type Bucket []*list.List
22
+type Bucket list.List
23
+
24
+func (b *Bucket) Find(id peer.ID) *list.Element {
25
+ bucket_list := (*list.List)(b)
26
+ for e := bucket_list.Front(); e != nil; e = e.Next() {
27
+ if e.Value.(*peer.Peer).ID.Equal(id) {
28
+ return e
29
+ }
30
+ }
31
+ return nil
32
+}
33
+
34
+func (b *Bucket) MoveToFront(e *list.Element) {
35
+ bucket_list := (*list.List)(b)
36
+ bucket_list.MoveToFront(e)
37
+}
38
+
39
+func (b *Bucket) PushFront(p *peer.Peer) {
40
+ bucket_list := (*list.List)(b)
41
+ bucket_list.PushFront(p)
42
+}
43
+
44
+func (b *Bucket) PopBack() *peer.Peer {
45
+ bucket_list := (*list.List)(b)
46
+ last := bucket_list.Back()
47
+ bucket_list.Remove(last)
48
+ return last.Value.(*peer.Peer)
49
+}
50
+
51
+func (b *Bucket) Len() int {
52
+ bucket_list := (*list.List)(b)
53
+ return bucket_list.Len()
54
+}
55
+
56
+func (b *Bucket) Split(cpl int, target ID) *Bucket {
57
+ bucket_list := (*list.List)(b)
58
+ out := list.New()
59
+ e := bucket_list.Front()
60
+ for e != nil {
61
+ peer_id := convertPeerID(e.Value.(*peer.Peer).ID)
62
+ peer_cpl := xor(peer_id, target).commonPrefixLen()
63
+ if peer_cpl > cpl {
64
+ cur := e
65
+ out.PushBack(e.Value)
66
+ e = e.Next()
67
+ bucket_list.Remove(cur)
68
+ continue
69
+ }
70
+ }
71
+ return (*Bucket)(out)
72
+}
73
74
// RoutingTable defines the routing table.
75
type RoutingTable struct {
76
77
+ // ID of the local peer
78
+ local ID
79
+
80
// kBuckets define all the fingers to other nodes.
25
- Buckets []Bucket
81
+ Buckets []*Bucket
82
+ bucketsize int
83
+}
84
+
85
+func convertPeerID(id peer.ID) ID {
86
+ hash := sha256.Sum256(id)
87
+ return hash[:]
88
+}
89
+
90
+func convertKey(id u.Key) ID {
91
+ hash := sha256.Sum256([]byte(id))
92
+ return hash[:]
93
+}
94
+
95
+// Update adds or moves the given peer to the front of its respective bucket
96
+// If a peer gets removed from a bucket, it is returned
97
+func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer {
98
+ peer_id := convertPeerID(p.ID)
99
+ cpl := xor(peer_id, rt.local).commonPrefixLen()
100
+
101
+ b_id := cpl
102
+ if b_id >= len(rt.Buckets) {
103
+ b_id = len(rt.Buckets) - 1
104
+ }
105
+
106
+ bucket := rt.Buckets[b_id]
107
+ e := bucket.Find(p.ID)
108
+ if e == nil {
109
+ // New peer, add to bucket
110
+ bucket.PushFront(p)
111
+
112
+ // Are we past the max bucket size?
113
+ if bucket.Len() > rt.bucketsize {
114
+ if b_id == len(rt.Buckets) - 1 {
115
+ new_bucket := bucket.Split(b_id, rt.local)
116
+ rt.Buckets = append(rt.Buckets, new_bucket)
117
+
118
+ // If all elements were on left side of split...
119
+ if bucket.Len() > rt.bucketsize {
120
+ return bucket.PopBack()
121
+ }
122
+ } else {
123
+ // If the bucket cant split kick out least active node
124
+ return bucket.PopBack()
125
+ }
126
+ }
127
+ return nil
128
+ } else {
129
+ // If the peer is already in the table, move it to the front.
130
+ // This signifies that it it "more active" and the less active nodes
131
+ // Will as a result tend towards the back of the list
132
+ bucket.MoveToFront(e)
133
+ return nil
134
+ }
135
+}
136
+
137
+// A helper struct to sort peers by their distance to the local node
138
+type peerDistance struct {
139
+ p *peer.Peer
140
+ distance ID
141
+}
142
+type peerSorterArr []*peerDistance
143
+func (p peerSorterArr) Len() int {return len(p)}
144
+func (p peerSorterArr) Swap(a, b int) {p[a],p[b] = p[b],p[a]}
145
+func (p peerSorterArr) Less(a, b int) bool {
146
+ return p[a].distance.Less(p[b])
147
+}
148
+//
149
+
150
+func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer {
151
+ peers := rt.NearestPeers(id, 1)
152
+ return peers[0]
153
}
154
155
//TODO: make this accept an ID, requires method of converting keys to IDs
29
-func (rt *RoutingTable) NearestNode(key u.Key) *peer.Peer {
30
- panic("Function not implemented.")
156
+func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer {
157
+ cpl := xor(id, rt.local).commonPrefixLen()
158
+
159
+ // Get bucket at cpl index or last bucket
160
+ var bucket *Bucket
161
+ if cpl >= len(rt.Buckets) {
162
+ bucket = rt.Buckets[len(rt.Buckets) - 1]
163
+ } else {
164
+ bucket = rt.Buckets[cpl]
165
+ }
166
+
167
+ if bucket.Len() == 0 {
168
+ // This can happen, very rarely.
169
+ panic("Case not yet implemented.")
170
+ }
171
+
172
+ var peerArr peerSorterArr
173
+
174
+ plist := (*list.List)(bucket)
175
+ for e := plist.Front();e != nil; e = e.Next() {
176
+ p := e.Value.(*peer.Peer)
177
+ p_id := convertPeerID(p.ID)
178
+ pd := peerDistance{
179
+ p: p,
180
+ distance: xor(rt.local, p_id),
181
+ }
182
+ peerArr = append(peerArr, &pd)
183
+ }
184
+
185
+ sort.Sort(peerArr)
186
+
187
+ var out []*peer.Peer
188
+ for i := 0; i < count && i < peerArr.Len(); i++ {
189
+ out = append(out, peerArr[i].p)
190
+ }
191
+
192
+ return out
193
}
194
195
func (id ID) Equal(other ID) bool {