improve support for merkle tree torrent creation

This commit is contained in:
Arvid Norberg 2011-07-30 17:35:22 +00:00
parent fbd7d49d7e
commit 7817229f5d
6 changed files with 71 additions and 10 deletions

View File

@ -87,6 +87,7 @@
incoming connection
* added more detailed instrumentation of the disk I/O thread
* improve support for merkle tree torrent creation
* exposed comparison operators on torrent_handle to python
* exposed alert error_codes to python
* fixed bug in announce_entry::next_announce_in and min_announce_in

View File

@ -341,6 +341,9 @@ merkle
The benefit is that the resulting torrent file will be much smaller and
not grow with more pieces. When this option is specified, it is
recommended to have a fairly small piece size, say 64 kiB.
When creating merkle torrents, the full hash tree is also generated
and should be saved off separately. It is accessed through the
``merkle_tree()`` function.
modification_time
This will include the file modification time as part of the torrent.
@ -488,3 +491,19 @@ set_priv() priv()
Sets and queries the private flag of the torrent.
merkle_tree()
-------------
::
std::vector<sha1_hash> const& merkle_tree() const;
This function returns the merkle hash tree, if the torrent was created as a merkle
torrent. The tree is created by ``generate()`` and won't be valid until that function
has been called. When creating a merkle tree torrent, the actual tree itself has to
be saved off separately and fed into libtorrent the first time you start seeding it,
through the ``torrent_info::set_merkle_tree()`` function. From that point onwards, the
tree will be saved in the resume data.

View File

@ -1666,6 +1666,9 @@ The ``torrent_info`` has the following synopsis::
sha1_hash const& hash_for_piece(unsigned int index) const;
char const* hash_for_piece_ptr(unsigned int index) const;
std::vector<sha1_hash> const& merkle_tree() const;
void set_merkle_tree(std::vector<sha1_hash>& h);
boost::shared_array<char> metadata() const;
int metadata_size() const;
};
@ -2079,6 +2082,23 @@ torrent file. For more information on the ``sha1_hash``, see the big_number_ cla
``hash_for_piece_ptr()`` returns a pointer to the 20 byte sha1 digest for the piece.
Note that the string is not null-terminated.
merkle_tree() set_merkle_tree()
-------------------------------
::
std::vector<sha1_hash> const& merkle_tree() const;
void set_merkle_tree(std::vector<sha1_hash>& h);
``merkle_tree()`` returns a reference to the merkle tree for this torrent, if any.
``set_merkle_tree()`` moves the passed in merkle tree into the torrent_info object.
i.e. ``h`` will not be identical after the call. You need to set the merkle tree for
a torrent that you've just created (as a merkle torrent). The merkle tree is retrieved
from the ``create_torrent::merkle_tree()`` function, and need to be saved separately
from the torrent file itself. Once it's added to libtorrent, the merkle tree will be
persisted in the resume data.
name() comment() creation_date() creator()
------------------------------------------

View File

@ -64,8 +64,10 @@ void print_usage()
"Generates a torrent file from the specified file\n"
"or directory and writes it to standard out\n\n"
"OPTIONS:\n"
"-m generate a merkle hash tree torrent.\n"
"-m file generate a merkle hash tree torrent.\n"
" merkle torrents require client support\n"
" the resulting full merkle tree is written to\n"
" the specified file\n"
"-f include sha-1 file hashes in the torrent\n"
" this helps supporting mixing sources from\n"
" other networks\n"
@ -109,6 +111,7 @@ int main(int argc, char* argv[])
int flags = 0;
std::string outfile;
std::string merklefile;
#ifdef TORRENT_WINDOWS
// don't ever write binary data to the console on windows
// it will just be interpreted as text and corrupted
@ -143,6 +146,8 @@ int main(int argc, char* argv[])
piece_size = atoi(argv[i]);
break;
case 'm':
++i;
merklefile = argv[i];
flags |= create_torrent::merkle;
break;
case 'o':
@ -204,6 +209,18 @@ int main(int argc, char* argv[])
if (output != stdout)
fclose(output);
if (!merklefile.empty())
{
output = fopen(merklefile.c_str(), "wb+");
int ret = fwrite(&t.merkle_tree()[0], 1, t.merkle_tree().size(), output);
if (ret != t.merkle_tree().size())
{
fprintf(stderr, "failed to write %s: (%d) %s\n"
, merklefile.c_str(), errno, strerror(errno));
}
fclose(output);
}
#ifndef BOOST_NO_EXCEPTIONS
}
catch (std::exception& e)

View File

@ -97,6 +97,7 @@ namespace libtorrent
bool priv() const { return m_private; }
bool should_add_file_hashes() const { return m_calculate_file_hashes; }
std::vector<sha1_hash> const& merkle_tree() const { return m_merkle_tree; }
private:
@ -117,6 +118,11 @@ namespace libtorrent
std::vector<sha1_hash> m_filehashes;
// if we're generating a merkle torrent, this is the
// merkle tree we got. This should be saved in fast-resume
// in order to start seeding the torrent
mutable std::vector<sha1_hash> m_merkle_tree;
// dht nodes to add to the routing table/bootstrap from
typedef std::vector<std::pair<std::string, int> > nodes_t;
nodes_t m_nodes;

View File

@ -387,18 +387,16 @@ namespace libtorrent
info["piece length"] = m_files.piece_length();
if (m_merkle_torrent)
{
std::vector<sha1_hash> merkle_tree;
int num_leafs = merkle_num_leafs(m_files.num_pieces());
int num_nodes = merkle_num_nodes(num_leafs);
int first_leaf = num_nodes - num_leafs;
merkle_tree.resize(num_nodes);
m_merkle_tree.resize(num_nodes);
int num_pieces = m_piece_hash.size();
for (int i = 0; i < num_pieces; ++i)
merkle_tree[first_leaf + i] = m_piece_hash[i];
m_merkle_tree[first_leaf + i] = m_piece_hash[i];
sha1_hash filler(0);
for (int i = num_pieces; i < num_leafs; ++i)
merkle_tree[first_leaf + i] = filler;
m_merkle_tree[first_leaf + i] = filler;
// now that we have initialized all leaves, build
// each level bottom-up
@ -410,16 +408,16 @@ namespace libtorrent
for (int i = level_start; i < level_start + level_size; i += 2, ++parent)
{
hasher h;
h.update((char const*)&merkle_tree[i][0], 20);
h.update((char const*)&merkle_tree[i+1][0], 20);
merkle_tree[parent] = h.final();
h.update((char const*)&m_merkle_tree[i][0], 20);
h.update((char const*)&m_merkle_tree[i+1][0], 20);
m_merkle_tree[parent] = h.final();
}
level_start = merkle_get_parent(level_start);
level_size /= 2;
}
TORRENT_ASSERT(level_size == 1);
std::string& p = info["root hash"].string();
p.assign((char const*)&merkle_tree[0][0], 20);
p.assign((char const*)&m_merkle_tree[0][0], 20);
}
else
{