/* * This file is part of the Monero P2Pool * Copyright (c) 2021-2026 SChernykh * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "common.h" #include "crypto.h" #include "p2pool.h" #include "stratum_server.h" #include "p2p_server.h" #include #ifdef WITH_GRPC #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4574) #endif #include #ifdef _MSC_VER #pragma warning(pop) #endif #endif // WITH_GRPC #include #ifdef WITH_RANDOMX #include "randomx.h" #endif #ifdef WITH_TLS #include "tls.h" #endif #if defined(_WIN32) && defined(_MSC_VER) && !defined(NDEBUG) #include #pragma comment(lib, "Dbghelp.lib") #endif void p2pool_usage() { printf("P2Pool %s\n" "\nUsage:\n\n" \ "--wallet Main wallet address (the one that starts with 4...). To mine to a subaddress of this wallet, use it together with --subaddress\n" "--subaddress Subaddress to mine to. It must belong to the same wallet that was specified with --wallet parameter\n" "--host IP address of your Monero node, default is 127.0.0.1\n" "--rpc-port monerod RPC API port number, default is 18081\n" "--zmq-port monerod ZMQ pub port number, default is 18083 (same port as in monerod's \"--zmq-pub\" command line parameter)\n" "--stratum Comma-separated list of IP:port for stratum server to listen on\n" "--p2p Comma-separated list of IP:port for p2p server to listen on\n" "--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to\n" "--stratum-ban-time N Number of seconds to ban misbehaving stratum client, default is %u\n" "--light-mode Don't allocate RandomX dataset, saves 2GB of RAM\n" "--loglevel Verbosity of the log, integer number between 0 and %d\n" "--data-dir Path to store general p2pool files (log, cache, peer data, etc.), default is current directory\n" "--log-file Path to the log file, default is \"p2pool.log\" in p2pool's working directory\n" "--sidechain-config Name of the p2pool sidechain parameters file (only use it if you run your own sidechain)\n" "--data-api Path to the p2pool JSON data (use it in tandem with an external web-server). Not affected by --data-dir setting!\n" "--local-api Enable /local/ path in api path for Stratum Server and built-in miner statistics\n" "--stratum-api An alias for --local-api\n" "--no-cache Disable p2pool.cache\n" "--no-color Disable colors in console output\n" #ifdef WITH_RANDOMX "--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n" #endif "--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 450)\n" "--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 450)\n" "--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n" "--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d\n" "--nano Connect to p2pool-nano sidechain. Note that it will also change default p2p port from %d to %d\n" "--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum (WARNING: incompatible with Nicehash and MRR)\n" "--rpc-login Specify username[:password] required for Monero RPC server\n" "--socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections\n" "--socks5-proxy-type The type of SOCKS5 proxy. Can be one of the following values: auto, plain, tor, i2p. Default is auto (auto-detected by the port number)\n" "--no-dns Disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)\n" "--p2p-external-port Port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections\n" #ifdef WITH_UPNP "--no-upnp Disable UPnP port forwarding\n" "--no-igd An alias for --no-upnp\n" "--upnp-stratum Port forward Stratum port (it's not forwarded by default)\n" #endif "--merge-mine IP:port and wallet address for another blockchain to merge mine with\n" "--version Print p2pool's version and build details\n" #ifdef WITH_TLS "--tls-cert file Load TLS certificate chain from \"file\" in the PEM format\n" "--tls-cert-key file Load TLS certificate private key from \"file\" in the PEM format\n" "--rpc-ssl Enable SSL on RPC connections to the Monero node\n" "--rpc-ssl-fingerprint base64-encoded fingerprint of the Monero node's certificate (optional, use it for certificate pinning)\n" #endif "--stratum-proxy-protocol Enable HAProxy PROXY protocol v2 for incoming Stratum connections (use with HAProxy send-proxy-v2)\n" "--p2p-proxy-protocol Enable HAProxy PROXY protocol v2 for incoming P2P connections (use with HAProxy send-proxy-v2)\n" "--no-stratum-http Disable HTTP on Stratum ports\n" "--full-validation Enables full share validation / increases CPU usage\n" "--onion-address Tell other peers to use this .onion address to connect to this node through TOR\n" "--i2p-address Tell other peers to use this .b32.i2p address to connect to this node through I2P\n" "--no-clearnet-p2p Forces P2P server to listen on 127.0.0.1 and to not connect to clearnet IPs\n" "--params-file File name to load parameters from. It can't be used together with any other command line parameters\n" "--help Show this help message\n\n" "Example command line:\n\n" "%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n", p2pool::VERSION, static_cast(p2pool::DEFAULT_STRATUM_BAN_TIME), p2pool::log::MAX_GLOBAL_LOG_LEVEL, p2pool::DEFAULT_P2P_PORT, p2pool::DEFAULT_P2P_PORT_MINI, p2pool::DEFAULT_P2P_PORT, p2pool::DEFAULT_P2P_PORT_NANO, #ifdef _WIN32 "p2pool.exe" #else "./p2pool" #endif , p2pool::DEFAULT_STRATUM_PORT , p2pool::DEFAULT_P2P_PORT ); } int p2pool_test() { printf("Self-test started\n"); #ifdef WITH_RANDOMX const char myKey[] = "test key 000"; const char myInput[] = "This is a test"; char hash[RANDOMX_HASH_SIZE]; const randomx_flags flags = randomx_get_flags() | RANDOMX_FLAG_FULL_MEM; randomx_cache* myCache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES); if (!myCache) { myCache = randomx_alloc_cache(flags); if (!myCache) { printf("Cache allocation failed\n"); return 1; } } printf("RandomX cache allocated\n"); randomx_init_cache(myCache, myKey, sizeof(myKey) - 1); printf("RandomX cache initialized\n"); randomx_dataset* myDataset = randomx_alloc_dataset(flags | RANDOMX_FLAG_LARGE_PAGES); if (!myDataset) { myDataset = randomx_alloc_dataset(flags); if (!myDataset) { printf("Dataset allocation failed\n"); return 1; } } printf("RandomX dataset allocated\n"); { const uint32_t numThreads = std::max(std::thread::hardware_concurrency(), 1U); const uint32_t numItems = randomx_dataset_item_count(); std::vector threads; threads.reserve(numThreads); for (uint32_t i = 1; i < numThreads; ++i) { const uint32_t a = (numItems * i) / numThreads; const uint32_t b = (numItems * (i + 1)) / numThreads; threads.emplace_back([myDataset, myCache, a, b]() { randomx_init_dataset(myDataset, myCache, a, b - a); }); } randomx_init_dataset(myDataset, myCache, 0, numItems / numThreads); for (std::thread& t : threads) { t.join(); } } printf("RandomX dataset initialized\n"); randomx_release_cache(myCache); randomx_vm* myMachine = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, nullptr, myDataset); if (!myMachine) { myMachine = randomx_create_vm(flags, nullptr, myDataset); if (!myMachine) { printf("Failed to create a virtual machine"); return 1; } } printf("RandomX VM created\n"); memset(hash, 0, sizeof(hash)); memcpy(hash, myInput, sizeof(myInput)); for (int i = 0; i < 100; ++i) { printf("RandomX: calculating hash %d...", i); randomx_calculate_hash(myMachine, &hash, sizeof(hash), hash); printf("done\n"); } char buf[RANDOMX_HASH_SIZE * 2 + 1] = {}; p2pool::log::Stream s(buf); s << p2pool::log::hex_buf(hash, RANDOMX_HASH_SIZE) << '\0'; constexpr char expected_hash[] = "3b5ecc2bb14f467161a04fe476b541194fba82dbbbfc7c320961f922a0294dee"; if (memcmp(buf, expected_hash, static_cast(RANDOMX_HASH_SIZE * 2)) != 0) { printf("Invalid hash calculated: expected %s, got %s\n", expected_hash, buf); return 1; } randomx_destroy_vm(myMachine); randomx_release_dataset(myDataset); #endif printf("Self-test passed\n"); return 0; } static p2pool::Params get_params(int argc, const char* const argv[]) noexcept { try { std::vector> args; args.reserve(argc); // Group command-line parameters by the pattern "--name [data1 data2 ... data_n]" // Each parameter starting with "--" can have 0 or more values attached to it for (int i = 1; i < argc; ++i) { std::string_view arg = argv[i]; if ((arg.size() > 2) && (arg[0] == '-') && (arg[1] == '-')) { // Store the parameter name without the "--" prefix arg.remove_prefix(2); args.emplace_back(1, std::string(arg)); } else if (!args.empty()) { args.back().emplace_back(std::move(arg)); } } p2pool::Params params(args); if (params.valid()) { return params; } } catch (const std::exception&) { } printf("Invalid or missing command line. Try \"p2pool --help\".\n"); abort(); } static p2pool::Params get_params(const std::string& params_file) noexcept { try { p2pool::Params params(p2pool::parse_config(params_file)); if (params.valid()) { return params; } } catch (const std::exception&) { } printf("Invalid or missing command line. Try \"p2pool --help\".\n"); abort(); } int main(int argc, char* argv[]) { if (argc == 1) { p2pool_usage(); return 0; } std::string params_file; for (int i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "/help") || !strcmp(argv[i], "-h") || !strcmp(argv[i], "/h") || !strcmp(argv[i], "/?")) { p2pool_usage(); return 0; } if (!strcmp(argv[i], "--version") || !strcmp(argv[i], "/version") || !strcmp(argv[i], "-v") || !strcmp(argv[i], "/v")) { puts(p2pool::p2pool_version().c_str()); return 0; } if (!strcmp(argv[i], "--test")) { return p2pool_test(); } if (!strcmp(argv[i], "--params-file") && (i + 1 < argc)) { params_file = argv[++i]; } } if (!params_file.empty() && (argc != 3)) { fprintf(stderr, "--params-file can't be combined with other command line parameters\n"); return 1; } #if defined(_WIN32) && defined(_MSC_VER) && !defined(NDEBUG) SymInitialize(GetCurrentProcess(), NULL, TRUE); #endif __nss_module_disable_loading(); memory_tracking_start(); int result; { // Create the default libuv loop and initialize libuv here // It will call the important stuff like WSAStartup and many other things // Some P2Pool code will not work without libuv initialized, so the code above this line must be minimal uv_default_loop(); const p2pool::Params params = params_file.empty() ? get_params(argc, argv) : get_params(params_file); if (!params.m_dataDir.empty()) { printf("Using \"%s\" for P2Pool files\n", params.m_dataDir.c_str()); // Try to create it if it doesn't exist std::error_code err; std::filesystem::create_directories(params.m_dataDir, err); } p2pool::log::start(params); p2pool::init_crypto_cache(); result = static_cast(curl_global_init_mem(CURL_GLOBAL_ALL, p2pool::malloc_hook, p2pool::free_hook, p2pool::realloc_hook, p2pool::strdup_hook, p2pool::calloc_hook)); if (result != CURLE_OK) { return result; } #ifdef WITH_GRPC if (params.grpc_needed()) { grpc_init(); } #endif #ifdef WITH_TLS if (!p2pool::ServerTls::global_init()) { printf("ServerTls::global_init failed\n"); return 1; } #endif try { p2pool::p2pool pool(params); result = pool.run(); } catch (...) { result = 1; } #ifdef WITH_GRPC if (params.grpc_needed()) { grpc_shutdown(); } #endif curl_global_cleanup(); p2pool::destroy_crypto_cache(); #ifdef WITH_INDEXED_HASHES p2pool::indexed_hash::cleanup_storage(); #endif p2pool::log::stop(); uv_loop_close(uv_default_loop()); #if ((UV_VERSION_MAJOR > 1) || ((UV_VERSION_MAJOR == 1) && (UV_VERSION_MINOR >= 38))) uv_library_shutdown(); #endif } if (!memory_tracking_stop()) { result = 1; } #if defined(_WIN32) && defined(_MSC_VER) && !defined(NDEBUG) SymCleanup(GetCurrentProcess()); #endif return result; }