From 1ba6507a2fa471986c42a670ba57e66a41a37d05 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Jan 2019 19:39:17 +0000 Subject: [PATCH] Tests for dht::ServiceAddressLookup --- CMakeLists.txt | 1 + llarp/dht/context.hpp | 18 +- llarp/dht/serviceaddresslookup.cpp | 12 +- test/crypto/mock_crypto.hpp | 23 +- .../test_llarp_dht_serviceaddresslookup.cpp | 221 ++++++++++++++++++ 5 files changed, 256 insertions(+), 19 deletions(-) create mode 100644 test/dht/test_llarp_dht_serviceaddresslookup.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 68b38f3f4..9f2e6b8b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -632,6 +632,7 @@ set(TEST_SRC test/dht/test_llarp_dht_explorenetworkjob.cpp test/dht/test_llarp_dht_kademlia.cpp test/dht/test_llarp_dht_key.cpp + test/dht/test_llarp_dht_serviceaddresslookup.cpp test/dht/test_llarp_dht_taglookup.cpp test/dht/test_llarp_dht_tx.cpp test/dht/test_llarp_dht_txowner.cpp diff --git a/llarp/dht/context.hpp b/llarp/dht/context.hpp index 918b51172..b7b3fcb0b 100644 --- a/llarp/dht/context.hpp +++ b/llarp/dht/context.hpp @@ -31,16 +31,18 @@ namespace llarp /// on behalf of whoasked request introset for target from dht router with /// key askpeer virtual void - LookupIntroSetRecursive( - const service::Address& target, const Key_t& whoasked, - uint64_t whoaskedTX, const Key_t& askpeer, uint64_t R, - service::IntroSetLookupHandler result = nullptr) = 0; + LookupIntroSetRecursive(const service::Address& target, + const Key_t& whoasked, uint64_t whoaskedTX, + const Key_t& askpeer, uint64_t R, + service::IntroSetLookupHandler result = + service::IntroSetLookupHandler()) = 0; virtual void - LookupIntroSetIterative( - const service::Address& target, const Key_t& whoasked, - uint64_t whoaskedTX, const Key_t& askpeer, - service::IntroSetLookupHandler result = nullptr) = 0; + LookupIntroSetIterative(const service::Address& target, + const Key_t& whoasked, uint64_t whoaskedTX, + const Key_t& askpeer, + service::IntroSetLookupHandler result = + service::IntroSetLookupHandler()) = 0; virtual std::set< service::IntroSet > FindRandomIntroSetsWithTagExcluding( diff --git a/llarp/dht/serviceaddresslookup.cpp b/llarp/dht/serviceaddresslookup.cpp index 571c1c2cd..b630e4b39 100644 --- a/llarp/dht/serviceaddresslookup.cpp +++ b/llarp/dht/serviceaddresslookup.cpp @@ -39,8 +39,16 @@ namespace llarp ServiceAddressLookup::GetNextPeer(Key_t &next, const std::set< Key_t > &exclude) { - Key_t k = target.ToKey(); - return parent->Nodes()->FindCloseExcluding(k, next, exclude); + Key_t k = target.ToKey(); + auto nodes = parent->Nodes(); + if(nodes) + { + return nodes->FindCloseExcluding(k, next, exclude); + } + else + { + return false; + } } void diff --git a/test/crypto/mock_crypto.hpp b/test/crypto/mock_crypto.hpp index 27896411f..42506f767 100644 --- a/test/crypto/mock_crypto.hpp +++ b/test/crypto/mock_crypto.hpp @@ -12,12 +12,12 @@ namespace llarp struct MockCrypto final : public Crypto { MOCK_METHOD3(xchacha20, - bool(llarp_buffer_t, const SharedSecret &, + bool(const llarp_buffer_t &, const SharedSecret &, const TunnelNonce &)); MOCK_METHOD4(xchacha20_alt, - bool(llarp_buffer_t, llarp_buffer_t, const SharedSecret &, - const byte_t *)); + bool(const llarp_buffer_t &, const llarp_buffer_t &, + const SharedSecret &, const byte_t *)); MOCK_METHOD4(dh_client, bool(SharedSecret &, const PubKey &, const SecretKey &, @@ -35,21 +35,26 @@ namespace llarp bool(SharedSecret &, const PubKey &, const SecretKey &, const TunnelNonce &)); - MOCK_METHOD2(hash, bool(byte_t *, llarp_buffer_t)); + MOCK_METHOD2(hash, bool(byte_t *, const llarp_buffer_t &)); - MOCK_METHOD2(shorthash, bool(ShortHash &, llarp_buffer_t)); + MOCK_METHOD2(shorthash, bool(ShortHash &, const llarp_buffer_t &)); - MOCK_METHOD3(hmac, bool(byte_t *, llarp_buffer_t, const SharedSecret &)); + MOCK_METHOD3(hmac, + bool(byte_t *, const llarp_buffer_t &, + const SharedSecret &)); - MOCK_METHOD3(sign, bool(Signature &, const SecretKey &, llarp_buffer_t)); + MOCK_METHOD3(sign, + bool(Signature &, const SecretKey &, + const llarp_buffer_t &)); MOCK_METHOD3(verify, - bool(const PubKey &, llarp_buffer_t, const Signature &)); + bool(const PubKey &, const llarp_buffer_t &, + const Signature &)); MOCK_METHOD2(seed_to_secretkey, bool(llarp::SecretKey &, const llarp::IdentitySecret &)); - MOCK_METHOD1(randomize, void(llarp_buffer_t)); + MOCK_METHOD1(randomize, void(const llarp_buffer_t &)); MOCK_METHOD2(randbytes, void(void *, size_t)); diff --git a/test/dht/test_llarp_dht_serviceaddresslookup.cpp b/test/dht/test_llarp_dht_serviceaddresslookup.cpp new file mode 100644 index 000000000..758f4b479 --- /dev/null +++ b/test/dht/test_llarp_dht_serviceaddresslookup.cpp @@ -0,0 +1,221 @@ +#include + +#include +#include +#include +#include +#include + +#include + +using namespace llarp; +using namespace ::testing; + +using test::makeBuf; + +struct MockIntroSetHandler +{ + MOCK_METHOD1(call, void(const std::vector< service::IntroSet > &)); +}; + +static constexpr uint64_t EXPIRY = 1548503831ull; + +struct TestDhtServiceAddressLookup : public ::testing::Test +{ + test::MockCrypto crypto; + MockIntroSetHandler introsetHandler; + + dht::Key_t ourKey; + dht::Key_t txKey; + uint64_t txId; + dht::TXOwner txOwner; + service::Address address; + + test::MockContext context; + uint64_t r; + std::unique_ptr< dht::ServiceAddressLookup > serviceAddressLookup; + + TestDhtServiceAddressLookup() + : ourKey(makeBuf< dht::Key_t >(0xFF)) + , txKey(makeBuf< dht::Key_t >(0x01)) + , txId(2) + , txOwner(txKey, txId) + , address(makeBuf< service::Address >(0x03)) + , r(4) + { + EXPECT_CALL(context, OurKey()).WillOnce(ReturnRef(ourKey)); + + serviceAddressLookup = std::make_unique< dht::ServiceAddressLookup >( + txOwner, address, &context, r, + std::bind(&MockIntroSetHandler::call, &introsetHandler, + std::placeholders::_1)); + } +}; + +TEST_F(TestDhtServiceAddressLookup, validate) +{ + // Concerns + // - introset fails to verify + // - introset topic is not the target + // - happy path + + { + service::IntroSet introset; + EXPECT_CALL(context, Crypto()).WillOnce(Return(&crypto)); + EXPECT_CALL(context, Now()).WillOnce(Return(EXPIRY)); + EXPECT_CALL(crypto, verify(_, _, _)).WillOnce(Return(false)); + + ASSERT_FALSE(serviceAddressLookup->Validate(introset)); + } + + { + service::IntroSet introset; + + // Fiddle with the introset so we pass the Verify call + introset.I.emplace_back(); + introset.I.front().expiresAt = + EXPIRY + service::MAX_INTROSET_TIME_DELTA + 1; + + // Set expectations + EXPECT_CALL(context, Crypto()).WillOnce(Return(&crypto)); + EXPECT_CALL(context, Now()).WillOnce(Return(EXPIRY)); + EXPECT_CALL(crypto, verify(_, _, _)).WillOnce(Return(true)); + + ASSERT_FALSE(serviceAddressLookup->Validate(introset)); + } + + { + service::IntroSet introset; + // Set the current address of the lookup to be equal to the default address. + // This is easier than manipulating the ServiceInfo address. + serviceAddressLookup->target.Zero(); + + // Fiddle with the introset so we pass the Verify call + introset.I.emplace_back(); + introset.I.front().expiresAt = + EXPIRY + service::MAX_INTROSET_TIME_DELTA + 1; + + // Set expectations + EXPECT_CALL(context, Crypto()).WillOnce(Return(&crypto)); + EXPECT_CALL(context, Now()).WillOnce(Return(EXPIRY)); + EXPECT_CALL(crypto, verify(_, _, _)).WillOnce(Return(true)); + + ASSERT_TRUE(serviceAddressLookup->Validate(introset)); + } +} + +TEST_F(TestDhtServiceAddressLookup, start) +{ + // Verify input arguments are passed correctly. + // The actual logic is inside the `dht::AbstractContext` implementation. + + // clang-format off + EXPECT_CALL(context, DHTSendTo( + Eq(txKey.as_array()), + WhenDynamicCastTo< dht::FindIntroMessage* >(NotNull()), + true) + ).Times(1); + // clang-format off + + ASSERT_NO_THROW(serviceAddressLookup->Start(txOwner)); +} + +TEST_F(TestDhtServiceAddressLookup, get_next_peer) +{ + // Concerns + // - Nodes returns nullptr + // - Happy path + + dht::Key_t key = makeBuf< dht::Key_t >(0x02); + std::set< dht::Key_t > exclude; + { + EXPECT_CALL(context, Nodes()).WillOnce(ReturnNull()); + ASSERT_FALSE(serviceAddressLookup->GetNextPeer(key, exclude)); + } + + { + uint64_t randVal = 0; + + dht::Bucket< dht::RCNode > nodes(ourKey, [&]() { return randVal++; }); + nodes.nodes.emplace(makeBuf< dht::Key_t >(0x03), dht::RCNode()); + EXPECT_CALL(context, Nodes()).WillOnce(Return(&nodes)); + ASSERT_TRUE(serviceAddressLookup->GetNextPeer(key, exclude)); + } +} + +TEST_F(TestDhtServiceAddressLookup, do_next) +{ + // Concerns: + // - R != 0 + // - R = 0 + + const dht::Key_t key = makeBuf< dht::Key_t >(0x02); + + { + // R != 0 + EXPECT_CALL(context, LookupIntroSetRecursive(address, txKey, txId, key, r - 1, _)); + ASSERT_NO_THROW(serviceAddressLookup->DoNextRequest(key)); + } + + { + // R = 0 + serviceAddressLookup->R = 0; + EXPECT_CALL(context, LookupIntroSetIterative(address, txKey, txId, key, _)); + ASSERT_NO_THROW(serviceAddressLookup->DoNextRequest(key)); + } +} + +TEST_F(TestDhtServiceAddressLookup, send_reply) +{ + // Concerns + // - handle result is set, is called + // - handle result is not set + + { + serviceAddressLookup->valuesFound.emplace_back(); + EXPECT_CALL(introsetHandler, call(SizeIs(1))); + + // clang-format off + EXPECT_CALL( + context, + DHTSendTo( + Eq(txKey.as_array()), + WhenDynamicCastTo( + AllOf( + NotNull(), + Field(&dht::GotIntroMessage::I, SizeIs(1)) + ) + ), + true + ) + ); + // clang-format on + + ASSERT_NO_THROW(serviceAddressLookup->SendReply()); + } + + { + serviceAddressLookup->valuesFound.clear(); + serviceAddressLookup->valuesFound.emplace_back(); + serviceAddressLookup->handleResult = + decltype(serviceAddressLookup->handleResult)(); + + // clang-format off + EXPECT_CALL( + context, + DHTSendTo( + Eq(txKey.as_array()), + WhenDynamicCastTo( + AllOf( + NotNull(), + Field(&dht::GotIntroMessage::I, SizeIs(1)) + ) + ), + true + ) + ); + // clang-format on + + ASSERT_NO_THROW(serviceAddressLookup->SendReply()); + } +}