/* This file is part of cpp-ethereum. cpp-ethereum 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. cpp-ethereum 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 cpp-ethereum. If not, see . */ /** @file Farm.h * @author Gav Wood * @date 2015 */ #pragma once #include #include #include #include #include #include #include #include namespace dev { namespace eth { /** * @brief A collective of Miners. * Miners ask for work, then submit proofs * @threadsafe */ template class GenericFarm: public GenericFarmFace { public: using WorkPackage = typename PoW::WorkPackage; using Solution = typename PoW::Solution; using Miner = GenericMiner; struct SealerDescriptor { std::function instances; std::function create; }; ~GenericFarm() { stop(); } /** * @brief Sets the current mining mission. * @param _wp The work package we wish to be mining. */ void setWork(WorkPackage const& _wp) { WriteGuard l(x_minerWork); cdebug << "Farm::setWork()"; if (_wp.headerHash == m_work.headerHash) return; m_work = _wp; for (auto const& m: m_miners) m->setWork(m_work); resetTimer(); } void setSealers(std::map const& _sealers) { m_sealers = _sealers; } /** * @brief Start a number of miners. */ bool start(std::string const& _sealer) { WriteGuard l(x_minerWork); cdebug << "start()"; if (!m_miners.empty() && m_lastSealer == _sealer) return true; if (!m_sealers.count(_sealer)) return false; m_miners.clear(); auto ins = m_sealers[_sealer].instances(); m_miners.reserve(ins); for (unsigned i = 0; i < ins; ++i) { m_miners.push_back(std::shared_ptr(m_sealers[_sealer].create(std::make_pair(this, i)))); m_miners.back()->setWork(m_work); } m_isMining = true; m_lastSealer = _sealer; resetTimer(); return true; } /** * @brief Stop all mining activities. */ void stop() { WriteGuard l(x_minerWork); m_miners.clear(); m_work.reset(); m_isMining = false; } bool isMining() const { return m_isMining; } /** * @brief Get information on the progress of mining this work package. * @return The progress with mining so far. */ WorkingProgress const& miningProgress() const { WorkingProgress p; p.ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - m_lastStart).count(); { ReadGuard l2(x_minerWork); for (auto const& i: m_miners) p.hashes += i->hashCount(); } ReadGuard l(x_progress); m_progress = p; return m_progress; } /** * @brief Reset the mining progess counter. */ void resetMiningProgress() { DEV_READ_GUARDED(x_minerWork) for (auto const& i: m_miners) i->resetHashCount(); resetTimer(); } using SolutionFound = std::function; /** * @brief Provides a valid header based upon that received previously with setWork(). * @param _bi The now-valid header. * @return true if the header was good and that the Farm should pause until more work is submitted. */ void onSolutionFound(SolutionFound const& _handler) { m_onSolutionFound = _handler; } WorkPackage work() const { ReadGuard l(x_minerWork); return m_work; } private: /** * @brief Called from a Miner to note a WorkPackage has a solution. * @param _p The solution. * @param _wp The WorkPackage that the Solution is for. * @return true iff the solution was good (implying that mining should be . */ bool submitProof(Solution const& _s, Miner* _m) override { if (m_onSolutionFound && m_onSolutionFound(_s)) { if (x_minerWork.try_lock()) { for (std::shared_ptr const& m: m_miners) if (m.get() != _m) m->setWork(); m_work.reset(); x_minerWork.unlock(); return true; } } return false; } void resetTimer() { m_lastStart = std::chrono::steady_clock::now(); } mutable SharedMutex x_minerWork; std::vector> m_miners; WorkPackage m_work; std::atomic m_isMining = {false}; mutable SharedMutex x_progress; mutable WorkingProgress m_progress; std::chrono::steady_clock::time_point m_lastStart; SolutionFound m_onSolutionFound; std::map m_sealers; std::string m_lastSealer; }; } }