2020-04-17 19:33:06 +02:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2023-01-31 16:46:30 +01:00
|
|
|
* Copyright (C) 2020-2023 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/>.
|
|
|
|
|
*/
|
|
|
|
|
#ifndef DOWNLOADMANAGER_H
|
|
|
|
|
#define DOWNLOADMANAGER_H
|
|
|
|
|
|
|
|
|
|
#include "ConnectionManager.h"
|
|
|
|
|
#include "Action.h"
|
|
|
|
|
#include "Blockchain.h"
|
|
|
|
|
#include "InventoryItem.h"
|
2020-10-29 21:47:53 +01:00
|
|
|
#include "P2PNet.h"
|
2021-02-05 17:22:52 +01:00
|
|
|
#include "NotificationCenter.h"
|
2020-05-09 19:58:44 +02:00
|
|
|
#include <uint256.h>
|
2020-04-17 19:33:06 +02:00
|
|
|
|
|
|
|
|
#include <condition_variable>
|
|
|
|
|
#include <deque>
|
2020-05-09 19:58:44 +02:00
|
|
|
#include <vector>
|
2023-01-31 16:46:30 +01:00
|
|
|
#include <functional>
|
2020-04-17 19:33:06 +02:00
|
|
|
#include <boost/asio/io_context_strand.hpp>
|
|
|
|
|
#include <boost/unordered_map.hpp>
|
|
|
|
|
|
|
|
|
|
class P2PNetInterface;
|
2023-01-31 16:46:30 +01:00
|
|
|
class HeaderSyncInterface;
|
|
|
|
|
class DataListenerInterface;
|
2020-04-17 19:33:06 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A download manager as its name implies should manage what is to be downloaded.
|
|
|
|
|
* various peers could tell us about stuff that needs to be downloaded and we should
|
|
|
|
|
* round-robin over those that supply this info to actually download it.
|
|
|
|
|
*/
|
|
|
|
|
class DownloadManager
|
|
|
|
|
{
|
|
|
|
|
public:
|
2020-05-11 12:49:10 +02:00
|
|
|
/**
|
|
|
|
|
* @brief DownloadManager constructor.
|
|
|
|
|
* @param service the IO-service. See WorkerThreads
|
|
|
|
|
* @param basedir the directory to load and save state data.
|
|
|
|
|
*/
|
2020-10-29 21:47:53 +01:00
|
|
|
DownloadManager(boost::asio::io_service &service, const boost::filesystem::path &basedir, P2PNet::Chain chain = P2PNet::MainChain);
|
2020-04-17 19:33:06 +02:00
|
|
|
|
|
|
|
|
// start reaching out and synchronizing.
|
|
|
|
|
void start();
|
|
|
|
|
|
|
|
|
|
const ConnectionManager &connectionManager() const;
|
|
|
|
|
ConnectionManager &connectionManager();
|
|
|
|
|
|
|
|
|
|
inline Blockchain &blockchain() {
|
|
|
|
|
return m_blockchain;
|
|
|
|
|
}
|
|
|
|
|
inline const Blockchain &blockchain() const {
|
|
|
|
|
return m_blockchain;
|
|
|
|
|
}
|
2021-02-05 17:22:52 +01:00
|
|
|
|
|
|
|
|
NotificationCenter ¬ifications() {
|
|
|
|
|
return m_notifications;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-05 21:53:08 +01:00
|
|
|
/// We sync our chain using headers only, this returns true if we found the tip
|
|
|
|
|
inline bool isChainUpToDate() {
|
|
|
|
|
return std::abs(m_blockchain.expectedBlockHeight() - m_blockchain.height()) < 3;
|
|
|
|
|
}
|
2020-04-17 19:33:06 +02:00
|
|
|
|
|
|
|
|
inline uint64_t servicesBitfield() const {
|
|
|
|
|
return m_connectionManager.servicesBitfield();
|
|
|
|
|
}
|
|
|
|
|
inline void setServicesBitfield(const uint64_t &servicesBitfield) {
|
|
|
|
|
m_connectionManager.setServicesBitfield(servicesBitfield);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline int blockHeight() const {
|
|
|
|
|
return m_connectionManager.blockHeight();
|
|
|
|
|
}
|
2020-05-11 17:42:44 +02:00
|
|
|
/// called by the blockchain to let us know the blockchain changed size
|
2020-04-17 19:33:06 +02:00
|
|
|
void headersDownloadFinished(int newBlockHeight, int peerId);
|
|
|
|
|
|
|
|
|
|
inline boost::asio::io_context::strand &strand() {
|
|
|
|
|
return m_strand;
|
|
|
|
|
}
|
|
|
|
|
inline boost::asio::io_service &service() {
|
|
|
|
|
return m_connectionManager.service();
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-05 22:06:41 +01:00
|
|
|
/// Returns which chain this DownloadManager is following.
|
2020-10-29 21:47:53 +01:00
|
|
|
P2PNet::Chain chain() const;
|
|
|
|
|
|
2020-04-17 19:33:06 +02:00
|
|
|
/**
|
|
|
|
|
* returns peerId that is downloading headers. Or -1 if nobody is.
|
|
|
|
|
*/
|
|
|
|
|
inline int peerDownloadingHeaders() const {
|
|
|
|
|
return m_peerDownloadingHeaders;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-11 17:42:44 +02:00
|
|
|
/// Handle INV messages a peer received.
|
2020-04-17 19:33:06 +02:00
|
|
|
void parseInvMessage(Message message, int sourcePeerId);
|
2020-05-11 17:42:44 +02:00
|
|
|
/// Handle a transaction a peer received.
|
2020-04-17 19:33:06 +02:00
|
|
|
void parseTransaction(Tx tx, int sourcePeerId);
|
|
|
|
|
|
2020-05-11 17:42:44 +02:00
|
|
|
/// notify that a peer is no longer connected.
|
2020-04-17 19:33:06 +02:00
|
|
|
void peerDisconnected(int connectionId);
|
|
|
|
|
|
|
|
|
|
// Callback to let us know the data is invalid.
|
|
|
|
|
// This typically leads us to ban the peer.
|
|
|
|
|
void reportDataFailure(int connectionId);
|
|
|
|
|
|
2020-05-11 17:42:44 +02:00
|
|
|
/// Create and run an action.
|
|
|
|
|
/// This will avoid duplicates by returning a running action if it already exists.
|
|
|
|
|
/// Actions are owned and deleted by the downloadmanager.
|
2020-04-17 19:33:06 +02:00
|
|
|
template<class T>
|
|
|
|
|
T* addAction() {
|
|
|
|
|
T *t;
|
|
|
|
|
{ // lock scope
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_lock);
|
|
|
|
|
if (m_shuttingDown) return nullptr;
|
|
|
|
|
for (auto a : m_runningActions) {
|
|
|
|
|
t = dynamic_cast<T*>(a);
|
|
|
|
|
if (t)
|
|
|
|
|
return t;
|
|
|
|
|
}
|
2020-09-17 20:53:56 +02:00
|
|
|
t = new T(this);
|
2020-04-17 19:33:06 +02:00
|
|
|
m_runningActions.push_back(t);
|
|
|
|
|
}
|
|
|
|
|
t->start();
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-11 17:42:44 +02:00
|
|
|
/// called by an action when it finds itself out of work.
|
2020-04-17 19:33:06 +02:00
|
|
|
void done(Action *action);
|
|
|
|
|
|
2020-05-11 17:42:44 +02:00
|
|
|
/// Observer pattern subscribe method.
|
2020-04-17 19:33:06 +02:00
|
|
|
void addDataListener(DataListenerInterface *listener);
|
2020-05-11 17:42:44 +02:00
|
|
|
/// Observer pattern unsubscribe method.
|
2020-04-17 19:33:06 +02:00
|
|
|
void removeDataListener(DataListenerInterface *listener);
|
|
|
|
|
|
2020-05-11 17:42:44 +02:00
|
|
|
/// Observer pattern subscribe method.
|
2020-04-17 19:33:06 +02:00
|
|
|
void addP2PNetListener(P2PNetInterface *listener);
|
2020-05-11 17:42:44 +02:00
|
|
|
/// Observer pattern unsubscribe method.
|
2020-04-17 19:33:06 +02:00
|
|
|
void removeP2PNetListener(P2PNetInterface *listener);
|
2023-01-31 16:46:30 +01:00
|
|
|
/// return a copy of the listeners
|
|
|
|
|
std::vector<P2PNetInterface*> p2pNetListeners() const;
|
2020-04-17 19:33:06 +02:00
|
|
|
|
2023-01-31 16:46:30 +01:00
|
|
|
/// Observer pattern subscribe method.
|
|
|
|
|
void addHeaderListener(HeaderSyncInterface *listener);
|
|
|
|
|
/// Observer pattern unsubscribe method.
|
|
|
|
|
void removeHeaderListener(HeaderSyncInterface *listener);
|
2020-04-17 19:33:06 +02:00
|
|
|
|
2020-05-11 17:42:44 +02:00
|
|
|
/// Shut down this download manager, the connection manager and others.
|
|
|
|
|
/// It is required to call this prior to calling the destructor in order to
|
|
|
|
|
/// cleanly shut down the system and stop all tasks in all threads.
|
|
|
|
|
/// This method is blocking and wont return until finished.
|
2020-04-17 19:33:06 +02:00
|
|
|
void shutdown();
|
|
|
|
|
|
2022-11-11 19:25:19 +01:00
|
|
|
/**
|
|
|
|
|
* Flush all changes to disk.
|
|
|
|
|
*/
|
|
|
|
|
void saveData();
|
|
|
|
|
|
2020-10-29 22:18:10 +01:00
|
|
|
void getMoreHeaders();
|
|
|
|
|
|
2020-04-17 19:33:06 +02:00
|
|
|
private:
|
|
|
|
|
void finishShutdown();
|
|
|
|
|
|
|
|
|
|
boost::asio::io_context::strand m_strand;
|
2020-10-29 21:47:53 +01:00
|
|
|
const P2PNet::Chain m_chain;
|
2020-04-17 19:33:06 +02:00
|
|
|
ConnectionManager m_connectionManager;
|
|
|
|
|
Blockchain m_blockchain;
|
2021-02-05 17:22:52 +01:00
|
|
|
NotificationCenter m_notifications;
|
2020-04-17 19:33:06 +02:00
|
|
|
|
|
|
|
|
std::deque<Action*> m_runningActions;
|
|
|
|
|
mutable std::mutex m_lock;
|
|
|
|
|
std::atomic<bool> m_shuttingDown;
|
|
|
|
|
std::condition_variable m_waitVariable;
|
|
|
|
|
|
|
|
|
|
int m_peerDownloadingHeaders = -1;
|
|
|
|
|
|
|
|
|
|
std::vector<DataListenerInterface*> m_dataListeners;
|
2023-01-31 16:46:30 +01:00
|
|
|
std::vector<P2PNetInterface*> m_netListeners;
|
|
|
|
|
std::vector<HeaderSyncInterface*> m_headerSyncListeners;
|
2020-04-17 19:33:06 +02:00
|
|
|
|
|
|
|
|
void runQueue(); ///< find new items to download
|
|
|
|
|
|
|
|
|
|
struct DownloadTarget {
|
|
|
|
|
DownloadTarget(const InventoryItem &item) : inv(item) {}
|
|
|
|
|
InventoryItem inv;
|
|
|
|
|
std::vector<int> sourcePeers;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ActiveDownload {
|
|
|
|
|
uint32_t targetId = 0;
|
|
|
|
|
uint32_t downloadStartTime = 0;
|
|
|
|
|
int primary = -1, secondary = -1; // downloaders.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mutable std::mutex m_downloadsLock;
|
|
|
|
|
std::map<uint32_t, DownloadTarget> m_downloadQueue;
|
|
|
|
|
typedef boost::unordered_map<uint256, uint32_t, HashShortener> DownloadTargetIds;
|
|
|
|
|
DownloadTargetIds m_downloadTargetIds; // uint256 -> downloadQueue-Id
|
2020-11-13 20:11:06 +01:00
|
|
|
uint32_t m_nextDownloadTarget = 1;
|
2020-04-17 19:33:06 +02:00
|
|
|
std::array<ActiveDownload, 10> m_downloads;
|
2021-05-28 14:53:19 +02:00
|
|
|
bool m_isBehind; // we expect new headers
|
2020-04-17 19:33:06 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif
|