diff --git a/ChangeLog b/ChangeLog index 3a7c58b7b..12315a8e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/docs/make_torrent.rst b/docs/make_torrent.rst index 76c36e1f8..b0286dc45 100644 --- a/docs/make_torrent.rst +++ b/docs/make_torrent.rst @@ -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 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. + + + diff --git a/docs/manual.rst b/docs/manual.rst index b247f0d1b..1146466e0 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -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 const& merkle_tree() const; + void set_merkle_tree(std::vector& h); + boost::shared_array 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 const& merkle_tree() const; + void set_merkle_tree(std::vector& 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() ------------------------------------------ diff --git a/examples/make_torrent.cpp b/examples/make_torrent.cpp index 4cb939c81..8c0bc9808 100644 --- a/examples/make_torrent.cpp +++ b/examples/make_torrent.cpp @@ -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) diff --git a/include/libtorrent/create_torrent.hpp b/include/libtorrent/create_torrent.hpp index 35442c468..14e020dc9 100644 --- a/include/libtorrent/create_torrent.hpp +++ b/include/libtorrent/create_torrent.hpp @@ -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 const& merkle_tree() const { return m_merkle_tree; } private: @@ -117,6 +118,11 @@ namespace libtorrent std::vector 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 m_merkle_tree; + // dht nodes to add to the routing table/bootstrap from typedef std::vector > nodes_t; nodes_t m_nodes; diff --git a/src/create_torrent.cpp b/src/create_torrent.cpp index 7dcb14cc6..bb6f51a71 100644 --- a/src/create_torrent.cpp +++ b/src/create_torrent.cpp @@ -387,18 +387,16 @@ namespace libtorrent info["piece length"] = m_files.piece_length(); if (m_merkle_torrent) { - std::vector 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 {