github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/exchange/bitswap/bitswap.go (about)

     1  // package bitswap implements the IPFS Exchange interface with the BitSwap
     2  // bilateral exchange protocol.
     3  package bitswap
     4  
     5  import (
     6  	"time"
     7  
     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"
    10  
    11  	blocks "github.com/jbenet/go-ipfs/blocks"
    12  	blockstore "github.com/jbenet/go-ipfs/blockstore"
    13  	exchange "github.com/jbenet/go-ipfs/exchange"
    14  	bsmsg "github.com/jbenet/go-ipfs/exchange/bitswap/message"
    15  	bsnet "github.com/jbenet/go-ipfs/exchange/bitswap/network"
    16  	notifications "github.com/jbenet/go-ipfs/exchange/bitswap/notifications"
    17  	strategy "github.com/jbenet/go-ipfs/exchange/bitswap/strategy"
    18  	inet "github.com/jbenet/go-ipfs/net"
    19  	peer "github.com/jbenet/go-ipfs/peer"
    20  	u "github.com/jbenet/go-ipfs/util"
    21  )
    22  
    23  var log = u.Logger("bitswap")
    24  
    25  // NetMessageSession initializes a BitSwap session that communicates over the
    26  // provided NetMessage service.
    27  // Runs until context is cancelled
    28  func NetMessageSession(ctx context.Context, p peer.Peer,
    29  	net inet.Network, srv inet.Service, directory bsnet.Routing,
    30  	d ds.ThreadSafeDatastore, nice bool) exchange.Interface {
    31  
    32  	networkAdapter := bsnet.NetMessageAdapter(srv, net, nil)
    33  
    34  	notif := notifications.New()
    35  
    36  	go func() {
    37  		select {
    38  		case <-ctx.Done():
    39  			notif.Shutdown()
    40  		}
    41  	}()
    42  
    43  	bs := &bitswap{
    44  		blockstore:    blockstore.NewBlockstore(d),
    45  		notifications: notif,
    46  		strategy:      strategy.New(nice),
    47  		routing:       directory,
    48  		sender:        networkAdapter,
    49  		wantlist:      u.NewKeySet(),
    50  	}
    51  	networkAdapter.SetDelegate(bs)
    52  
    53  	return bs
    54  }
    55  
    56  // bitswap instances implement the bitswap protocol.
    57  type bitswap struct {
    58  
    59  	// sender delivers messages on behalf of the session
    60  	sender bsnet.Adapter
    61  
    62  	// blockstore is the local database
    63  	// NB: ensure threadsafety
    64  	blockstore blockstore.Blockstore
    65  
    66  	// routing interface for communication
    67  	routing bsnet.Routing
    68  
    69  	notifications notifications.PubSub
    70  
    71  	// strategy listens to network traffic and makes decisions about how to
    72  	// interact with partners.
    73  	// TODO(brian): save the strategy's state to the datastore
    74  	strategy strategy.Strategy
    75  
    76  	wantlist u.KeySet
    77  }
    78  
    79  // GetBlock attempts to retrieve a particular block from peers within the
    80  // deadline enforced by the context
    81  //
    82  // TODO ensure only one active request per key
    83  func (bs *bitswap) Block(parent context.Context, k u.Key) (*blocks.Block, error) {
    84  	log.Debugf("Get Block %v", k)
    85  	now := time.Now()
    86  	defer func() {
    87  		log.Debugf("GetBlock took %f secs", time.Now().Sub(now).Seconds())
    88  	}()
    89  
    90  	ctx, cancelFunc := context.WithCancel(parent)
    91  	defer cancelFunc()
    92  
    93  	bs.wantlist.Add(k)
    94  	promise := bs.notifications.Subscribe(ctx, k)
    95  
    96  	const maxProviders = 20
    97  	peersToQuery := bs.routing.FindProvidersAsync(ctx, k, maxProviders)
    98  
    99  	go func() {
   100  		message := bsmsg.New()
   101  		for _, wanted := range bs.wantlist.Keys() {
   102  			message.AddWanted(wanted)
   103  		}
   104  		for peerToQuery := range peersToQuery {
   105  			log.Debugf("bitswap got peersToQuery: %s", peerToQuery)
   106  			go func(p peer.Peer) {
   107  
   108  				log.Debugf("bitswap dialing peer: %s", p)
   109  				err := bs.sender.DialPeer(ctx, p)
   110  				if err != nil {
   111  					log.Errorf("Error sender.DialPeer(%s)", p)
   112  					return
   113  				}
   114  
   115  				response, err := bs.sender.SendRequest(ctx, p, message)
   116  				if err != nil {
   117  					log.Error("Error sender.SendRequest(%s) = %s", p, err)
   118  					return
   119  				}
   120  				// FIXME ensure accounting is handled correctly when
   121  				// communication fails. May require slightly different API to
   122  				// get better guarantees. May need shared sequence numbers.
   123  				bs.strategy.MessageSent(p, message)
   124  
   125  				if response == nil {
   126  					return
   127  				}
   128  				bs.ReceiveMessage(ctx, p, response)
   129  			}(peerToQuery)
   130  		}
   131  	}()
   132  
   133  	select {
   134  	case block := <-promise:
   135  		bs.wantlist.Remove(k)
   136  		return &block, nil
   137  	case <-parent.Done():
   138  		return nil, parent.Err()
   139  	}
   140  }
   141  
   142  // HasBlock announces the existance of a block to this bitswap service. The
   143  // service will potentially notify its peers.
   144  func (bs *bitswap) HasBlock(ctx context.Context, blk blocks.Block) error {
   145  	log.Debugf("Has Block %v", blk.Key())
   146  	bs.wantlist.Remove(blk.Key())
   147  	bs.sendToPeersThatWant(ctx, blk)
   148  	return bs.routing.Provide(ctx, blk.Key())
   149  }
   150  
   151  // TODO(brian): handle errors
   152  func (bs *bitswap) ReceiveMessage(ctx context.Context, p peer.Peer, incoming bsmsg.BitSwapMessage) (
   153  	peer.Peer, bsmsg.BitSwapMessage) {
   154  	log.Debugf("ReceiveMessage from %v", p.Key())
   155  	log.Debugf("Message wantlist: %v", incoming.Wantlist())
   156  
   157  	if p == nil {
   158  		log.Error("Received message from nil peer!")
   159  		// TODO propagate the error upward
   160  		return nil, nil
   161  	}
   162  	if incoming == nil {
   163  		log.Error("Got nil bitswap message!")
   164  		// TODO propagate the error upward
   165  		return nil, nil
   166  	}
   167  
   168  	// Record message bytes in ledger
   169  	// TODO: this is bad, and could be easily abused.
   170  	// Should only track *useful* messages in ledger
   171  	bs.strategy.MessageReceived(p, incoming) // FIRST
   172  
   173  	for _, block := range incoming.Blocks() {
   174  		// TODO verify blocks?
   175  		if err := bs.blockstore.Put(&block); err != nil {
   176  			continue // FIXME(brian): err ignored
   177  		}
   178  		bs.notifications.Publish(block)
   179  		err := bs.HasBlock(ctx, block)
   180  		if err != nil {
   181  			log.Warningf("HasBlock errored: %s", err)
   182  		}
   183  	}
   184  
   185  	message := bsmsg.New()
   186  	for _, wanted := range bs.wantlist.Keys() {
   187  		message.AddWanted(wanted)
   188  	}
   189  	for _, key := range incoming.Wantlist() {
   190  		// TODO: might be better to check if we have the block before checking
   191  		//			if we should send it to someone
   192  		if bs.strategy.ShouldSendBlockToPeer(key, p) {
   193  			if block, errBlockNotFound := bs.blockstore.Get(key); errBlockNotFound != nil {
   194  				continue
   195  			} else {
   196  				message.AddBlock(*block)
   197  			}
   198  		}
   199  	}
   200  	defer bs.strategy.MessageSent(p, message)
   201  
   202  	log.Debug("Returning message.")
   203  	return p, message
   204  }
   205  
   206  func (bs *bitswap) ReceiveError(err error) {
   207  	log.Errorf("Bitswap ReceiveError: %s", err)
   208  	// TODO log the network error
   209  	// TODO bubble the network error up to the parent context/error logger
   210  }
   211  
   212  // send strives to ensure that accounting is always performed when a message is
   213  // sent
   214  func (bs *bitswap) send(ctx context.Context, p peer.Peer, m bsmsg.BitSwapMessage) {
   215  	bs.sender.SendMessage(ctx, p, m)
   216  	bs.strategy.MessageSent(p, m)
   217  }
   218  
   219  func (bs *bitswap) sendToPeersThatWant(ctx context.Context, block blocks.Block) {
   220  	log.Debugf("Sending %v to peers that want it", block.Key())
   221  
   222  	for _, p := range bs.strategy.Peers() {
   223  		if bs.strategy.BlockIsWantedByPeer(block.Key(), p) {
   224  			log.Debugf("%v wants %v", p, block.Key())
   225  			if bs.strategy.ShouldSendBlockToPeer(block.Key(), p) {
   226  				message := bsmsg.New()
   227  				message.AddBlock(block)
   228  				for _, wanted := range bs.wantlist.Keys() {
   229  					message.AddWanted(wanted)
   230  				}
   231  				bs.send(ctx, p, message)
   232  			}
   233  		}
   234  	}
   235  }