namespace p2tp {
- typedef std::pair<tint,bin64_t> tintbin;
+ struct tintbin {
+ tint time;
+ bin64_t bin;
+ tintbin(const tintbin& b) : time(b.time), bin(b.bin) {}
+ tintbin() : time(0), bin(bin64_t::NONE) {}
+ tintbin(tint time_, bin64_t bin_) : time(time_), bin(bin_) {}
+ };
typedef std::deque<tintbin> tbqueue;
typedef std::deque<bin64_t> binqueue;
P2TP_HANDSHAKE = 0,
P2TP_DATA = 1,
P2TP_ACK = 2,
- P2TP_ACKTS = 8,
+ P2TP_ACK_TS = 8,
P2TP_HINT = 3,
P2TP_HASH = 4,
P2TP_PEX_ADD = 5,
bool OfferData (bin64_t bin, uint8_t* data, size_t length);
static FileTransfer* Find (const Sha1Hash& hash);
- static FileTransfer* file (int fd) { return fd<files.size() ? files[fd] : NULL; }
+ static FileTransfer* file (int fd) {
+ return fd<files.size() ? files[fd] : NULL;
+ }
+
+ int GetPeakCount () const { return peak_count; }
+ bin64_t GetPeak (int i) const { return peaks[i]; }
+ const Sha1Hash& GetPeakHash (int i) const { return peak_hashes[i]; }
+ bin64_t GetPeakFor (bin64_t pos) const;
+ const Sha1Hash& GetHash (bin64_t pos) const {
+ assert(pos<sizek*2);
+ return hashes[pos];
+ }
+ const Sha1Hash& GetRootHash () const { return root_hash; }
+
friend int Open (const char* filename);
friend int Open (const Sha1Hash& hash, const char* filename);
/** A map for all packets obtained and succesfully checked. */
bins ack_out;
/** History of bin retrieval. */
- binqueue ack_history;
+ binqueue data_in;
/** Piece picker strategy. */
PiecePicker* picker;
/** File for keeping the Merkle hash tree. */
int peer_cwnd;
virtual void OnDataSent(bin64_t b) = 0;
virtual void OnDataRecvd(bin64_t b) = 0;
- virtual void OnAckRcvd(bin64_t b, tint peer_stamp) = 0;
+ virtual void OnAckRcvd(const tintbin& tsack) = 0;
virtual ~CongestionController() = 0;
};
class PeerSelector {
public:
- virtual void PeerKnown (const Sha1Hash& root, struct sockaddr_in& addr) = 0;
- virtual struct sockaddr_in
- GetPeer (const Sha1Hash& for_root) = 0;
+ virtual void PeerKnown
+ (const Sha1Hash& root, struct sockaddr_in& addr) = 0;
+ virtual struct sockaddr_in GetPeer
+ (const Sha1Hash& for_root) = 0;
};
class DataStorer {
static void Recv (int socket);
void Recv (Datagram& dgram);
tint Send ();
- tint SendSomething ();
- void SendHandshake ();
void OnAck (Datagram& dgram);
+ void OnAckTs (Datagram& dgram);
void OnData (Datagram& dgram);
void OnHint (Datagram& dgram);
void OnHash (Datagram& dgram);
+ void OnPex (Datagram& dgram);
void AddHandshake (Datagram& dgram);
- bin AddData (Datagram& dgram);
+ bin64_t AddData (Datagram& dgram);
void AddAck (Datagram& dgram);
void AddHint (Datagram& dgram);
- void AddUncleHashes (Datagram& dgram, bin pos);
+ void AddUncleHashes (Datagram& dgram, bin64_t pos);
void AddPeakHashes (Datagram& dgram);
- void CleanStaleHints();
-
+ 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; }
+
static int DecodeID(int scrambled);
static int EncodeID(int unscrambled);
- static Channel* channel(int i) {return i<channels.size()?channels[i]:NULL;}
+ static Channel* channel(int i) {
+ return i<channels.size()?channels[i]:NULL;
+ }
+
+ FileTransfer& file() { return *file_; }
/*friend int Connect (int fd, int sock,
const struct sockaddr_in& addr, uint32_t peerch=0);
/** The UDP socket fd. */
int socket;
/** Descriptor of the file in question. */
- FileTransfer* file;
+ FileTransfer* file_;
/** 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. */
bins ack_in;
/** Last data received; needs to be acked immediately. */
- bin64_t data_in;
+ tintbin data_in_;
/** Index in the history array. */
- int ack_out;
+ uint32_t ack_out_;
/** Transmit schedule: in most cases filled with the peer's hints */
- tbqueue hint_in;
+ binqueue hint_in;
/** Hints sent (to detect and reschedule ignored hints). */
tbqueue hint_out;
/** The congestion control strategy. */
/** For repeats. */
tint last_send_time, last_recv_time;
+ /** Get a rewuest for one packet from the queue of peer's requests. */
+ bin64_t DequeueHint();
+ void CleanStaleHints();
+
static PeerSelector* peer_selector;
static int MAX_REORDERING;
// ABCD E000
// AB CD E0 0
// AAAA BBBB CCCC DDDD E 0 0 0
- leech->OfferHash(bin64_t(1,0), seed->hashes[bin64_t(1,0)]);
+ // calculated leech->OfferHash(bin64_t(1,0), seed->hashes[bin64_t(1,0)]);
leech->OfferHash(bin64_t(1,1), seed->hashes[bin64_t(1,1)]);
for (int i=0; i<5; i++) {
- if (i==2) {
+ if (i==2) { // now: stop, save, start
delete leech;
FileTransfer::instance = 1;
leech = new FileTransfer(seed->root_hash,"copy");
EXPECT_EQ(2,leech->completek);
- //leech->OfferHash(bin64_t(1,0), seed->hashes[bin64_t(1,0)]);
- //leech->OfferHash(bin64_t(1,1), seed->hashes[bin64_t(1,1)]);
}
bin64_t next = leech->picker->Pick(seed->ack_out,0);
ASSERT_NE(bin64_t::NONE,next);
size_t len = pread(seed->fd,buf,1024,next.base_offset()<<10); // FIXME TEST FOR ERROR
bin64_t sibling = next.sibling();
leech->OfferHash(sibling, seed->hashes[sibling]); // i=4 => out of bounds
+ uint8_t memo = *buf;
+ *buf = 'z';
+ EXPECT_FALSE(leech->OfferData(next, buf, len));
+ *buf = memo;
EXPECT_TRUE(leech->OfferData(next, buf, len));
}
EXPECT_EQ(4100,leech->size);
/*
FIXME
- always rehashes (even fresh files)
- - different heights => bins::remove is buggy
*/
int main (int argc, char** argv) {
#include "ext/seq_picker.cpp"
+// FIXME: separate Bootstrap() and Download(), then Size(), Progress(), SeqProgress()
FileTransfer::FileTransfer (const Sha1Hash& _root_hash, const char* filename) :
root_hash(_root_hash), fd(0), hashfd(0), dry_run(false),
void FileTransfer::OfferHash (bin64_t pos, const Sha1Hash& hash) {
if (!size) // only peak hashes are accepted at this point
return OfferPeak(pos,hash);
- if (pos>=sizek*2)
- return;
- if (ack_out.get(pos)!=bins::EMPTY)
+ int pi=0;
+ while (pi<peak_count && !pos.within(peaks[pi]))
+ pi++;
+ if (pi==peak_count)
+ return;
+ if (pos==peaks[pi] && hash!=peak_hashes[pi])
+ return;
+ else if (ack_out.get(pos.parent())!=bins::EMPTY)
return; // have this hash already, even accptd data
hashes[pos] = hash;
}
return false;
if (ack_out.get(pos)==bins::FILLED)
return true; // ???
- int peak=0;
- while (peak<peak_count && !pos.within(peaks[peak]))
- peak++;
- if (peak==peak_count)
+ int pi=0;
+ while (pi<peak_count && !pos.within(peaks[pi]))
+ pi++;
+ if (pi==peak_count)
return false;
- Sha1Hash hash(data,length);
- if (pos==peaks[peak]) {
- if (hash!=peak_hashes[peak])
- return false;
- } else {
- hashes[pos] = hash;
- for(bin64_t p = pos.parent(); p.within(peaks[peak]) && ack_out.get(p)==bins::EMPTY; p=p.parent()) {
- Sha1Hash phash = Sha1Hash(hashes[p.left()],hashes[p.right()]) ;
- if (hashes[p]!=phash)
- return false; // hash mismatch
- }
+ bin64_t peak = peaks[pi];
+
+ Sha1Hash hash(data,length);
+ bin64_t p = pos;
+ while ( p!=peak && ack_out.get(p)==bins::EMPTY ) {
+ hashes[p] = hash;
+ p = p.parent();
+ hash = Sha1Hash(hashes[p.left()],hashes[p.right()]) ;
}
+ if (hash!=hashes[p])
+ return false;
+
//printf("g %lli %s\n",(uint64_t)pos,hash.hex().c_str());
// walk to the nearest proven hash FIXME 0-layer peak
ack_out.set(pos,bins::FILLED);