docs:
http://www.bittorrent.org/beps/bep_0003.html (bittorrent protocol)
http://www.bittorrent.org/beps/bep_0006.html (fast extensions)
http://www.bittorrent.org/beps/bep_0023.html (compact peer list)
http://www.bittorrent.org/beps/bep_0000.html (index)
http://wiki.theory.org/BitTorrentSpecification (warning: messy, stale and often incorrect)

clients:
http://www.bittorrent.com/
http://libtorrent.rakshasa.no/
http://azureus.sourceforge.net/
http://www.bitcomet.com/
http://www.utorrent.com/
http://dcg.ethz.ch/projects/bitthief/ (evil)
http://bittyrant.cs.washington.edu/ ("strategic")

mailing lists:
http://groups.yahoo.com/group/BitTorrent (old, no longer active)
http://lists.ibiblio.org/mailman/listinfo/bittorrent


program lifetime:
- parse torrent file
- open/create destination file
	if open existing, also check .torrent.pieces file.
		if it exists, use it to determine progress
		else, read all data to determine current state
- spawn proc that connects to tracker
- spawn proc that accepts incoming connections
- start main event loop


events:
every 10 seconds:
	determine highest up/down speed, change chokedness accordingly
every 30 seconds:
	determine new optimistic unchoke
every n minutes:
	send new tracker request
need to do tracker request:
	do request.  results will come back as new event
new list of peers from tracker, or error from tracker:
	merge in list of known peer to which we are not connected
	if below max number of connections, spawn proc to dial peers
dialed new peer/new incoming peer:
	spawn proc to initialize connection, sending bitstring of pieces we have, and reading response from remote
incoming from remote peer:
	unchoke:
		we can send requests now.
		based on gamestate (endgame, rarest-first, random, seeding), we start sending out requests.  more than one, to keep the pipeline filled.
	choke:
		arrr, peer didn't like us.  what to do when we were halfway through a piece?  let it finish by new peer?  or just discard?  finishing is most efficient for now, at least as long as we don't retaliate
	interested:
		we have something peer wants.  perhaps he is lucky and gets a piece soon, at next chokeround.
	not interested:
		peer got a piece and no longer thinks we are interesting.
		cancel all his pieces.  keep connected, he may still send us data.
	have:
		peer has some piece.  perhaps we want it.  if we are unchoked by remote, and are not currently busy with a piece, we should start requesting that piece.
	request:
		peer wants a part of some piece.
		check if he is really unchoked (if not, discard message)
		check if we have this piece (if not, discard message)
		queue request somewhere, making sure it gets send to the remote peer eventually
	cancel:
		peer no longer wants a part of some piece he previously requested
		remote from the queue (if it is there at all)
	piece:
		we have a part of a piece from the peer
		verify we requested it from the peer
		verify we still need it
		make sure it gets written to disk
		if last piece, check if piece is correct.
			if not, disconnect peer (blacklist for some time in the future?)
		write piece to file
		pick next piece to request from peer, and queue requests.
	keepalive:
		peer says it is still alive, nothing to do...


processes:

main
	where everything comes together
ticker
        for periodic events.  sends to main() once every 10 seconds
        (for (un)choking, calculating stats)
listener
        for incoming connections.  handshakes with new peer, sends
        peer on newpeerchan.  only listens after receive on
        canlistenchan.
track,
        tracker requester.  waits on trackreqchan for stats to send
        to tracker, performs request, and sends to new interval,peers,error
        to trackchan (main).
limiter (upload)
        all uploaded traffic goes through this, upchan gets number
        of requested bytes and response channel.  limiter gives
        bandwidth it has, then waits until it can give out bandwidth
        again.
limiter (download)
	same as uploader, but for incoming data.
disk writer
	xxx

per peer:
peernetreader
        loops: msgread(), which uses netread() which uses the
        limiter), sends msg,peer,xxx to peerinmsgchan (main()).
        xxx does something smart with writing requests to peer, to
        keep us from reading too many message without slowing down
        on writes to that peer
peernetwriter
        loops: send on wantmsgchan (main()) to indicate it can
        write, receive a list of messages to write on peer's getmsgch,
        netwrite's the packed message.
diskreader
        loops: receives request (piece,begin,length) from peer's
        readch (send by readblock(), from handleinmsg()), reads
        data from file, responds on diskreadchan (main()).
diskwriter
        loops: receives request (piece, begin, buf), writes data
        to file, sends on diskwrittenchan (main()).

dialer
        spawned in dialpeers(), called from main().  dials peers,
        does handshake, and sends new peer on newpeerchan (main).
sched->schedule()
	xxx why in a prog?  i recall there was a reason to do that...
trackkick
        sleeps n msec, then sends to trackkickchan (main) to cause
        a new tracker request (by sending stats on trackreqchan,
        to prog track())
kicklistener
	just sends on canlistenchan to make listener() do another listen
dialkiller
        kills the dialer after Dialtimeout seconds, unless it is
        killed first because the dialer was done.

state:
- pieces we currently have
- pieces we still need
- which peer has which pieces
- whom we are currently requesting which pieces from
- which pieces have not yet been requested
- which peer addresses do we know
- which peers are we connected to
- what is the chokeness/interestedness status of the connected peers, remote and local
- how many blocks of a piece do we have?
- for each peer, how much did we transfer in the past n (10) seconds?
- last activity at all from peer



considerations:
- the procs reading pieces from peers need to be directly connected to the procs that write to disk.  and vice versa (procs writing pieces to peer need to be connected to procs that read from disk).  if this weren't so, we could be building up huge buffers (e.g. for very slow disk):  we read more from the network than we write to disk.  by directly connecting them (through a buffered channel probably), the procs get some leeway but the situation does not get out of hand.
- we send multiple blocks to peers, queued.  we need to keep track of a queue of blocks the remote side wants (if they queue too many request, we'll just have to disconnect them).  the queue has to be accessible because it may need to be flushed.

writing/reading from the network/disk is a big issue, so it gets its own "section".

reading from peer/writing to disk:
- done in a proc, "peer net reader".  messages are read, and sent on a buffered channel with low capacity, say 4 messages.  the other proc is called "peer disk writer".
- "peer disk writer" checks if message is a block.  if so, it checks whether it is expected.  if so, it writes it to disk.  if not it discards the message, making sure it doesn't count as download traffic.  now it forwards the message to the main event loop.
- peerdiskwriter checks whether a message is allowed by asking piecekeeper, over a channel.  piecekeepers data is kept updated by main.
- all messages from peernetreader go through peerdiskwriter.  this is to ensure messages are delivered in-order.
- the channel between peerdiskwriter and main is not buffered.  main should always be available.
- multiple peerdiskwriter (one per peer even) exist.  this is okay, writes to the destination files never overlap.


writing to peer/reading from disk:
- we want to keep peer updated on chokeness/interestedness, cancels and requests
- data we send is much bulkier and does not have such a high priority.  thus we create two channels to send on, one for pieces/bulk/low-priority, one for meta/small/high-priority
- main will have to queue new requests and dequeue when receiving cancel messages
- proc peernetwriter will receive messages from the two channels and write to the peer.  messages with blocks already contain their data, which has been read from disk.
- once data has been read from disk, we always send it.  the worst we do is that the peer receives data he no longer wants, and probably won't count it for reciprocation.  if we were to drop the data, we might have a denial of service:  the peer can time messages so that we drop it just after reading it from disk, wasting our disk bandwidth.
- the channel with the bulk/block/data messages, read by peernetwriter, is filled by peerdiskreader on the other side.  non-block messages do not pass through this proc.  the channel is buffered for a few data messages.
- peerdiskreader requests info for next block to read from main, over channel.  main will keep channel when no next block has been requested.  where there is a next block (either because it came in or it was already in the queue), the request is responded to with the new block info, over a response channel given in the original request.
- xxx perhaps we should drop messages when peer chokes us and later unchokes us?  makes sense.  on choke, we can probably send a special "flush data messages from peerdiskreader"-message.


rate limiting:
- every read/write must go through a proc that handles the limiting.  it sends a request for reading/writing x bytes.  the proc then responds with the number of bytes that can be read/written.  this is like readn/read.


file system:
ctl
	for setting up/down speeds
	command to read new info from tracker
	stop seeding/quit?
	disconnect some peer

event
	for reading:
	- connected to peer
	- disconnected from peer
	- have new peers from tracker
	- have piece x
	- have pieces <bitstring> (read once, for first read)

stats
	for reading, at read stats are generated:
		eta, up/down speed, up/down bytes, etc

pieces
	read bitstring of pieces?

peers/list
	read list of peers as received from tracker
peers/n
        state
		for reading up/down speed/bytes, local/remote interested/chokeness, which pieces
