@@ -1843,6 +1843,84 @@ TEST_CASE("drop peers who straggle", "[overlay][connections][straggler]")
18431843 }
18441844}
18451845
1846+ TEST_CASE (" GET_SCP_STATE rate limiting" , " [overlay]" )
1847+ {
1848+ VirtualClock clock;
1849+ Config cfg1 = getTestConfig (0 );
1850+ Config cfg2 = getTestConfig (1 );
1851+
1852+ // Bump up close time and max slots to remember to production levels. These
1853+ // must be large enough that crankSome calls between requests don't cause a
1854+ // window reset.
1855+ cfg1.ARTIFICIALLY_SET_CLOSE_TIME_FOR_TESTING = 5 ;
1856+ cfg2.ARTIFICIALLY_SET_CLOSE_TIME_FOR_TESTING = 5 ;
1857+ cfg1.MAX_SLOTS_TO_REMEMBER = 12 ;
1858+ cfg2.MAX_SLOTS_TO_REMEMBER = 12 ;
1859+
1860+ // The window size + 1 second. Minimum time required to ensure the rate
1861+ // limit window resets.
1862+ std::chrono::seconds const WINDOW_CLEAR_DURATION (
1863+ cfg1.ARTIFICIALLY_SET_CLOSE_TIME_FOR_TESTING *
1864+ cfg1.MAX_SLOTS_TO_REMEMBER +
1865+ 1 );
1866+
1867+ // Should be no more than 10 processed GET_SCP_STATE messages per window
1868+ uint32_t constexpr MAX_PER_WINDOW = 10 ;
1869+
1870+ auto app1 = createTestApplication (clock, cfg1);
1871+ auto app2 = createTestApplication (clock, cfg2);
1872+
1873+ LoopbackPeerConnection conn (*app1, *app2);
1874+ auto sender = conn.getInitiator ();
1875+ auto receiver = conn.getAcceptor ();
1876+ testutil::crankSome (clock);
1877+ REQUIRE (conn.getInitiator ()->isAuthenticatedForTesting ());
1878+ REQUIRE (conn.getAcceptor ()->isAuthenticatedForTesting ());
1879+
1880+ // Advance past QUERY_WINDOW so the first test request triggers a window
1881+ // reset
1882+ testutil::crankFor (clock, WINDOW_CLEAR_DURATION );
1883+
1884+ // Send requests up to the limit.
1885+ for (int i = 0 ; i < MAX_PER_WINDOW ; i++)
1886+ {
1887+ sender->sendGetScpState (0 );
1888+ testutil::crankSome (clock);
1889+ }
1890+
1891+ // Should have logged 10 queries, and the peers should remain connected
1892+ REQUIRE (receiver->getSCPStateQueryCountForTesting () == MAX_PER_WINDOW );
1893+
1894+ // Send 5 more -- all should be dropped
1895+ for (int i = 0 ; i < 5 ; i++)
1896+ {
1897+ sender->sendGetScpState (0 );
1898+ testutil::crankSome (clock);
1899+ }
1900+ // Should still be at the maximum query count, as the additional messages
1901+ // should have been dropped
1902+ REQUIRE (receiver->getSCPStateQueryCountForTesting () == MAX_PER_WINDOW );
1903+
1904+ // Advance past the window duration so the next request triggers a window
1905+ // reset
1906+ testutil::crankFor (clock, WINDOW_CLEAR_DURATION );
1907+
1908+ // Send a GET_SCP_STATE message to trigger the window reset
1909+ sender->sendGetScpState (0 );
1910+ testutil::crankSome (clock);
1911+
1912+ // Should just have processed the one message sent after the window clear
1913+ // duration
1914+ REQUIRE (receiver->getSCPStateQueryCountForTesting () == 1 );
1915+
1916+ // Peers should still be connected
1917+ REQUIRE (sender->isConnectedForTesting ());
1918+ REQUIRE (receiver->isConnectedForTesting ());
1919+
1920+ testutil::shutdownWorkScheduler (*app2);
1921+ testutil::shutdownWorkScheduler (*app1);
1922+ }
1923+
18461924TEST_CASE (" reject peers with the same nodeid" , " [overlay][connections]" )
18471925{
18481926 VirtualClock clock;
0 commit comments