Skip to content

Commit 3b07290

Browse files
Merge pull request BurntSushi#1 from geoah/master
Split `go-ipfs/routing/kbucket` into `go-libp2p-kbucket`
2 parents 1f74119 + e8919b2 commit 3b07290

14 files changed

+1036
-7
lines changed

.gx/lastpubver

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.0.0: QmbS7NcH4KqZL71ZwzVZAt1CpVpAtUBaHXuDenjnXF78Wa

.travis.yml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
os:
2+
- linux
3+
- osx
4+
5+
language: go
6+
7+
go:
8+
- 1.7
9+
10+
env:
11+
- GO15VENDOREXPERIMENT=1
12+
13+
install: true
14+
15+
script:
16+
- make deps
17+
- go test ./...
18+
19+
cache:
20+
directories:
21+
- $GOPATH/src/gx
22+
23+
notifications:
24+
email: false

LICENSE

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
MIT License
1+
The MIT License (MIT)
22

3-
Copyright (c) 2016 libp2p
3+
Copyright (c) 2016 Protocol Labs
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99
copies of the Software, and to permit persons to whom the Software is
1010
furnished to do so, subject to the following conditions:
1111

12-
The above copyright notice and this permission notice shall be included in all
13-
copies or substantial portions of the Software.
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
1414

1515
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21-
SOFTWARE.
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

Makefile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export IPFS_API ?= v04x.ipfs.io
2+
3+
gx:
4+
go get -u github.com/whyrusleeping/gx
5+
go get -u github.com/whyrusleeping/gx-go
6+
7+
deps: gx
8+
gx --verbose install --global
9+
gx-go rewrite

README.md

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,26 @@
11
# go-libp2p-kbucket
2-
A kbucket implementation for use as a routing table
2+
3+
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
4+
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
5+
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
6+
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
7+
8+
> A kbucket implementation for use as a routing table
9+
10+
## Documenation
11+
12+
See https://godoc.org/github.com/libp2p/go-libp2p-kbucket.
13+
14+
## Contribute
15+
16+
Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/go-key/issues)!
17+
18+
This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
19+
20+
### Want to hack on IPFS?
21+
22+
[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md)
23+
24+
## License
25+
26+
MIT

bucket.go

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package kbucket
2+
3+
import (
4+
"container/list"
5+
"sync"
6+
7+
peer "github.com/ipfs/go-libp2p-peer"
8+
)
9+
10+
// Bucket holds a list of peers.
11+
type Bucket struct {
12+
lk sync.RWMutex
13+
list *list.List
14+
}
15+
16+
func newBucket() *Bucket {
17+
b := new(Bucket)
18+
b.list = list.New()
19+
return b
20+
}
21+
22+
func (b *Bucket) Peers() []peer.ID {
23+
b.lk.RLock()
24+
defer b.lk.RUnlock()
25+
ps := make([]peer.ID, 0, b.list.Len())
26+
for e := b.list.Front(); e != nil; e = e.Next() {
27+
id := e.Value.(peer.ID)
28+
ps = append(ps, id)
29+
}
30+
return ps
31+
}
32+
33+
func (b *Bucket) Has(id peer.ID) bool {
34+
b.lk.RLock()
35+
defer b.lk.RUnlock()
36+
for e := b.list.Front(); e != nil; e = e.Next() {
37+
if e.Value.(peer.ID) == id {
38+
return true
39+
}
40+
}
41+
return false
42+
}
43+
44+
func (b *Bucket) Remove(id peer.ID) {
45+
b.lk.Lock()
46+
defer b.lk.Unlock()
47+
for e := b.list.Front(); e != nil; e = e.Next() {
48+
if e.Value.(peer.ID) == id {
49+
b.list.Remove(e)
50+
}
51+
}
52+
}
53+
54+
func (b *Bucket) MoveToFront(id peer.ID) {
55+
b.lk.Lock()
56+
defer b.lk.Unlock()
57+
for e := b.list.Front(); e != nil; e = e.Next() {
58+
if e.Value.(peer.ID) == id {
59+
b.list.MoveToFront(e)
60+
}
61+
}
62+
}
63+
64+
func (b *Bucket) PushFront(p peer.ID) {
65+
b.lk.Lock()
66+
b.list.PushFront(p)
67+
b.lk.Unlock()
68+
}
69+
70+
func (b *Bucket) PopBack() peer.ID {
71+
b.lk.Lock()
72+
defer b.lk.Unlock()
73+
last := b.list.Back()
74+
b.list.Remove(last)
75+
return last.Value.(peer.ID)
76+
}
77+
78+
func (b *Bucket) Len() int {
79+
b.lk.RLock()
80+
defer b.lk.RUnlock()
81+
return b.list.Len()
82+
}
83+
84+
// Split splits a buckets peers into two buckets, the methods receiver will have
85+
// peers with CPL equal to cpl, the returned bucket will have peers with CPL
86+
// greater than cpl (returned bucket has closer peers)
87+
func (b *Bucket) Split(cpl int, target ID) *Bucket {
88+
b.lk.Lock()
89+
defer b.lk.Unlock()
90+
91+
out := list.New()
92+
newbuck := newBucket()
93+
newbuck.list = out
94+
e := b.list.Front()
95+
for e != nil {
96+
peerID := ConvertPeerID(e.Value.(peer.ID))
97+
peerCPL := commonPrefixLen(peerID, target)
98+
if peerCPL > cpl {
99+
cur := e
100+
out.PushBack(e.Value)
101+
e = e.Next()
102+
b.list.Remove(cur)
103+
continue
104+
}
105+
e = e.Next()
106+
}
107+
return newbuck
108+
}

keyspace/keyspace.go

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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 Bytes 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+
// Bytes is the new value of the identifier, in the KeySpace.
21+
Bytes []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+
toSortCopy := make([]Key, len(toSort))
90+
copy(toSortCopy, toSort)
91+
bdtc := &byDistanceToCenter{
92+
Center: center,
93+
Keys: toSortCopy, // copy
94+
}
95+
sort.Sort(bdtc)
96+
return bdtc.Keys
97+
}

keyspace/xor.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package keyspace
2+
3+
import (
4+
"bytes"
5+
"crypto/sha256"
6+
"math/big"
7+
8+
u "github.com/ipfs/go-ipfs-util"
9+
)
10+
11+
// XORKeySpace is a KeySpace which:
12+
// - normalizes identifiers using a cryptographic hash (sha256)
13+
// - measures distance by XORing keys together
14+
var XORKeySpace = &xorKeySpace{}
15+
var _ KeySpace = XORKeySpace // ensure it conforms
16+
17+
type xorKeySpace struct{}
18+
19+
// Key converts an identifier into a Key in this space.
20+
func (s *xorKeySpace) Key(id []byte) Key {
21+
hash := sha256.Sum256(id)
22+
key := hash[:]
23+
return Key{
24+
Space: s,
25+
Original: id,
26+
Bytes: key,
27+
}
28+
}
29+
30+
// Equal returns whether keys are equal in this key space
31+
func (s *xorKeySpace) Equal(k1, k2 Key) bool {
32+
return bytes.Equal(k1.Bytes, k2.Bytes)
33+
}
34+
35+
// Distance returns the distance metric in this key space
36+
func (s *xorKeySpace) Distance(k1, k2 Key) *big.Int {
37+
// XOR the keys
38+
k3 := u.XOR(k1.Bytes, k2.Bytes)
39+
40+
// interpret it as an integer
41+
dist := big.NewInt(0).SetBytes(k3)
42+
return dist
43+
}
44+
45+
// Less returns whether the first key is smaller than the second.
46+
func (s *xorKeySpace) Less(k1, k2 Key) bool {
47+
a := k1.Bytes
48+
b := k2.Bytes
49+
for i := 0; i < len(a); i++ {
50+
if a[i] != b[i] {
51+
return a[i] < b[i]
52+
}
53+
}
54+
return true
55+
}
56+
57+
// ZeroPrefixLen returns the number of consecutive zeroes in a byte slice.
58+
func ZeroPrefixLen(id []byte) int {
59+
for i := 0; i < len(id); i++ {
60+
for j := 0; j < 8; j++ {
61+
if (id[i]>>uint8(7-j))&0x1 != 0 {
62+
return i*8 + j
63+
}
64+
}
65+
}
66+
return len(id) * 8
67+
}

0 commit comments

Comments
 (0)