Add files for swift over UDP.
[swifty.git] / src / libswift_udp / swift.h
diff --git a/src/libswift_udp/swift.h b/src/libswift_udp/swift.h
new file mode 100644 (file)
index 0000000..00275a1
--- /dev/null
@@ -0,0 +1,781 @@
+/*
+ *  swift.h
+ *  the main header file for libswift, normally you should only read this one
+ *
+ *  Created by Victor Grishchenko on 3/6/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+/*
+
+  The swift protocol
+
+  Messages
+
+  HANDSHAKE    00, channelid
+  Communicates the channel id of the sender. The
+  initial handshake packet also has the root hash
+  (a HASH message).
+
+  DATA        01, bin_32, buffer
+  1K of data.
+
+  ACK        02, bin_32, timestamp_32
+  HAVE       03, bin_32
+  Confirms successfull delivery of data. Used for
+  congestion control, as well.
+
+  HINT        08, bin_32
+  Practical value of "hints" is to avoid overlap, mostly.
+  Hints might be lost in the network or ignored.
+  Peer might send out data without a hint.
+  Hint which was not responded (by DATA) in some RTTs
+  is considered to be ignored.
+  As peers cant pick randomly kilobyte here and there,
+  they send out "long hints" for non-base bins.
+
+  HASH        04, bin_32, sha1hash
+  SHA1 hash tree hashes for data verification. The
+  connection to a fresh peer starts with bootstrapping
+  him with peak hashes. Later, before sending out
+  any data, a peer sends the necessary uncle hashes.
+
+  PEX+/PEX-    05/06, ipv4 addr, port
+  Peer exchange messages; reports all connected and
+  disconected peers. Might has special meaning (as
+  in the case with swarm supervisors).
+
+*/
+#ifndef SWIFT_H
+#define SWIFT_H
+
+#include <deque>
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <string>
+#include <math.h>
+
+#include "compat.h"
+#include <event2/event.h>
+#include <event2/event_struct.h>
+#include <event2/buffer.h>
+#include "bin.h"
+#include "binmap.h"
+#include "hashtree.h"
+#include "avgspeed.h"
+#include "availability.h"
+
+
+namespace swift {
+
+#define SWIFT_MAX_UDP_OVER_ETH_PAYLOAD         (1500-20-8)
+// Arno: Maximum size of non-DATA messages in a UDP packet we send.
+#define SWIFT_MAX_NONDATA_DGRAM_SIZE           (SWIFT_MAX_UDP_OVER_ETH_PAYLOAD-SWIFT_DEFAULT_CHUNK_SIZE-1-4)
+// Arno: Maximum size of a UDP packet we send. Note: depends on CHUNKSIZE 8192
+#define SWIFT_MAX_SEND_DGRAM_SIZE                      (SWIFT_MAX_NONDATA_DGRAM_SIZE+1+4+8192)
+// Arno: Maximum size of a UDP packet we are willing to accept. Note: depends on CHUNKSIZE 8192
+#define SWIFT_MAX_RECV_DGRAM_SIZE                      (SWIFT_MAX_SEND_DGRAM_SIZE*2)
+
+#define layer2bytes(ln,cs)     (uint64_t)( ((double)cs)*pow(2.0,(double)ln))
+#define bytes2layer(bn,cs)  (int)log2(  ((double)bn)/((double)cs) )
+
+// Arno, 2011-12-22: Enable Riccardo's VodPiecePicker
+#define ENABLE_VOD_PIECEPICKER         1
+
+
+/** IPv4 address, just a nice wrapping around struct sockaddr_in. */
+    struct Address {
+       struct sockaddr_in  addr;
+       static uint32_t LOCALHOST;
+       void set_port (uint16_t port) {
+           addr.sin_port = htons(port);
+       }
+       void set_port (const char* port_str) {
+           int p;
+           if (sscanf(port_str,"%i",&p))
+               set_port(p);
+       }
+       void set_ipv4 (uint32_t ipv4) {
+           addr.sin_addr.s_addr = htonl(ipv4);
+       }
+       void set_ipv4 (const char* ipv4_str) ;
+       //{    inet_aton(ipv4_str,&(addr.sin_addr));    }
+       void clear () {
+           memset(&addr,0,sizeof(struct sockaddr_in));
+           addr.sin_family = AF_INET;
+       }
+       Address() {
+           clear();
+       }
+       Address(const char* ip, uint16_t port)  {
+           clear();
+           set_ipv4(ip);
+           set_port(port);
+       }
+       Address(const char* ip_port);
+       Address(uint16_t port) {
+           clear();
+           set_ipv4((uint32_t)INADDR_ANY);
+           set_port(port);
+       }
+       Address(uint32_t ipv4addr, uint16_t port) {
+           clear();
+           set_ipv4(ipv4addr);
+           set_port(port);
+       }
+       Address(const struct sockaddr_in& address) : addr(address) {}
+       uint32_t ipv4 () const { return ntohl(addr.sin_addr.s_addr); }
+       uint16_t port () const { return ntohs(addr.sin_port); }
+       operator sockaddr_in () const {return addr;}
+       bool operator == (const Address& b) const {
+           return addr.sin_family==b.addr.sin_family &&
+               addr.sin_port==b.addr.sin_port &&
+               addr.sin_addr.s_addr==b.addr.sin_addr.s_addr;
+       }
+       const char* str () const {
+               // Arno, 2011-10-04: not thread safe, replace.
+           static char rs[4][32];
+           static int i;
+           i = (i+1) & 3;
+           sprintf(rs[i],"%i.%i.%i.%i:%i",ipv4()>>24,(ipv4()>>16)&0xff,
+                   (ipv4()>>8)&0xff,ipv4()&0xff,port());
+           return rs[i];
+       }
+       const char* ipv4str () const {
+               // Arno, 2011-10-04: not thread safe, replace.
+           static char rs[4][32];
+           static int i;
+           i = (i+1) & 3;
+           sprintf(rs[i],"%i.%i.%i.%i",ipv4()>>24,(ipv4()>>16)&0xff,
+                   (ipv4()>>8)&0xff,ipv4()&0xff);
+           return rs[i];
+       }
+       bool operator != (const Address& b) const { return !(*this==b); }
+       bool is_private() const {
+               // TODO IPv6
+               uint32_t no = ipv4(); uint8_t no0 = no>>24,no1 = (no>>16)&0xff;
+               if (no0 == 10) return true;
+               else if (no0 == 172 && no1 >= 16 && no1 <= 31) return true;
+               else if (no0 == 192 && no1 == 168) return true;
+               else return false;
+       }
+    };
+
+// Arno, 2011-10-03: Use libevent callback functions, no on_error?
+#define sockcb_t               event_callback_fn
+    struct sckrwecb_t {
+       sckrwecb_t (evutil_socket_t s=0, sockcb_t mr=NULL, sockcb_t mw=NULL,
+                   sockcb_t oe=NULL) :
+           sock(s), may_read(mr), may_write(mw), on_error(oe) {}
+       evutil_socket_t sock;
+       sockcb_t   may_read;
+       sockcb_t   may_write;
+       sockcb_t   on_error;
+    };
+
+    struct now_t  {
+       static tint now;
+    };
+
+#define NOW now_t::now
+
+    /** tintbin is basically a pair<tint,bin64_t> plus some nice operators.
+        Most frequently used in different queues (acknowledgements, requests,
+        etc). */
+    struct tintbin {
+        tint    time;
+        bin_t bin;
+        tintbin(const tintbin& b) : time(b.time), bin(b.bin) {}
+        tintbin() : time(TINT_NEVER), bin(bin_t::NONE) {}
+        tintbin(tint time_, bin_t bin_) : time(time_), bin(bin_) {}
+        tintbin(bin_t bin_) : time(NOW), bin(bin_) {}
+        bool operator < (const tintbin& b) const
+                       { return time > b.time; }
+        bool operator == (const tintbin& b) const
+                       { return time==b.time && bin==b.bin; }
+        bool operator != (const tintbin& b) const
+                       { return !(*this==b); }
+    };
+
+    typedef std::deque<tintbin> tbqueue;
+    typedef std::deque<bin_t> binqueue;
+    typedef Address   Address;
+
+    /** A heap (priority queue) for timestamped bin numbers (tintbins). */
+    class tbheap {
+        tbqueue data_;
+    public:
+        int size () const { return data_.size(); }
+        bool is_empty () const { return data_.empty(); }
+        tintbin         pop() {
+            tintbin ret = data_.front();
+            std::pop_heap(data_.begin(),data_.end());
+            data_.pop_back();
+            return ret;
+        }
+        void            push(const tintbin& tb) {
+            data_.push_back(tb);
+            push_heap(data_.begin(),data_.end());
+        }
+        const tintbin&  peek() const {
+            return data_.front();
+        }
+    };
+
+    /** swift protocol message types; these are used on the wire. */
+    typedef enum {
+        SWIFT_HANDSHAKE = 0,
+        SWIFT_DATA = 1,
+        SWIFT_ACK = 2,
+        SWIFT_HAVE = 3,
+        SWIFT_HASH = 4,
+        SWIFT_PEX_ADD = 5,
+        SWIFT_PEX_REQ = 6,
+        SWIFT_SIGNED_HASH = 7,
+        SWIFT_HINT = 8,
+        SWIFT_MSGTYPE_RCVD = 9,
+        SWIFT_RANDOMIZE = 10, //FRAGRAND
+        SWIFT_VERSION = 11, // Arno, 2011-10-19: TODO to match RFC-rev-03
+        SWIFT_MESSAGE_COUNT = 12
+    } messageid_t;
+
+    typedef enum {
+        DDIR_UPLOAD,
+        DDIR_DOWNLOAD
+    } data_direction_t;
+
+    class PiecePicker;
+    //class CongestionController; // Arno: Currently part of Channel. See ::NextSendTime
+    class PeerSelector;
+    class Channel;
+    typedef void (*ProgressCallback) (int transfer, bin_t bin);
+
+    /** A class representing single file transfer. */
+    class    FileTransfer {
+
+    public:
+
+        /** A constructor. Open/submit/retrieve a file.
+         *  @param file_name    the name of the file
+         *  @param root_hash    the root hash of the file; zero hash if the file
+                *                          is newly submitted
+                */
+        FileTransfer(const char *file_name, const Sha1Hash& root_hash=Sha1Hash::ZERO,bool force_check_diskvshash=true,bool check_netwvshash=true,uint32_t chunk_size=SWIFT_DEFAULT_CHUNK_SIZE);
+
+        /**    Close everything. */
+        ~FileTransfer();
+
+
+        /** While we need to feed ACKs to every peer, we try (1) avoid
+            unnecessary duplication and (2) keep minimum state. Thus,
+            we use a rotating queue of bin completion events. */
+        //bin64_t         RevealAck (uint64_t& offset);
+        /** Rotating queue read for channels of this transmission. */
+        // Jori
+        int             RevealChannel (int& i);
+        // Gertjan
+        int             RandomChannel (int own_id);
+
+
+        /** Find transfer by the root hash. */
+        static FileTransfer* Find (const Sha1Hash& hash);
+        /** Find transfer by the file descriptor. */
+        static FileTransfer* file (int fd) {
+            return fd<files.size() ? files[fd] : NULL;
+        }
+
+        /** The binmap for data already retrieved and checked. */
+        binmap_t&           ack_out ()  { return file_.ack_out(); }
+        /** Piece picking strategy used by this transfer. */
+        PiecePicker&    picker () { return *picker_; }
+        /** The number of channels working for this transfer. */
+        int             channel_count () const { return hs_in_.size(); }
+        /** Hash tree checked file; all the hashes and data are kept here. */
+        HashTree&       file() { return file_; }
+        /** File descriptor for the data file. */
+        int             fd () const { return file_.file_descriptor(); }
+        /** Root SHA1 hash of the transfer (and the data file). */
+        const Sha1Hash& root_hash () const { return file_.root_hash(); }
+        /** Ric: the availability in the swarm */
+        Availability&  availability() { return *availability_; }
+
+               // RATELIMIT
+        /** Arno: Call when n bytes are received. */
+        void                   OnRecvData(int n);
+        /** Arno: Call when n bytes are sent. */
+        void                   OnSendData(int n);
+        /** Arno: Call when no bytes are sent due to rate limiting. */
+        void                   OnSendNoData();
+        /** Arno: Return current speed for the given direction in bytes/s */
+               double                  GetCurrentSpeed(data_direction_t ddir);
+               /** Arno: Return maximum speed for the given direction in bytes/s */
+               double                  GetMaxSpeed(data_direction_t ddir);
+               /** Arno: Set maximum speed for the given direction in bytes/s */
+               void                    SetMaxSpeed(data_direction_t ddir, double m);
+               /** Arno: Return the number of non-seeders current channeled with. */
+               uint32_t                GetNumLeechers();
+               /** Arno: Return the number of seeders current channeled with. */
+               uint32_t                GetNumSeeders();
+               /** Arno: Return the set of Channels for this transfer. MORESTATS */
+               std::set<Channel *> GetChannels() { return mychannels_; }
+
+               /** Arno: set the tracker for this transfer. Reseting it won't kill
+                * any existing connections.
+                */
+               void SetTracker(Address tracker) { tracker_ = tracker; }
+
+               /** Arno: (Re)Connect to tracker for this transfer, or global Channel::tracker if not set */
+               void ConnectToTracker();
+
+               /** Arno: Reconnect to the tracker if no established peers and
+                * exp backoff allows it.
+                */
+               void ReConnectToTrackerIfAllowed(bool hasestablishedpeers);
+
+               /** Arno: Return the Channel to peer "addr" that is not equal to "notc". */
+               Channel * FindChannel(const Address &addr, Channel *notc);
+
+               // SAFECLOSE
+               static void LibeventCleanCallback(int fd, short event, void *arg);
+    protected:
+
+        HashTree        file_;
+
+        /** Piece picker strategy. */
+        PiecePicker*    picker_;
+
+        /** Channels working for this transfer. */
+        binqueue        hs_in_;                        // Arno, 2011-10-03: Should really be queue of channel ID (=uint32_t)
+
+        /** Messages we are accepting.    */
+        uint64_t        cap_out_;
+
+        tint            init_time_;
+        
+        // Ric: PPPLUG
+        /** Availability in the swarm */
+        Availability*  availability_;
+
+#define SWFT_MAX_TRANSFER_CB 8
+        ProgressCallback callbacks[SWFT_MAX_TRANSFER_CB];
+        uint8_t         cb_agg[SWFT_MAX_TRANSFER_CB];
+        int             cb_installed;
+
+               // RATELIMIT
+        std::set<Channel *>    mychannels_; // Arno, 2012-01-31: May be duplicate of hs_in_
+        MovingAverageSpeed     cur_speed_[2];
+        double                         max_speed_[2];
+        int                                    speedzerocount_;
+
+        // SAFECLOSE
+        struct event           evclean_;
+
+        Address                        tracker_; // Tracker for this transfer
+        tint                           tracker_retry_interval_;
+        tint                           tracker_retry_time_;
+
+    public:
+        void            OnDataIn (bin_t pos);
+        // Gertjan fix: return bool
+        bool            OnPexIn (const Address& addr);
+
+        static std::vector<FileTransfer*> files;
+
+
+        friend class Channel;
+        // Ric: maybe not really needed
+        friend class Availability;
+        friend uint64_t  Size (int fdes);
+        friend bool      IsComplete (int fdes);
+        friend uint64_t  Complete (int fdes);
+        friend uint64_t  SeqComplete (int fdes);
+        friend int     Open (const char* filename, const Sha1Hash& hash, Address tracker, bool force_check_diskvshash, bool check_netwvshash, uint32_t chunk_size);
+        friend void    Close (int fd) ;
+        friend void AddProgressCallback (int transfer,ProgressCallback cb,uint8_t agg);
+        friend void RemoveProgressCallback (int transfer,ProgressCallback cb);
+        friend void ExternallyRetrieved (int transfer,bin_t piece);
+    };
+
+
+    /** PiecePicker implements some strategy of choosing (picking) what
+        to request next, given the possible range of choices:
+        data acknowledged by the peer minus data already retrieved.
+        May pick sequentially, do rarest first or in some other way. */
+    class PiecePicker {
+    public:
+        virtual void Randomize (uint64_t twist) = 0;
+        /** The piece picking method itself.
+         *  @param  offered     the data acknowledged by the peer
+         *  @param  max_width   maximum number of packets to ask for
+         *  @param  expires     (not used currently) when to consider request expired
+         *  @return             the bin number to request */
+        virtual bin_t Pick (binmap_t& offered, uint64_t max_width, tint expires) = 0;
+        virtual void LimitRange (bin_t range) = 0;
+        /** updates the playback position for streaming piece picking.
+         *  @param  amount             amount to increment in bin unit size (1KB default) */
+        virtual void updatePlaybackPos (int amount=1) = 0;
+        virtual ~PiecePicker() {}
+    };
+
+
+    class PeerSelector { // Arno: partically unused
+    public:
+        virtual void AddPeer (const Address& addr, const Sha1Hash& root) = 0;
+        virtual Address GetPeer (const Sha1Hash& for_root) = 0;
+    };
+
+
+    /* class DataStorer { // Arno: never implemented
+    public:
+        DataStorer (const Sha1Hash& id, size_t size);
+        virtual size_t    ReadData (bin_t pos,uint8_t** buf) = 0;
+        virtual size_t    WriteData (bin_t pos, uint8_t* buf, size_t len) = 0;
+    }; */
+
+
+    /**    swift channel's "control block"; channels loosely correspond to TCP
+          connections or FTP sessions; one channel is created for one file
+          being transferred between two peers. As we don't need buffers and
+          lots of other TCP stuff, sizeof(Channel+members) must be below 1K.
+          Normally, API users do not deal with this class. */
+    class Channel {
+
+#define DGRAM_MAX_SOCK_OPEN 128
+       static int sock_count;
+       static sckrwecb_t sock_open[DGRAM_MAX_SOCK_OPEN];
+
+    public:
+        Channel    (FileTransfer* file, int socket=INVALID_SOCKET, Address peer=Address());
+        ~Channel();
+
+        typedef enum {
+            KEEP_ALIVE_CONTROL,
+            PING_PONG_CONTROL,
+            SLOW_START_CONTROL,
+            AIMD_CONTROL,
+            LEDBAT_CONTROL,
+            CLOSE_CONTROL
+        } send_control_t;
+
+        static Address  tracker; // Global tracker for all transfers
+        struct event *evsend_ptr_; // Arno: timer per channel // SAFECLOSE
+        static struct event_base *evbase;
+        static struct event evrecv;
+        static const char* SEND_CONTROL_MODES[];
+
+           static tint epoch, start;
+           static uint64_t global_dgrams_up, global_dgrams_down, global_raw_bytes_up, global_raw_bytes_down, global_bytes_up, global_bytes_down;
+        static void CloseChannelByAddress(const Address &addr);
+
+        // SOCKMGMT
+        // Arno: channel is also a "singleton" class that manages all sockets
+        // for a swift process
+        static void LibeventSendCallback(int fd, short event, void *arg);
+        static void LibeventReceiveCallback(int fd, short event, void *arg);
+        static void RecvDatagram (evutil_socket_t socket); // Called by LibeventReceiveCallback
+           static int RecvFrom(evutil_socket_t sock, Address& addr, struct evbuffer *evb); // Called by RecvDatagram
+           static int SendTo(evutil_socket_t sock, const Address& addr, struct evbuffer *evb); // Called by Channel::Send()
+           static evutil_socket_t Bind(Address address, sckrwecb_t callbacks=sckrwecb_t());
+           static Address BoundAddress(evutil_socket_t sock);
+           static evutil_socket_t default_socket()
+            { return sock_count ? sock_open[0].sock : INVALID_SOCKET; }
+
+           /** close the port */
+           static void CloseSocket(evutil_socket_t sock);
+           static void Shutdown ();
+           /** the current time */
+           static tint Time();
+
+           // Arno: Per instance methods
+        void        Recv (struct evbuffer *evb);
+        void        Send ();  // Called by LibeventSendCallback
+        void        Close ();
+
+        void        OnAck (struct evbuffer *evb);
+        void        OnHave (struct evbuffer *evb);
+        bin_t       OnData (struct evbuffer *evb);
+        void        OnHint (struct evbuffer *evb);
+        void        OnHash (struct evbuffer *evb);
+        void        OnPex (struct evbuffer *evb);
+        void        OnHandshake (struct evbuffer *evb);
+        void        OnRandomize (struct evbuffer *evb); //FRAGRAND
+        void        AddHandshake (struct evbuffer *evb);
+        bin_t       AddData (struct evbuffer *evb);
+        void        AddAck (struct evbuffer *evb);
+        void        AddHave (struct evbuffer *evb);
+        void        AddHint (struct evbuffer *evb);
+        void        AddUncleHashes (struct evbuffer *evb, bin_t pos);
+        void        AddPeakHashes (struct evbuffer *evb);
+        void        AddPex (struct evbuffer *evb);
+        void        OnPexReq(void);
+        void        AddPexReq(struct evbuffer *evb);
+        void        BackOffOnLosses (float ratio=0.5);
+        tint        SwitchSendControl (int control_mode);
+        tint        NextSendTime ();
+        tint        KeepAliveNextSendTime ();
+        tint        PingPongNextSendTime ();
+        tint        CwndRateNextSendTime ();
+        tint        SlowStartNextSendTime ();
+        tint        AimdNextSendTime ();
+        tint        LedbatNextSendTime ();
+        /** Arno: return true if this peer has complete file. May be fuzzy if Peak Hashes not in */
+        bool           IsComplete();
+        /** Arno: return (UDP) port for this channel */
+        uint16_t       GetMyPort();
+        bool           IsDiffSenderOrDuplicate(Address addr, uint32_t chid);
+
+        static int  MAX_REORDERING;
+        static tint TIMEOUT;
+        static tint MIN_DEV;
+        static tint MAX_SEND_INTERVAL;
+        static tint LEDBAT_TARGET;
+        static float LEDBAT_GAIN;
+        static tint LEDBAT_DELAY_BIN;
+        static bool SELF_CONN_OK;
+        static tint MAX_POSSIBLE_RTT;
+        static tint MIN_PEX_REQUEST_INTERVAL;
+        static FILE* debug_file;
+
+        const std::string id_string () const;
+        /** A channel is "established" if had already sent and received packets. */
+        bool        is_established () { return peer_channel_id_ && own_id_mentioned_; }
+        FileTransfer& transfer() { return *transfer_; }
+        HashTree&   file () { return transfer_->file(); }
+        const Address& peer() const { return peer_; }
+        const Address& recv_peer() const { return recv_peer_; }
+        tint ack_timeout () {
+               tint dev = dev_avg_ < MIN_DEV ? MIN_DEV : dev_avg_;
+               tint tmo = rtt_avg_ + dev * 4;
+               return tmo < 30*TINT_SEC ? tmo : 30*TINT_SEC;
+        }
+        uint32_t    id () const { return id_; }
+
+        // MORESTATS
+        uint64_t raw_bytes_up() { return raw_bytes_up_; }
+        uint64_t raw_bytes_down() { return raw_bytes_down_; }
+        uint64_t bytes_up() { return bytes_up_; }
+        uint64_t bytes_down() { return bytes_down_; }
+
+        static int  DecodeID(int scrambled);
+        static int  EncodeID(int unscrambled);
+        static Channel* channel(int i) {
+            return i<channels.size()?channels[i]:NULL;
+        }
+        static void CloseTransfer (FileTransfer* trans);
+
+        // SAFECLOSE
+        void           ClearEvents();
+        void           Schedule4Close() { scheduled4close_ = true; }
+        bool           IsScheduled4Close() { return scheduled4close_; }
+
+
+    protected:
+        /** Channel id: index in the channel array. */
+        uint32_t    id_;
+        /**    Socket address of the peer. */
+        Address     peer_;
+        /**    The UDP socket fd. */
+        evutil_socket_t      socket_;
+        /**    Descriptor of the file in question. */
+        FileTransfer*    transfer_;
+        /**    Peer channel id; zero if we are trying to open a channel. */
+        uint32_t    peer_channel_id_;
+        bool        own_id_mentioned_;
+        /**    Peer's progress, based on acknowledgements. */
+        binmap_t    ack_in_;
+        /**    Last data received; needs to be acked immediately. */
+        tintbin     data_in_;
+        bin_t       data_in_dbl_;
+        /** The history of data sent and still unacknowledged. */
+        tbqueue     data_out_;
+        /** Timeouted data (potentially to be retransmitted). */
+        tbqueue     data_out_tmo_;
+        bin_t       data_out_cap_;
+        /** Index in the history array. */
+        binmap_t    have_out_;
+        /**    Transmit schedule: in most cases filled with the peer's hints */
+        tbqueue     hint_in_;
+        /** Hints sent (to detect and reschedule ignored hints). */
+        tbqueue     hint_out_;
+        uint64_t    hint_out_size_;
+        /** Types of messages the peer accepts. */
+        uint64_t    cap_in_;
+        /** For repeats. */
+        //tint        last_send_time, last_recv_time;
+        /** PEX progress */
+        bool        pex_requested_;
+        tint        last_pex_request_time_;
+        tint        next_pex_request_time_;
+        bool        pex_request_outstanding_;
+        tbqueue     reverse_pex_out_;          // Arno, 2011-10-03: should really be a queue of (tint,channel id(= uint32_t)) pairs.
+        int         useless_pex_count_;
+        /** Smoothed averages for RTT, RTT deviation and data interarrival periods. */
+        tint        rtt_avg_, dev_avg_, dip_avg_;
+        tint        last_send_time_;
+        tint        last_recv_time_;
+        tint        last_data_out_time_;
+        tint        last_data_in_time_;
+        tint        last_loss_time_;
+        tint        next_send_time_;
+        /** Congestion window; TODO: int, bytes. */
+        float       cwnd_;
+        int         cwnd_count1_;
+        /** Data sending interval. */
+        tint        send_interval_;
+        /** The congestion control strategy. */
+        int         send_control_;
+        /** Datagrams (not data) sent since last recv.    */
+        int         sent_since_recv_;
+
+        /** Arno: Fix for KEEP_ALIVE_CONTROL */
+        bool           lastrecvwaskeepalive_;
+        bool           lastsendwaskeepalive_;
+
+        /** Recent acknowlegements for data previously sent.    */
+        int         ack_rcvd_recent_;
+        /** Recent non-acknowlegements (losses) of data previously sent.    */
+        int         ack_not_rcvd_recent_;
+        /** LEDBAT one-way delay machinery */
+        tint        owd_min_bins_[4];
+        int         owd_min_bin_;
+        tint        owd_min_bin_start_;
+        tint        owd_current_[4];
+        int         owd_cur_bin_;
+        /** Stats */
+        int         dgrams_sent_;
+        int         dgrams_rcvd_;
+        // Arno, 2011-11-28: for detailed, per-peer stats. MORESTATS
+        uint64_t raw_bytes_up_, raw_bytes_down_, bytes_up_, bytes_down_;
+
+        // SAFECLOSE
+        bool           scheduled4close_;
+        /** Arno: Socket address of the peer where packets are received from,
+         * when an IANA private address, otherwise 0.
+         * May not be equal to peer_. 2PEERSBEHINDSAMENAT */
+        Address     recv_peer_;
+
+        int         PeerBPS() const {
+            return TINT_SEC / dip_avg_ * 1024;
+        }
+        /** Get a request for one packet from the queue of peer's requests. */
+        bin_t       DequeueHint(bool *retransmitptr);
+        bin_t       ImposeHint();
+        void        TimeoutDataOut ();
+        void        CleanStaleHintOut();
+        void        CleanHintOut(bin_t pos);
+        void        Reschedule();
+        void           UpdateDIP(bin_t pos); // RETRANSMIT
+
+
+        static PeerSelector* peer_selector;
+
+        static tint     last_tick;
+        //static tbheap   send_queue;
+
+        static std::vector<Channel*> channels;
+
+        friend int      Listen (Address addr);
+        friend void     Shutdown (int sock_des);
+        friend void     AddPeer (Address address, const Sha1Hash& root);
+        friend void     SetTracker(const Address& tracker);
+        friend int      Open (const char*, const Sha1Hash&, Address tracker, bool force_check_diskvshash, bool check_netwvshash, uint32_t chunk_size) ; // FIXME
+    };
+
+
+
+    /*************** The top-level API ****************/
+    /** Start listening a port. Returns socket descriptor. */
+    int     Listen (Address addr);
+    /** Stop listening to a port. */
+    void    Shutdown (int sock_des=-1);
+
+    /** Open a file, start a transmission; fill it with content for a given
+        root hash and tracker (optional). If "force_check_diskvshash" is true, the
+        hashtree state will be (re)constructed from the file on disk (if any).
+        If not, open will try to reconstruct the hashtree state from
+        the .mhash and .mbinmap files on disk. .mhash files are created
+        automatically, .mbinmap files must be written by checkpointing the
+        transfer by calling FileTransfer::serialize(). If the reconstruction
+        fails, it will hashcheck anyway. Roothash is optional for new files or
+        files already hashchecked and checkpointed. If "check_netwvshash" is
+        false, no uncle hashes will be sent and no data will be verified against
+        then on receipt. In this mode, checking disk contents against hashes
+        no longer works on restarts, unless checkpoints are used.
+        */
+    int     Open (const char* filename, const Sha1Hash& hash=Sha1Hash::ZERO,Address tracker=Address(), bool force_check_diskvshash=true, bool check_netwvshash=true, uint32_t chunk_size=SWIFT_DEFAULT_CHUNK_SIZE);
+    /** Get the root hash for the transmission. */
+    const Sha1Hash& RootMerkleHash (int file) ;
+    /** Close a file and a transmission. */
+    void    Close (int fd) ;
+    /** Add a possible peer which participares in a given transmission. In the case
+        root hash is zero, the peer might be talked to regarding any transmission
+        (likely, a tracker, cache or an archive). */
+    void    AddPeer (Address address, const Sha1Hash& root=Sha1Hash::ZERO);
+
+    void    SetTracker(const Address& tracker);
+    /** Set the default tracker that is used when Open is not passed a tracker
+        address. */
+
+    /** Returns size of the file in bytes, 0 if unknown. Might be rounded up to a kilobyte
+        before the transmission is complete. */
+    uint64_t  Size (int fdes);
+    /** Returns the amount of retrieved and verified data, in bytes.
+        A 100% complete transmission has Size()==Complete(). */
+    uint64_t  Complete (int fdes);
+    bool      IsComplete (int fdes);
+    /** Returns the number of bytes that are complete sequentially, starting from the
+        beginning, till the first not-yet-retrieved packet. */
+    uint64_t  SeqComplete (int fdes);
+    /***/
+    int       Find (Sha1Hash hash);
+    /** Returns the number of bytes in a chunk for this transmission */
+    uint32_t     ChunkSize(int fdes);
+
+    /** Get the address bound to the socket descriptor returned by Listen() */
+    Address BoundAddress(evutil_socket_t sock);
+
+    void AddProgressCallback (int transfer,ProgressCallback cb,uint8_t agg);
+    void RemoveProgressCallback (int transfer,ProgressCallback cb);
+    void ExternallyRetrieved (int transfer,bin_t piece);
+
+
+    /** Must be called by any client using the library */
+    void LibraryInit(void);
+
+    int evbuffer_add_string(struct evbuffer *evb, std::string str);
+    int evbuffer_add_8(struct evbuffer *evb, uint8_t b);
+    int evbuffer_add_16be(struct evbuffer *evb, uint16_t w);
+    int evbuffer_add_32be(struct evbuffer *evb, uint32_t i);
+    int evbuffer_add_64be(struct evbuffer *evb, uint64_t l);
+    int evbuffer_add_hash(struct evbuffer *evb, const Sha1Hash& hash);
+
+    uint8_t evbuffer_remove_8(struct evbuffer *evb);
+    uint16_t evbuffer_remove_16be(struct evbuffer *evb);
+    uint32_t evbuffer_remove_32be(struct evbuffer *evb);
+    uint64_t evbuffer_remove_64be(struct evbuffer *evb);
+    Sha1Hash evbuffer_remove_hash(struct evbuffer* evb);
+
+    const char* tintstr(tint t=0);
+    std::string sock2str (struct sockaddr_in addr);
+ #define SWIFT_MAX_CONNECTIONS 20
+
+    void nat_test_update(void);
+
+    // Arno: Save transfer's binmap for zero-hashcheck restart
+    void Checkpoint(int fdes);
+
+} // namespace end
+
+// #define SWIFT_MUTE
+
+#ifndef SWIFT_MUTE
+#define dprintf(...) do { if (Channel::debug_file) fprintf(Channel::debug_file,__VA_ARGS__); } while (0)
+#define dflush() fflush(Channel::debug_file)
+#else
+#define dprintf(...) do {} while(0)
+#define dflush() do {} while(0)
+#endif
+#define eprintf(...) fprintf(stderr,__VA_ARGS__)
+
+#endif