client.go 3.93 KB
Newer Older
1
package supernode
2 3 4

import (
	"bytes"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5
	"errors"
6 7
	"time"

8
	proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
9
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
10

11
	key "github.com/ipfs/go-ipfs/blocks/key"
12 13 14 15 16
	"github.com/ipfs/go-ipfs/p2p/host"
	peer "github.com/ipfs/go-ipfs/p2p/peer"
	routing "github.com/ipfs/go-ipfs/routing"
	pb "github.com/ipfs/go-ipfs/routing/dht/pb"
	proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy"
Jeromy's avatar
Jeromy committed
17
	logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0"
18 19
)

Jeromy's avatar
Jeromy committed
20
var log = logging.Logger("supernode")
21 22

type Client struct {
23
	peerhost  host.Host
24 25 26 27 28 29
	peerstore peer.Peerstore
	proxy     proxy.Proxy
	local     peer.ID
}

// TODO take in datastore/cache
30
func NewClient(px proxy.Proxy, h host.Host, ps peer.Peerstore, local peer.ID) (*Client, error) {
31 32 33 34
	return &Client{
		proxy:     px,
		local:     local,
		peerstore: ps,
35
		peerhost:  h,
36 37 38
	}, nil
}

39
func (c *Client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan peer.PeerInfo {
Jeromy's avatar
Jeromy committed
40
	ctx = logging.ContextWithLoggable(ctx, logging.Uuid("findProviders"))
Brian Tiger Chow's avatar
Brian Tiger Chow committed
41
	defer log.EventBegin(ctx, "findProviders", &k).Done()
42 43 44 45 46 47
	ch := make(chan peer.PeerInfo)
	go func() {
		defer close(ch)
		request := pb.NewMessage(pb.Message_GET_PROVIDERS, string(k), 0)
		response, err := c.proxy.SendRequest(ctx, request)
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
48
			log.Debug(err)
49 50 51 52 53
			return
		}
		for _, p := range pb.PBPeersToPeerInfos(response.GetProviderPeers()) {
			select {
			case <-ctx.Done():
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
54
				log.Debug(ctx.Err())
55 56 57 58 59 60 61 62
				return
			case ch <- p:
			}
		}
	}()
	return ch
}

63
func (c *Client) PutValue(ctx context.Context, k key.Key, v []byte) error {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
64
	defer log.EventBegin(ctx, "putValue", &k).Done()
65 66 67 68 69 70 71 72 73
	r, err := makeRecord(c.peerstore, c.local, k, v)
	if err != nil {
		return err
	}
	pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(k), 0)
	pmes.Record = r
	return c.proxy.SendMessage(ctx, pmes) // wrap to hide the remote
}

74
func (c *Client) GetValue(ctx context.Context, k key.Key) ([]byte, error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
75
	defer log.EventBegin(ctx, "getValue", &k).Done()
76 77 78
	msg := pb.NewMessage(pb.Message_GET_VALUE, string(k), 0)
	response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
79
		return nil, err
80 81 82 83
	}
	return response.Record.GetValue(), nil
}

84
func (c *Client) Provide(ctx context.Context, k key.Key) error {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
85
	defer log.EventBegin(ctx, "provide", &k).Done()
86
	msg := pb.NewMessage(pb.Message_ADD_PROVIDER, string(k), 0)
87 88
	// FIXME how is connectedness defined for the local node
	pri := []pb.PeerRoutingInfo{
rht's avatar
rht committed
89
		{
90
			PeerInfo: peer.PeerInfo{
91 92
				ID:    c.local,
				Addrs: c.peerhost.Addrs(),
93 94 95 96 97
			},
		},
	}
	msg.ProviderPeers = pb.PeerRoutingInfosToPBPeers(pri)
	return c.proxy.SendMessage(ctx, msg) // TODO wrap to hide remote
98 99 100
}

func (c *Client) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
101
	defer log.EventBegin(ctx, "findPeer", id).Done()
102 103 104
	request := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0)
	response, err := c.proxy.SendRequest(ctx, request) // hide remote
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
105
		return peer.PeerInfo{}, err
106 107 108 109 110 111 112 113 114 115
	}
	for _, p := range pb.PBPeersToPeerInfos(response.GetCloserPeers()) {
		if p.ID == id {
			return p, nil
		}
	}
	return peer.PeerInfo{}, errors.New("could not find peer")
}

// creates and signs a record for the given key/value pair
116
func makeRecord(ps peer.Peerstore, p peer.ID, k key.Key, v []byte) (*pb.Record, error) {
117 118 119 120 121 122 123 124 125 126 127 128 129 130
	blob := bytes.Join([][]byte{[]byte(k), v, []byte(p)}, []byte{})
	sig, err := ps.PrivKey(p).Sign(blob)
	if err != nil {
		return nil, err
	}
	return &pb.Record{
		Key:       proto.String(string(k)),
		Value:     v,
		Author:    proto.String(string(p)),
		Signature: sig,
	}, nil
}

func (c *Client) Ping(ctx context.Context, id peer.ID) (time.Duration, error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
131
	defer log.EventBegin(ctx, "ping", id).Done()
132
	return time.Nanosecond, errors.New("supernode routing does not support the ping method")
133 134
}

135 136 137 138
func (c *Client) Bootstrap(ctx context.Context) error {
	return c.proxy.Bootstrap(ctx)
}

139
var _ routing.IpfsRouting = &Client{}