diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1700,6 +1700,7 @@ // If a proof radix tree timed out, cleanup if (pnode->m_proof_relay && + !pnode->m_proof_relay->sharedProofs.isNull() && now > (pnode->m_proof_relay->lastSharedProofsUpdate.load() + AVALANCHE_AVAPROOFS_TIMEOUT)) { LogPrint(BCLog::AVALANCHE, diff --git a/src/radix.h b/src/radix.h --- a/src/radix.h +++ b/src/radix.h @@ -104,6 +104,13 @@ return *this; } + /** The tree is clear of node and leaf */ + bool isNull() const { + RCULock lock; + RadixElement e = root.load(); + return e.isLeaf() && e.getLeaf() == nullptr; + } + /** * Insert a value into the tree. * Returns true if the value was inserted, false if it was already present. diff --git a/src/test/radix_tests.cpp b/src/test/radix_tests.cpp --- a/src/test/radix_tests.cpp +++ b/src/test/radix_tests.cpp @@ -644,4 +644,50 @@ BOOST_CHECK(!mytree.get(3)); } +BOOST_AUTO_TEST_CASE(radix_is_null) { + using E = TestElement; + + RadixTree mytree; + BOOST_CHECK(mytree.isNull()); + + auto zero = RCUPtr::make(0); + auto one = RCUPtr::make(1); + auto two = RCUPtr::make(2); + auto three = RCUPtr::make(3); + + auto keyZero = zero->getId(); + auto keyOne = one->getId(); + auto keyTwo = two->getId(); + auto keyThree = three->getId(); + + // Insert an element, we have 0 node and 1 leaf + BOOST_CHECK(mytree.insert(zero)); + BOOST_CHECK(!mytree.isNull()); + + // Remove it, we have 0 node and 0 leaf + BOOST_CHECK(mytree.remove(keyZero)); + BOOST_CHECK(mytree.isNull()); + + // Insert more, we have 1 node and 3 leaves + BOOST_CHECK(mytree.insert(one)); + BOOST_CHECK(mytree.insert(two)); + BOOST_CHECK(mytree.insert(three)); + BOOST_CHECK(!mytree.isNull()); + + // Remove them, we have 1 node and 0 leaf + BOOST_CHECK(mytree.remove(keyOne)); + BOOST_CHECK(mytree.remove(keyTwo)); + BOOST_CHECK(mytree.remove(keyThree)); + BOOST_CHECK(!mytree.isNull()); + + // Copy the tree + RadixTree copiedTree = mytree; + BOOST_CHECK(!mytree.isNull()); + BOOST_CHECK(!copiedTree.isNull()); + + // Assign to empty + mytree = {}; + BOOST_CHECK(mytree.isNull()); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/functional/abc_p2p_compactproofs.py b/test/functional/abc_p2p_compactproofs.py --- a/test/functional/abc_p2p_compactproofs.py +++ b/test/functional/abc_p2p_compactproofs.py @@ -519,6 +519,12 @@ # Check we get no proof assert_equal(len(slow_peer.get_proofs()), 0) + # The proof tree is cleaned up only once + for _ in range(3): + with node.assert_debug_log(["sending pong"], [f"Cleaning up timed out compact proofs from peer {slow_peer.nodeid}"]): + node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) + slow_peer.sync_with_ping() + def test_compact_proofs_download_on_connect(self): self.log.info( "Check the node get compact proofs upon avalanche outbound discovery")