virtual.go 5.8 KB
Newer Older
Brian Tiger Chow's avatar
Brian Tiger Chow committed
1 2 3
package bitswap

import (
4
	"context"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
5
	"errors"
Steven Allen's avatar
Steven Allen committed
6
	"sync"
7
	"time"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
8

9 10 11
	bsmsg "github.com/ipfs/go-ipfs/exchange/bitswap/message"
	bsnet "github.com/ipfs/go-ipfs/exchange/bitswap/network"
	mockrouting "github.com/ipfs/go-ipfs/routing/mock"
12

Hector Sanjuan's avatar
Hector Sanjuan committed
13
	delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay"
Steven Allen's avatar
Steven Allen committed
14 15 16 17 18
	logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log"
	routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing"
	testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil"
	peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer"
	ifconnmgr "gx/ipfs/Qmax8X1Kfahf5WfSB68EWDG3d3qyS3Sqs1v412fjPTfRwx/go-libp2p-interface-connmgr"
Steven Allen's avatar
Steven Allen committed
19
	cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
20 21
)

22 23
var log = logging.Logger("bstestnet")

24
func VirtualNetwork(rs mockrouting.Server, d delay.D) Network {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
25
	return &network{
26
		clients:       make(map[peer.ID]*receiverQueue),
27
		delay:         d,
28
		routingserver: rs,
29
		conns:         make(map[string]struct{}),
Brian Tiger Chow's avatar
Brian Tiger Chow committed
30 31 32 33
	}
}

type network struct {
Steven Allen's avatar
Steven Allen committed
34
	mu            sync.Mutex
35
	clients       map[peer.ID]*receiverQueue
36 37
	routingserver mockrouting.Server
	delay         delay.D
38
	conns         map[string]struct{}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
39 40
}

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
type message struct {
	from       peer.ID
	msg        bsmsg.BitSwapMessage
	shouldSend time.Time
}

// receiverQueue queues up a set of messages to be sent, and sends them *in
// order* with their delays respected as much as sending them in order allows
// for
type receiverQueue struct {
	receiver bsnet.Receiver
	queue    []*message
	active   bool
	lk       sync.Mutex
}

57
func (n *network) Adapter(p testutil.Identity) bsnet.BitSwapNetwork {
Steven Allen's avatar
Steven Allen committed
58 59 60
	n.mu.Lock()
	defer n.mu.Unlock()

Brian Tiger Chow's avatar
Brian Tiger Chow committed
61
	client := &networkClient{
62
		local:   p.ID(),
63
		network: n,
Brian Tiger Chow's avatar
Brian Tiger Chow committed
64
		routing: n.routingserver.Client(p),
Brian Tiger Chow's avatar
Brian Tiger Chow committed
65
	}
66
	n.clients[p.ID()] = &receiverQueue{receiver: client}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
67 68 69
	return client
}

70
func (n *network) HasPeer(p peer.ID) bool {
Steven Allen's avatar
Steven Allen committed
71 72 73
	n.mu.Lock()
	defer n.mu.Unlock()

74
	_, found := n.clients[p]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
75 76 77
	return found
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
78 79 80 81
// TODO should this be completely asynchronous?
// TODO what does the network layer do with errors received from services?
func (n *network) SendMessage(
	ctx context.Context,
82 83
	from peer.ID,
	to peer.ID,
84
	mes bsmsg.BitSwapMessage) error {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
85

Steven Allen's avatar
Steven Allen committed
86 87 88
	n.mu.Lock()
	defer n.mu.Unlock()

89
	receiver, ok := n.clients[to]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
90 91 92 93 94 95 96
	if !ok {
		return errors.New("Cannot locate peer on network")
	}

	// nb: terminate the context since the context wouldn't actually be passed
	// over the network in a real scenario

97 98 99 100 101 102
	msg := &message{
		from:       from,
		msg:        mes,
		shouldSend: time.Now().Add(n.delay.Get()),
	}
	receiver.enqueue(msg)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
103 104 105 106 107

	return nil
}

func (n *network) deliver(
108 109
	r bsnet.Receiver, from peer.ID, message bsmsg.BitSwapMessage) error {
	if message == nil || from == "" {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
110 111 112
		return errors.New("Invalid input")
	}

113 114
	n.delay.Wait()

115 116
	r.ReceiveMessage(context.TODO(), from, message)
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
117 118 119
}

type networkClient struct {
120
	local peer.ID
Brian Tiger Chow's avatar
Brian Tiger Chow committed
121
	bsnet.Receiver
122
	network *network
123
	routing routing.IpfsRouting
Brian Tiger Chow's avatar
Brian Tiger Chow committed
124 125 126 127
}

func (nc *networkClient) SendMessage(
	ctx context.Context,
128
	to peer.ID,
Brian Tiger Chow's avatar
Brian Tiger Chow committed
129 130 131 132
	message bsmsg.BitSwapMessage) error {
	return nc.network.SendMessage(ctx, nc.local, to, message)
}

133
// FindProvidersAsync returns a channel of providers for the given key
134
func (nc *networkClient) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <-chan peer.ID {
135 136 137 138 139 140 141 142 143

	// NB: this function duplicates the PeerInfo -> ID transformation in the
	// bitswap network adapter. Not to worry. This network client will be
	// deprecated once the ipfsnet.Mock is added. The code below is only
	// temporary.

	out := make(chan peer.ID)
	go func() {
		defer close(out)
144
		providers := nc.routing.FindProvidersAsync(ctx, k, max)
145 146 147 148 149 150 151 152
		for info := range providers {
			select {
			case <-ctx.Done():
			case out <- info.ID:
			}
		}
	}()
	return out
153 154
}

155 156 157 158
func (nc *networkClient) ConnectionManager() ifconnmgr.ConnManager {
	return &ifconnmgr.NullConnMgr{}
}

Jeromy's avatar
Jeromy committed
159 160 161 162 163 164 165
type messagePasser struct {
	net    *network
	target peer.ID
	local  peer.ID
	ctx    context.Context
}

166 167
func (mp *messagePasser) SendMsg(ctx context.Context, m bsmsg.BitSwapMessage) error {
	return mp.net.SendMessage(ctx, mp.local, mp.target, m)
Jeromy's avatar
Jeromy committed
168 169 170 171 172 173
}

func (mp *messagePasser) Close() error {
	return nil
}

174 175 176 177
func (mp *messagePasser) Reset() error {
	return nil
}

Jeromy's avatar
Jeromy committed
178 179 180 181 182 183 184 185 186
func (n *networkClient) NewMessageSender(ctx context.Context, p peer.ID) (bsnet.MessageSender, error) {
	return &messagePasser{
		net:    n.network,
		target: p,
		local:  n.local,
		ctx:    ctx,
	}, nil
}

187
// Provide provides the key to the network
188
func (nc *networkClient) Provide(ctx context.Context, k *cid.Cid) error {
189
	return nc.routing.Provide(ctx, k, true)
190 191
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
192 193 194
func (nc *networkClient) SetDelegate(r bsnet.Receiver) {
	nc.Receiver = r
}
195 196

func (nc *networkClient) ConnectTo(_ context.Context, p peer.ID) error {
Steven Allen's avatar
Steven Allen committed
197 198 199 200 201
	nc.network.mu.Lock()

	otherClient, ok := nc.network.clients[p]
	if !ok {
		nc.network.mu.Unlock()
202 203
		return errors.New("no such peer in network")
	}
Steven Allen's avatar
Steven Allen committed
204

205 206
	tag := tagForPeers(nc.local, p)
	if _, ok := nc.network.conns[tag]; ok {
Steven Allen's avatar
Steven Allen committed
207
		nc.network.mu.Unlock()
208 209 210 211
		log.Warning("ALREADY CONNECTED TO PEER (is this a reconnect? test lib needs fixing)")
		return nil
	}
	nc.network.conns[tag] = struct{}{}
Steven Allen's avatar
Steven Allen committed
212 213
	nc.network.mu.Unlock()

214 215
	// TODO: add handling for disconnects

216
	otherClient.receiver.PeerConnected(nc.local)
217 218 219
	nc.Receiver.PeerConnected(p)
	return nil
}
220

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
func (rq *receiverQueue) enqueue(m *message) {
	rq.lk.Lock()
	defer rq.lk.Unlock()
	rq.queue = append(rq.queue, m)
	if !rq.active {
		rq.active = true
		go rq.process()
	}
}

func (rq *receiverQueue) process() {
	for {
		rq.lk.Lock()
		if len(rq.queue) == 0 {
			rq.active = false
			rq.lk.Unlock()
			return
		}
		m := rq.queue[0]
		rq.queue = rq.queue[1:]
		rq.lk.Unlock()

		time.Sleep(time.Until(m.shouldSend))
		rq.receiver.ReceiveMessage(context.TODO(), m.from, m.msg)
	}
}

248 249 250 251 252 253
func tagForPeers(a, b peer.ID) string {
	if a < b {
		return string(a + b)
	}
	return string(b + a)
}