P2P: save/load onion peers, added onion seed nodes
This commit is contained in:
@@ -47,6 +47,7 @@
|
||||
LOG_CATEGORY(P2PServer)
|
||||
|
||||
static constexpr char saved_peer_list_file_name[] = "p2pool_peers.txt";
|
||||
static constexpr char saved_onion_peer_list_file_name[] = "p2pool_onion_peers.txt";
|
||||
static const char* seed_nodes[] = { "seeds.p2pool.io", "main.p2poolpeers.net", "main.gupax.io", "" };
|
||||
static const char* seed_nodes_mini[] = { "seeds-mini.p2pool.io", "mini.p2poolpeers.net", "mini.gupax.io", "" };
|
||||
static const char* seed_nodes_nano[] = { "seeds-nano.p2pool.io", "nano.p2poolpeers.net", "nano.gupax.io", ""};
|
||||
@@ -57,6 +58,11 @@ static constexpr uint64_t PEER_REQUEST_DELAY = 60;
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
static constexpr hash seed_onion_nodes[] = {
|
||||
from_onion_v3_const("p2pseeds5qoenuuseyuqxhzzefzxpbhiq4z4h5hfbry5dxd5y2fwudyd.onion"),
|
||||
from_onion_v3_const("p2pool2giz2r5cpqicajwoazjcxkfujxswtk3jolfk2ubilhrkqam2id.onion")
|
||||
};
|
||||
|
||||
P2PServer::P2PServer(p2pool* pool)
|
||||
: TCPServer(DEFAULT_BACKLOG, P2PClient::allocate, pool->params().m_socks5Proxy)
|
||||
, m_pool(pool)
|
||||
@@ -422,11 +428,24 @@ void P2PServer::update_peer_connections()
|
||||
if (!m_socks5Proxy.empty()) {
|
||||
std::vector<hash> pubkeys = m_pool->side_chain().seen_onion_pubkeys();
|
||||
|
||||
// Add seed nodes
|
||||
pubkeys.insert(pubkeys.end(), seed_onion_nodes, seed_onion_nodes + array_size(seed_onion_nodes));
|
||||
|
||||
for (uint32_t i = m_numOnionConnections, n = N / 2; (i < n) && !pubkeys.empty();) {
|
||||
const uint64_t k = get_random64() % pubkeys.size();
|
||||
const std::string addr = to_onion_v3(pubkeys[k]);
|
||||
|
||||
if ((connected_clients_domain.find(addr) == connected_clients_domain.end()) && connect_to_peer(addr, DEFAULT_P2P_PORT_ONION)) {
|
||||
int port = DEFAULT_P2P_PORT_ONION;
|
||||
|
||||
for (const hash& seed_onion : seed_onion_nodes) {
|
||||
if (pubkeys[k] == seed_onion) {
|
||||
const SideChain& s = m_pool->side_chain();
|
||||
port = s.is_mini() ? DEFAULT_P2P_PORT_MINI : (s.is_nano() ? DEFAULT_P2P_PORT_NANO : DEFAULT_P2P_PORT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((connected_clients_domain.find(addr) == connected_clients_domain.end()) && connect_to_peer(addr, port)) {
|
||||
++i;
|
||||
++num_outgoing;
|
||||
}
|
||||
@@ -579,6 +598,31 @@ void P2PServer::save_peer_list()
|
||||
f.close();
|
||||
|
||||
LOGINFO(5, "peer list saved (" << peer_list.size() << " peers)");
|
||||
|
||||
const SideChain& s = m_pool->side_chain();
|
||||
|
||||
if (s.onion_pubkeys_count() > 0) {
|
||||
const std::string onion_path = DATA_DIR + saved_onion_peer_list_file_name;
|
||||
|
||||
f.open(onion_path, std::ios::binary);
|
||||
|
||||
if (!f.is_open()) {
|
||||
LOGERR(1, "failed to save onion peer list " << onion_path << ": error " << errno);
|
||||
}
|
||||
else {
|
||||
const std::vector<hash> pubkeys = s.seen_onion_pubkeys();
|
||||
|
||||
for (const hash& h : pubkeys) {
|
||||
f << to_onion_v3(h) << '\n';
|
||||
}
|
||||
|
||||
f.flush();
|
||||
f.close();
|
||||
|
||||
LOGINFO(5, "onion peer list saved (" << pubkeys.size() << " peers)");
|
||||
}
|
||||
}
|
||||
|
||||
m_peerListLastSaved = seconds_since_epoch();
|
||||
}
|
||||
|
||||
@@ -662,34 +706,54 @@ void P2PServer::load_peer_list()
|
||||
}
|
||||
};
|
||||
|
||||
SideChain& s = m_pool->side_chain();
|
||||
|
||||
if (m_pool->params().m_dns) {
|
||||
if (m_pool->side_chain().is_default()) {
|
||||
if (s.is_default()) {
|
||||
load_from_seed_nodes(seed_nodes, DEFAULT_P2P_PORT);
|
||||
}
|
||||
else if (m_pool->side_chain().is_mini()) {
|
||||
else if (s.is_mini()) {
|
||||
load_from_seed_nodes(seed_nodes_mini, DEFAULT_P2P_PORT_MINI);
|
||||
}
|
||||
else if (m_pool->side_chain().is_nano()) {
|
||||
else if (s.is_nano()) {
|
||||
load_from_seed_nodes(seed_nodes_nano, DEFAULT_P2P_PORT_NANO);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally load peers from p2pool_peers.txt
|
||||
const std::string path = DATA_DIR + saved_peer_list_file_name;
|
||||
// Finally load peers from p2pool_peers.txt and p2pool_onion_peers.txt
|
||||
for (size_t i = 0, n = (m_socks5Proxy.empty() ? 1 : 2); i < n; ++i) {
|
||||
const std::string path = DATA_DIR + (i ? saved_onion_peer_list_file_name : saved_peer_list_file_name);
|
||||
|
||||
std::ifstream f(path);
|
||||
if (f.is_open()) {
|
||||
std::string address;
|
||||
while (f.good()) {
|
||||
std::getline(f, address);
|
||||
if (!address.empty()) {
|
||||
if (!saved_list.empty()) {
|
||||
saved_list += ',';
|
||||
std::ifstream f(path);
|
||||
|
||||
if (f.is_open()) {
|
||||
std::string address;
|
||||
std::vector<hash> pubkeys;
|
||||
|
||||
while (f.good()) {
|
||||
std::getline(f, address);
|
||||
|
||||
if (!address.empty()) {
|
||||
if (i > 0) {
|
||||
const hash h = from_onion_v3(address);
|
||||
if (!h.empty()) {
|
||||
pubkeys.emplace_back(h);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!saved_list.empty()) {
|
||||
saved_list += ',';
|
||||
}
|
||||
saved_list += address;
|
||||
}
|
||||
}
|
||||
saved_list += address;
|
||||
}
|
||||
f.close();
|
||||
|
||||
if (i > 0) {
|
||||
s.add_onion_pubkeys(pubkeys);
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
|
||||
if (saved_list.empty()) {
|
||||
|
||||
@@ -1384,6 +1384,23 @@ std::vector<hash> SideChain::seen_onion_pubkeys() const
|
||||
return result;
|
||||
}
|
||||
|
||||
void SideChain::add_onion_pubkeys(const std::vector<hash>& pubkeys)
|
||||
{
|
||||
if (pubkeys.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
|
||||
WriteLock lock(m_seenDataLock);
|
||||
|
||||
for (const hash& h : pubkeys) {
|
||||
if (!h.empty()) {
|
||||
m_seenOnionPubkeys[h] = cur_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SideChain::verify_loop(PoolBlock* block)
|
||||
{
|
||||
// PoW is already checked at this point
|
||||
|
||||
@@ -88,6 +88,7 @@ public:
|
||||
[[nodiscard]] bool p2pool_update_available() const;
|
||||
|
||||
[[nodiscard]] std::vector<hash> seen_onion_pubkeys() const;
|
||||
void add_onion_pubkeys(const std::vector<hash>& pubkeys);
|
||||
|
||||
#ifdef P2POOL_UNIT_TESTS
|
||||
difficulty_type m_testMainChainDiff;
|
||||
|
||||
37
src/util.cpp
37
src/util.cpp
@@ -1008,42 +1008,13 @@ hash from_onion_v3(const std::string& address)
|
||||
return {};
|
||||
}
|
||||
|
||||
uint8_t buf[HASH_SIZE + 4] = {};
|
||||
uint8_t* p = buf;
|
||||
const hash result = from_onion_v3_const(address.c_str());
|
||||
|
||||
uint64_t data = 0;
|
||||
uint64_t bit_size = 0;
|
||||
|
||||
for (size_t i = 0; i < 56; ++i) {
|
||||
const char c = address[i];
|
||||
uint64_t digit;
|
||||
|
||||
if ('a' <= c && c <= 'z') {
|
||||
digit = static_cast<uint64_t>(c - 'a');
|
||||
}
|
||||
else if ('A' <= c && c <= 'Z') {
|
||||
digit = static_cast<uint64_t>(c - 'A');
|
||||
}
|
||||
else if ('2' <= c && c <= '7') {
|
||||
digit = static_cast<uint64_t>(c - '2') + 26;
|
||||
}
|
||||
else {
|
||||
LOGWARN(3, "Invalid onion address \"" << address << "\": has an invalid character \"" << c << '"');
|
||||
return {};
|
||||
}
|
||||
|
||||
data = (data << 5) | digit;
|
||||
bit_size += 5;
|
||||
|
||||
while (bit_size >= 8) {
|
||||
bit_size -= 8;
|
||||
*(p++) = static_cast<uint8_t>(data >> bit_size);
|
||||
}
|
||||
if (result.empty()) {
|
||||
LOGWARN(3, "Invalid onion address \"" << address << "\": has invalid character(s)");
|
||||
return {};
|
||||
}
|
||||
|
||||
hash result;
|
||||
memcpy(result.h, buf, HASH_SIZE);
|
||||
|
||||
// Checksum validation
|
||||
if (to_onion_v3(result) != address) {
|
||||
LOGWARN(3, "Invalid onion address \"" << address << "\": checksum failed");
|
||||
|
||||
43
src/util.h
43
src/util.h
@@ -400,6 +400,49 @@ FORCEINLINE void secure_zero_memory(T& value)
|
||||
std::string to_onion_v3(const hash& pubkey);
|
||||
hash from_onion_v3(const std::string& address);
|
||||
|
||||
static FORCEINLINE constexpr hash from_onion_v3_const(const char* address)
|
||||
{
|
||||
uint8_t buf[HASH_SIZE + 4] = {};
|
||||
uint8_t* p = buf;
|
||||
|
||||
uint64_t data = 0;
|
||||
uint64_t bit_size = 0;
|
||||
|
||||
for (size_t i = 0; i < 56; ++i) {
|
||||
const char c = address[i];
|
||||
uint64_t digit = 0;
|
||||
|
||||
if ('a' <= c && c <= 'z') {
|
||||
digit = static_cast<uint64_t>(c - 'a');
|
||||
}
|
||||
else if ('A' <= c && c <= 'Z') {
|
||||
digit = static_cast<uint64_t>(c - 'A');
|
||||
}
|
||||
else if ('2' <= c && c <= '7') {
|
||||
digit = static_cast<uint64_t>(c - '2') + 26;
|
||||
}
|
||||
else {
|
||||
return {};
|
||||
}
|
||||
|
||||
data = (data << 5) | digit;
|
||||
bit_size += 5;
|
||||
|
||||
while (bit_size >= 8) {
|
||||
bit_size -= 8;
|
||||
*(p++) = static_cast<uint8_t>(data >> bit_size);
|
||||
}
|
||||
}
|
||||
|
||||
hash result;
|
||||
|
||||
for (size_t i = 0; i < HASH_SIZE; ++i) {
|
||||
result.h[i] = buf[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
void memory_tracking_start();
|
||||
|
||||
Reference in New Issue
Block a user