2020-04-17 19:33:06 +02:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2021-02-02 13:08:07 +01:00
|
|
|
* Copyright (C) 2020 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 "SyncChainAction.h"
|
|
|
|
|
#include "DownloadManager.h"
|
|
|
|
|
#include "Peer.h"
|
|
|
|
|
|
|
|
|
|
#include <Logger.h>
|
|
|
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
|
|
2020-11-13 20:40:23 +01:00
|
|
|
static int MinGoodPeers = 4;
|
2020-11-05 17:42:51 +01:00
|
|
|
static int MaxGoodPeers = 8;
|
2020-04-17 19:33:06 +02:00
|
|
|
constexpr int RINGBUF_SIZE = 10;
|
|
|
|
|
|
|
|
|
|
SyncChainAction::SyncChainAction(DownloadManager *parent)
|
2023-11-03 22:12:02 +01:00
|
|
|
: Action(parent),
|
|
|
|
|
m_states(RINGBUF_SIZE)
|
2020-04-17 19:33:06 +02:00
|
|
|
{
|
|
|
|
|
m_startHeight = parent->blockHeight();
|
2020-11-13 20:40:23 +01:00
|
|
|
if (parent->chain() != P2PNet::MainChain) {
|
|
|
|
|
MinGoodPeers = 3;
|
|
|
|
|
MaxGoodPeers = 3;
|
|
|
|
|
}
|
2020-04-17 19:33:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SyncChainAction::execute(const boost::system::error_code &error)
|
|
|
|
|
{
|
|
|
|
|
if (error)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This class observes the system, typically shortly after startup.
|
|
|
|
|
* The point is to make sure we have a fully up-to-date chain.
|
2023-03-27 15:19:23 +02:00
|
|
|
*
|
|
|
|
|
* We also monitor the 'getheaders' updating of a big amount (again,
|
|
|
|
|
* only at startup) and kick a node that is slow in giving us headers.
|
2020-04-17 19:33:06 +02:00
|
|
|
* -----------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* The goal is to get various peers to agree on what is the 'tip'.
|
|
|
|
|
* These peers should also come from different sections of the Internet
|
|
|
|
|
* since most of the sybils would end up with very similar IPs. (TODO)
|
|
|
|
|
*
|
2023-03-27 15:19:23 +02:00
|
|
|
* The downloadmanager/connection manager duo make sure that all peers
|
|
|
|
|
* get a 'getheaders' call regularly, and kick out peers that are not on
|
|
|
|
|
* our chain.
|
|
|
|
|
* Additionally they make sure that INV announcements of blocks are validated
|
|
|
|
|
* similarly and the Peer objects 'peerHeight()' is updated on a new block.
|
2020-04-17 19:33:06 +02:00
|
|
|
*/
|
2023-03-27 15:19:23 +02:00
|
|
|
|
|
|
|
|
const int localHeight = m_dlm->blockHeight();
|
|
|
|
|
const int goal = m_dlm->blockchain().expectedBlockHeight();
|
|
|
|
|
const int theoreticalDiff = std::abs(localHeight - goal);
|
|
|
|
|
|
|
|
|
|
const auto connected = m_dlm->connectionManager().connectedPeers();
|
|
|
|
|
if (m_dlm->connectionManager().peerCount() < MinGoodPeers) {
|
|
|
|
|
logInfo() << "SyncChain has" << connected.size() << "peers, which is less than I need. Connecting a new peer";
|
|
|
|
|
connectToNextPeer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int peersAgree = 0;
|
|
|
|
|
int goodPeers = 0;
|
|
|
|
|
int peersGotRequestedToSendHeaders = 0;
|
|
|
|
|
for (const auto &peer : connected) {
|
|
|
|
|
if (!peer->receivedHeaders()) {
|
|
|
|
|
if (peer->requestedHeader())
|
|
|
|
|
++peersGotRequestedToSendHeaders;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (peer->peerAddress().punishment() > 300)
|
|
|
|
|
continue;
|
|
|
|
|
++goodPeers;
|
|
|
|
|
if (peer->peerHeight() == localHeight)
|
|
|
|
|
++peersAgree;
|
|
|
|
|
}
|
|
|
|
|
if ((peersAgree >= MinGoodPeers && theoreticalDiff < 6)
|
|
|
|
|
|| peersAgree >= MaxGoodPeers) { // DONE!
|
|
|
|
|
logInfo() << "==== SyncChainAction ==== DONE";
|
|
|
|
|
|
|
|
|
|
if (localHeight - m_startHeight > 800) { // 800 should match ConnectionManager::connectionEstablished
|
|
|
|
|
// in ConnectionManager we check a peer is on the right chain by doing a 'headers' call.
|
|
|
|
|
// Unless we are behind too far as that would not be useful.
|
|
|
|
|
// So, now we are caught up, make those peers show some headers.
|
|
|
|
|
for (const auto &peer : connected) {
|
|
|
|
|
if (!peer->receivedHeaders()) {
|
2023-04-17 19:25:07 +02:00
|
|
|
logInfo() << "Post catching up, request headers of peer" << peer->connectionId();
|
2023-03-27 15:19:23 +02:00
|
|
|
m_dlm->connectionManager().requestHeaders(peer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_dlm->done(this); // deletes me
|
|
|
|
|
return; // action is done
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-17 19:33:06 +02:00
|
|
|
if (m_dlm->peerDownloadingHeaders() != -1) { // a download is in progress
|
|
|
|
|
// no need to do anything, just wait.
|
|
|
|
|
|
|
|
|
|
// We will check if we are actually progressing up in block-height since
|
|
|
|
|
// our peer might stop sending us headers for some reason.
|
|
|
|
|
|
2023-03-27 15:19:23 +02:00
|
|
|
const uint32_t now = time(nullptr);
|
2020-04-17 19:33:06 +02:00
|
|
|
int score = 0;
|
|
|
|
|
int prevHeight = 0;
|
|
|
|
|
uint32_t oldestTime = 0;
|
|
|
|
|
for (int i = m_stateIndex + 1; i != m_stateIndex; ++i) {
|
|
|
|
|
if (prevHeight == 0) {
|
2020-05-09 19:58:44 +02:00
|
|
|
if (i < RINGBUF_SIZE) {
|
|
|
|
|
prevHeight = m_states[i].height;
|
|
|
|
|
if (prevHeight == 0) // when no measurements present, allow starting
|
|
|
|
|
score += 100;
|
|
|
|
|
else
|
|
|
|
|
oldestTime = m_states[i].timestamp;
|
|
|
|
|
}
|
2020-04-17 19:33:06 +02:00
|
|
|
} else {
|
|
|
|
|
int diff = m_states[i].height - prevHeight;
|
|
|
|
|
if (diff > 900)
|
|
|
|
|
score += 100;
|
|
|
|
|
}
|
|
|
|
|
if (i >= RINGBUF_SIZE - 1) i = -1; // behave like a ring-buffer.
|
|
|
|
|
}
|
2023-11-03 22:12:02 +01:00
|
|
|
if (score <= 400) {
|
2020-04-17 19:33:06 +02:00
|
|
|
// getting maybe 3000 block-headers in 15 secs is too slow :(
|
|
|
|
|
|
|
|
|
|
assert(oldestTime > 0);
|
|
|
|
|
if (now - oldestTime > 60) {
|
2023-03-25 17:26:24 +01:00
|
|
|
// this action should run every 1.5 seconds, if 10 measurements took
|
2020-04-17 19:33:06 +02:00
|
|
|
// more than a minute then we just slept or something.
|
|
|
|
|
logDebug() << "Slowness detected in header download, probably due to app-sleep. Waiting longer";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// take action, find a different peer to download from.
|
|
|
|
|
auto &cm = m_dlm->connectionManager();
|
|
|
|
|
if (cm.connectedPeers().size() > 1) { // only if we actually have another
|
|
|
|
|
logInfo() << "SyncChain disconnects peer that is holding up downloads" << m_dlm->peerDownloadingHeaders();
|
2020-05-10 01:04:24 +02:00
|
|
|
auto p = cm.peer(m_dlm->peerDownloadingHeaders());
|
|
|
|
|
if (p)
|
|
|
|
|
cm.disconnect(p);
|
2023-11-03 22:12:02 +01:00
|
|
|
|
|
|
|
|
for (size_t i = 0; i < RINGBUF_SIZE; ++i) {
|
|
|
|
|
m_states[i].height = 0; // reset history and score.
|
|
|
|
|
}
|
2020-04-17 19:33:06 +02:00
|
|
|
} else if (canAddNewPeer()){
|
|
|
|
|
logInfo() << "SyncChain would like a faster peer. Connecting to new one";
|
|
|
|
|
connectToNextPeer();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-03 22:12:02 +01:00
|
|
|
m_states[m_stateIndex].height = std::max(1, m_dlm->blockHeight());
|
2020-04-17 19:33:06 +02:00
|
|
|
m_states[m_stateIndex++].timestamp = now;
|
|
|
|
|
if (m_stateIndex >= RINGBUF_SIZE)
|
|
|
|
|
m_stateIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-03-27 15:19:23 +02:00
|
|
|
if (!connected.empty() && peersGotRequestedToSendHeaders == 0 && peersAgree == 0) {
|
|
|
|
|
// make sure that we actually try to download headers if we weren't already
|
|
|
|
|
m_dlm->getMoreHeaders();
|
|
|
|
|
}
|
|
|
|
|
if (static_cast<int>(connected.size()) >= MinGoodPeers && goodPeers > MinGoodPeers && peersAgree < MinGoodPeers) {
|
|
|
|
|
// ok, this is annoying.
|
|
|
|
|
// we have enough peers, they have all had the headers sent, we are not downloading any headers.
|
|
|
|
|
// but we seem to be behind...
|
2020-04-17 19:33:06 +02:00
|
|
|
|
2023-03-27 15:19:23 +02:00
|
|
|
// Lets add more peers.
|
|
|
|
|
logInfo() << "SyncChain has" << goodPeers << "good peers, but we are still behind. Connecting a new peer";
|
2020-04-17 19:33:06 +02:00
|
|
|
connectToNextPeer();
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-27 15:19:23 +02:00
|
|
|
|
2020-04-17 19:33:06 +02:00
|
|
|
again();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SyncChainAction::connectToNextPeer()
|
|
|
|
|
{
|
2022-11-04 11:50:49 +01:00
|
|
|
auto address = m_dlm->connectionManager().peerAddressDb().findBest(5); // 5 is service Bloom and Network
|
2020-04-17 19:33:06 +02:00
|
|
|
if (address.isValid())
|
|
|
|
|
m_dlm->connectionManager().connect(address);
|
|
|
|
|
|
|
|
|
|
m_lastPeerAddedTime = time(nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SyncChainAction::canAddNewPeer()
|
|
|
|
|
{
|
|
|
|
|
return time(nullptr) - m_lastPeerAddedTime > 30;
|
|
|
|
|
}
|