From 1188ec2dcd9f83e1824582714530e6f7c89b1868 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Mon, 24 Feb 2014 00:31:13 +0000 Subject: [PATCH] expose new DHT put/get functionality in the public session API --- include/libtorrent/alert_types.hpp | 59 +++++++++++++++ include/libtorrent/aux_/session_impl.hpp | 17 +++++ include/libtorrent/kademlia/dht_tracker.hpp | 15 ++++ include/libtorrent/kademlia/item.hpp | 42 ++++++++--- include/libtorrent/session.hpp | 62 ++++++++++++++++ src/alert.cpp | 21 ++++++ src/kademlia/dht_tracker.cpp | 81 +++++++++++++++++++++ src/kademlia/get_item.cpp | 33 ++++++++- src/kademlia/item.cpp | 27 ++++++- src/session.cpp | 40 ++++++++++ src/session_impl.cpp | 65 +++++++++++++++++ test/test_dht.cpp | 6 +- 12 files changed, 446 insertions(+), 22 deletions(-) diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index 1477acf9a..d8882d762 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -1943,7 +1943,66 @@ namespace libtorrent op_t operation; }; + // this alert is posted as a response to a call to session::get_item(), + // specifically the overload for looking up immutable items in the DHT. + struct TORRENT_EXPORT dht_immutable_item_alert: alert + { + dht_immutable_item_alert(sha1_hash const& t, entry const& i) + : target(t), item(i) {} + + TORRENT_DEFINE_ALERT(dht_immutable_item_alert); + const static int static_category = alert::error_notification + | alert::dht_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // the target hash of the immutable item. This must + // match the sha-1 hash of the bencoded form of ``item``. + sha1_hash target; + + // the data for this item + entry item; + }; + + // this alert is posted as a response to a call to session::get_item(), + // specifically the overload for looking up mutable items in the DHT. + struct TORRENT_EXPORT dht_mutable_item_alert: alert + { + dht_mutable_item_alert(boost::array k + , boost::array sig + , boost::uint64_t sequence + , std::string const& s + , entry const& i) + : key(k), signature(sig), seq(sequence), salt(s), item(i) {} + + TORRENT_DEFINE_ALERT(dht_mutable_item_alert); + + const static int static_category = alert::error_notification + | alert::dht_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // the public key that was looked up + boost::array key; + + // the signature of the data. This is not the signature of the + // plain encoded form of the item, but it includes the sequence number + // and possibly the hash as well. See the dht_store document for more + // information. This is primarily useful for echoing back in a store + // request. + boost::array signature; + + // the sequence number of this item + boost::uint64_t seq; + + // the salf, if any, used to lookup and store this item. If no + // salt was used, this is an empty string + std::string salt; + + // the data for this item + entry item; + }; #undef TORRENT_DEFINE_ALERT } diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 13572e847..9f8eeab51 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -119,6 +119,7 @@ namespace libtorrent namespace dht { struct dht_tracker; + class item; } struct bencode_map_entry; @@ -313,6 +314,22 @@ namespace libtorrent // the DHT, to get the initial peers quickly void prioritize_dht(boost::weak_ptr t); + void get_immutable_callback(sha1_hash target + , dht::item const& i); + void get_mutable_callback(dht::item const& i); + + void dht_get_immutable_item(sha1_hash const& target); + + void dht_get_mutable_item(boost::array key + , std::string salt = std::string()); + + void dht_put_item(entry data); + + void dht_put_mutable_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt = std::string()); + #ifndef TORRENT_NO_DEPRECATE entry dht_state() const; #endif diff --git a/include/libtorrent/kademlia/dht_tracker.hpp b/include/libtorrent/kademlia/dht_tracker.hpp index 4a43013a9..ca837e52e 100644 --- a/include/libtorrent/kademlia/dht_tracker.hpp +++ b/include/libtorrent/kademlia/dht_tracker.hpp @@ -93,6 +93,21 @@ namespace libtorrent { namespace dht void announce(sha1_hash const& ih, int listen_port, int flags , boost::function const&)> f); + void get_item(sha1_hash const& target + , boost::function cb); + + // key is a 32-byte binary string, the public key to look up. + // the salt is optional + void get_item(char const* key + , boost::function cb + , std::string salt = std::string()); + + void put_item(entry data + , boost::function cb); + + void put_item(char const* key + , boost::function cb, std::string salt = std::string()); + void dht_status(session_status& s); void network_stats(int& sent, int& received); diff --git a/include/libtorrent/kademlia/item.hpp b/include/libtorrent/kademlia/item.hpp index 82622f963..f1a58982a 100644 --- a/include/libtorrent/kademlia/item.hpp +++ b/include/libtorrent/kademlia/item.hpp @@ -38,10 +38,14 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include namespace libtorrent { namespace dht { +// calculate the target hash for an item. Either v must be specified, +// which is the content. That's for immutable items. For mutable items, +// instead specify the salt and the public key (pk). sha1_hash TORRENT_EXTRA_EXPORT item_target_id( std::pair v , std::pair salt @@ -54,7 +58,16 @@ bool TORRENT_EXTRA_EXPORT verify_mutable_item( char const* pk, char const* sig); -void TORRENT_EXTRA_EXPORT sign_mutable_item( +// TODO: since this is a public function, it should probably be moved +// out of this header and into one with other public functions. + +// given a byte range ``v`` and an optional byte range ``salt``, a +// sequence number, public key ``pk`` (must be 32 bytes) and a secret key +// ``sk`` (must be 64 bytes), this function produces a signature which +// is written into a 64 byte buffer pointed to by ``sig``. The caller +// is responsible for allocating the destination buffer that's passed in +// as the ``sig`` argument. Typically it would be allocated on the stack. +void TORRENT_EXPORT sign_mutable_item( std::pair v, std::pair salt, boost::uint64_t seq, @@ -93,18 +106,20 @@ public: void assign(entry const& v) { - assign(v, std::pair(static_cast(NULL), 0), 0, NULL, NULL); + assign(v, std::pair(static_cast(NULL) + , 0), 0, NULL, NULL); } - void assign(entry const& v - , std::pair salt + void assign(entry const& v, std::pair salt , boost::uint64_t seq, char const* pk, char const* sk); void assign(lazy_entry const* v) { - assign(v, std::pair(static_cast(NULL), 0), 0, NULL, NULL); + assign(v, std::pair(static_cast(NULL) + , 0), 0, NULL, NULL); } - bool assign(lazy_entry const* v - , std::pair salt + bool assign(lazy_entry const* v, std::pair salt , boost::uint64_t seq, char const* pk, char const* sig); + void assign(entry const& v, std::string salt, boost::uint64_t seq + , char const* pk, char const* sig); void clear() { m_value = entry(); } bool empty() const { return m_value.type() == entry::undefined_t; } @@ -114,15 +129,18 @@ public: sha1_hash cas(); entry const& value() const { return m_value; } - char const* pk() { TORRENT_ASSERT(m_mutable); return m_pk; } - char const* sig() { TORRENT_ASSERT(m_mutable); return m_sig; } - boost::uint64_t seq() { TORRENT_ASSERT(m_mutable); return m_seq; } + boost::array const& pk() const + { TORRENT_ASSERT(m_mutable); return m_pk; } + boost::array const& sig() const + { TORRENT_ASSERT(m_mutable); return m_sig; } + boost::uint64_t seq() const { TORRENT_ASSERT(m_mutable); return m_seq; } + std::string const& salt() const { return m_salt; } private: entry m_value; std::string m_salt; - char m_pk[item_pk_len]; - char m_sig[item_sig_len]; + boost::array m_pk; + boost::array m_sig; boost::uint64_t m_seq; bool m_mutable; }; diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index bc2382ec2..a887c4522 100644 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -502,6 +502,68 @@ namespace libtorrent void add_dht_node(std::pair const& node); void add_dht_router(std::pair const& node); + // query the DHT for an immutable item at the ``target`` hash. + // the result is posted as a dht_immutable_item_alert. + void dht_get_item(sha1_hash const& target); + + // query the DHT for a mutable item under the public key ``key``. + // this is an ed25519 key. ``salt`` is optional and may be left + // as an empty string if no salt is to be used. + // if the item is found in the DHT, a dht_mutable_item_alert is + // posted. + void dht_get_item(boost::array key + , std::string salt = std::string()); + + // store the given bencoded data as an immutable item in the DHT. + // the returned hash is the key that is to be used to look the item + // up agan. It's just the sha-1 hash of the bencoded form of the + // structure. + sha1_hash dht_put_item(entry data); + + // store an immutable item. The ``key`` is the public key the blob is + // to be stored under. The optional ``salt`` argument is a string that + // is to be mixed in with the key when determining where in the DHT + // the value is to be stored. The callback function is called from within + // the libtorrent network thread once we've found where to store the blob, + // possibly with the current value stored under the key. + // The values passed to the callback functions are: + // + // entry& value + // the current value stored under the key (may be empty). Also expected + // to be set to the value to be stored by the function. + // + // boost::array& signature + // the signature authenticating the current value. This may be zeroes + // if there is currently no value stored. The functon is expected to + // fill in this buffer with the signature of the new value to store. + // To generate the signature, you may want to use the + // ``sign_mutable_item`` function. + // + // boost::uint64_t& seq + // current sequence number. May be zero if there is no current value. + // The function is expected to set this to the new sequence number of + // the value that is to be stored. Sequence numbers must be monotonically + // increasing. Attempting to overwrite a value with a lower or equal + // sequence number will fail, even if the signature is correct. + // + // std::string const& salt + // this is the salt that was used for this put call. + // + // Since the callback function ``cb`` is called from within libtorrent, + // it is critical to not perform any blocking operations. Ideally not + // even locking a mutex. Pass any data required for this function along + // with the function object's context and make the function entirely + // self-contained. The only reason data blobs' values are computed + // via a function instead of just passing in the new value is to avoid + // race conditions. If you want to *update* the value in the DHT, you + // must first retrieve it, then modify it, then write it back. The way + // the DHT works, it is natural to always do a lookup before storing and + // calling the callback in between is convenient. + void dht_put_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt = std::string()); + #ifndef TORRENT_NO_DEPRECATE // deprecated in 0.15 // use save_state and load_state instead diff --git a/src/alert.cpp b/src/alert.cpp index ffa0ca043..841f456be 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -579,5 +579,26 @@ namespace libtorrent { return msg; } + std::string dht_immutable_item_alert::message() const + { + char msg[1050]; + snprintf(msg, sizeof(msg), "DHT immutable item %s [ %s ]" + , to_hex(target.to_string()).c_str() + , item.to_string().c_str()); + return msg; + } + + std::string dht_mutable_item_alert::message() const + { + char msg[1050]; + snprintf(msg, sizeof(msg), "DHT mutable item (key=%s salt=%s seq=%" PRId64 ") [ %s ]" + , to_hex(std::string(&key[0], 32)).c_str() + , salt.c_str() + , seq + , item.to_string().c_str()); + return msg; + } + + } // namespace libtorrent diff --git a/src/kademlia/dht_tracker.cpp b/src/kademlia/dht_tracker.cpp index ec454e6f2..27e701a00 100644 --- a/src/kademlia/dht_tracker.cpp +++ b/src/kademlia/dht_tracker.cpp @@ -422,6 +422,87 @@ namespace libtorrent { namespace dht m_dht.announce(ih, listen_port, flags, f); } + // these functions provide a slightly higher level + // interface to the get/put functionality in the DHT + bool get_immutable_item_callback(item& it, boost::function f) + { + // the reason to wrap here is to control the return value + // since it controls whether we re-put the content + TORRENT_ASSERT(!it.is_mutable()); + f(it); + return false; + } + + bool get_mutable_item_callback(item& it, boost::function f) + { + // the reason to wrap here is to control the return value + // since it controls whether we re-put the content + TORRENT_ASSERT(it.is_mutable()); + f(it); + return false; + } + + bool put_immutable_item_callback(item& it, boost::function f + , entry data) + { + TORRENT_ASSERT(!it.is_mutable()); + it.assign(data); + // TODO: ideally this function would be called when the + // put completes + f(); + return true; + } + + bool put_mutable_item_callback(item& it, boost::function cb) + { + cb(it); + return true; + } + + void dht_tracker::get_item(sha1_hash const& target + , boost::function cb) + { + m_dht.get_item(target, boost::bind(&get_immutable_item_callback, _1, cb)); + } + + // key is a 32-byte binary string, the public key to look up. + // the salt is optional + void dht_tracker::get_item(char const* key + , boost::function cb + , std::string salt) + { + sha1_hash target = item_target_id( + std::pair(NULL, 0) + , std::pair(salt.c_str(), salt.size()) + , key); + + m_dht.get_item(target, boost::bind(&get_mutable_item_callback, _1, cb)); + } + + void dht_tracker::put_item(entry data + , boost::function cb) + { + std::string flat_data; + bencode(std::back_inserter(flat_data), data); + sha1_hash target = item_target_id( + std::pair(flat_data.c_str(), flat_data.size()) + , std::pair(NULL, 0), NULL); + + m_dht.get_item(target, boost::bind(&put_immutable_item_callback + , _1, cb, data)); + } + + void dht_tracker::put_item(char const* key + , boost::function cb, std::string salt) + { + sha1_hash target = item_target_id( + std::pair(NULL, 0) + , std::pair(salt.c_str(), salt.size()) + , key); + + m_dht.get_item(target, boost::bind(&put_mutable_item_callback + , _1, cb)); + } // translate bittorrent kademlia message into the generice kademlia message // used by the library diff --git a/src/kademlia/get_item.cpp b/src/kademlia/get_item.cpp index fda338659..26db9af00 100644 --- a/src/kademlia/get_item.cpp +++ b/src/kademlia/get_item.cpp @@ -47,6 +47,8 @@ void get_item::got_data(lazy_entry const* v, boost::uint64_t seq, char const* sig) { + // we received data! + std::pair salt(m_salt.c_str(), m_salt.size()); sha1_hash incoming_target = item_target_id(v->data_section(), salt, pk); @@ -54,6 +56,9 @@ void get_item::got_data(lazy_entry const* v, if (pk && sig) { + // this is mutable data. If it passes the signature + // check, remember it. Just keep the version with + // the highest sequence number. if (m_data.empty() || m_data.seq() < seq) { if (!m_data.assign(v, salt, seq, pk, sig)) @@ -62,8 +67,15 @@ void get_item::got_data(lazy_entry const* v, } else if (m_data.empty()) { + // this is the first time we receive data, + // and it's immutable + m_data.assign(v); bool put_requested = m_data_callback(m_data); + + // if we intend to put, we need to keep going + // until we find the closest nodes, since those + // are the ones we're putting to if (put_requested) { #if TORRENT_USE_ASSERTS @@ -71,6 +83,10 @@ void get_item::got_data(lazy_entry const* v, bencode(std::back_inserter(buffer), m_data.value()); TORRENT_ASSERT(m_target == hasher(&buffer[0], buffer.size()).final()); #endif + + // this function is called when we're done, passing + // in all relevant nodes we received data from close + // to the target. m_nodes_callback = boost::bind(&get_item::put, this, _1); } else @@ -126,13 +142,17 @@ void get_item::done() { if (m_data.is_mutable() || m_data.empty()) { + // for mutable data, we only call the callback at the end, + // when we've heard from everyone, to be sure we got the + // latest version of the data (i.e. highest sequence number) bool put_requested = m_data_callback(m_data); if (put_requested) { #if TORRENT_USE_ASSERTS if (m_data.is_mutable()) { - TORRENT_ASSERT(m_target == hasher(m_data.pk(), item_pk_len).final()); + TORRENT_ASSERT(m_target == hasher(m_data.pk().data(), + item_pk_len).final()); } else { @@ -141,12 +161,19 @@ void get_item::done() TORRENT_ASSERT(m_target == hasher(&buffer[0], buffer.size()).final()); } #endif + + // this function is called when we're done, passing + // in all relevant nodes we received data from close + // to the target. m_nodes_callback = boost::bind(&get_item::put, this, _1); } } find_data::done(); } +// this function sends a put message to the nodes +// closest to the target. Those nodes are passed in +// as the v argument void get_item::put(std::vector > const& v) { #ifdef TORRENT_DHT_VERBOSE_LOGGING @@ -181,9 +208,9 @@ void get_item::put(std::vector > const& v) a["token"] = i->second; if (m_data.is_mutable()) { - a["k"] = std::string(m_data.pk(), item_pk_len); + a["k"] = std::string(m_data.pk().data(), item_pk_len); a["seq"] = m_data.seq(); - a["sig"] = std::string(m_data.sig(), item_sig_len); + a["sig"] = std::string(m_data.sig().data(), item_sig_len); } m_node.m_rpc.invoke(e, i->first.ep(), o); } diff --git a/src/kademlia/item.cpp b/src/kademlia/item.cpp index 218678772..fdbb03927 100644 --- a/src/kademlia/item.cpp +++ b/src/kademlia/item.cpp @@ -120,6 +120,12 @@ bool verify_mutable_item( (unsigned char const*)pk) == 1; } +// given the bencoded buffer ``v``, the salt (which is optional and may have +// a length of zero to be omitted), sequence number ``seq``, public key (32 +// bytes ed25519 key) ``pk`` and a secret/private key ``sk`` (64 bytes ed25519 +// key) a signature ``sig`` is produced. The ``sig`` pointer must point to +// at least 64 bytes of available space. This space is where the signature is +// written. void sign_mutable_item( std::pair v, std::pair salt, @@ -178,8 +184,8 @@ void item::assign(entry const& v, std::pair salt int bsize = bencode(buffer, v); TORRENT_ASSERT(bsize <= 1000); sign_mutable_item(std::make_pair(buffer, bsize) - , salt, seq, pk, sk, m_sig); - memcpy(m_pk, pk, item_pk_len); + , salt, seq, pk, sk, m_sig.c_array()); + memcpy(m_pk.c_array(), pk, item_pk_len); m_seq = seq; m_mutable = true; } @@ -196,10 +202,12 @@ bool item::assign(lazy_entry const* v { if (!verify_mutable_item(v->data_section(), salt, seq, pk, sig)) return false; - memcpy(m_pk, pk, item_pk_len); - memcpy(m_sig, sig, item_sig_len); + memcpy(m_pk.c_array(), pk, item_pk_len); + memcpy(m_sig.c_array(), sig, item_sig_len); if (salt.second > 0) m_salt.assign(salt.first, salt.second); + else + m_salt.clear(); m_seq = seq; m_mutable = true; } @@ -210,6 +218,17 @@ bool item::assign(lazy_entry const* v return true; } +void item::assign(entry const& v, std::string salt, boost::uint64_t seq + , char const* pk, char const* sig) +{ + memcpy(m_pk.c_array(), pk, item_pk_len); + memcpy(m_sig.c_array(), sig, item_sig_len); + m_salt = salt; + m_seq = seq; + m_mutable = true; + m_value = v; +} + sha1_hash item::cas() { TORRENT_ASSERT(m_mutable); diff --git a/src/session.cpp b/src/session.cpp index 334106fa0..737b2a875 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -332,6 +332,9 @@ namespace libtorrent #define TORRENT_ASYNC_CALL2(x, a1, a2) \ m_impl->m_io_service.dispatch(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)) +#define TORRENT_ASYNC_CALL3(x, a1, a2, a3) \ + m_impl->m_io_service.dispatch(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3)) + #define TORRENT_WAIT \ mutex::scoped_lock l(m_impl->mut); \ while (!done) { m_impl->cond.wait(l); }; @@ -877,6 +880,43 @@ namespace libtorrent #endif } + void session::dht_get_item(sha1_hash const& target) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(dht_get_immutable_item, target); +#endif + } + + void session::dht_get_item(boost::array key + , std::string salt) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL2(dht_get_mutable_item, key, salt); +#endif + } + + sha1_hash session::dht_put_item(entry data) + { + std::vector buf; + bencode(std::back_inserter(buf), data); + sha1_hash ret = hasher(&buf[0], buf.size()).final(); + +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(dht_put_item, data); +#endif + return ret; + } + + void session::dht_put_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL3(dht_put_mutable_item, key, cb, salt); +#endif + } + void session::set_pe_settings(pe_settings const& settings) { #ifndef TORRENT_DISABLE_ENCRYPTION diff --git a/src/session_impl.cpp b/src/session_impl.cpp index cdb5ca5f8..7aa5c8f2c 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -106,6 +106,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #endif // TORRENT_DISABLE_DHT #include "libtorrent/http_tracker_connection.hpp" @@ -5771,6 +5772,70 @@ retry: ++host; } } + + // callback for dht_immutable_get + void session_impl::get_immutable_callback(sha1_hash target + , dht::item const& i) + { + TORRENT_ASSERT(!i.is_mutable()); + m_alerts.post_alert(dht_immutable_item_alert(target, i.value())); + } + + void session_impl::dht_get_immutable_item(sha1_hash const& target) + { + if (!m_dht) return; + m_dht->get_item(target, boost::bind(&session_impl::get_immutable_callback + , this, target, _1)); + } + + // callback for dht_mutable_get + void session_impl::get_mutable_callback(dht::item const& i) + { + TORRENT_ASSERT(i.is_mutable()); + m_alerts.post_alert(dht_mutable_item_alert(i.pk(), i.sig(), i.seq() + , i.salt(), i.value())); + } + + // key is a 32-byte binary string, the public key to look up. + // the salt is optional + void session_impl::dht_get_mutable_item(boost::array key + , std::string salt) + { + if (!m_dht) return; + m_dht->get_item(key.data(), boost::bind(&session_impl::get_mutable_callback + , this, _1), salt); + } + + void nop() {} + + void session_impl::dht_put_item(entry data) + { + if (!m_dht) return; + m_dht->put_item(data, boost::bind(&nop)); + } + + void put_mutable_callback(dht::item& i + , boost::function& + , boost::uint64_t&, std::string const&)> cb) + { + entry value = i.value(); + boost::array sig = i.sig(); + boost::array pk = i.pk(); + boost::uint64_t seq = i.seq(); + std::string salt = i.salt(); + cb(value, sig, seq, salt); + i.assign(value, salt, seq, pk.data(), sig.data()); + } + + void session_impl::dht_put_mutable_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt) + { + if (!m_dht) return; + m_dht->put_item(key.data(), boost::bind(&put_mutable_callback, _1, cb), salt); + } + #endif void session_impl::maybe_update_udp_mapping(int nat, int local_port, int external_port) diff --git a/test/test_dht.cpp b/test/test_dht.cpp index 7b59b7ca0..bfa2afa57 100644 --- a/test/test_dht.cpp +++ b/test/test_dht.cpp @@ -1635,8 +1635,8 @@ int test_main() if (g_got_items.empty()) break; TEST_EQUAL(g_got_items.front().value(), items[0].ent); - TEST_CHECK(memcmp(g_got_items.front().pk(), public_key, item_pk_len) == 0); - TEST_CHECK(memcmp(g_got_items.front().sig(), signature, item_sig_len) == 0); + TEST_CHECK(memcmp(g_got_items.front().pk().data(), public_key, item_pk_len) == 0); + TEST_CHECK(memcmp(g_got_items.front().sig().data(), signature, item_sig_len) == 0); TEST_EQUAL(g_got_items.front().seq(), seq); g_got_items.clear(); @@ -1761,7 +1761,7 @@ int test_main() sha1_hash target = hasher(public_key, item_pk_len).final(); g_put_item.assign(items[0].ent, empty_salt, seq, public_key, private_key); - std::string sig(g_put_item.sig(), item_sig_len); + std::string sig(g_put_item.sig().data(), item_sig_len); node.get_item(target, get_item_cb); TEST_EQUAL(g_sent_packets.size(), num_test_nodes);