Compare commits

..

7 Commits

Author SHA1 Message Date
SChernykh 248fcee174 Update version 2022-01-09 17:19:20 +01:00
SChernykh 89bcceb986 Added p2pool mini seed nodes and default port 2021-12-30 11:10:18 +01:00
SChernykh 2b01350add Added outpeers and inpeers console commands 2021-12-26 15:28:33 +01:00
SChernykh f1b6212c82 P2PServer: add connection limits
#87 Added `--out-peers` and `--in-peers` command line options.
2021-12-26 14:59:45 +01:00
SChernykh 7d961ab3b9 Merge pull request #89 from bladedoyle/monero_p2pool_support
Monero latest release supports p2pool
2021-12-24 22:40:54 +01:00
Blade Doyle 862e7d965b Monero latest release supports p2pool 2021-12-24 11:38:53 -08:00
SChernykh da2d9f7e2e Update README.md 2021-12-07 19:11:51 +01:00
15 changed files with 122 additions and 75 deletions
+3 -33
View File
@@ -8,6 +8,7 @@ Pool status and monitoring pages can be found at https://p2pool.io/ and https://
![C/C++ CI](https://github.com/SChernykh/p2pool/actions/workflows/c-cpp.yml/badge.svg)
![CodeQL](https://github.com/SChernykh/p2pool/actions/workflows/codeql-analysis.yml/badge.svg)
![msvc-analysis](https://github.com/SChernykh/p2pool/actions/workflows/msvc-analysis.yml/badge.svg)
![cppcheck](https://github.com/SChernykh/p2pool/actions/workflows/cppcheck.yml/badge.svg)
<a href="https://scan.coverity.com/projects/schernykh-p2pool">
<img alt="Coverity Scan Build Status"
@@ -65,16 +66,6 @@ cmake ..
make -j$(nproc)
```
monerod binary compatible with p2pool:
```
sudo apt update && sudo apt install git build-essential cmake pkg-config libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev ccache doxygen graphviz
git clone --recursive https://github.com/monero-project/monero/
cd monero
git checkout release-v0.17
git submodule sync && git submodule update --init --force --recursive
make release-static -j$(nproc)
```
### Arch Linux [AUR](https://wiki.archlinux.org/title/Arch_User_Repository)
Make the package: [p2pool-git](https://aur.archlinux.org/packages/p2pool-git/)
@@ -111,16 +102,6 @@ cmake ..
make -j$(sysctl -n hw.logicalcpu)
```
monerod binary compatible with p2pool:
```
git clone --recursive https://github.com/monero-project/monero/
cd monero
git checkout release-v0.17
git submodule sync && git submodule update --init --force --recursive
brew update && brew bundle --file=contrib/brew/Brewfile
make -j$(sysctl -n hw.logicalcpu)
```
### Windows
p2pool binary (Visual Studio Community 2019 build):
@@ -136,15 +117,6 @@ then open generated build\p2pool.sln in Visual Studio and build it there
Alternatively, you can select "Clone a repository" within the GUI, then select "Build" from the menu.
monerod binary compatible with p2pool:
```
git clone --recursive https://github.com/monero-project/monero/
cd monero
git checkout release-v0.17
git submodule sync && git submodule update --init --force --recursive
```
then follow the instructions from https://github.com/monero-project/monero/#on-windows
## How to mine on P2Pool
This guide assumes that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup. It's highly recommended to create a new mainnet wallet for mining because **wallet addresses are public on p2pool**.
@@ -167,13 +139,12 @@ Step-by-step guide:
### GNU/Linux
- Download binaries from https://github.com/SChernykh/p2pool/releases/latest
- Alternatively, grab the latest source code for both p2pool and monerod and build them (see above, also notice that the branch name for monerod changed, you'll need to checkout p2pool-api-v0.17)
- Alternatively, grab the latest source code for p2pool and build it
- Prepare enough huge pages (each of monerod/p2pool/xmrig needs them): `sudo sysctl vm.nr_hugepages=3072`
- Get xmrig (linux-static-x64) binary from https://github.com/xmrig/xmrig/releases/latest
- Check that ports 18080 (Monero p2p port) and 37889 (p2pool p2p port) are open in your firewall to ensure better connectivity
- Use the `monerod` binary bundled with p2pool (official binaries don't have p2pool support yet)
- Use the `monerod` binary v0.17.3.0 or newer
- Run `./monerod --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` **don't forget --zmq-pub parameter in the command line**
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-fce167d7c)** on startup. Wait until it's synchronized.
- Run `./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
- p2pool has verbose logging by default, you can reduce it by using "loglevel N" command where N is between 0 and 6. Default loglevel is 3.
- You can use `logrotate` with a config like this to control logfile growth:
@@ -207,7 +178,6 @@ Step-by-step guide:
- Open a command prompt and navigate to the folder where you extracted p2pool.
- *When running these commands, Windows Firewall may prompt to allow connections, click "Allow"*
- Run `.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` *NOTE: don't forget --zmq-pub parameter in the command line*
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-fce167d7c)** on startup. Wait until it's synchronized.
- Run `.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes, once completed xmrig should be able to connect to the stratum server on port 3333.
- Run `.\xmrig.exe -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
+2 -2
View File
@@ -18,8 +18,8 @@ P2POOL_VOLUME=p2pool
##
# Monero Node Settings
#
# Version of Monero to build
MONERO_GIT_TAG=release-v0.17
# Version of Monero to build. Must be v0.17.3.0 or later for p2pool support. "latest" pulls the most recent release tag
MONERO_GIT_TAG=latest
#
# Limit the size of the blockchain on disk (comment to disable)
PRUNE_NODE="--prune-blockchain"
+14 -3
View File
@@ -18,7 +18,7 @@ git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool/docker-compose
vi .env
```
<b>WALLET_ADDRESS</b> is the only setting that needs to be updated in that file
**WALLET_ADDRESS** is the only setting that needs to be updated in that file
#### Build the docker containers
```
@@ -31,8 +31,19 @@ docker-compose up
```
#### Optional
* You can run everythng in the background by adding the "-d" argument to the "docker-compose up" command: ```docker-compose up -d```
* You can see logs when running in the background for with the "docker logs" command: ```docker logs -f p2pool-xmrig``` or ```docker logs -f p2pool-p2pool``` or ```docker logs -f p2pool-monero```
* Open ports 18080 (Monero p2p port) and 37889 (P2Pool p2p port) in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine
* An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333
* Configure your kernel for maximum mining performance: [XMRig RandomX Optimization Guide](https://xmrig.com/docs/miner/randomx-optimization-guide)
#### Other usefull commands
* You can **run everythng in the background** by adding the "-d" argument to the "docker-compose up" command: ```docker-compose up -d```
* You can **stop everything** with CTRL-C or ```docker-compose down```
* You can see logs when running in the background for with the "docker logs" command: ```docker logs -f p2pool-xmrig``` or ```docker logs -f p2pool-p2pool``` or ```docker logs -f p2pool-monero```
#### Uninstall
Change to p2pool/docker-compose directory
Stop and remove all containers: ```docker-compose down```
Remove the p2pool data: ```docker volume rm p2pool```
Remove the monero data: ```docker volume rm monero```
+1 -2
View File
@@ -1,3 +1,4 @@
---
version: '3.4'
networks:
@@ -67,8 +68,6 @@ services:
networks:
- p2pool
privileged: true
cap_add:
- ALL
volumes:
- /dev:/dev:ro
- /lib/modules:/lib/modules:ro
+3 -3
View File
@@ -1,5 +1,5 @@
FROM ubuntu:latest
ARG MONERO_GIT_TAG="master"
ARG MONERO_GIT_TAG="latest"
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
@@ -47,6 +47,7 @@ RUN set -e && \
WORKDIR /usr/src
RUN git clone --recursive https://github.com/monero-project/monero && \
cd monero && \
if [ "x$MONERO_GIT_TAG" = "xlatest" ]; then MONERO_GIT_TAG=$(git describe --tags $(git rev-list --tags --max-count=1)); fi && \
git checkout $MONERO_GIT_TAG && \
git submodule sync && git submodule update --init --force --recursive && \
make release-static -j$(nproc)
@@ -54,9 +55,8 @@ RUN git clone --recursive https://github.com/monero-project/monero && \
# ---
FROM ubuntu:latest
ARG MONERO_GIT_TAG="master"
COPY --from=0 /usr/src/monero/build/Linux/$MONERO_GIT_TAG/release/bin/* /
COPY --from=0 /usr/src/monero/build/Linux/*/release/bin/* /
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
+21 -1
View File
@@ -69,7 +69,7 @@ typedef struct cmd {
cmdfunc *func;
} cmd;
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_exit;
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_outpeers, do_inpeers, do_exit;
static cmd cmds[] = {
{ STRCONST("help"), "", "display list of commands", do_help },
@@ -78,6 +78,8 @@ static cmd cmds[] = {
{ STRCONST("addpeers"), "<peeraddr>", "add peer", do_addpeers },
{ STRCONST("droppeers"), "", "disconnect all peers", do_droppeers },
{ STRCONST("peers"), "", "show all peers", do_showpeers },
{ STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers },
{ STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers },
{ STRCONST("exit"), "", "terminate p2pool", do_exit },
{ STRCNULL, NULL, NULL, NULL }
};
@@ -138,6 +140,24 @@ static int do_showpeers(p2pool* m_pool, const char* /* args */)
return 0;
}
static int do_outpeers(p2pool* m_pool, const char* args)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->set_max_outgoing_peers(strtoul(args, nullptr, 10));
LOGINFO(0, "max outgoing peers set to " << m_pool->p2p_server()->max_outgoing_peers());
}
return 0;
}
static int do_inpeers(p2pool* m_pool, const char* args)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->set_max_incoming_peers(strtoul(args, nullptr, 10));
LOGINFO(0, "max incoming peers set to " << m_pool->p2p_server()->max_incoming_peers());
}
return 0;
}
static int do_exit(p2pool *m_pool, const char * /* args */)
{
bkg_jobs_tracker.wait();
+2
View File
@@ -40,6 +40,8 @@ static void usage()
"--no-cache Disable p2pool.cache\n"
"--no-color Disable colors in console output\n"
"--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n"
"--out-peers Maximum number of outgoing connections for p2p server (any value between 10 and 1000)\n"
"--in-peers Maximum number of incoming connections for p2p server (any value between 10 and 1000)\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",
+30 -14
View File
@@ -28,9 +28,8 @@
static constexpr char log_category_prefix[] = "P2PServer ";
static constexpr char saved_peer_list_file_name[] = "p2pool_peers.txt";
static const char* seed_nodes[] = {
"seeds.p2pool.io"
};
static const char* seed_nodes[] = { "seeds.p2pool.io", ""};
static const char* seed_nodes_mini[] = { "seeds-mini.p2pool.io", "" };
static constexpr int DEFAULT_BACKLOG = 16;
static constexpr uint64_t DEFAULT_BAN_TIME = 600;
@@ -53,6 +52,9 @@ P2PServer::P2PServer(p2pool* pool)
, m_peerId(m_rng())
, m_peerListLastSaved(0)
{
set_max_outgoing_peers(pool->params().m_maxOutgoingPeers);
set_max_incoming_peers(pool->params().m_maxIncomingPeers);
uv_mutex_init_checked(&m_rngLock);
uv_mutex_init_checked(&m_blockLock);
uv_mutex_init_checked(&m_peerListLock);
@@ -226,8 +228,8 @@ void P2PServer::update_peer_connections()
peer_list = m_peerList;
}
// Try to have at least 10 outgoing connections
for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < 10) && !peer_list.empty();) {
// Try to have at least N outgoing connections (N defaults to 10, can be set via --out-peers command line parameter)
for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < m_maxOutgoingPeers) && !peer_list.empty();) {
const uint64_t k = get_random64() % peer_list.size();
const Peer& peer = peer_list[k];
@@ -368,12 +370,10 @@ void P2PServer::load_peer_list()
{
std::string saved_list;
// Load peers from seed nodes if we're on the default sidechain
if (m_pool->side_chain().is_default()) {
const int p2p_port = DEFAULT_P2P_PORT;
for (size_t i = 0; i < array_size(seed_nodes); ++i) {
LOGINFO(4, "loading peers from " << seed_nodes[i]);
// Load peers from seed nodes if we're on the default or mini sidechain
auto load_from_seed_nodes = [&saved_list](const char** nodes, int p2p_port) {
for (size_t i = 0; nodes[i][0]; ++i) {
LOGINFO(4, "loading peers from " << nodes[i]);
addrinfo hints{};
hints.ai_family = AF_UNSPEC;
@@ -381,7 +381,7 @@ void P2PServer::load_peer_list()
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
addrinfo* result;
const int err = getaddrinfo(seed_nodes[i], nullptr, &hints, &result);
const int err = getaddrinfo(nodes[i], nullptr, &hints, &result);
if (err == 0) {
for (addrinfo* r = result; r != NULL; r = r->ai_next) {
const char* addr_str;
@@ -404,7 +404,7 @@ void P2PServer::load_peer_list()
}
if (s.m_pos) {
LOGINFO(4, "added " << static_cast<char*>(buf) << " from " << seed_nodes[i]);
LOGINFO(4, "added " << static_cast<char*>(buf) << " from " << nodes[i]);
if (!saved_list.empty()) {
saved_list += ',';
}
@@ -414,9 +414,16 @@ void P2PServer::load_peer_list()
freeaddrinfo(result);
}
else {
LOGWARN(3, "getaddrinfo failed for " << seed_nodes[i] << ": " << gai_strerror(err));
LOGWARN(3, "getaddrinfo failed for " << nodes[i] << ": " << gai_strerror(err));
}
}
};
if (m_pool->side_chain().is_default()) {
load_from_seed_nodes(seed_nodes, DEFAULT_P2P_PORT);
}
else if (m_pool->side_chain().is_mini()) {
load_from_seed_nodes(seed_nodes_mini, DEFAULT_P2P_PORT_MINI);
}
// Finally load peers from p2pool_peers.txt
@@ -903,6 +910,15 @@ bool P2PServer::P2PClient::on_connect()
{
P2PServer* server = static_cast<P2PServer*>(m_owner);
if (!server) {
return false;
}
if (m_isIncoming && (server->m_numIncomingConnections > server->m_maxIncomingPeers)) {
LOGINFO(5, "Connection from " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " rejected (incoming connections limit was reached)");
return false;
}
// Don't allow multiple connections to/from the same IP
// server->m_clientsListLock is already locked here
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
+9
View File
@@ -29,6 +29,7 @@ class BlockCache;
static constexpr size_t P2P_BUF_SIZE = 128 * 1024;
static constexpr size_t PEER_LIST_RESPONSE_MAX_PEERS = 16;
static constexpr int DEFAULT_P2P_PORT = 37889;
static constexpr int DEFAULT_P2P_PORT_MINI = 37888;
class P2PServer : public TCPServer<P2P_BUF_SIZE, P2P_BUF_SIZE>
{
@@ -130,11 +131,19 @@ public:
void show_peers();
size_t peer_list_size() const { return m_peerList.size(); }
uint32_t max_outgoing_peers() const { return m_maxOutgoingPeers; }
uint32_t max_incoming_peers() const { return m_maxIncomingPeers; }
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 1000U); }
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 1000U); }
private:
p2pool* m_pool;
BlockCache* m_cache;
bool m_cacheLoaded;
std::string m_initialPeerList;
uint32_t m_maxOutgoingPeers;
uint32_t m_maxIncomingPeers;
uv_rwlock_t m_cachedBlocksLock;
unordered_map<hash, PoolBlock*> m_cachedBlocks;
+10
View File
@@ -111,6 +111,16 @@ p2pool::p2pool(int argc, char* argv[])
m_sideChain = new SideChain(this, type);
if (m_params->m_p2pAddresses.empty()) {
const int p2p_port = m_sideChain->is_mini() ? DEFAULT_P2P_PORT_MINI : DEFAULT_P2P_PORT;
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
s << "[::]:" << p2p_port << ",0.0.0.0:" << p2p_port << '\0';
m_params->m_p2pAddresses = buf;
}
if (m_params->m_disableRandomX) {
m_hasher = new RandomX_Hasher_RPC(this);
}
+11 -13
View File
@@ -30,11 +30,11 @@ Params::Params(int argc, char* argv[])
}
if ((strcmp(argv[i], "--rpc-port") == 0) && (i + 1 < argc)) {
m_rpcPort = static_cast<uint32_t>(atoi(argv[++i]));
m_rpcPort = strtoul(argv[++i], nullptr, 10);
}
if ((strcmp(argv[i], "--zmq-port") == 0) && (i + 1 < argc)) {
m_zmqPort = static_cast<uint32_t>(atoi(argv[++i]));
m_zmqPort = strtoul(argv[++i], nullptr, 10);
}
if (strcmp(argv[i], "--light-mode") == 0) {
@@ -58,7 +58,7 @@ Params::Params(int argc, char* argv[])
}
if ((strcmp(argv[i], "--loglevel") == 0) && (i + 1 < argc)) {
const int level = std::min(std::max(atoi(argv[++i]), 0), log::MAX_GLOBAL_LOG_LEVEL);
const int level = std::min(std::max<int>(strtol(argv[++i], nullptr, 10), 0), log::MAX_GLOBAL_LOG_LEVEL);
log::GLOBAL_LOG_LEVEL = level;
}
@@ -85,6 +85,14 @@ Params::Params(int argc, char* argv[])
if (strcmp(argv[i], "--no-randomx") == 0) {
m_disableRandomX = true;
}
if ((strcmp(argv[i], "--out-peers") == 0) && (i + 1 < argc)) {
m_maxOutgoingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 1000UL);
}
if ((strcmp(argv[i], "--in-peers") == 0) && (i + 1 < argc)) {
m_maxIncomingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 1000UL);
}
}
if (m_stratumAddresses.empty()) {
@@ -96,16 +104,6 @@ Params::Params(int argc, char* argv[])
m_stratumAddresses = buf;
}
if (m_p2pAddresses.empty()) {
const int p2p_port = DEFAULT_P2P_PORT;
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
s << "[::]:" << p2p_port << ",0.0.0.0:" << p2p_port << '\0';
m_p2pAddresses = buf;
}
}
bool Params::ok() const
+2
View File
@@ -40,6 +40,8 @@ struct Params
bool m_localStats = false;
bool m_blockCache = true;
bool m_disableRandomX = false;
uint32_t m_maxOutgoingPeers = 10;
uint32_t m_maxIncomingPeers = 1000;
};
} // namespace p2pool
+12 -3
View File
@@ -51,9 +51,8 @@ static_assert(1 <= UNCLE_BLOCK_DEPTH && UNCLE_BLOCK_DEPTH <= 10, "Invalid UNCLE_
namespace p2pool {
static constexpr uint8_t default_consensus_id[HASH_SIZE] = {
34,175,126,231,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24
};
static constexpr uint8_t default_consensus_id[HASH_SIZE] = { 34,175,126,231,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24 };
static constexpr uint8_t mini_consensus_id[HASH_SIZE] = { 57,130,201,26,149,174,199,250,66,80,189,18,108,216,194,220,136,23,63,24,64,113,221,44,219,86,39,163,53,24,126,196 };
SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
: m_pool(pool)
@@ -97,11 +96,16 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
<< m_unclePenalty << '\0';
constexpr char default_config[] = "mainnet\0" "default\0" "\0" "10\0" "100000\0" "2160\0" "20\0";
constexpr char mini_config[] = "mainnet\0" "mini\0" "\0" "10\0" "100000\0" "2160\0" "20\0";
// Hardcoded default consensus ID
if (memcmp(buf, default_config, sizeof(default_config) - 1) == 0) {
m_consensusId.assign(default_consensus_id, default_consensus_id + HASH_SIZE);
}
// Hardcoded mini consensus ID
else if (memcmp(buf, mini_config, sizeof(mini_config) - 1) == 0) {
m_consensusId.assign(mini_consensus_id, mini_consensus_id + HASH_SIZE);
}
else {
const randomx_flags flags = randomx_get_flags();
randomx_cache* cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
@@ -785,6 +789,11 @@ bool SideChain::is_default() const
return (memcmp(m_consensusId.data(), default_consensus_id, HASH_SIZE) == 0);
}
bool SideChain::is_mini() const
{
return (memcmp(m_consensusId.data(), mini_consensus_id, HASH_SIZE) == 0);
}
bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards)
{
const size_t num_shares = shares.size();
+1
View File
@@ -71,6 +71,7 @@ public:
uint64_t miner_count();
time_t last_updated() const;
bool is_default() const;
bool is_mini() const;
const PoolBlock* chainTip() const { return m_chainTip; }
+1 -1
View File
@@ -32,7 +32,7 @@ namespace p2pool {
#define STR2(X) STR(X)
#define STR(X) #X
const char* VERSION = "v1.4.0 (built"
const char* VERSION = "v1.5 (built"
#if defined(__clang__)
" with clang/" __clang_version__
#elif defined(__GNUC__)