bitswap.go 8.62 KB
Newer Older
Brian Tiger Chow's avatar
Brian Tiger Chow committed
1 2
// package bitswap implements the IPFS Exchange interface with the BitSwap
// bilateral exchange protocol.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
3 4 5
package bitswap

import (
Jeromy's avatar
Jeromy committed
6 7
	"time"

8
	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
9
	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
10

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
11
	blocks "github.com/jbenet/go-ipfs/blocks"
12
	blockstore "github.com/jbenet/go-ipfs/blockstore"
13 14 15 16 17
	exchange "github.com/jbenet/go-ipfs/exchange"
	bsmsg "github.com/jbenet/go-ipfs/exchange/bitswap/message"
	bsnet "github.com/jbenet/go-ipfs/exchange/bitswap/network"
	notifications "github.com/jbenet/go-ipfs/exchange/bitswap/notifications"
	strategy "github.com/jbenet/go-ipfs/exchange/bitswap/strategy"
18
	peer "github.com/jbenet/go-ipfs/peer"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
19
	u "github.com/jbenet/go-ipfs/util"
20
	"github.com/jbenet/go-ipfs/util/eventlog"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
21 22
)

23
var log = eventlog.Logger("bitswap")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
24

25 26 27
// New initializes a BitSwap instance that communicates over the
// provided BitSwapNetwork. This function registers the returned instance as
// the network delegate.
28
// Runs until context is cancelled
29 30
func New(ctx context.Context, p peer.Peer,
	network bsnet.BitSwapNetwork, routing bsnet.Routing,
31
	d ds.ThreadSafeDatastore, nice bool) exchange.Interface {
32

33 34
	notif := notifications.New()
	go func() {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
35 36
		<-ctx.Done()
		notif.Shutdown()
37 38
	}()

39 40
	bs := &bitswap{
		blockstore:    blockstore.NewBlockstore(d),
41
		notifications: notif,
42
		strategy:      strategy.New(nice),
43
		routing:       routing,
Brian Tiger Chow's avatar
Brian Tiger Chow committed
44
		sender:        network,
45
		wantlist:      u.NewKeySet(),
Brian Tiger Chow's avatar
naming  
Brian Tiger Chow committed
46
		blockRequests: make(chan u.Key, 32),
47
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
48
	network.SetDelegate(bs)
Jeromy's avatar
Jeromy committed
49
	go bs.run(ctx)
50 51 52 53

	return bs
}

54 55
// bitswap instances implement the bitswap protocol.
type bitswap struct {
56

57
	// sender delivers messages on behalf of the session
58
	sender bsnet.BitSwapNetwork
59

60
	// blockstore is the local database
61
	// NB: ensure threadsafety
62
	blockstore blockstore.Blockstore
63

64
	// routing interface for communication
65
	routing bsnet.Routing
66

67
	notifications notifications.PubSub
68

Brian Tiger Chow's avatar
naming  
Brian Tiger Chow committed
69
	blockRequests chan u.Key
Jeromy's avatar
Jeromy committed
70

71
	// strategy listens to network traffic and makes decisions about how to
72
	// interact with partners.
73 74
	// TODO(brian): save the strategy's state to the datastore
	strategy strategy.Strategy
75

76
	wantlist u.KeySet
77 78
}

79 80
// GetBlock attempts to retrieve a particular block from peers within the
// deadline enforced by the context
81 82
//
// TODO ensure only one active request per key
Jeromy's avatar
Jeromy committed
83
func (bs *bitswap) GetBlock(parent context.Context, k u.Key) (*blocks.Block, error) {
84 85 86 87

	// make sure to derive a new |ctx| and pass it to children. It's correct to
	// listen on |parent| here, but incorrect to pass |parent| to new async
	// functions. This is difficult to enforce. May this comment keep you safe.
88

89
	ctx, cancelFunc := context.WithCancel(parent)
90 91
	defer cancelFunc()

92 93
	ctx = eventlog.ContextWithMetadata(ctx, eventlog.Uuid("GetBlockRequest"))
	log.Event(ctx, "GetBlockRequestBegin", &k)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
94
	defer log.Event(ctx, "GetBlockRequestEnd", &k)
95

96
	bs.wantlist.Add(k)
97
	promise := bs.notifications.Subscribe(ctx, k)
98

Jeromy's avatar
Jeromy committed
99
	select {
Brian Tiger Chow's avatar
naming  
Brian Tiger Chow committed
100
	case bs.blockRequests <- k:
Jeromy's avatar
Jeromy committed
101 102 103
	case <-parent.Done():
		return nil, parent.Err()
	}
104 105

	select {
106
	case block := <-promise:
107
		bs.wantlist.Remove(k)
108
		return &block, nil
109 110
	case <-parent.Done():
		return nil, parent.Err()
111 112 113
	}
}

Jeromy's avatar
Jeromy committed
114 115 116 117 118 119
func (bs *bitswap) GetBlocks(parent context.Context, ks []u.Key) (*blocks.Block, error) {
	// TODO: something smart
	return nil, nil
}

func (bs *bitswap) sendWantListTo(ctx context.Context, peers <-chan peer.Peer) error {
Jeromy's avatar
Jeromy committed
120 121 122
	if peers == nil {
		panic("Cant send wantlist to nil peerchan")
	}
Jeromy's avatar
Jeromy committed
123 124 125 126 127
	message := bsmsg.New()
	for _, wanted := range bs.wantlist.Keys() {
		message.AddWanted(wanted)
	}
	for peerToQuery := range peers {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
128
		log.Event(ctx, "PeerToQuery", peerToQuery)
Jeromy's avatar
Jeromy committed
129 130
		go func(p peer.Peer) {

Brian Tiger Chow's avatar
Brian Tiger Chow committed
131
			log.Event(ctx, "DialPeer", p)
Jeromy's avatar
Jeromy committed
132 133
			err := bs.sender.DialPeer(ctx, p)
			if err != nil {
134
				log.Errorf("Error sender.DialPeer(%s): %s", p, err)
Jeromy's avatar
Jeromy committed
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
				return
			}

			response, err := bs.sender.SendRequest(ctx, p, message)
			if err != nil {
				log.Errorf("Error sender.SendRequest(%s) = %s", p, err)
				return
			}
			// FIXME ensure accounting is handled correctly when
			// communication fails. May require slightly different API to
			// get better guarantees. May need shared sequence numbers.
			bs.strategy.MessageSent(p, message)

			if response == nil {
				return
			}
			bs.ReceiveMessage(ctx, p, response)
		}(peerToQuery)
	}
	return nil
}

func (bs *bitswap) run(ctx context.Context) {

159 160
	// Every so often, we should resend out our current want list
	rebroadcastTime := time.Second * 5
Brian Tiger Chow's avatar
Brian Tiger Chow committed
161

Brian Tiger Chow's avatar
Brian Tiger Chow committed
162
	var providers <-chan peer.Peer // NB: must be initialized to zero value
Jeromy's avatar
Jeromy committed
163
	broadcastSignal := time.After(bs.strategy.GetRebroadcastDelay())
Brian Tiger Chow's avatar
Brian Tiger Chow committed
164

Jeromy's avatar
Jeromy committed
165 166
	// Number of unsent keys for the current batch
	unsentKeys := 0
Jeromy's avatar
Jeromy committed
167 168
	for {
		select {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
169
		case <-broadcastSignal:
Jeromy's avatar
Jeromy committed
170
			unsentKeys = 0
Jeromy's avatar
Jeromy committed
171 172 173 174
			wantlist := bs.wantlist.Keys()
			if len(wantlist) == 0 {
				continue
			}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
175
			if providers == nil {
Jeromy's avatar
Jeromy committed
176
				// rely on semi randomness of maps
Jeromy's avatar
Jeromy committed
177
				firstKey := wantlist[0]
Jeromy's avatar
Jeromy committed
178
				providers = bs.routing.FindProvidersAsync(ctx, firstKey, maxProvidersPerRequest)
Jeromy's avatar
Jeromy committed
179
			}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
180
			err := bs.sendWantListTo(ctx, providers)
Jeromy's avatar
Jeromy committed
181
			if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
182
				log.Errorf("error sending wantlist: %s", err)
Jeromy's avatar
Jeromy committed
183
			}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
184
			providers = nil
Jeromy's avatar
Jeromy committed
185
			broadcastSignal = time.After(bs.strategy.GetRebroadcastDelay())
Brian Tiger Chow's avatar
Brian Tiger Chow committed
186

Brian Tiger Chow's avatar
naming  
Brian Tiger Chow committed
187
		case k := <-bs.blockRequests:
Brian Tiger Chow's avatar
Brian Tiger Chow committed
188 189
			if unsentKeys == 0 {
				providers = bs.routing.FindProvidersAsync(ctx, k, maxProvidersPerRequest)
Jeromy's avatar
Jeromy committed
190
			}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
191
			unsentKeys++
Jeromy's avatar
Jeromy committed
192

Jeromy's avatar
Jeromy committed
193
			if unsentKeys >= bs.strategy.GetBatchSize() {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
194 195
				// send wantlist to providers
				err := bs.sendWantListTo(ctx, providers)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
196 197 198
				if err != nil {
					log.Errorf("error sending wantlist: %s", err)
				}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
199
				unsentKeys = 0
Jeromy's avatar
Jeromy committed
200
				broadcastSignal = time.After(bs.strategy.GetRebroadcastDelay())
Brian Tiger Chow's avatar
Brian Tiger Chow committed
201
				providers = nil
Jeromy's avatar
Jeromy committed
202 203 204
			} else {
				// set a timeout to wait for more blocks or send current wantlist

Jeromy's avatar
Jeromy committed
205
				broadcastSignal = time.After(bs.strategy.GetBatchDelay())
Jeromy's avatar
Jeromy committed
206 207 208 209 210 211 212
			}
		case <-ctx.Done():
			return
		}
	}
}

213 214
// HasBlock announces the existance of a block to this bitswap service. The
// service will potentially notify its peers.
215
func (bs *bitswap) HasBlock(ctx context.Context, blk blocks.Block) error {
216
	log.Debugf("Has Block %v", blk.Key())
217
	bs.wantlist.Remove(blk.Key())
218
	bs.sendToPeersThatWant(ctx, blk)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
219
	return bs.routing.Provide(ctx, blk.Key())
220 221
}

222
// TODO(brian): handle errors
223 224
func (bs *bitswap) ReceiveMessage(ctx context.Context, p peer.Peer, incoming bsmsg.BitSwapMessage) (
	peer.Peer, bsmsg.BitSwapMessage) {
Jeromy's avatar
Jeromy committed
225
	log.Debugf("ReceiveMessage from %s", p)
226
	log.Debugf("Message wantlist: %v", incoming.Wantlist())
227

228
	if p == nil {
229
		log.Error("Received message from nil peer!")
230 231
		// TODO propagate the error upward
		return nil, nil
232 233
	}
	if incoming == nil {
234
		log.Error("Got nil bitswap message!")
235 236
		// TODO propagate the error upward
		return nil, nil
237
	}
238

239 240 241
	// Record message bytes in ledger
	// TODO: this is bad, and could be easily abused.
	// Should only track *useful* messages in ledger
242
	bs.strategy.MessageReceived(p, incoming) // FIRST
243

244
	for _, block := range incoming.Blocks() {
245
		// TODO verify blocks?
246
		if err := bs.blockstore.Put(&block); err != nil {
Jeromy's avatar
Jeromy committed
247
			log.Criticalf("error putting block: %s", err)
248
			continue // FIXME(brian): err ignored
249
		}
250 251 252 253 254
		bs.notifications.Publish(block)
		err := bs.HasBlock(ctx, block)
		if err != nil {
			log.Warningf("HasBlock errored: %s", err)
		}
255 256
	}

257 258
	message := bsmsg.New()
	for _, wanted := range bs.wantlist.Keys() {
259
		message.AddWanted(wanted)
260
	}
261
	for _, key := range incoming.Wantlist() {
262 263
		// TODO: might be better to check if we have the block before checking
		//			if we should send it to someone
264
		if bs.strategy.ShouldSendBlockToPeer(key, p) {
265 266 267
			if block, errBlockNotFound := bs.blockstore.Get(key); errBlockNotFound != nil {
				continue
			} else {
268
				message.AddBlock(*block)
269
			}
270 271
		}
	}
272

Jeromy's avatar
Jeromy committed
273
	bs.strategy.MessageSent(p, message)
274
	log.Debug("Returning message.")
275 276 277 278
	return p, message
}

func (bs *bitswap) ReceiveError(err error) {
279
	log.Errorf("Bitswap ReceiveError: %s", err)
280 281
	// TODO log the network error
	// TODO bubble the network error up to the parent context/error logger
282
}
283

284 285
// send strives to ensure that accounting is always performed when a message is
// sent
286
func (bs *bitswap) send(ctx context.Context, p peer.Peer, m bsmsg.BitSwapMessage) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
287
	bs.sender.SendMessage(ctx, p, m)
288
	bs.strategy.MessageSent(p, m)
289 290
}

291
func (bs *bitswap) sendToPeersThatWant(ctx context.Context, block blocks.Block) {
292
	log.Debugf("Sending %v to peers that want it", block.Key())
293

294 295
	for _, p := range bs.strategy.Peers() {
		if bs.strategy.BlockIsWantedByPeer(block.Key(), p) {
296
			log.Debugf("%v wants %v", p, block.Key())
297 298
			if bs.strategy.ShouldSendBlockToPeer(block.Key(), p) {
				message := bsmsg.New()
299
				message.AddBlock(block)
300
				for _, wanted := range bs.wantlist.Keys() {
301
					message.AddWanted(wanted)
302
				}
303
				bs.send(ctx, p, message)
304 305 306 307
			}
		}
	}
}