lazy "allowed set" generation. add regression test for "allowed set" generation.

This commit is contained in:
Charles Kerr 2008-01-06 21:56:30 +00:00
parent 113382f181
commit 35f73c097c
5 changed files with 116 additions and 70 deletions

View File

@ -70,6 +70,25 @@ noinst_HEADERS = \
upnp.h \
utils.h
noinst_PROGRAMS = \
test-fastset
TESTS = $(noinst_PROGRAMS)
TEST_LDADD = \
./libtransmission.a \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/libevent/libevent.la \
$(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
test_fastset_SOURCES = test-fastset.c
test_fastset_LDADD = $(TEST_LDADD)
EXTRA_libtransmission_a_SOURCES = \
version.h

View File

@ -34,7 +34,7 @@
#define ENABLE_LTEP */
/* enable fast peers extension protocol */
#define ENABLE_FASTPEER
/* #define ENABLE_FASTPEER */
/***
****

View File

@ -455,71 +455,44 @@ torrentConstructor( tr_peerMgr * manager, tr_torrent * tor )
}
/**
***
**/
* For explanation, see http://www.bittorrent.org/fast_extensions.html
* Also see the "test-allowed-set" unit test
*/
struct tr_bitfield *
tr_peerMgrGenerateAllowedSet( const uint32_t setCount,
const uint32_t pieceCount,
const uint8_t infohash[20],
const struct in_addr * ip )
tr_peerMgrGenerateAllowedSet( const uint32_t k, /* number of pieces in set */
const uint32_t sz, /* number of pieces in torrent */
const uint8_t * infohash, /* torrent's SHA1 hash*/
const struct in_addr * ip ) /* peer's address */
{
/* This has been checked against the spec example implementation. Feeding it with :
setCount = 9, pieceCount = 1313, infohash = Oxaa,0xaa,...0xaa, ip = 80.4.4.200
generate :
1059, 431, 808, 1217, 287, 376, 1188, 353, 508
but since we're storing in a bitfield, it won't be in this order... */
/* TODO : We should translate link-local IPv4 adresses to external IP,
* so that being on same local network gives us the same allowed pieces */
uint8_t *seed;
char buf[4];
uint32_t allowedPieceCount = 0;
tr_bitfield_t * ret;
uint8_t w[SHA_DIGEST_LENGTH + 4];
uint8_t x[SHA_DIGEST_LENGTH];
tr_bitfield_t * a;
uint32_t a_size;
#if 0
printf( "%d piece allowed fast set for torrent with %d pieces and hex infohash\n", setCount, pieceCount );
printf( "%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x for node with IP %s:\n",
infohash[0], infohash[1], infohash[2], infohash[3], infohash[4], infohash[5], infohash[6], infohash[7], infohash[8], infohash[9],
infohash[10], infohash[11], infohash[12], infohash[13], infohash[14], infohash[15], infohash[16], infohash[7], infohash[18], infohash[19],
inet_ntoa( *ip ) );
#endif
*(uint32_t*)w = ntohl( htonl(ip->s_addr) & 0xffffff00 ); /* (1) */
memcpy( w + 4, infohash, SHA_DIGEST_LENGTH ); /* (2) */
tr_sha1( x, w, sizeof( w ), NULL ); /* (3) */
a = tr_bitfieldNew( sz );
a_size = 0;
seed = malloc(4 + SHA_DIGEST_LENGTH);
ret = tr_bitfieldNew( pieceCount );
/* We need a seed based on most significant bytes of peer address
concatenated with torrent's infohash */
*(uint32_t*)buf = ntohl( htonl(ip->s_addr) & 0xffffff00 );
memcpy( seed, &buf, 4 );
memcpy( seed + 4, infohash, SHA_DIGEST_LENGTH );
tr_sha1( seed, seed, 4 + SHA_DIGEST_LENGTH, NULL );
while ( allowedPieceCount < setCount )
while( a_size < k )
{
int i;
for ( i = 0 ; i < 5 && allowedPieceCount < setCount ; i++ )
for ( i=0; i<5 && a_size<k; ++i ) /* (4) */
{
/* We generate indices from 4-byte chunks of the seed */
uint32_t j = i * 4;
uint32_t y = ntohl( *(uint32_t*)(seed + j) );
uint32_t index = y % pieceCount;
if ( !tr_bitfieldHas( ret, index ) )
{
tr_bitfieldAdd( ret, index );
allowedPieceCount++;
uint32_t j = i * 4; /* (5) */
uint32_t y = ntohl(*(uint32_t*)(x+j)); /* (6) */
uint32_t index = y % sz; /* (7) */
if ( !tr_bitfieldHas( a, index ) ) { /* (8) */
tr_bitfieldAdd( a, index ); /* (9) */
++a_size;
}
}
/* We randomize the seed, in case we need to iterate more */
tr_sha1( seed, seed, SHA_DIGEST_LENGTH, NULL );
tr_sha1( x, x, sizeof( x ), NULL ); /* (3) */
}
tr_free( seed );
return ret;
return a;
}
tr_peerMgr*

View File

@ -527,15 +527,30 @@ sendFastAllowed( tr_peermsgs * msgs,
}
}
static tr_bitfield*
getPeerAllowedPieces( tr_peermsgs * msgs )
{
if( !msgs->peerAllowedPieces && tr_peerIoSupportsFEXT( msgs->io ) )
{
msgs->peerAllowedPieces = tr_peerMgrGenerateAllowedSet(
MAX_FAST_ALLOWED_COUNT,
msgs->torrent->info.pieceCount,
msgs->torrent->info.hash,
tr_peerIoGetAddress( msgs->io, NULL ) );
}
return msgs->peerAllowedPieces;
}
static void
sendFastAllowedSet( tr_peermsgs * msgs )
{
int i = 0;
while (i <= msgs->torrent->info.pieceCount )
{
if ( tr_bitfieldHas( msgs->peerAllowedPieces, i) )
if ( tr_bitfieldHas( getPeerAllowedPieces( msgs ), i) )
sendFastAllowed( msgs, i );
i++;
}
@ -998,7 +1013,7 @@ peerMadeRequest( tr_peermsgs * msgs, const struct peer_request * req )
const int clientHasPiece = reqIsValid && tr_cpPieceIsComplete( msgs->torrent->completion, req->index );
const int peerIsChoked = msgs->info->peerIsChoked;
const int peerIsFast = tr_peerIoSupportsFEXT( msgs->io );
const int pieceIsFast = reqIsValid && tr_bitfieldHas( msgs->peerAllowedPieces, req->index );
const int pieceIsFast = reqIsValid && tr_bitfieldHas( getPeerAllowedPieces( msgs ), req->index );
const int canSendFast = clientCanSendFastBlock( msgs );
if( !reqIsValid ) /* bad request */
@ -1878,20 +1893,8 @@ tr_peerMsgsNew( struct tr_torrent * torrent,
m->clientSuggestedPieces = NULL;
*setme = tr_publisherSubscribe( m->publisher, func, userData );
if ( tr_peerIoSupportsFEXT( m->io ) )
{
/* This peer is fastpeer-enabled, generate its allowed set
* (before registering our callbacks) */
if ( !m->peerAllowedPieces ) {
const struct in_addr *peerAddr = tr_peerIoGetAddress( m->io, NULL );
m->peerAllowedPieces = tr_peerMgrGenerateAllowedSet( MAX_FAST_ALLOWED_COUNT,
m->torrent->info.pieceCount,
m->torrent->info.hash,
peerAddr );
}
if ( tr_peerIoSupportsFEXT( m->io ) ) {
m->clientAllowedPieces = tr_bitfieldNew( m->torrent->info.pieceCount );
m->clientSuggestedPieces = tr_bitfieldNew( m->torrent->info.pieceCount );
}

View File

@ -0,0 +1,51 @@
#include <stdio.h>
#include "transmission.h"
#include "net.h"
#include "peer-mgr.h"
#include "utils.h"
#define check(A) { \
++test; \
if (A) \
fprintf( stderr, "PASS test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
else { \
fprintf( stderr, "FAILPASS test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
return test; \
} \
}
int main( void )
{
uint32_t i;
int test = 0;
tr_bitfield * bitfield;
uint8_t infohash[SHA_DIGEST_LENGTH];
struct in_addr addr;
uint32_t sz;
uint32_t k;
int pieces[] = { 1059,431,808,1217,287,376,1188,353,508 };
for( i=0; i<SHA_DIGEST_LENGTH; ++i )
infohash[i] = 0xaa;
tr_netResolve( "80.4.4.200", &addr );
sz = 1313;
k = 7;
bitfield = tr_peerMgrGenerateAllowedSet( k, sz, infohash, &addr );
check( tr_bitfieldCountTrueBits( bitfield ) == k );
for( i=0; i<k; ++i )
check( tr_bitfieldHas( bitfield, pieces[i] ) );
tr_bitfieldFree( bitfield );
k = 9;
bitfield = tr_peerMgrGenerateAllowedSet( k, sz, infohash, &addr );
check( tr_bitfieldCountTrueBits( bitfield ) == k );
for( i=0; i<k; ++i )
check( tr_bitfieldHas( bitfield, pieces[i] ) );
tr_bitfieldFree( bitfield );
return 0;
}