Skip to content

Commit

Permalink
Merge pull request #1 from geoah/master
Browse files Browse the repository at this point in the history
Split `go-ipfs/routing/kbucket` into `go-libp2p-kbucket`
  • Loading branch information
whyrusleeping committed Aug 31, 2016
2 parents 1f74119 + e8919b2 commit 3b07290
Show file tree
Hide file tree
Showing 14 changed files with 1,036 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gx/lastpubver
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.0.0: QmbS7NcH4KqZL71ZwzVZAt1CpVpAtUBaHXuDenjnXF78Wa
24 changes: 24 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
os:
- linux
- osx

language: go

go:
- 1.7

env:
- GO15VENDOREXPERIMENT=1

install: true

script:
- make deps
- go test ./...

cache:
directories:
- $GOPATH/src/gx

notifications:
email: false
12 changes: 6 additions & 6 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License
The MIT License (MIT)

Copyright (c) 2016 libp2p
Copyright (c) 2016 Protocol Labs

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

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

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export IPFS_API ?= v04x.ipfs.io

gx:
go get -u github.com/whyrusleeping/gx
go get -u github.com/whyrusleeping/gx-go

deps: gx
gx --verbose install --global
gx-go rewrite
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,26 @@
# go-libp2p-kbucket
A kbucket implementation for use as a routing table

[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)

> A kbucket implementation for use as a routing table
## Documenation

See https://godoc.org/github.com/libp2p/go-libp2p-kbucket.

## Contribute

Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/go-key/issues)!

This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).

### Want to hack on IPFS?

[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md)

## License

MIT
108 changes: 108 additions & 0 deletions bucket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package kbucket

import (
"container/list"
"sync"

peer "github.com/ipfs/go-libp2p-peer"
)

// Bucket holds a list of peers.
type Bucket struct {
lk sync.RWMutex
list *list.List
}

func newBucket() *Bucket {
b := new(Bucket)
b.list = list.New()
return b
}

func (b *Bucket) Peers() []peer.ID {
b.lk.RLock()
defer b.lk.RUnlock()
ps := make([]peer.ID, 0, b.list.Len())
for e := b.list.Front(); e != nil; e = e.Next() {
id := e.Value.(peer.ID)
ps = append(ps, id)
}
return ps
}

func (b *Bucket) Has(id peer.ID) bool {
b.lk.RLock()
defer b.lk.RUnlock()
for e := b.list.Front(); e != nil; e = e.Next() {
if e.Value.(peer.ID) == id {
return true
}
}
return false
}

func (b *Bucket) Remove(id peer.ID) {
b.lk.Lock()
defer b.lk.Unlock()
for e := b.list.Front(); e != nil; e = e.Next() {
if e.Value.(peer.ID) == id {
b.list.Remove(e)
}
}
}

func (b *Bucket) MoveToFront(id peer.ID) {
b.lk.Lock()
defer b.lk.Unlock()
for e := b.list.Front(); e != nil; e = e.Next() {
if e.Value.(peer.ID) == id {
b.list.MoveToFront(e)
}
}
}

func (b *Bucket) PushFront(p peer.ID) {
b.lk.Lock()
b.list.PushFront(p)
b.lk.Unlock()
}

func (b *Bucket) PopBack() peer.ID {
b.lk.Lock()
defer b.lk.Unlock()
last := b.list.Back()
b.list.Remove(last)
return last.Value.(peer.ID)
}

func (b *Bucket) Len() int {
b.lk.RLock()
defer b.lk.RUnlock()
return b.list.Len()
}

// Split splits a buckets peers into two buckets, the methods receiver will have
// peers with CPL equal to cpl, the returned bucket will have peers with CPL
// greater than cpl (returned bucket has closer peers)
func (b *Bucket) Split(cpl int, target ID) *Bucket {
b.lk.Lock()
defer b.lk.Unlock()

out := list.New()
newbuck := newBucket()
newbuck.list = out
e := b.list.Front()
for e != nil {
peerID := ConvertPeerID(e.Value.(peer.ID))
peerCPL := commonPrefixLen(peerID, target)
if peerCPL > cpl {
cur := e
out.PushBack(e.Value)
e = e.Next()
b.list.Remove(cur)
continue
}
e = e.Next()
}
return newbuck
}
97 changes: 97 additions & 0 deletions keyspace/keyspace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package keyspace

import (
"sort"

"math/big"
)

// Key represents an identifier in a KeySpace. It holds a reference to the
// associated KeySpace, as well references to both the Original identifier,
// as well as the new, KeySpace Bytes one.
type Key struct {

// Space is the KeySpace this Key is related to.
Space KeySpace

// Original is the original value of the identifier
Original []byte

// Bytes is the new value of the identifier, in the KeySpace.
Bytes []byte
}

// Equal returns whether this key is equal to another.
func (k1 Key) Equal(k2 Key) bool {
if k1.Space != k2.Space {
panic("k1 and k2 not in same key space.")
}
return k1.Space.Equal(k1, k2)
}

// Less returns whether this key comes before another.
func (k1 Key) Less(k2 Key) bool {
if k1.Space != k2.Space {
panic("k1 and k2 not in same key space.")
}
return k1.Space.Less(k1, k2)
}

// Distance returns this key's distance to another
func (k1 Key) Distance(k2 Key) *big.Int {
if k1.Space != k2.Space {
panic("k1 and k2 not in same key space.")
}
return k1.Space.Distance(k1, k2)
}

// KeySpace is an object used to do math on identifiers. Each keyspace has its
// own properties and rules. See XorKeySpace.
type KeySpace interface {

// Key converts an identifier into a Key in this space.
Key([]byte) Key

// Equal returns whether keys are equal in this key space
Equal(Key, Key) bool

// Distance returns the distance metric in this key space
Distance(Key, Key) *big.Int

// Less returns whether the first key is smaller than the second.
Less(Key, Key) bool
}

// byDistanceToCenter is a type used to sort Keys by proximity to a center.
type byDistanceToCenter struct {
Center Key
Keys []Key
}

func (s byDistanceToCenter) Len() int {
return len(s.Keys)
}

func (s byDistanceToCenter) Swap(i, j int) {
s.Keys[i], s.Keys[j] = s.Keys[j], s.Keys[i]
}

func (s byDistanceToCenter) Less(i, j int) bool {
a := s.Center.Distance(s.Keys[i])
b := s.Center.Distance(s.Keys[j])
return a.Cmp(b) == -1
}

// SortByDistance takes a KeySpace, a center Key, and a list of Keys toSort.
// It returns a new list, where the Keys toSort have been sorted by their
// distance to the center Key.
func SortByDistance(sp KeySpace, center Key, toSort []Key) []Key {
toSortCopy := make([]Key, len(toSort))
copy(toSortCopy, toSort)
bdtc := &byDistanceToCenter{
Center: center,
Keys: toSortCopy, // copy
}
sort.Sort(bdtc)
return bdtc.Keys
}
67 changes: 67 additions & 0 deletions keyspace/xor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package keyspace

import (
"bytes"
"crypto/sha256"
"math/big"

u "github.com/ipfs/go-ipfs-util"
)

// XORKeySpace is a KeySpace which:
// - normalizes identifiers using a cryptographic hash (sha256)
// - measures distance by XORing keys together
var XORKeySpace = &xorKeySpace{}
var _ KeySpace = XORKeySpace // ensure it conforms

type xorKeySpace struct{}

// Key converts an identifier into a Key in this space.
func (s *xorKeySpace) Key(id []byte) Key {
hash := sha256.Sum256(id)
key := hash[:]
return Key{
Space: s,
Original: id,
Bytes: key,
}
}

// Equal returns whether keys are equal in this key space
func (s *xorKeySpace) Equal(k1, k2 Key) bool {
return bytes.Equal(k1.Bytes, k2.Bytes)
}

// Distance returns the distance metric in this key space
func (s *xorKeySpace) Distance(k1, k2 Key) *big.Int {
// XOR the keys
k3 := u.XOR(k1.Bytes, k2.Bytes)

// interpret it as an integer
dist := big.NewInt(0).SetBytes(k3)
return dist
}

// Less returns whether the first key is smaller than the second.
func (s *xorKeySpace) Less(k1, k2 Key) bool {
a := k1.Bytes
b := k2.Bytes
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return a[i] < b[i]
}
}
return true
}

// ZeroPrefixLen returns the number of consecutive zeroes in a byte slice.
func ZeroPrefixLen(id []byte) int {
for i := 0; i < len(id); i++ {
for j := 0; j < 8; j++ {
if (id[i]>>uint8(7-j))&0x1 != 0 {
return i*8 + j
}
}
}
return len(id) * 8
}

0 comments on commit 3b07290

Please sign in to comment.