Skip to content
This repository was archived by the owner on Oct 28, 2021. It is now read-only.

Commit e80e197

Browse files
authored
Fix failing Discovery tests (#5581)
Fix failing Discovery tests
2 parents 3f7e3f0 + 2888707 commit e80e197

File tree

4 files changed

+116
-54
lines changed

4 files changed

+116
-54
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- Changed: [#5568](https://github.com/ethereum/aleth/pull/5568) Improve rlpx handshake log messages and create new rlpx log channel.
1111
- Changed: [#5576](https://github.com/ethereum/aleth/pull/5576) Moved sstore_combinations and static_Call50000_sha256 tests to stTimeConsuming test suite. (testeth runs them only with `--all` flag)
1212
- Fixed: [#5562](https://github.com/ethereum/aleth/pull/5562) Don't send header request messages to peers that haven't sent us Status yet.
13+
- Fixed: [#5581](https://github.com/ethereum/aleth/pull/5581) Fixed finding neighbour nodes in Discovery.
1314

1415
## [1.6.0] - 2019-04-16
1516

libp2p/NodeTable.cpp

Lines changed: 20 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ void NodeTable::doDiscoveryRound(
203203
if (!m_socket->isOpen())
204204
return;
205205

206+
// send s_alpha FindNode packets to nodes we know, closest to target
206207
auto const nearestNodes = nearestNodeEntries(_node);
207208
auto newTriedCount = 0;
208209
for (auto const& nodeEntry : nearestNodes)
@@ -262,57 +263,29 @@ void NodeTable::doDiscoveryRound(
262263
});
263264
}
264265

265-
vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeID _target)
266+
vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeID const& _target)
266267
{
267-
// send s_alpha FindNode packets to nodes we know, closest to target
268-
static unsigned lastBin = s_bins - 1;
269-
unsigned head = distance(m_hostNodeID, _target);
270-
unsigned tail = head == 0 ? lastBin : (head - 1) % s_bins;
271-
272-
map<unsigned, list<shared_ptr<NodeEntry>>> found;
268+
auto const distanceToTargetLess = [](pair<int, shared_ptr<NodeEntry>> const& _node1,
269+
pair<int, shared_ptr<NodeEntry>> const& _node2) {
270+
return _node1.first < _node2.first;
271+
};
272+
273+
std::multiset<pair<int, shared_ptr<NodeEntry>>, decltype(distanceToTargetLess)>
274+
nodesByDistanceToTarget(distanceToTargetLess);
275+
for (auto const& bucket : m_buckets)
276+
for (auto const& nodeWeakPtr : bucket.nodes)
277+
if (auto node = nodeWeakPtr.lock())
278+
{
279+
nodesByDistanceToTarget.emplace(distance(_target, node->id()), node);
273280

274-
// if d is 0, then we roll look forward, if last, we reverse, else, spread from d
275-
if (head > 1 && tail != lastBin)
276-
while (head != tail && head < s_bins)
277-
{
278-
Guard l(x_state);
279-
for (auto const& n : m_buckets[head].nodes)
280-
if (auto p = n.lock())
281-
found[distance(_target, p->id())].push_back(p);
282-
283-
if (tail)
284-
for (auto const& n : m_buckets[tail].nodes)
285-
if (auto p = n.lock())
286-
found[distance(_target, p->id())].push_back(p);
287-
288-
head++;
289-
if (tail)
290-
tail--;
291-
}
292-
else if (head < 2)
293-
while (head < s_bins)
294-
{
295-
Guard l(x_state);
296-
for (auto const& n : m_buckets[head].nodes)
297-
if (auto p = n.lock())
298-
found[distance(_target, p->id())].push_back(p);
299-
head++;
300-
}
301-
else
302-
while (tail > 0)
303-
{
304-
Guard l(x_state);
305-
for (auto const& n : m_buckets[tail].nodes)
306-
if (auto p = n.lock())
307-
found[distance(_target, p->id())].push_back(p);
308-
tail--;
309-
}
281+
if (nodesByDistanceToTarget.size() > s_bucketSize)
282+
nodesByDistanceToTarget.erase(--nodesByDistanceToTarget.end());
283+
}
310284

311285
vector<shared_ptr<NodeEntry>> ret;
312-
for (auto& nodes : found)
313-
for (auto const& n : nodes.second)
314-
if (ret.size() < s_bucketSize && n->endpoint() && isAllowedEndpoint(n->endpoint()))
315-
ret.push_back(n);
286+
for (auto& distanceAndNode : nodesByDistanceToTarget)
287+
ret.emplace_back(move(distanceAndNode.second));
288+
316289
return ret;
317290
}
318291

libp2p/NodeTable.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ class NodeTable : UDPSocketEvents
253253
void doDiscoveryRound(NodeID _target, unsigned _round,
254254
std::shared_ptr<std::set<std::shared_ptr<NodeEntry>>> _tried);
255255

256-
/// Returns nodes from node table which are closest to target.
257-
std::vector<std::shared_ptr<NodeEntry>> nearestNodeEntries(NodeID _target);
256+
/// Returns s_bucketSize nodes from node table which are closest to target.
257+
std::vector<std::shared_ptr<NodeEntry>> nearestNodeEntries(NodeID const& _target);
258258

259259
/// Asynchronously drops _leastSeen node if it doesn't reply and adds _replacement node,
260260
/// otherwise _replacement is thrown away.

test/unittests/libp2p/net.cpp

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ struct TestNodeTable: public NodeTable
5454
static vector<pair<Public, uint16_t>> createTestNodes(unsigned _count)
5555
{
5656
vector<pair<Public, uint16_t>> ret;
57-
asserts(_count <= 1000);
57+
asserts(_count <= 2000);
5858

5959
ret.clear();
6060
for (unsigned i = 0; i < _count; i++)
@@ -210,13 +210,14 @@ struct TestNodeTable: public NodeTable
210210
concurrent_queue<bytes> packetsReceived;
211211

212212

213-
using NodeTable::m_hostNodeID;
213+
using NodeTable::m_allowLocalDiscovery;
214214
using NodeTable::m_hostNodeEndpoint;
215+
using NodeTable::m_hostNodeID;
215216
using NodeTable::m_socket;
217+
using NodeTable::nearestNodeEntries;
218+
using NodeTable::nodeEntry;
216219
using NodeTable::noteActiveNode;
217220
using NodeTable::setRequestTimeToLive;
218-
using NodeTable::nodeEntry;
219-
using NodeTable::m_allowLocalDiscovery;
220221
};
221222

222223
/**
@@ -550,6 +551,93 @@ BOOST_AUTO_TEST_CASE(noteActiveNodeEvictsTheNodeWhenBucketIsFull)
550551
BOOST_CHECK_EQUAL(evicted->replacementNodeEntry->id(), newNodeId);
551552
}
552553

554+
BOOST_AUTO_TEST_CASE(nearestNodeEntriesOneNode)
555+
{
556+
TestNodeTableHost nodeTableHost(1);
557+
nodeTableHost.populate(1);
558+
559+
auto& nodeTable = nodeTableHost.nodeTable;
560+
vector<shared_ptr<NodeEntry>> const nearest = nodeTable->nearestNodeEntries(NodeID::random());
561+
562+
BOOST_REQUIRE_EQUAL(nearest.size(), 1);
563+
BOOST_REQUIRE_EQUAL(nearest.front()->id(), nodeTableHost.testNodes.front().first);
564+
}
565+
566+
unsigned xorDistance(h256 const& _h1, h256 const& _h2)
567+
{
568+
u256 d = _h1 ^ _h2;
569+
unsigned ret = 0;
570+
while (d >>= 1)
571+
++ret;
572+
return ret;
573+
};
574+
575+
BOOST_AUTO_TEST_CASE(nearestNodeEntriesOneDistantNode)
576+
{
577+
// specific case that was failing - one node in bucket #252, target corresponding to bucket #253
578+
unique_ptr<TestNodeTableHost> nodeTableHost;
579+
do
580+
{
581+
nodeTableHost.reset(new TestNodeTableHost(1));
582+
nodeTableHost->populate(1);
583+
} while (nodeTableHost->nodeTable->bucketSize(252) != 1);
584+
585+
auto& nodeTable = nodeTableHost->nodeTable;
586+
587+
h256 const hostNodeIDHash = sha3(nodeTable->m_hostNodeID);
588+
589+
NodeID target = NodeID::random();
590+
while (xorDistance(hostNodeIDHash, sha3(target)) != 254)
591+
target = NodeID::random();
592+
593+
vector<shared_ptr<NodeEntry>> const nearest = nodeTable->nearestNodeEntries(target);
594+
595+
BOOST_REQUIRE_EQUAL(nearest.size(), 1);
596+
BOOST_REQUIRE_EQUAL(nearest.front()->id(), nodeTableHost->testNodes.front().first);
597+
}
598+
599+
BOOST_AUTO_TEST_CASE(nearestNodeEntriesManyNodes)
600+
{
601+
unsigned constexpr nodeCount = 128;
602+
TestNodeTableHost nodeTableHost(nodeCount);
603+
nodeTableHost.populate(nodeCount);
604+
605+
auto& nodeTable = nodeTableHost.nodeTable;
606+
607+
NodeID const target = NodeID::random();
608+
vector<shared_ptr<NodeEntry>> nearest = nodeTable->nearestNodeEntries(target);
609+
610+
BOOST_REQUIRE_EQUAL(nearest.size(), 16);
611+
612+
// get all nodes sorted by distance to target
613+
list<NodeEntry> const allNodeEntries = nodeTable->snapshot();
614+
h256 const targetNodeIDHash = sha3(target);
615+
vector<pair<unsigned, NodeID>> nodesByDistanceToTarget;
616+
for (auto const& nodeEntry : allNodeEntries)
617+
{
618+
NodeID const& nodeID = nodeEntry.id();
619+
nodesByDistanceToTarget.emplace_back(xorDistance(targetNodeIDHash, sha3(nodeID)), nodeID);
620+
}
621+
// stable sort to keep them in the order as they are in buckets
622+
// (the same order they are iterated in nearestNodeEntries implementation)
623+
std::stable_sort(nodesByDistanceToTarget.begin(), nodesByDistanceToTarget.end(),
624+
[](pair<unsigned, NodeID> const& _n1, pair<unsigned, NodeID> const& _n2) {
625+
return _n1.first < _n2.first;
626+
});
627+
// get 16 with lowest distance
628+
std::vector<NodeID> expectedNearestIDs;
629+
std::transform(nodesByDistanceToTarget.begin(), nodesByDistanceToTarget.begin() + 16,
630+
std::back_inserter(expectedNearestIDs),
631+
[](pair<unsigned, NodeID> const& _n) { return _n.second; });
632+
633+
634+
vector<NodeID> nearestIDs;
635+
for (auto const& nodeEntry : nearest)
636+
nearestIDs.push_back(nodeEntry->id());
637+
638+
BOOST_REQUIRE(nearestIDs == expectedNearestIDs);
639+
}
640+
553641
BOOST_AUTO_TEST_CASE(unexpectedPong)
554642
{
555643
// NodeTable receiving PONG
@@ -829,7 +917,7 @@ BOOST_AUTO_TEST_CASE(unexpectedFindNode)
829917

830918
BOOST_AUTO_TEST_CASE(evictionWithOldNodeAnswering)
831919
{
832-
TestNodeTableHost nodeTableHost(1000);
920+
TestNodeTableHost nodeTableHost(2000);
833921
auto& nodeTable = nodeTableHost.nodeTable;
834922

835923
// socket receiving PING

0 commit comments

Comments
 (0)