Commit ae212638 authored by Aarsh Shah's avatar Aarsh Shah

k-bucket support for peoper kad bootstrapping

parent 146e1744
......@@ -3,6 +3,7 @@ package kbucket
import (
"container/list"
"sync"
"time"
"github.com/libp2p/go-libp2p-core/peer"
)
......@@ -11,14 +12,32 @@ import (
type Bucket struct {
lk sync.RWMutex
list *list.List
lastQueriedAtLk sync.RWMutex
lastQueriedAt time.Time
}
func newBucket() *Bucket {
b := new(Bucket)
b.list = list.New()
b.lastQueriedAt = time.Now()
return b
}
func (b *Bucket) GetLastQueriedAt() time.Time {
b.lastQueriedAtLk.RLock()
defer b.lastQueriedAtLk.RUnlock()
return b.lastQueriedAt
}
func (b *Bucket) ResetLastQueriedAt(newTime time.Time) {
b.lastQueriedAtLk.Lock()
defer b.lastQueriedAtLk.Unlock()
b.lastQueriedAt = newTime
}
func (b *Bucket) Peers() []peer.ID {
b.lk.RLock()
defer b.lk.RUnlock()
......
......@@ -4,11 +4,14 @@ package kbucket
import (
"errors"
"fmt"
"io"
"math/rand"
"sync"
"time"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/peerstore"
mh "github.com/multiformats/go-multihash"
logging "github.com/ipfs/go-log"
)
......@@ -17,6 +20,7 @@ var log = logging.Logger("table")
var ErrPeerRejectedHighLatency = errors.New("peer rejected; latency too high")
var ErrPeerRejectedNoCapacity = errors.New("peer rejected; insufficient capacity")
var ErrGenRandPeerIDFailed = errors.New("failed to generate random peerID in bucket: exhausted attempts")
// RoutingTable defines the routing table.
type RoutingTable struct {
......@@ -57,6 +61,42 @@ func NewRoutingTable(bucketsize int, localID ID, latency time.Duration, m peerst
return rt
}
func (rt *RoutingTable) GenRandPeerID(bucketID int) (peer.ID, error) {
rt.tabLock.RLock()
bucketLen := len(rt.Buckets)
rt.tabLock.RUnlock()
var targetCpl int
if bucketID >= bucketLen-1 {
targetCpl = bucketLen
} else {
targetCpl = bucketID
}
// should give up after a fixed number of attempts so we don't take up too much time/cpu
for i := 0; i < 200; i++ {
peerID, err := randPeerID()
if err != nil {
log.Debugf("failed to generate random peerID in bucket %d, error is %+v", bucketID, err)
continue
}
if CommonPrefixLen(ConvertPeerID(peerID), rt.local) == targetCpl {
return peerID, err
}
}
return "", ErrGenRandPeerIDFailed
}
func randPeerID() (peer.ID, error) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
buf := make([]byte, 16)
if _, err := io.ReadFull(r, buf); err != nil {
return "", err
}
h, _ := mh.Sum(buf, mh.SHA2_256, -1)
return peer.ID(h), nil
}
// Update adds or moves the given peer to the front of its respective bucket
func (rt *RoutingTable) Update(p peer.ID) (evicted peer.ID, err error) {
peerID := ConvertPeerID(p)
......
......@@ -48,6 +48,41 @@ func TestBucket(t *testing.T) {
}
}
func TestGenRandPeerID(t *testing.T) {
local := test.RandPeerIDFatal(t)
m := pstore.NewMetrics()
rt := NewRoutingTable(1, ConvertPeerID(local), time.Hour, m)
// create 3 buckets
for i := 0; i < 5; i++ {
for {
if p := test.RandPeerIDFatal(t); CommonPrefixLen(ConvertPeerID(local), ConvertPeerID(p)) == i {
rt.Update(p)
break
}
}
}
for i := 0; i < 5; i++ {
peerID, err := rt.GenRandPeerID(i)
if err != nil || len(peerID) == 0 {
t.Fatalf("error %+v & peerID %s for bucket %d", err, peerID, i)
}
var expectedCpl int
if i < len(rt.Buckets)-1 {
expectedCpl = i
} else {
expectedCpl = len(rt.Buckets)
}
if CommonPrefixLen(ConvertPeerID(peerID), rt.local) != expectedCpl {
t.Fatalf("cpl should be %d for bucket %d, generated peerID is %s", expectedCpl, i, peerID)
}
}
}
func TestTableCallbacks(t *testing.T) {
local := test.RandPeerIDFatal(t)
m := pstore.NewMetrics()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment