diff --git a/.gitignore b/.gitignore index 6a8aeadc..f42fcf92 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ compile_commands.json *.ipch *.bin *.json -cmake-build* \ No newline at end of file +cmake-build* + +.cursorrules \ No newline at end of file diff --git a/src/CryptoNoteConfig.h b/src/CryptoNoteConfig.h index d72bc046..943db74c 100644 --- a/src/CryptoNoteConfig.h +++ b/src/CryptoNoteConfig.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include namespace cn @@ -142,6 +143,16 @@ namespace cn const char CRYPTONOTE_BLOCKCHAIN_INDICES_FILENAME[] = "blockchainindices.dat"; const char MINER_CONFIG_FILE_NAME[] = "miner_conf.json"; + // Memory management constants + const uint32_t ABSOLUTE_MAX_OBJECTS = 1200; // can safely be handled memory wise, but a limiting factor to prevent DDOS attack + const uint32_t FALLBACK_MAX_OBJECTS = 800; // Default when memory check fails + const uint64_t MIN_MEMORY_MB = 150; // Minimum memory required for handling fallback max objects + + // Memory management constants validation + static_assert(ABSOLUTE_MAX_OBJECTS > FALLBACK_MAX_OBJECTS, + "Maximum object count must be greater than Fallback"); + + } // namespace parameters const uint64_t START_BLOCK_REWARD = (UINT64_C(5000) * parameters::POINT); // start reward (Consensus I) diff --git a/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp index beea9661..0105e00b 100644 --- a/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp @@ -21,6 +21,12 @@ #include "CryptoNoteCore/VerificationContext.h" #include "P2p/LevinProtocol.h" +#include "../CryptoNoteConfig.h" +#include // for UINT64_MAX +#ifdef __linux__ +#include +#endif + using namespace logging; using namespace common; @@ -44,6 +50,7 @@ void relay_post_notify(IP2pEndpoint &p2p, typename t_parametr::request &arg, con } // namespace + CryptoNoteProtocolHandler::CryptoNoteProtocolHandler(const Currency ¤cy, platform_system::Dispatcher &dispatcher, ICore &rcore, IP2pEndpoint *p_net_layout, logging::ILogger &log) : m_currency(currency), m_p2p(p_net_layout), @@ -53,11 +60,14 @@ CryptoNoteProtocolHandler::CryptoNoteProtocolHandler(const Currency ¤cy, p m_observedHeight(0), m_peersCount(0), logger(log, "protocol"), - m_dispatcher(dispatcher) - { - if (!m_p2p) - m_p2p = &m_p2p_stub; - } + m_dispatcher(dispatcher), + m_maxObjectCount(calculateMaxObjectCount()) +{ + logger(INFO) << "Max object count: " << m_maxObjectCount; + + if (!m_p2p) + m_p2p = &m_p2p_stub; +} size_t CryptoNoteProtocolHandler::getPeerCount() const { @@ -392,9 +402,18 @@ int CryptoNoteProtocolHandler::handle_notify_new_transactions(int command, NOTIF int CryptoNoteProtocolHandler::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request &arg, CryptoNoteConnectionContext &context) { logger(logging::TRACE) << context << "NOTIFY_REQUEST_GET_OBJECTS"; - if(arg.blocks.size() > COMMAND_RPC_GET_OBJECTS_MAX_COUNT || arg.txs.size() > COMMAND_RPC_GET_OBJECTS_MAX_COUNT) + + uint32_t maxObjects = m_maxObjectCount.load(); + const size_t totalObjects = arg.blocks.size() + arg.txs.size(); + + logger(INFO) << "DEBUG: Request for " << totalObjects << " objects (limit: " << maxObjects << ")"; + + + if (totalObjects > maxObjects) { - logger(logging::ERROR) << context << "GET_OBJECTS_MAX_COUNT exceeded blocks: " << arg.blocks.size() << " txes: " << arg.txs.size(); + logger(logging::ERROR) << context << "Requested objects count exceeds limit of " + << maxObjects << ": blocks " << arg.blocks.size() + << " + txs " << arg.txs.size() << " = " << totalObjects; context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -594,14 +613,15 @@ int CryptoNoteProtocolHandler::processObjects(CryptoNoteConnectionContext& conte } return 0; - } + bool CryptoNoteProtocolHandler::on_idle() { return m_core.on_idle(); } + int CryptoNoteProtocolHandler::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request &arg, CryptoNoteConnectionContext &context) { logger(logging::TRACE) << context << "NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << arg.block_ids.size(); @@ -1121,4 +1141,37 @@ int CryptoNoteProtocolHandler::doPushLiteBlock(NOTIFY_NEW_LITE_BLOCK::request ar return 1; } +uint64_t CryptoNoteProtocolHandler::getAvailableMemory() const { +#ifdef _WIN32 + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + if (GlobalMemoryStatusEx(&memInfo)) { + return memInfo.ullAvailPhys; + } + logger(ERROR) << "Failed to get Windows memory info: " << GetLastError(); + return 0; +#else + struct sysinfo memInfo; + if (sysinfo(&memInfo) == 0) { + uint64_t available = memInfo.freeram; + available *= memInfo.mem_unit; + return available; + } + logger(ERROR) << "Failed to get system memory info: " << errno; + return 0; +#endif +} + +uint32_t CryptoNoteProtocolHandler::calculateMaxObjectCount() const { + uint64_t availableMB = getAvailableMemory() / (1024 * 1024); + + if (availableMB < cn::parameters::MIN_MEMORY_MB) { + logger(logging::WARNING) << "Memory check failed or small value, using fallback max objects"; + return cn::parameters::FALLBACK_MAX_OBJECTS; + } + + // If we have enough memory, use max objects which would be a key value to prevent DDOS attack + return cn::parameters::ABSOLUTE_MAX_OBJECTS; +} + }; // namespace cn diff --git a/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h index ccb1d1bf..df02cf2e 100644 --- a/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h @@ -8,9 +8,10 @@ #pragma once #include +#include #include - +#include "../CryptoNoteConfig.h" #include "CryptoNoteCore/ICore.h" #include "CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" @@ -118,5 +119,10 @@ namespace cn std::atomic m_peersCount; tools::ObserverManager m_observerManager; + + std::atomic m_maxObjectCount; + uint64_t getAvailableMemory() const; + uint32_t calculateMaxObjectCount() const; + }; }