提交 a4388624 作者: Jeromy

more work implementing coral type lookups

上级 67ddab1e
......@@ -11,6 +11,37 @@ import (
"time"
)
func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) {
var addrs []*ma.Multiaddr
for i := 0; i < 4; i++ {
a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i))
if err != nil {
t.Fatal(err)
}
addrs = append(addrs, a)
}
var peers []*peer.Peer
for i := 0; i < 4; i++ {
p := new(peer.Peer)
p.AddAddress(addrs[i])
p.ID = peer.ID([]byte(fmt.Sprintf("peer_%d", i)))
peers = append(peers, p)
}
var dhts []*IpfsDHT
for i := 0; i < 4; i++ {
d, err := NewDHT(peers[i])
if err != nil {
t.Fatal(err)
}
dhts = append(dhts, d)
d.Start()
}
return addrs, peers, dhts
}
func TestPing(t *testing.T) {
u.Debug = false
addr_a, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222")
......@@ -90,11 +121,13 @@ func TestValueGetSet(t *testing.T) {
dht_a.Start()
dht_b.Start()
errsa := dht_a.network.GetChan().Errors
errsb := dht_b.network.GetChan().Errors
go func() {
select {
case err := <-dht_a.network.Chan.Errors:
case err := <-errsa:
t.Fatal(err)
case err := <-dht_b.network.Chan.Errors:
case err := <-errsb:
t.Fatal(err)
}
}()
......@@ -118,6 +151,52 @@ func TestValueGetSet(t *testing.T) {
func TestProvides(t *testing.T) {
u.Debug = false
addrs, _, dhts := setupDHTS(4, t)
_, err := dhts[0].Connect(addrs[1])
if err != nil {
t.Fatal(err)
}
_, err = dhts[1].Connect(addrs[2])
if err != nil {
t.Fatal(err)
}
_, err = dhts[1].Connect(addrs[3])
if err != nil {
t.Fatal(err)
}
err = dhts[3].PutLocal(u.Key("hello"), []byte("world"))
if err != nil {
t.Fatal(err)
}
err = dhts[3].Provide(u.Key("hello"))
if err != nil {
t.Fatal(err)
}
time.Sleep(time.Millisecond * 60)
provs, err := dhts[0].FindProviders(u.Key("hello"), time.Second)
if err != nil {
t.Fatal(err)
}
if len(provs) != 1 {
t.Fatal("Didnt get back providers")
}
for i := 0; i < 4; i++ {
dhts[i].Halt()
}
}
func TestLayeredGet(t *testing.T) {
u.Debug = false
var addrs []*ma.Multiaddr
for i := 0; i < 4; i++ {
a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i))
......@@ -147,7 +226,7 @@ func TestProvides(t *testing.T) {
_, err := dhts[0].Connect(addrs[1])
if err != nil {
t.Fatal(err)
t.Fatalf("Failed to connect: %s", err)
}
_, err = dhts[1].Connect(addrs[2])
......@@ -172,13 +251,13 @@ func TestProvides(t *testing.T) {
time.Sleep(time.Millisecond * 60)
provs, err := dhts[0].FindProviders(u.Key("hello"), time.Second)
val, err := dhts[0].GetValue(u.Key("hello"), time.Second)
if err != nil {
t.Fatal(err)
}
if len(provs) != 1 {
t.Fatal("Didnt get back providers")
if string(val) != "world" {
t.Fatal("Got incorrect value.")
}
for i := 0; i < 4; i++ {
......@@ -186,7 +265,7 @@ func TestProvides(t *testing.T) {
}
}
func TestLayeredGet(t *testing.T) {
func TestFindPeer(t *testing.T) {
u.Debug = false
var addrs []*ma.Multiaddr
for i := 0; i < 4; i++ {
......@@ -230,25 +309,17 @@ func TestLayeredGet(t *testing.T) {
t.Fatal(err)
}
err = dhts[3].PutLocal(u.Key("hello"), []byte("world"))
p, err := dhts[0].FindPeer(peers[2].ID, time.Second)
if err != nil {
t.Fatal(err)
}
err = dhts[3].Provide(u.Key("hello"))
if err != nil {
t.Fatal(err)
if p == nil {
t.Fatal("Failed to find peer.")
}
time.Sleep(time.Millisecond * 60)
val, err := dhts[0].GetValue(u.Key("hello"), time.Second)
if err != nil {
t.Fatal(err)
}
if string(val) != "world" {
t.Fatal("Got incorrect value.")
if !p.ID.Equal(peers[2].ID) {
t.Fatal("Didnt find expected peer.")
}
for i := 0; i < 4; i++ {
......
......@@ -3,8 +3,6 @@ package dht
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"math/rand"
"time"
......@@ -35,19 +33,19 @@ func GenerateMessageID() uint64 {
// This is the top level "Store" operation of the DHT
func (s *IpfsDHT) PutValue(key u.Key, value []byte) {
complete := make(chan struct{})
for i, route := range s.routes {
for _, route := range s.routes {
p := route.NearestPeer(kb.ConvertKey(key))
if p == nil {
s.network.Chan.Errors <- fmt.Errorf("No peer found on level %d", i)
continue
s.network.Error(kb.ErrLookupFailure)
go func() {
complete <- struct{}{}
}()
continue
}
go func() {
err := s.putValueToNetwork(p, string(key), value)
if err != nil {
s.network.Chan.Errors <- err
s.network.Error(err)
}
complete <- struct{}{}
}()
......@@ -61,19 +59,46 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) {
// If the search does not succeed, a multiaddr string of a closer peer is
// returned along with util.ErrSearchIncomplete
func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) {
for _, route := range s.routes {
var p *peer.Peer
p = route.NearestPeer(kb.ConvertKey(key))
if p == nil {
return nil, errors.New("Table returned nil peer!")
}
route_level := 0
b, err := s.getValueSingle(p, key, timeout)
if err == nil {
return b, nil
p := s.routes[route_level].NearestPeer(kb.ConvertKey(key))
if p == nil {
return nil, kb.ErrLookupFailure
}
for route_level < len(s.routes) && p != nil {
pmes, err := s.getValueSingle(p, key, timeout, route_level)
if err != nil {
return nil, u.WrapError(err, "getValue Error")
}
if err != u.ErrSearchIncomplete {
return nil, err
if pmes.GetSuccess() {
if pmes.Value == nil { // We were given provider[s]
return s.getFromPeerList(key, timeout, pmes.GetPeers(), route_level)
}
// Success! We were given the value
return pmes.GetValue(), nil
} else {
// We were given a closer node
closers := pmes.GetPeers()
if len(closers) > 0 {
maddr, err := ma.NewMultiaddr(closers[0].GetAddr())
if err != nil {
// ??? Move up route level???
panic("not yet implemented")
}
// TODO: dht.Connect has overhead due to an internal
// ping to the target. Use something else
p, err = s.Connect(maddr)
if err != nil {
// Move up route level
panic("not yet implemented.")
}
} else {
route_level++
}
}
}
return nil, u.ErrNotFound
......@@ -86,7 +111,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) {
func (s *IpfsDHT) Provide(key u.Key) error {
peers := s.routes[0].NearestPeers(kb.ConvertKey(key), PoolSize)
if len(peers) == 0 {
//return an error
return kb.ErrLookupFailure
}
pmes := DHTMessage{
......@@ -97,7 +122,7 @@ func (s *IpfsDHT) Provide(key u.Key) error {
for _, p := range peers {
mes := swarm.NewMessage(p, pbmes)
s.network.Chan.Outgoing <- mes
s.network.Send(mes)
}
return nil
}
......@@ -105,6 +130,9 @@ func (s *IpfsDHT) Provide(key u.Key) error {
// FindProviders searches for peers who can provide the value for given key.
func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) {
p := s.routes[0].NearestPeer(kb.ConvertKey(key))
if p == nil {
return nil, kb.ErrLookupFailure
}
pmes := DHTMessage{
Type: PBDHTMessage_GET_PROVIDERS,
......@@ -116,7 +144,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer,
listenChan := s.ListenFor(pmes.Id, 1, time.Minute)
u.DOut("Find providers for: '%s'", key)
s.network.Chan.Outgoing <- mes
s.network.Send(mes)
after := time.After(timeout)
select {
case <-after:
......@@ -129,17 +157,12 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer,
if err != nil {
return nil, err
}
var addrs map[u.Key]string
err = json.Unmarshal(pmes_out.GetValue(), &addrs)
if err != nil {
return nil, err
}
var prov_arr []*peer.Peer
for pid, addr := range addrs {
p := s.network.Find(pid)
for _, prov := range pmes_out.GetPeers() {
p := s.network.Find(u.Key(prov.GetId()))
if p == nil {
maddr, err := ma.NewMultiaddr(addr)
maddr, err := ma.NewMultiaddr(prov.GetAddr())
if err != nil {
u.PErr("error connecting to new peer: %s", err)
continue
......@@ -162,48 +185,36 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer,
// FindPeer searches for a peer with given ID.
func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) {
p := s.routes[0].NearestPeer(kb.ConvertPeerID(id))
pmes := DHTMessage{
Type: PBDHTMessage_FIND_NODE,
Key: string(id),
Id: GenerateMessageID(),
route_level := 0
p := s.routes[route_level].NearestPeer(kb.ConvertPeerID(id))
if p == nil {
return nil, kb.ErrLookupFailure
}
mes := swarm.NewMessage(p, pmes.ToProtobuf())
listenChan := s.ListenFor(pmes.Id, 1, time.Minute)
s.network.Chan.Outgoing <- mes
after := time.After(timeout)
select {
case <-after:
s.Unlisten(pmes.Id)
return nil, u.ErrTimeout
case resp := <-listenChan:
pmes_out := new(PBDHTMessage)
err := proto.Unmarshal(resp.Data, pmes_out)
if err != nil {
return nil, err
for route_level < len(s.routes) {
pmes, err := s.findPeerSingle(p, id, timeout, route_level)
plist := pmes.GetPeers()
if len(plist) == 0 {
route_level++
}
addr := string(pmes_out.GetValue())
maddr, err := ma.NewMultiaddr(addr)
found := plist[0]
addr, err := ma.NewMultiaddr(found.GetAddr())
if err != nil {
return nil, err
return nil, u.WrapError(err, "FindPeer received bad info")
}
found_peer, err := s.Connect(maddr)
nxtPeer, err := s.Connect(addr)
if err != nil {
u.POut("Found peer but couldnt connect.")
return nil, err
return nil, u.WrapError(err, "FindPeer failed to connect to new peer.")
}
if !found_peer.ID.Equal(id) {
u.POut("FindPeer: searching for '%s' but found '%s'", id.Pretty(), found_peer.ID.Pretty())
return found_peer, u.ErrSearchIncomplete
if pmes.GetSuccess() {
return nxtPeer, nil
} else {
p = nxtPeer
}
return found_peer, nil
}
return nil, u.ErrNotFound
}
// Ping a peer, log the time it took
......@@ -216,14 +227,14 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error {
before := time.Now()
response_chan := dht.ListenFor(pmes.Id, 1, time.Minute)
dht.network.Chan.Outgoing <- mes
dht.network.Send(mes)
tout := time.After(timeout)
select {
case <-response_chan:
roundtrip := time.Since(before)
p.SetLatency(roundtrip)
u.POut("Ping took %s.", roundtrip.String())
u.DOut("Ping took %s.", roundtrip.String())
return nil
case <-tout:
// Timed out, think about removing peer from network
......@@ -249,7 +260,7 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) {
pbmes := pmes.ToProtobuf()
for _, p := range targets {
mes := swarm.NewMessage(p, pbmes)
dht.network.Chan.Outgoing <- mes
dht.network.Send(mes)
}
var out []*diagInfo
......
......@@ -3,11 +3,16 @@ package dht
import (
"bytes"
"crypto/sha256"
"errors"
peer "github.com/jbenet/go-ipfs/peer"
u "github.com/jbenet/go-ipfs/util"
)
// Returned if a routing table query returns no results. This is NOT expected
// behaviour
var ErrLookupFailure = errors.New("failed to find any peer in table")
// ID for IpfsDHT should be a byte slice, to allow for simpler operations
// (xor). DHT ids are based on the peer.IDs.
//
......@@ -19,8 +24,8 @@ func (id ID) Equal(other ID) bool {
return bytes.Equal(id, other)
}
func (id ID) Less(other interface{}) bool {
a, b := equalizeSizes(id, other.(ID))
func (id ID) Less(other ID) bool {
a, b := equalizeSizes(id, other)
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return a[i] < b[i]
......@@ -80,3 +85,14 @@ func ConvertKey(id u.Key) ID {
hash := sha256.Sum256([]byte(id))
return hash[:]
}
// Returns true if a is closer to key than b is
func Closer(a, b peer.ID, key u.Key) bool {
aid := ConvertPeerID(a)
bid := ConvertPeerID(b)
tgt := ConvertKey(key)
adist := xor(aid, tgt)
bdist := xor(bid, tgt)
return adist.Less(bdist)
}
......@@ -355,3 +355,17 @@ func (s *Swarm) Drop(p *peer.Peer) error {
return conn.Close()
}
func (s *Swarm) Send(mes *Message) {
s.Chan.Outgoing <- mes
}
func (s *Swarm) Error(e error) {
s.Chan.Errors <- e
}
func (s *Swarm) GetChan() *Chan {
return s.Chan
}
var _ Network = &Swarm{}
package util
import (
"bytes"
"errors"
"fmt"
"os"
"os/user"
"runtime"
"strings"
b58 "github.com/jbenet/go-base58"
......@@ -34,6 +36,30 @@ func (k Key) Pretty() string {
return b58.Encode([]byte(k))
}
type IpfsError struct {
Inner error
Note string
Stack string
}
func (ie *IpfsError) Error() string {
buf := new(bytes.Buffer)
fmt.Fprintln(buf, ie.Inner)
fmt.Fprintln(buf, ie.Note)
fmt.Fprintln(buf, ie.Stack)
return buf.String()
}
func WrapError(err error, note string) error {
ie := new(IpfsError)
ie.Inner = err
ie.Note = note
stack := make([]byte, 2048)
n := runtime.Stack(stack, false)
ie.Stack = string(stack[:n])
return ie
}
// Hash is the global IPFS hash function. uses multihash SHA2_256, 256 bits
func Hash(data []byte) (mh.Multihash, error) {
return mh.Sum(data, mh.SHA2_256, -1)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论