address issues from code review (issue #25)

This commit is contained in:
Jeromy 2014-08-08 18:09:21 -07:00
parent 5bd8258ea1
commit ae556952dc
10 changed files with 297 additions and 256 deletions

View File

@ -1,8 +1,8 @@
package dht package dht
// A helper struct to make working with protbuf types easier // A helper struct to make working with protbuf types easier
type pDHTMessage struct { type DHTMessage struct {
Type DHTMessage_MessageType Type PBDHTMessage_MessageType
Key string Key string
Value []byte Value []byte
Response bool Response bool
@ -10,8 +10,8 @@ type pDHTMessage struct {
Success bool Success bool
} }
func (m *pDHTMessage) ToProtobuf() *DHTMessage { func (m *DHTMessage) ToProtobuf() *PBDHTMessage {
pmes := new(DHTMessage) pmes := new(PBDHTMessage)
if m.Value != nil { if m.Value != nil {
pmes.Value = m.Value pmes.Value = m.Value
} }

View File

@ -49,7 +49,7 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket {
e := bucket_list.Front() e := bucket_list.Front()
for e != nil { for e != nil {
peer_id := convertPeerID(e.Value.(*peer.Peer).ID) peer_id := convertPeerID(e.Value.(*peer.Peer).ID)
peer_cpl := xor(peer_id, target).commonPrefixLen() peer_cpl := prefLen(peer_id, target)
if peer_cpl > cpl { if peer_cpl > cpl {
cur := e cur := e
out.PushBack(e.Value) out.PushBack(e.Value)

154
dht.go
View File

@ -1,15 +1,15 @@
package dht package dht
import ( import (
"sync"
"time"
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"sync"
"time"
peer "github.com/jbenet/go-ipfs/peer" peer "github.com/jbenet/go-ipfs/peer"
swarm "github.com/jbenet/go-ipfs/swarm" swarm "github.com/jbenet/go-ipfs/swarm"
u "github.com/jbenet/go-ipfs/util" u "github.com/jbenet/go-ipfs/util"
identify "github.com/jbenet/go-ipfs/identify"
ma "github.com/jbenet/go-multiaddr" ma "github.com/jbenet/go-multiaddr"
@ -54,16 +54,22 @@ type IpfsDHT struct {
diaglock sync.Mutex diaglock sync.Mutex
} }
// The listen info struct holds information about a message that is being waited for
type listenInfo struct { type listenInfo struct {
// Responses matching the listen ID will be sent through resp
resp chan *swarm.Message resp chan *swarm.Message
// count is the number of responses to listen for
count int count int
// eol is the time at which this listener will expire
eol time.Time eol time.Time
} }
// Create a new DHT object with the given peer as the 'local' host // Create a new DHT object with the given peer as the 'local' host
func NewDHT(p *peer.Peer) (*IpfsDHT, error) { func NewDHT(p *peer.Peer) (*IpfsDHT, error) {
if p == nil { if p == nil {
panic("Tried to create new dht with nil peer") return nil, errors.New("nil peer passed to NewDHT()")
} }
network := swarm.NewSwarm(p) network := swarm.NewSwarm(p)
err := network.Listen() err := network.Listen()
@ -90,50 +96,24 @@ func (dht *IpfsDHT) Start() {
} }
// Connect to a new peer at the given address // Connect to a new peer at the given address
// TODO: move this into swarm
func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) {
maddrstr, _ := addr.String() maddrstr, _ := addr.String()
u.DOut("Connect to new peer: %s", maddrstr) u.DOut("Connect to new peer: %s", maddrstr)
if addr == nil { npeer, err := dht.network.Connect(addr)
panic("addr was nil!")
}
peer := new(peer.Peer)
peer.AddAddress(addr)
conn,err := swarm.Dial("tcp", peer)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = identify.Handshake(dht.self, peer, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) dht.Update(npeer)
if err != nil {
return nil, err
}
// Send node an address that you can be reached on
myaddr := dht.self.NetAddress("tcp")
mastr,err := myaddr.String()
if err != nil {
panic("No local address to send")
}
conn.Outgoing.MsgChan <- []byte(mastr)
dht.network.StartConn(conn)
removed := dht.routes[0].Update(peer)
if removed != nil {
panic("need to remove this peer.")
}
// Ping new peer to register in their routing table // Ping new peer to register in their routing table
// NOTE: this should be done better... // NOTE: this should be done better...
err = dht.Ping(peer, time.Second * 2) err = dht.Ping(npeer, time.Second*2)
if err != nil { if err != nil {
panic("Failed to ping new peer.") return nil, errors.New("Failed to ping newly connected peer.")
} }
return peer, nil return npeer, nil
} }
// Read in all messages from swarm and handle them appropriately // Read in all messages from swarm and handle them appropriately
@ -149,18 +129,14 @@ func (dht *IpfsDHT) handleMessages() {
u.DOut("handleMessages closing, bad recv on incoming") u.DOut("handleMessages closing, bad recv on incoming")
return return
} }
pmes := new(DHTMessage) pmes := new(PBDHTMessage)
err := proto.Unmarshal(mes.Data, pmes) err := proto.Unmarshal(mes.Data, pmes)
if err != nil { if err != nil {
u.PErr("Failed to decode protobuf message: %s", err) u.PErr("Failed to decode protobuf message: %s", err)
continue continue
} }
// Update peers latest visit in routing table dht.Update(mes.Peer)
removed := dht.routes[0].Update(mes.Peer)
if removed != nil {
panic("Need to handle removed peer.")
}
// Note: not sure if this is the correct place for this // Note: not sure if this is the correct place for this
if pmes.GetResponse() { if pmes.GetResponse() {
@ -180,7 +156,6 @@ func (dht *IpfsDHT) handleMessages() {
dht.Unlisten(pmes.GetId()) dht.Unlisten(pmes.GetId())
} }
} else { } else {
// this is expected behaviour during a timeout
u.DOut("Received response with nobody listening...") u.DOut("Received response with nobody listening...")
} }
@ -190,32 +165,39 @@ func (dht *IpfsDHT) handleMessages() {
u.DOut("[peer: %s]", dht.self.ID.Pretty()) u.DOut("[peer: %s]", dht.self.ID.Pretty())
u.DOut("Got message type: '%s' [id = %x, from = %s]", u.DOut("Got message type: '%s' [id = %x, from = %s]",
DHTMessage_MessageType_name[int32(pmes.GetType())], PBDHTMessage_MessageType_name[int32(pmes.GetType())],
pmes.GetId(), mes.Peer.ID.Pretty()) pmes.GetId(), mes.Peer.ID.Pretty())
switch pmes.GetType() { switch pmes.GetType() {
case DHTMessage_GET_VALUE: case PBDHTMessage_GET_VALUE:
dht.handleGetValue(mes.Peer, pmes) dht.handleGetValue(mes.Peer, pmes)
case DHTMessage_PUT_VALUE: case PBDHTMessage_PUT_VALUE:
dht.handlePutValue(mes.Peer, pmes) dht.handlePutValue(mes.Peer, pmes)
case DHTMessage_FIND_NODE: case PBDHTMessage_FIND_NODE:
dht.handleFindPeer(mes.Peer, pmes) dht.handleFindPeer(mes.Peer, pmes)
case DHTMessage_ADD_PROVIDER: case PBDHTMessage_ADD_PROVIDER:
dht.handleAddProvider(mes.Peer, pmes) dht.handleAddProvider(mes.Peer, pmes)
case DHTMessage_GET_PROVIDERS: case PBDHTMessage_GET_PROVIDERS:
dht.handleGetProviders(mes.Peer, pmes) dht.handleGetProviders(mes.Peer, pmes)
case DHTMessage_PING: case PBDHTMessage_PING:
dht.handlePing(mes.Peer, pmes) dht.handlePing(mes.Peer, pmes)
case DHTMessage_DIAGNOSTIC: case PBDHTMessage_DIAGNOSTIC:
dht.handleDiagnostic(mes.Peer, pmes) dht.handleDiagnostic(mes.Peer, pmes)
} }
case err := <-dht.network.Chan.Errors: case err := <-dht.network.Chan.Errors:
u.DErr("dht err: %s", err) u.DErr("dht err: %s", err)
panic(err)
case <-dht.shutdown: case <-dht.shutdown:
checkTimeouts.Stop() checkTimeouts.Stop()
return return
case <-checkTimeouts.C: case <-checkTimeouts.C:
// Time to collect some garbage!
dht.cleanExpiredProviders()
dht.cleanExpiredListeners()
}
}
}
func (dht *IpfsDHT) cleanExpiredProviders() {
dht.providerLock.Lock() dht.providerLock.Lock()
for k, parr := range dht.providers { for k, parr := range dht.providers {
var cleaned []*providerInfo var cleaned []*providerInfo
@ -227,6 +209,9 @@ func (dht *IpfsDHT) handleMessages() {
dht.providers[k] = cleaned dht.providers[k] = cleaned
} }
dht.providerLock.Unlock() dht.providerLock.Unlock()
}
func (dht *IpfsDHT) cleanExpiredListeners() {
dht.listenLock.Lock() dht.listenLock.Lock()
var remove []uint64 var remove []uint64
now := time.Now() now := time.Now()
@ -240,12 +225,10 @@ func (dht *IpfsDHT) handleMessages() {
} }
dht.listenLock.Unlock() dht.listenLock.Unlock()
} }
}
}
func (dht *IpfsDHT) putValueToPeer(p *peer.Peer, key string, value []byte) error { func (dht *IpfsDHT) putValueToPeer(p *peer.Peer, key string, value []byte) error {
pmes := pDHTMessage{ pmes := DHTMessage{
Type: DHTMessage_PUT_VALUE, Type: PBDHTMessage_PUT_VALUE,
Key: key, Key: key,
Value: value, Value: value,
Id: GenerateMessageID(), Id: GenerateMessageID(),
@ -256,12 +239,12 @@ func (dht *IpfsDHT) putValueToPeer(p *peer.Peer, key string, value []byte) error
return nil return nil
} }
func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) {
dskey := ds.NewKey(pmes.GetKey()) dskey := ds.NewKey(pmes.GetKey())
var resp *pDHTMessage var resp *DHTMessage
i_val, err := dht.datastore.Get(dskey) i_val, err := dht.datastore.Get(dskey)
if err == nil { if err == nil {
resp = &pDHTMessage{ resp = &DHTMessage{
Response: true, Response: true,
Id: *pmes.Id, Id: *pmes.Id,
Key: *pmes.Key, Key: *pmes.Key,
@ -271,7 +254,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) {
} else if err == ds.ErrNotFound { } else if err == ds.ErrNotFound {
// Find closest peer(s) to desired key and reply with that info // Find closest peer(s) to desired key and reply with that info
closer := dht.routes[0].NearestPeer(convertKey(u.Key(pmes.GetKey()))) closer := dht.routes[0].NearestPeer(convertKey(u.Key(pmes.GetKey())))
resp = &pDHTMessage{ resp = &DHTMessage{
Response: true, Response: true,
Id: *pmes.Id, Id: *pmes.Id,
Key: *pmes.Key, Key: *pmes.Key,
@ -285,7 +268,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) {
} }
// Store a value in this peer local storage // Store a value in this peer local storage
func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *DHTMessage) { func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *PBDHTMessage) {
dskey := ds.NewKey(pmes.GetKey()) dskey := ds.NewKey(pmes.GetKey())
err := dht.datastore.Put(dskey, pmes.GetValue()) err := dht.datastore.Put(dskey, pmes.GetValue())
if err != nil { if err != nil {
@ -294,8 +277,8 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *DHTMessage) {
} }
} }
func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) {
resp := pDHTMessage{ resp := DHTMessage{
Type: pmes.GetType(), Type: pmes.GetType(),
Response: true, Response: true,
Id: pmes.GetId(), Id: pmes.GetId(),
@ -304,36 +287,41 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) {
dht.network.Chan.Outgoing <- swarm.NewMessage(p, resp.ToProtobuf()) dht.network.Chan.Outgoing <- swarm.NewMessage(p, resp.ToProtobuf())
} }
func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *DHTMessage) { func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) {
success := true
u.POut("handleFindPeer: searching for '%s'", peer.ID(pmes.GetKey()).Pretty()) u.POut("handleFindPeer: searching for '%s'", peer.ID(pmes.GetKey()).Pretty())
closest := dht.routes[0].NearestPeer(convertKey(u.Key(pmes.GetKey()))) closest := dht.routes[0].NearestPeer(convertKey(u.Key(pmes.GetKey())))
if closest == nil { if closest == nil {
panic("could not find anything.") u.PErr("handleFindPeer: could not find anything.")
success = false
} }
if len(closest.Addresses) == 0 { if len(closest.Addresses) == 0 {
panic("no addresses for connected peer...") u.PErr("handleFindPeer: no addresses for connected peer...")
success = false
} }
u.POut("handleFindPeer: sending back '%s'", closest.ID.Pretty()) u.POut("handleFindPeer: sending back '%s'", closest.ID.Pretty())
addr, err := closest.Addresses[0].String() addr, err := closest.Addresses[0].String()
if err != nil { if err != nil {
panic(err) u.PErr(err.Error())
success = false
} }
resp := pDHTMessage{ resp := DHTMessage{
Type: pmes.GetType(), Type: pmes.GetType(),
Response: true, Response: true,
Id: pmes.GetId(), Id: pmes.GetId(),
Value: []byte(addr), Value: []byte(addr),
Success: success,
} }
mes := swarm.NewMessage(p, resp.ToProtobuf()) mes := swarm.NewMessage(p, resp.ToProtobuf())
dht.network.Chan.Outgoing <- mes dht.network.Chan.Outgoing <- mes
} }
func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) {
dht.providerLock.RLock() dht.providerLock.RLock()
providers := dht.providers[u.Key(pmes.GetKey())] providers := dht.providers[u.Key(pmes.GetKey())]
dht.providerLock.RUnlock() dht.providerLock.RUnlock()
@ -355,17 +343,21 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) {
addrs[prov.Value.Key()] = str addrs[prov.Value.Key()] = str
} }
success := true
data, err := json.Marshal(addrs) data, err := json.Marshal(addrs)
if err != nil { if err != nil {
panic(err) u.POut("handleGetProviders: error marshalling struct to JSON: %s", err)
data = nil
success = false
} }
resp := pDHTMessage{ resp := DHTMessage{
Type: DHTMessage_GET_PROVIDERS, Type: PBDHTMessage_GET_PROVIDERS,
Key: pmes.GetKey(), Key: pmes.GetKey(),
Value: data, Value: data,
Id: pmes.GetId(), Id: pmes.GetId(),
Response: true, Response: true,
Success: success,
} }
mes := swarm.NewMessage(p, resp.ToProtobuf()) mes := swarm.NewMessage(p, resp.ToProtobuf())
@ -377,13 +369,12 @@ type providerInfo struct {
Value *peer.Peer Value *peer.Peer
} }
func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *DHTMessage) { func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *PBDHTMessage) {
//TODO: need to implement TTLs on providers //TODO: need to implement TTLs on providers
key := u.Key(pmes.GetKey()) key := u.Key(pmes.GetKey())
dht.addProviderEntry(key, p) dht.addProviderEntry(key, p)
} }
// Register a handler for a specific message ID, used for getting replies // Register a handler for a specific message ID, used for getting replies
// to certain messages (i.e. response to a GET_VALUE message) // to certain messages (i.e. response to a GET_VALUE message)
func (dht *IpfsDHT) ListenFor(mesid uint64, count int, timeout time.Duration) <-chan *swarm.Message { func (dht *IpfsDHT) ListenFor(mesid uint64, count int, timeout time.Duration) <-chan *swarm.Message {
@ -432,7 +423,7 @@ func (dht *IpfsDHT) addProviderEntry(key u.Key, p *peer.Peer) {
dht.providerLock.Unlock() dht.providerLock.Unlock()
} }
func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) { func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) {
dht.diaglock.Lock() dht.diaglock.Lock()
if dht.IsListening(pmes.GetId()) { if dht.IsListening(pmes.GetId()) {
//TODO: ehhh.......... //TODO: ehhh..........
@ -449,8 +440,6 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) {
dht.network.Chan.Outgoing <- mes dht.network.Chan.Outgoing <- mes
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
di := dht.getDiagInfo() di := dht.getDiagInfo()
buf.Write(di.Marshal()) buf.Write(di.Marshal())
@ -464,7 +453,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) {
//Timeout, return what we have //Timeout, return what we have
goto out goto out
case req_resp := <-listen_chan: case req_resp := <-listen_chan:
pmes_out := new(DHTMessage) pmes_out := new(PBDHTMessage)
err := proto.Unmarshal(req_resp.Data, pmes_out) err := proto.Unmarshal(req_resp.Data, pmes_out)
if err != nil { if err != nil {
// It broke? eh, whatever, keep going // It broke? eh, whatever, keep going
@ -476,8 +465,8 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) {
} }
out: out:
resp := pDHTMessage{ resp := DHTMessage{
Type: DHTMessage_DIAGNOSTIC, Type: PBDHTMessage_DIAGNOSTIC,
Id: pmes.GetId(), Id: pmes.GetId(),
Value: buf.Bytes(), Value: buf.Bytes(),
Response: true, Response: true,
@ -498,3 +487,10 @@ func (dht *IpfsDHT) GetLocal(key u.Key) ([]byte, error) {
func (dht *IpfsDHT) PutLocal(key u.Key, value []byte) error { func (dht *IpfsDHT) PutLocal(key u.Key, value []byte) error {
return dht.datastore.Put(ds.NewKey(string(key)), value) return dht.datastore.Put(ds.NewKey(string(key)), value)
} }
func (dht *IpfsDHT) Update(p *peer.Peer) {
removed := dht.routes[0].Update(p)
if removed != nil {
dht.network.Drop(removed)
}
}

View File

@ -2,12 +2,13 @@ package dht
import ( import (
"testing" "testing"
peer "github.com/jbenet/go-ipfs/peer"
ma "github.com/jbenet/go-multiaddr"
u "github.com/jbenet/go-ipfs/util"
"time" peer "github.com/jbenet/go-ipfs/peer"
u "github.com/jbenet/go-ipfs/util"
ma "github.com/jbenet/go-multiaddr"
"fmt" "fmt"
"time"
) )
func TestPing(t *testing.T) { func TestPing(t *testing.T) {
@ -120,7 +121,6 @@ func TestProvides(t *testing.T) {
addrs = append(addrs, a) addrs = append(addrs, a)
} }
var peers []*peer.Peer var peers []*peer.Peer
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
p := new(peer.Peer) p := new(peer.Peer)
@ -174,6 +174,8 @@ func TestProvides(t *testing.T) {
if len(provs) != 1 { if len(provs) != 1 {
t.Fatal("Didnt get back providers") t.Fatal("Didnt get back providers")
} }
for i := 0; i < 4; i++ {
dhts[i].Halt()
}
} }

View File

@ -38,7 +38,7 @@ func (dht *IpfsDHT) getDiagInfo() *diagInfo {
di.Keys = nil // Currently no way to query datastore di.Keys = nil // Currently no way to query datastore
for _,p := range dht.routes[0].listpeers() { for _,p := range dht.routes[0].listpeers() {
di.Connections = append(di.Connections, connDiagInfo{p.GetDistance(), p.ID}) di.Connections = append(di.Connections, connDiagInfo{p.GetLatency(), p.ID})
} }
return di return di
} }

View File

@ -9,7 +9,7 @@ It is generated from these files:
messages.proto messages.proto
It has these top-level messages: It has these top-level messages:
DHTMessage PBDHTMessage
*/ */
package dht package dht
@ -20,19 +20,19 @@ import math "math"
var _ = proto.Marshal var _ = proto.Marshal
var _ = math.Inf var _ = math.Inf
type DHTMessage_MessageType int32 type PBDHTMessage_MessageType int32
const ( const (
DHTMessage_PUT_VALUE DHTMessage_MessageType = 0 PBDHTMessage_PUT_VALUE PBDHTMessage_MessageType = 0
DHTMessage_GET_VALUE DHTMessage_MessageType = 1 PBDHTMessage_GET_VALUE PBDHTMessage_MessageType = 1
DHTMessage_ADD_PROVIDER DHTMessage_MessageType = 2 PBDHTMessage_ADD_PROVIDER PBDHTMessage_MessageType = 2
DHTMessage_GET_PROVIDERS DHTMessage_MessageType = 3 PBDHTMessage_GET_PROVIDERS PBDHTMessage_MessageType = 3
DHTMessage_FIND_NODE DHTMessage_MessageType = 4 PBDHTMessage_FIND_NODE PBDHTMessage_MessageType = 4
DHTMessage_PING DHTMessage_MessageType = 5 PBDHTMessage_PING PBDHTMessage_MessageType = 5
DHTMessage_DIAGNOSTIC DHTMessage_MessageType = 6 PBDHTMessage_DIAGNOSTIC PBDHTMessage_MessageType = 6
) )
var DHTMessage_MessageType_name = map[int32]string{ var PBDHTMessage_MessageType_name = map[int32]string{
0: "PUT_VALUE", 0: "PUT_VALUE",
1: "GET_VALUE", 1: "GET_VALUE",
2: "ADD_PROVIDER", 2: "ADD_PROVIDER",
@ -41,7 +41,7 @@ var DHTMessage_MessageType_name = map[int32]string{
5: "PING", 5: "PING",
6: "DIAGNOSTIC", 6: "DIAGNOSTIC",
} }
var DHTMessage_MessageType_value = map[string]int32{ var PBDHTMessage_MessageType_value = map[string]int32{
"PUT_VALUE": 0, "PUT_VALUE": 0,
"GET_VALUE": 1, "GET_VALUE": 1,
"ADD_PROVIDER": 2, "ADD_PROVIDER": 2,
@ -51,79 +51,111 @@ var DHTMessage_MessageType_value = map[string]int32{
"DIAGNOSTIC": 6, "DIAGNOSTIC": 6,
} }
func (x DHTMessage_MessageType) Enum() *DHTMessage_MessageType { func (x PBDHTMessage_MessageType) Enum() *PBDHTMessage_MessageType {
p := new(DHTMessage_MessageType) p := new(PBDHTMessage_MessageType)
*p = x *p = x
return p return p
} }
func (x DHTMessage_MessageType) String() string { func (x PBDHTMessage_MessageType) String() string {
return proto.EnumName(DHTMessage_MessageType_name, int32(x)) return proto.EnumName(PBDHTMessage_MessageType_name, int32(x))
} }
func (x *DHTMessage_MessageType) UnmarshalJSON(data []byte) error { func (x *PBDHTMessage_MessageType) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(DHTMessage_MessageType_value, data, "DHTMessage_MessageType") value, err := proto.UnmarshalJSONEnum(PBDHTMessage_MessageType_value, data, "PBDHTMessage_MessageType")
if err != nil { if err != nil {
return err return err
} }
*x = DHTMessage_MessageType(value) *x = PBDHTMessage_MessageType(value)
return nil return nil
} }
type DHTMessage struct { type PBDHTMessage struct {
Type *DHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.DHTMessage_MessageType" json:"type,omitempty"` Type *PBDHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.PBDHTMessage_MessageType" json:"type,omitempty"`
Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"`
Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"`
Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"`
Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"`
Success *bool `protobuf:"varint,6,opt,name=success" json:"success,omitempty"` Success *bool `protobuf:"varint,6,opt,name=success" json:"success,omitempty"`
Peers []*PBDHTMessage_PBPeer `protobuf:"bytes,7,rep,name=peers" json:"peers,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *DHTMessage) Reset() { *m = DHTMessage{} } func (m *PBDHTMessage) Reset() { *m = PBDHTMessage{} }
func (m *DHTMessage) String() string { return proto.CompactTextString(m) } func (m *PBDHTMessage) String() string { return proto.CompactTextString(m) }
func (*DHTMessage) ProtoMessage() {} func (*PBDHTMessage) ProtoMessage() {}
func (m *DHTMessage) GetType() DHTMessage_MessageType { func (m *PBDHTMessage) GetType() PBDHTMessage_MessageType {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
return *m.Type return *m.Type
} }
return DHTMessage_PUT_VALUE return PBDHTMessage_PUT_VALUE
} }
func (m *DHTMessage) GetKey() string { func (m *PBDHTMessage) GetKey() string {
if m != nil && m.Key != nil { if m != nil && m.Key != nil {
return *m.Key return *m.Key
} }
return "" return ""
} }
func (m *DHTMessage) GetValue() []byte { func (m *PBDHTMessage) GetValue() []byte {
if m != nil { if m != nil {
return m.Value return m.Value
} }
return nil return nil
} }
func (m *DHTMessage) GetId() uint64 { func (m *PBDHTMessage) GetId() uint64 {
if m != nil && m.Id != nil { if m != nil && m.Id != nil {
return *m.Id return *m.Id
} }
return 0 return 0
} }
func (m *DHTMessage) GetResponse() bool { func (m *PBDHTMessage) GetResponse() bool {
if m != nil && m.Response != nil { if m != nil && m.Response != nil {
return *m.Response return *m.Response
} }
return false return false
} }
func (m *DHTMessage) GetSuccess() bool { func (m *PBDHTMessage) GetSuccess() bool {
if m != nil && m.Success != nil { if m != nil && m.Success != nil {
return *m.Success return *m.Success
} }
return false return false
} }
func init() { func (m *PBDHTMessage) GetPeers() []*PBDHTMessage_PBPeer {
proto.RegisterEnum("dht.DHTMessage_MessageType", DHTMessage_MessageType_name, DHTMessage_MessageType_value) if m != nil {
return m.Peers
}
return nil
}
type PBDHTMessage_PBPeer struct {
Id *string `protobuf:"bytes,1,req,name=id" json:"id,omitempty"`
Addr *string `protobuf:"bytes,2,req,name=addr" json:"addr,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *PBDHTMessage_PBPeer) Reset() { *m = PBDHTMessage_PBPeer{} }
func (m *PBDHTMessage_PBPeer) String() string { return proto.CompactTextString(m) }
func (*PBDHTMessage_PBPeer) ProtoMessage() {}
func (m *PBDHTMessage_PBPeer) GetId() string {
if m != nil && m.Id != nil {
return *m.Id
}
return ""
}
func (m *PBDHTMessage_PBPeer) GetAddr() string {
if m != nil && m.Addr != nil {
return *m.Addr
}
return ""
}
func init() {
proto.RegisterEnum("dht.PBDHTMessage_MessageType", PBDHTMessage_MessageType_name, PBDHTMessage_MessageType_value)
} }

View File

@ -2,7 +2,7 @@ package dht;
//run `protoc --go_out=. *.proto` to generate //run `protoc --go_out=. *.proto` to generate
message DHTMessage { message PBDHTMessage {
enum MessageType { enum MessageType {
PUT_VALUE = 0; PUT_VALUE = 0;
GET_VALUE = 1; GET_VALUE = 1;
@ -13,6 +13,11 @@ message DHTMessage {
DIAGNOSTIC = 6; DIAGNOSTIC = 6;
} }
message PBPeer {
required string id = 1;
required string addr = 2;
}
required MessageType type = 1; required MessageType type = 1;
optional string key = 2; optional string key = 2;
optional bytes value = 3; optional bytes value = 3;
@ -23,4 +28,7 @@ message DHTMessage {
// Signals whether or not this message is a response to another message // Signals whether or not this message is a response to another message
optional bool response = 5; optional bool response = 5;
optional bool success = 6; optional bool success = 6;
// Used for returning peers from queries (normally, peers closer to X)
repeated PBPeer peers = 7;
} }

View File

@ -1,10 +1,11 @@
package dht package dht
import ( import (
"math/rand"
"time"
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"math/rand"
"time"
proto "code.google.com/p/goprotobuf/proto" proto "code.google.com/p/goprotobuf/proto"
@ -34,7 +35,7 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error {
var p *peer.Peer var p *peer.Peer
p = s.routes[0].NearestPeer(convertKey(key)) p = s.routes[0].NearestPeer(convertKey(key))
if p == nil { if p == nil {
panic("Table returned nil peer!") return errors.New("Table returned nil peer!")
} }
return s.putValueToPeer(p, string(key), value) return s.putValueToPeer(p, string(key), value)
@ -47,11 +48,11 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) {
var p *peer.Peer var p *peer.Peer
p = s.routes[0].NearestPeer(convertKey(key)) p = s.routes[0].NearestPeer(convertKey(key))
if p == nil { if p == nil {
panic("Table returned nil peer!") return nil, errors.New("Table returned nil peer!")
} }
pmes := pDHTMessage{ pmes := DHTMessage{
Type: DHTMessage_GET_VALUE, Type: PBDHTMessage_GET_VALUE,
Key: string(key), Key: string(key),
Id: GenerateMessageID(), Id: GenerateMessageID(),
} }
@ -68,12 +69,10 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) {
return nil, u.ErrTimeout return nil, u.ErrTimeout
case resp, ok := <-response_chan: case resp, ok := <-response_chan:
if !ok { if !ok {
panic("Channel was closed...") u.PErr("response channel closed before timeout, please investigate.")
return nil, u.ErrTimeout
} }
if resp == nil { pmes_out := new(PBDHTMessage)
panic("Why the hell is this response nil?")
}
pmes_out := new(DHTMessage)
err := proto.Unmarshal(resp.Data, pmes_out) err := proto.Unmarshal(resp.Data, pmes_out)
if err != nil { if err != nil {
return nil, err return nil, err
@ -96,8 +95,8 @@ func (s *IpfsDHT) Provide(key u.Key) error {
//return an error //return an error
} }
pmes := pDHTMessage{ pmes := DHTMessage{
Type: DHTMessage_ADD_PROVIDER, Type: PBDHTMessage_ADD_PROVIDER,
Key: string(key), Key: string(key),
} }
pbmes := pmes.ToProtobuf() pbmes := pmes.ToProtobuf()
@ -113,8 +112,8 @@ func (s *IpfsDHT) Provide(key u.Key) error {
func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) {
p := s.routes[0].NearestPeer(convertKey(key)) p := s.routes[0].NearestPeer(convertKey(key))
pmes := pDHTMessage{ pmes := DHTMessage{
Type: DHTMessage_GET_PROVIDERS, Type: PBDHTMessage_GET_PROVIDERS,
Key: string(key), Key: string(key),
Id: GenerateMessageID(), Id: GenerateMessageID(),
} }
@ -131,7 +130,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer,
return nil, u.ErrTimeout return nil, u.ErrTimeout
case resp := <-listen_chan: case resp := <-listen_chan:
u.DOut("FindProviders: got response.") u.DOut("FindProviders: got response.")
pmes_out := new(DHTMessage) pmes_out := new(PBDHTMessage)
err := proto.Unmarshal(resp.Data, pmes_out) err := proto.Unmarshal(resp.Data, pmes_out)
if err != nil { if err != nil {
return nil, err return nil, err
@ -171,8 +170,8 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer,
func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) {
p := s.routes[0].NearestPeer(convertPeerID(id)) p := s.routes[0].NearestPeer(convertPeerID(id))
pmes := pDHTMessage{ pmes := DHTMessage{
Type: DHTMessage_FIND_NODE, Type: PBDHTMessage_FIND_NODE,
Key: string(id), Key: string(id),
Id: GenerateMessageID(), Id: GenerateMessageID(),
} }
@ -187,7 +186,7 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error
s.Unlisten(pmes.Id) s.Unlisten(pmes.Id)
return nil, u.ErrTimeout return nil, u.ErrTimeout
case resp := <-listen_chan: case resp := <-listen_chan:
pmes_out := new(DHTMessage) pmes_out := new(PBDHTMessage)
err := proto.Unmarshal(resp.Data, pmes_out) err := proto.Unmarshal(resp.Data, pmes_out)
if err != nil { if err != nil {
return nil, err return nil, err
@ -218,7 +217,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error {
// Thoughts: maybe this should accept an ID and do a peer lookup? // Thoughts: maybe this should accept an ID and do a peer lookup?
u.DOut("Enter Ping.") u.DOut("Enter Ping.")
pmes := pDHTMessage{Id: GenerateMessageID(), Type: DHTMessage_PING} pmes := DHTMessage{Id: GenerateMessageID(), Type: PBDHTMessage_PING}
mes := swarm.NewMessage(p, pmes.ToProtobuf()) mes := swarm.NewMessage(p, pmes.ToProtobuf())
before := time.Now() before := time.Now()
@ -229,7 +228,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error {
select { select {
case <-response_chan: case <-response_chan:
roundtrip := time.Since(before) roundtrip := time.Since(before)
p.SetDistance(roundtrip) p.SetLatency(roundtrip)
u.POut("Ping took %s.", roundtrip.String()) u.POut("Ping took %s.", roundtrip.String())
return nil return nil
case <-tout: case <-tout:
@ -246,8 +245,8 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) {
targets := dht.routes[0].NearestPeers(convertPeerID(dht.self.ID), 10) targets := dht.routes[0].NearestPeers(convertPeerID(dht.self.ID), 10)
// TODO: Add timeout to this struct so nodes know when to return // TODO: Add timeout to this struct so nodes know when to return
pmes := pDHTMessage{ pmes := DHTMessage{
Type: DHTMessage_DIAGNOSTIC, Type: PBDHTMessage_DIAGNOSTIC,
Id: GenerateMessageID(), Id: GenerateMessageID(),
} }
@ -267,7 +266,7 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) {
u.DOut("Diagnostic request timed out.") u.DOut("Diagnostic request timed out.")
return out, u.ErrTimeout return out, u.ErrTimeout
case resp := <-listen_chan: case resp := <-listen_chan:
pmes_out := new(DHTMessage) pmes_out := new(PBDHTMessage)
err := proto.Unmarshal(resp.Data, pmes_out) err := proto.Unmarshal(resp.Data, pmes_out)
if err != nil { if err != nil {
// NOTE: here and elsewhere, need to audit error handling, // NOTE: here and elsewhere, need to audit error handling,

View File

@ -125,7 +125,7 @@ func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer {
func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer {
rt.tabLock.RLock() rt.tabLock.RLock()
defer rt.tabLock.RUnlock() defer rt.tabLock.RUnlock()
cpl := xor(id, rt.local).commonPrefixLen() cpl := prefLen(id, rt.local)
// Get bucket at cpl index or last bucket // Get bucket at cpl index or last bucket
var bucket *Bucket var bucket *Bucket

View File

@ -40,6 +40,10 @@ func (id ID) commonPrefixLen() int {
return len(id)*8 - 1 return len(id)*8 - 1
} }
func prefLen(a, b ID) int {
return xor(a, b).commonPrefixLen()
}
func xor(a, b ID) ID { func xor(a, b ID) ID {
a, b = equalizeSizes(a, b) a, b = equalizeSizes(a, b)