2020-04-17 19:33:06 +02:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2025-03-07 14:31:02 +01:00
|
|
|
* Copyright (C) 2020-2025 Tom Zander <tom@flowee.org>
|
2020-04-17 19:33:06 +02:00
|
|
|
*
|
|
|
|
|
* 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, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
#include "ConnectionManager.h"
|
|
|
|
|
#include "DownloadManager.h"
|
|
|
|
|
#include "P2PNetInterface.h"
|
|
|
|
|
#include "Peer.h"
|
|
|
|
|
#include "PrivacySegment.h"
|
2020-06-08 21:35:10 +02:00
|
|
|
#include "BroadcastTxData.h"
|
2020-04-17 19:33:06 +02:00
|
|
|
|
2020-05-18 19:49:24 +02:00
|
|
|
#include <random.h>
|
2020-04-17 19:33:06 +02:00
|
|
|
#include <streaming/BufferPool.h>
|
2022-01-24 12:06:37 +01:00
|
|
|
#include <streaming/BufferPools.h>
|
2020-04-17 19:33:06 +02:00
|
|
|
#include <streaming/P2PBuilder.h>
|
|
|
|
|
#include <APIProtocol.h>
|
|
|
|
|
|
2025-02-11 16:46:21 +01:00
|
|
|
#include <boost/asio/bind_executor.hpp>
|
2020-04-17 19:33:06 +02:00
|
|
|
|
2025-02-08 19:05:26 +01:00
|
|
|
ConnectionManager::ConnectionManager(boost::asio::io_context &context, const boost::filesystem::path &basedir, DownloadManager *parent)
|
2020-04-17 19:33:06 +02:00
|
|
|
: m_shuttingDown(false),
|
2025-02-08 19:05:26 +01:00
|
|
|
m_ioContext(context),
|
|
|
|
|
m_cronTimer(m_ioContext),
|
2020-04-17 19:33:06 +02:00
|
|
|
m_peerAddressDb(this),
|
2025-02-08 19:05:26 +01:00
|
|
|
m_network(m_ioContext),
|
2020-05-11 12:49:10 +02:00
|
|
|
m_dlManager(parent),
|
|
|
|
|
m_basedir(basedir)
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
// The nonce is used in the status message to allow detection of connect-to-self.
|
2020-05-18 19:49:24 +02:00
|
|
|
m_appNonce = GetRand(-1);
|
2020-04-17 19:33:06 +02:00
|
|
|
|
|
|
|
|
std::map<int, std::string> table;
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::Version, "version"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::VersionAck, "verack"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::Ping, "ping"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::Pong, "pong"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::PreferHeaders, "sendheaders"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::GetHeaders, "getheaders"));
|
2023-02-25 12:02:19 +01:00
|
|
|
table.insert(std::make_pair(Api::P2P::GetBlocks, "getblocks"));
|
2020-04-17 19:33:06 +02:00
|
|
|
table.insert(std::make_pair(Api::P2P::Headers, "headers"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::RejectData, "reject"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::Inventory, "inv"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::GetAddr, "getaddr"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::Addresses, "addr"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::Data_Transaction, "tx"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::Data_MerkleBlock, "merkleblock"));
|
2021-02-04 17:40:06 +01:00
|
|
|
table.insert(std::make_pair(Api::P2P::Data_DSProof, "dsproof-beta"));
|
2020-04-17 19:33:06 +02:00
|
|
|
table.insert(std::make_pair(Api::P2P::FilterLoad, "filterload"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::FilterClear, "filterclear"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::GetData, "getdata"));
|
2021-02-04 17:39:44 +01:00
|
|
|
table.insert(std::make_pair(Api::P2P::Mempool, "mempool"));
|
2023-02-25 12:02:19 +01:00
|
|
|
|
|
|
|
|
// specially added to detect not-bitcoincash clients:
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::AVAHello, "avahello"));
|
2023-02-25 14:56:19 +01:00
|
|
|
table.insert(std::make_pair(Api::P2P::AlertMessage, "alert"));
|
|
|
|
|
table.insert(std::make_pair(Api::P2P::ProtoConf, "protoconf"));
|
2023-02-25 12:02:19 +01:00
|
|
|
|
2020-04-17 19:33:06 +02:00
|
|
|
m_network.setMessageIdLookup(table);
|
|
|
|
|
|
2020-10-29 21:47:53 +01:00
|
|
|
// network selection
|
|
|
|
|
if (parent->chain() == P2PNet::Testnet4Chain) {
|
|
|
|
|
std::vector<uint8_t> magic(4);
|
|
|
|
|
magic[0] = 0xe2;
|
|
|
|
|
magic[1] = 0xb7;
|
|
|
|
|
magic[2] = 0xda;
|
|
|
|
|
magic[3] = 0xaf;
|
|
|
|
|
m_network.setLegacyNetworkId(magic);
|
|
|
|
|
|
|
|
|
|
m_peerAddressDb.setDefaultPortNr(28333);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-17 19:33:06 +02:00
|
|
|
m_cronTimer.expires_from_now(boost::posix_time::seconds(20));
|
2025-02-11 16:46:21 +01:00
|
|
|
m_cronTimer.async_wait(boost::asio::bind_executor(parent->strand(),
|
|
|
|
|
std::bind(&ConnectionManager::cron, this, std::placeholders::_1)));
|
2020-04-17 19:33:06 +02:00
|
|
|
|
2020-05-09 19:58:44 +02:00
|
|
|
m_userAgent = "Flowee-P2PNet-based app";
|
2020-05-11 12:49:10 +02:00
|
|
|
|
|
|
|
|
m_peerAddressDb.loadDatabase(m_basedir);
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::addInvMessage(const Message &message, int sourcePeerId)
|
|
|
|
|
{
|
|
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return;
|
2025-02-11 16:46:21 +01:00
|
|
|
m_dlManager->strand().post(std::bind(&DownloadManager::parseInvMessage, m_dlManager, message, sourcePeerId), std::allocator<void>());
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::addTransaction(const Tx &message, int sourcePeerId)
|
|
|
|
|
{
|
|
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return;
|
2025-02-11 16:46:21 +01:00
|
|
|
m_dlManager->strand().post(std::bind(&DownloadManager::parseTransaction, m_dlManager, message, sourcePeerId), std::allocator<void>());
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::connect(PeerAddress &address)
|
|
|
|
|
{
|
|
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return;
|
2025-03-07 14:31:02 +01:00
|
|
|
if (m_dlManager->powerMode() != P2PNet::NormalPower)
|
|
|
|
|
return;
|
2020-04-17 19:33:06 +02:00
|
|
|
auto con = m_network.connection(address.peerAddress());
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
|
|
|
|
// first check if we are already have a Peer for this endpoint
|
|
|
|
|
if (m_peers.find(con.connectionId()) == m_peers.end()) {
|
|
|
|
|
address.punishPeer(100); // when the connection succeeds, we remove the 100 again.
|
2020-05-03 21:01:57 +02:00
|
|
|
con.setOnError(std::bind(&ConnectionManager::handleError, this, std::placeholders::_1, std::placeholders::_2));
|
2021-08-05 23:03:19 +02:00
|
|
|
con.setMessageQueueSizes(m_queueSize, 3);
|
2020-05-05 22:53:25 +02:00
|
|
|
auto p = std::make_shared<Peer>(this, address);
|
|
|
|
|
p->connect(std::move(con));
|
2020-04-17 19:33:06 +02:00
|
|
|
m_peers.insert(std::make_pair(p->connectionId(), p));
|
2024-01-26 12:08:03 +01:00
|
|
|
for (auto iface : m_dlManager->p2pNetListeners()) {
|
|
|
|
|
iface->newConnection(p);
|
|
|
|
|
}
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 10:42:58 +02:00
|
|
|
void ConnectionManager::disconnect(const std::shared_ptr<Peer> &peer)
|
2020-04-26 16:20:45 +02:00
|
|
|
{
|
2021-11-01 17:19:00 +01:00
|
|
|
if (m_shuttingDown || peer.get() == nullptr)
|
2020-05-03 21:01:57 +02:00
|
|
|
return;
|
|
|
|
|
for (auto iface : m_dlManager->p2pNetListeners()) {
|
2024-01-04 17:02:37 +01:00
|
|
|
iface->lostPeer(peer);
|
2020-05-03 21:01:57 +02:00
|
|
|
}
|
2020-04-26 16:20:45 +02:00
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
2020-05-03 21:01:57 +02:00
|
|
|
assert(m_peers.find(peer->connectionId()) != m_peers.end());
|
2020-04-26 16:20:45 +02:00
|
|
|
removePeer(peer);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-17 19:33:06 +02:00
|
|
|
uint64_t ConnectionManager::servicesBitfield() const
|
|
|
|
|
{
|
|
|
|
|
return m_servicesBitfield;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::setServicesBitfield(const uint64_t &servicesBitfield)
|
|
|
|
|
{
|
|
|
|
|
m_servicesBitfield = servicesBitfield;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ConnectionManager::blockHeight() const
|
|
|
|
|
{
|
|
|
|
|
return m_blockHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::setBlockHeight(int blockHeight)
|
|
|
|
|
{
|
2020-05-11 23:15:30 +02:00
|
|
|
assert(blockHeight >= 0);
|
2020-04-17 19:33:06 +02:00
|
|
|
m_blockHeight = blockHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ConnectionManager::blockHeightFor(const uint256 &blockId)
|
|
|
|
|
{
|
|
|
|
|
return m_dlManager->blockchain().blockHeightFor(blockId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint256 ConnectionManager::blockHashFor(int height)
|
|
|
|
|
{
|
|
|
|
|
return m_dlManager->blockchain().block(height).createHash();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t ConnectionManager::appNonce() const
|
|
|
|
|
{
|
|
|
|
|
return m_appNonce;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 10:42:58 +02:00
|
|
|
void ConnectionManager::connectionEstablished(const std::shared_ptr<Peer> &peer)
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return;
|
|
|
|
|
assert(peer);
|
|
|
|
|
assert(peer->peerAddress().isValid());
|
|
|
|
|
peer->peerAddress().punishPeer(-100); // this mirrors the 100 when we started connecting.
|
|
|
|
|
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
|
|
|
|
// don't use if the client doesn't support any usable services.
|
2024-01-18 21:45:11 +01:00
|
|
|
if (!peer->supplies_bloom() || !peer->supplies_network() || !peer->relaysTransactions()) {
|
|
|
|
|
logWarning() << "Rejecting. Need BLOOM and NETWORK and tx-relay. Peer:" << peer->connectionId() << peer->userAgent()
|
2020-05-10 11:51:17 +02:00
|
|
|
<< peer->peerAddress();
|
2020-04-17 19:33:06 +02:00
|
|
|
removePeer(peer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto iface : m_dlManager->p2pNetListeners()) {
|
2024-01-04 17:02:37 +01:00
|
|
|
iface->newPeer(peer);
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
m_connectedPeers.insert(peer->connectionId());
|
|
|
|
|
|
2023-03-27 15:19:23 +02:00
|
|
|
|
|
|
|
|
// we use headers to verify that the peer is on the same chain as us. Every
|
|
|
|
|
// peer will have to answer the request at least once, and we add repeat requests
|
|
|
|
|
// after a certain amount of time.
|
|
|
|
|
if (std::abs(m_dlManager->blockchain().expectedBlockHeight() - m_blockHeight) < 800
|
2020-11-05 21:53:08 +01:00
|
|
|
&& (peer->peerAddress().lastReceivedGoodHeaders() == 0
|
2023-08-17 21:23:28 +02:00
|
|
|
|| (time(nullptr) - peer->peerAddress().lastReceivedGoodHeaders() > 3600 * 24 * 14))) {
|
2020-04-17 19:33:06 +02:00
|
|
|
// check if this peer is using the same chain as us.
|
2023-03-27 15:19:23 +02:00
|
|
|
// notice that we picked 800 above because the default requestHeaders
|
2023-03-20 19:46:37 +01:00
|
|
|
// will work correcctly with us being behind up to 1000 headers.
|
2024-01-04 21:50:37 +01:00
|
|
|
requestHeaders(peer, 9);
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::addBlockHeaders(const Message &message, int sourcePeerId)
|
|
|
|
|
{
|
|
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return;
|
2023-03-27 15:19:23 +02:00
|
|
|
// TODO if downloadmanager triggered this
|
2020-04-17 19:33:06 +02:00
|
|
|
// then update metadata on the speed of this peer.
|
|
|
|
|
m_dlManager->strand().post(std::bind(&Blockchain::processBlockHeaders,
|
2025-02-11 16:46:21 +01:00
|
|
|
&m_dlManager->blockchain(), message, sourcePeerId), std::allocator<void>());
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::addAddresses(const Message &message, int sourcePeerId)
|
|
|
|
|
{
|
|
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return;
|
|
|
|
|
m_dlManager->strand().post(std::bind(&PeerAddressDB::processAddressMessage,
|
2025-02-11 16:46:21 +01:00
|
|
|
&m_peerAddressDb, message, sourcePeerId), std::allocator<void>());
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-07 15:38:54 +02:00
|
|
|
bool ConnectionManager::punish(const std::shared_ptr<Peer> &peer, int amount)
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
assert(peer);
|
|
|
|
|
if (m_shuttingDown)
|
2020-05-07 15:38:54 +02:00
|
|
|
return false;
|
2020-04-17 19:33:06 +02:00
|
|
|
auto address = peer->peerAddress();
|
|
|
|
|
short total = PUNISHMENT_MAX;
|
|
|
|
|
short previous = total;
|
|
|
|
|
if (address.isValid()) {
|
|
|
|
|
previous = address.punishment();
|
|
|
|
|
total = address.punishPeer(amount);
|
|
|
|
|
|
|
|
|
|
for (auto iface : m_dlManager->p2pNetListeners()) {
|
2024-01-04 17:02:37 +01:00
|
|
|
iface->punishmentChanged(peer);
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (total >= PUNISHMENT_MAX) { // too much punishment leads to a ban
|
2020-05-10 11:51:17 +02:00
|
|
|
logWarning() << "Ban peer:" << peer->connectionId() << previous << "=>" << total
|
|
|
|
|
<< "Address:" << peer->peerAddress();
|
2020-04-17 19:33:06 +02:00
|
|
|
for (auto iface : m_dlManager->p2pNetListeners()) {
|
2024-01-04 17:02:37 +01:00
|
|
|
iface->lostPeer(peer);
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
2020-05-06 10:42:58 +02:00
|
|
|
removePeer(peer);
|
2020-05-07 15:38:54 +02:00
|
|
|
return true;
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
2020-05-07 15:38:54 +02:00
|
|
|
return false;
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-07 15:38:54 +02:00
|
|
|
bool ConnectionManager::punish(int connectionId, int amount)
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
2020-05-05 22:53:25 +02:00
|
|
|
std::shared_ptr<Peer> p;
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
|
|
|
|
auto peerIter = m_peers.find(connectionId);
|
|
|
|
|
if (peerIter == m_peers.end())
|
2020-05-07 15:38:54 +02:00
|
|
|
return false;
|
2020-04-17 19:33:06 +02:00
|
|
|
p = peerIter->second;
|
|
|
|
|
}
|
2020-05-07 15:38:54 +02:00
|
|
|
return punish(p, amount);
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-04 21:50:37 +01:00
|
|
|
void ConnectionManager::requestHeaders(const std::shared_ptr<Peer> &peer, int skipHeaders)
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
2020-11-13 20:11:06 +01:00
|
|
|
if (m_shuttingDown || peer.get() == nullptr)
|
2020-04-17 19:33:06 +02:00
|
|
|
return;
|
2024-01-04 21:50:37 +01:00
|
|
|
auto message = m_dlManager->blockchain().createGetHeadersRequest(skipHeaders);
|
2020-05-11 18:49:16 +02:00
|
|
|
peer->setRequestedHeader(true);
|
2020-04-17 19:33:06 +02:00
|
|
|
peer->sendMessage(message);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-05 22:53:25 +02:00
|
|
|
std::deque<std::shared_ptr<Peer>> ConnectionManager::connectedPeers() const
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
2020-05-05 22:53:25 +02:00
|
|
|
std::deque<std::shared_ptr<Peer>> answer;
|
2020-04-17 19:33:06 +02:00
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return answer;
|
|
|
|
|
for (auto i : m_connectedPeers) {
|
|
|
|
|
auto p = m_peers.find(i);
|
|
|
|
|
assert(p != m_peers.end());
|
|
|
|
|
answer.push_back(p->second);
|
|
|
|
|
}
|
|
|
|
|
return answer;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-05 21:47:54 +01:00
|
|
|
int ConnectionManager::unconnectedPeerCount() const
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
|
|
|
|
int answer = 0;
|
|
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return answer;
|
2021-02-05 17:29:01 +01:00
|
|
|
for (const auto &i : m_peers) {
|
2020-11-05 21:47:54 +01:00
|
|
|
if (i.second.get() && m_connectedPeers.find(i.first) == m_connectedPeers.end())
|
|
|
|
|
++answer;
|
|
|
|
|
}
|
|
|
|
|
return answer;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-05 22:53:25 +02:00
|
|
|
std::shared_ptr<Peer> ConnectionManager::peer(int connectionId) const
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
|
|
|
|
auto i = m_peers.find(connectionId);
|
|
|
|
|
if (i == m_peers.end())
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
return i->second;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 15:12:13 +01:00
|
|
|
void ConnectionManager::addPrivacySegment(const std::shared_ptr<PrivacySegment> &ps)
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
assert(ps);
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
// don't add it twice, please.
|
|
|
|
|
for (auto s : m_segments) {
|
|
|
|
|
assert (s != ps);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
m_segments.push_back(ps);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 15:12:13 +01:00
|
|
|
void ConnectionManager::removePrivacySegment(const std::shared_ptr<PrivacySegment> &ps)
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
assert(ps);
|
|
|
|
|
for (auto s = m_segments.begin(); s != m_segments.end(); ++s) {
|
|
|
|
|
if (ps == *s) {
|
|
|
|
|
m_segments.erase(s);
|
2024-10-11 19:44:54 +02:00
|
|
|
|
|
|
|
|
// shutdown peers that still point to this segment.
|
|
|
|
|
auto copy = m_peers; // avoid iterator invalidation due to modification
|
|
|
|
|
for (auto i = copy.begin(); i != copy.end(); ++i) {
|
2024-11-29 15:12:13 +01:00
|
|
|
auto peersPs = i->second->privacySegment().lock();
|
|
|
|
|
if (peersPs == ps)
|
2024-10-11 19:44:54 +02:00
|
|
|
disconnect(i->second);
|
|
|
|
|
}
|
2020-04-17 19:33:06 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::setUserAgent(const std::string &userAgent)
|
|
|
|
|
{
|
|
|
|
|
m_userAgent = userAgent;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-08 21:35:10 +02:00
|
|
|
void ConnectionManager::broadcastTransaction(const std::shared_ptr<BroadcastTxData> &txOwner)
|
|
|
|
|
{
|
|
|
|
|
const auto id = txOwner->privSegment();
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
|
|
|
|
for (auto iter = m_peers.begin(); iter != m_peers.end(); ++iter) {
|
2025-05-21 22:45:53 +02:00
|
|
|
auto peer = iter->second;
|
2024-11-29 15:12:13 +01:00
|
|
|
auto privacySegment = peer->privacySegment().lock();
|
|
|
|
|
if (privacySegment && privacySegment->segmentId() == id) {
|
2020-06-08 21:35:10 +02:00
|
|
|
peer->sendTx(txOwner);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-18 21:46:08 +01:00
|
|
|
// store it here so when the SPV action connects new peers to a segment,
|
|
|
|
|
// it can send those transactions to them too.
|
2020-06-08 21:35:10 +02:00
|
|
|
m_transactionsToBroadcast.push_back(txOwner);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-26 16:20:45 +02:00
|
|
|
int ConnectionManager::peerCount() const
|
|
|
|
|
{
|
|
|
|
|
assert(m_peers.size() <= INT_MAX);
|
|
|
|
|
return int(m_peers.size());
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-17 19:33:06 +02:00
|
|
|
|
|
|
|
|
void ConnectionManager::cron(const boost::system::error_code &error)
|
|
|
|
|
{
|
|
|
|
|
if (error)
|
|
|
|
|
return;
|
|
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return;
|
2025-03-07 14:31:02 +01:00
|
|
|
if (m_dlManager->powerMode() != P2PNet::NormalPower)
|
|
|
|
|
return;
|
2020-04-17 19:33:06 +02:00
|
|
|
m_cronTimer.expires_from_now(boost::posix_time::seconds(20));
|
2025-02-11 16:46:21 +01:00
|
|
|
m_cronTimer.async_wait(boost::asio::bind_executor(m_dlManager->strand(),
|
|
|
|
|
std::bind(&ConnectionManager::cron, this, std::placeholders::_1)));
|
2020-04-17 19:33:06 +02:00
|
|
|
|
|
|
|
|
logDebug() << "Cron";
|
|
|
|
|
int now = static_cast<uint32_t>(time(nullptr));
|
|
|
|
|
// check for connections that don't seem to connect.
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
2020-06-08 21:35:10 +02:00
|
|
|
for (auto iter = m_peers.begin(); iter != m_peers.end();) {
|
2020-05-05 22:53:25 +02:00
|
|
|
Peer *peer = iter->second.get();
|
2020-05-11 17:42:44 +02:00
|
|
|
bool kick = peer->status() != Peer::Connected || peer->protocolVersion() == 0;
|
|
|
|
|
if (peer->connectTime() == 0) // not connected yet
|
|
|
|
|
kick &= now - peer->timeOffset() > 10; // no more than 10 sec to try to connect
|
|
|
|
|
else
|
|
|
|
|
kick &= now - peer->connectTime() > 20; // no more than 20 seconds for version handshake.
|
|
|
|
|
if (kick) {
|
2020-05-10 11:51:17 +02:00
|
|
|
auto peerAddress = peer->peerAddress();
|
2020-05-11 19:54:49 +02:00
|
|
|
logInfo() << "peer:" << iter->first << "kicking. Address:" << peerAddress;
|
2020-04-17 19:33:06 +02:00
|
|
|
iter = m_peers.erase(iter);
|
2020-05-10 11:51:17 +02:00
|
|
|
peer->shutdown();
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2020-05-11 19:54:49 +02:00
|
|
|
auto log = logInfo() << "peer:" << iter->first;
|
|
|
|
|
if (peer->status() == Peer::Connecting)
|
|
|
|
|
log << "Address:" << peer->peerAddress() << "[connecting]";
|
|
|
|
|
else
|
|
|
|
|
log << peer->userAgent();
|
2024-11-29 15:12:13 +01:00
|
|
|
auto privacySegment = peer->privacySegment().lock();
|
|
|
|
|
if (privacySegment)
|
|
|
|
|
log << "Wallet:" << privacySegment->segmentId();
|
2020-05-11 19:54:49 +02:00
|
|
|
if (peer->connectTime() > 0)
|
|
|
|
|
log.nospace() << "(" << now - peer->connectTime() << "s)";
|
2023-03-27 15:19:23 +02:00
|
|
|
if (peer->peerHeight() > 0)
|
|
|
|
|
log.nospace() << " @" << peer->peerHeight();
|
2020-04-17 19:33:06 +02:00
|
|
|
++iter;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-08 21:35:10 +02:00
|
|
|
|
2024-01-18 21:46:08 +01:00
|
|
|
auto iter = m_transactionsToBroadcast.begin();
|
|
|
|
|
while (iter != m_transactionsToBroadcast.end()) {
|
2020-06-08 21:35:10 +02:00
|
|
|
auto locked = iter->lock();
|
|
|
|
|
if (!locked.get()) { // expired
|
|
|
|
|
logDebug() << "Transaction broadcast struct has expired.";
|
|
|
|
|
iter = m_transactionsToBroadcast.erase(iter);
|
|
|
|
|
} else {
|
|
|
|
|
++iter;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::handleError(int remoteId, const boost::system::error_code &error)
|
2020-04-26 16:20:45 +02:00
|
|
|
{
|
2025-02-11 16:46:21 +01:00
|
|
|
m_dlManager->strand().post(std::bind(&ConnectionManager::handleError_impl, this, remoteId, error), std::allocator<void>());
|
2020-04-26 16:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-10 11:51:17 +02:00
|
|
|
void ConnectionManager::handleError_impl(int peerId, const boost::system::error_code &error)
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return;
|
|
|
|
|
bool remove = false;
|
2020-05-07 15:38:54 +02:00
|
|
|
int punishment = 180; // unknown error
|
|
|
|
|
if (error == boost::asio::error::host_unreachable || error == boost::asio::error::network_unreachable
|
|
|
|
|
|| error.value() == EADDRNOTAVAIL) {
|
2020-04-17 19:33:06 +02:00
|
|
|
remove = true;
|
2023-04-21 22:06:46 +02:00
|
|
|
punishment = 45; // Typically structural networking issue. Could be fixed later, though.
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
2020-05-07 15:38:54 +02:00
|
|
|
else if (error == boost::asio::error::host_not_found) {
|
2020-04-17 19:33:06 +02:00
|
|
|
remove = true;
|
2020-05-07 15:38:54 +02:00
|
|
|
punishment = 450; // faulty DNS name.
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
2020-05-09 19:58:44 +02:00
|
|
|
else if (error == boost::asio::error::connection_refused
|
|
|
|
|
|| error == boost::asio::error::connection_aborted
|
|
|
|
|
|| error == boost::asio::error::connection_reset) {
|
|
|
|
|
remove = true;
|
2024-01-28 19:53:59 +01:00
|
|
|
punishment = -90; // almost give back the 100, but not entirly to down-prioritize it on random connects
|
2020-05-09 19:58:44 +02:00
|
|
|
}
|
2020-05-10 11:51:17 +02:00
|
|
|
auto remotePeer = peer(peerId);
|
2020-05-07 17:01:03 +02:00
|
|
|
if (!remotePeer)
|
|
|
|
|
return;
|
2024-01-30 20:48:36 +01:00
|
|
|
logWarning().nospace() << "Peer: " << peerId << " " << remotePeer->peerAddress()
|
|
|
|
|
<< " got error. (" << error.value() << "=" << error.message()
|
2020-05-11 19:54:49 +02:00
|
|
|
<< ") Punishment: " << punishment;
|
2020-05-07 15:38:54 +02:00
|
|
|
bool removed = punish(remotePeer, punishment);
|
2020-04-17 19:33:06 +02:00
|
|
|
|
2020-05-07 15:38:54 +02:00
|
|
|
if (remove && !removed) {
|
2020-05-10 11:51:17 +02:00
|
|
|
logDebug() << "removing" << peerId;
|
2020-05-07 15:38:54 +02:00
|
|
|
for (auto iface : m_dlManager->p2pNetListeners()) {
|
2024-01-04 17:02:37 +01:00
|
|
|
iface->lostPeer(remotePeer);
|
2020-05-07 15:38:54 +02:00
|
|
|
}
|
2020-04-17 19:33:06 +02:00
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
2020-05-07 15:38:54 +02:00
|
|
|
removePeer(remotePeer);
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 10:42:58 +02:00
|
|
|
void ConnectionManager::removePeer(const std::shared_ptr<Peer> &p)
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
const int id = p->connectionId();
|
2020-05-06 10:42:58 +02:00
|
|
|
p->shutdown();
|
2020-05-03 21:01:57 +02:00
|
|
|
|
2020-04-17 19:33:06 +02:00
|
|
|
auto i = m_connectedPeers.find(id);
|
|
|
|
|
if (i != m_connectedPeers.end()) {
|
|
|
|
|
m_connectedPeers.erase(i);
|
|
|
|
|
m_dlManager->peerDisconnected(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto iter = m_peers.find(id);
|
|
|
|
|
assert (iter != m_peers.end());
|
|
|
|
|
m_peers.erase(iter);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 14:02:36 +01:00
|
|
|
P2PNet::NetworkCertainty ConnectionManager::blockHeightCertainty() const
|
|
|
|
|
{
|
|
|
|
|
return m_blockHeightCertainty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::setBlockHeightCertainty(P2PNet::NetworkCertainty certainty)
|
|
|
|
|
{
|
|
|
|
|
if (certainty == m_blockHeightCertainty)
|
|
|
|
|
return;
|
|
|
|
|
m_blockHeightCertainty = certainty;
|
|
|
|
|
for (auto iface : m_dlManager->p2pNetListeners()) {
|
|
|
|
|
iface->newBlockHeightCertainty(certainty);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-27 15:17:56 +02:00
|
|
|
std::deque<std::weak_ptr<BroadcastTxData> > ConnectionManager::transactionsToBroadcast() const
|
2021-07-30 14:04:58 +02:00
|
|
|
{
|
2023-03-27 15:17:56 +02:00
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
2021-07-30 14:04:58 +02:00
|
|
|
return m_transactionsToBroadcast;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 15:12:13 +01:00
|
|
|
std::deque<std::shared_ptr<PrivacySegment> > ConnectionManager::segments() const
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
return m_segments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionManager::shutdown()
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
|
|
|
|
if (m_shuttingDown)
|
|
|
|
|
return;
|
|
|
|
|
m_shuttingDown = true;
|
|
|
|
|
m_cronTimer.cancel();
|
|
|
|
|
|
|
|
|
|
auto copy(m_peers);
|
2021-02-05 17:29:01 +01:00
|
|
|
for (const auto &peer : copy) {
|
2020-05-06 10:42:58 +02:00
|
|
|
removePeer(peer.second);
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
assert(m_peers.empty());
|
2022-11-11 19:25:19 +01:00
|
|
|
saveData();
|
|
|
|
|
}
|
2020-04-17 19:33:06 +02:00
|
|
|
|
2022-11-11 19:25:19 +01:00
|
|
|
void ConnectionManager::saveData()
|
|
|
|
|
{
|
2020-05-11 12:49:10 +02:00
|
|
|
m_peerAddressDb.saveDatabase(m_basedir);
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
2020-05-09 19:58:44 +02:00
|
|
|
|
|
|
|
|
void ConnectionManager::setMessageQueueSize(int size)
|
|
|
|
|
{
|
|
|
|
|
assert(size >= 1);
|
|
|
|
|
assert(size <= 0xeFFF);
|
|
|
|
|
m_queueSize = static_cast<short>(size);
|
|
|
|
|
}
|
2025-03-07 14:31:02 +01:00
|
|
|
|
|
|
|
|
void ConnectionManager::onPowerModeChanged()
|
|
|
|
|
{
|
|
|
|
|
switch (m_dlManager->powerMode()) {
|
|
|
|
|
case P2PNet::NormalPower:
|
|
|
|
|
m_cronTimer.cancel();
|
|
|
|
|
m_cronTimer.expires_from_now(boost::posix_time::seconds(20));
|
|
|
|
|
m_cronTimer.async_wait(boost::asio::bind_executor(m_dlManager->strand(),
|
|
|
|
|
std::bind(&ConnectionManager::cron, this, std::placeholders::_1)));
|
|
|
|
|
break;
|
|
|
|
|
case P2PNet::LowPower: {
|
|
|
|
|
m_cronTimer.cancel();
|
|
|
|
|
auto copy(m_peers);
|
|
|
|
|
for (const auto &peer : copy) {
|
|
|
|
|
disconnect(peer.second);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|