strategy.go 2.05 KB
Newer Older
1 2 3 4 5
package strategy

import (
	"errors"

6
	bsmsg "github.com/jbenet/go-ipfs/exchange/bitswap/message"
7 8 9 10 11
	"github.com/jbenet/go-ipfs/peer"
	u "github.com/jbenet/go-ipfs/util"
)

// TODO declare thread-safe datastore
12
func New() Strategy {
13
	return &strategist{
14 15
		ledgerMap:    ledgerMap{},
		strategyFunc: yesManStrategy,
16 17 18 19
	}
}

type strategist struct {
20 21
	ledgerMap
	strategyFunc
22 23
}

24 25 26 27 28 29 30
// LedgerMap lists Ledgers by their Partner key.
type ledgerMap map[peerKey]*ledger

// FIXME share this externally
type peerKey u.Key

// Peers returns a list of peers
31
func (s *strategist) Peers() []*peer.Peer {
32 33 34 35
	response := make([]*peer.Peer, 0)
	for _, ledger := range s.ledgerMap {
		response = append(response, ledger.Partner)
	}
36 37 38
	return response
}

39
func (s *strategist) BlockIsWantedByPeer(k u.Key, p *peer.Peer) bool {
40 41
	ledger := s.ledger(p)
	return ledger.WantListContains(k)
42 43
}

44
func (s *strategist) ShouldSendBlockToPeer(k u.Key, p *peer.Peer) bool {
45 46
	ledger := s.ledger(p)
	return ledger.ShouldSend()
47 48 49 50 51 52
}

func (s *strategist) Seed(int64) {
	// TODO
}

53 54 55 56 57 58 59 60 61
func (s *strategist) MessageReceived(p *peer.Peer, m bsmsg.BitSwapMessage) error {
	l := s.ledger(p)
	for _, key := range m.Wantlist() {
		l.Wants(key)
	}
	for _, block := range m.Blocks() {
		// FIXME extract blocks.NumBytes(block) or block.NumBytes() method
		l.ReceivedBytes(len(block.Data))
	}
62 63 64
	return errors.New("TODO")
}

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
// TODO add contents of m.WantList() to my local wantlist? NB: could introduce
// race conditions where I send a message, but MessageSent gets handled after
// MessageReceived. The information in the local wantlist could become
// inconsistent. Would need to ensure that Sends and acknowledgement of the
// send happen atomically

func (s *strategist) MessageSent(p *peer.Peer, m bsmsg.BitSwapMessage) error {
	l := s.ledger(p)
	for _, block := range m.Blocks() {
		l.SentBytes(len(block.Data))
	}
	return nil
}

// ledger lazily instantiates a ledger
func (s *strategist) ledger(p *peer.Peer) *ledger {
	l, ok := s.ledgerMap[peerKey(p.Key())]
	if !ok {
		l = newLedger(p, s.strategyFunc)
		s.ledgerMap[peerKey(p.Key())] = l
	}
	return l
87
}