/* 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 Miner.h * @author Alex Leverington * @author Gav Wood * @date 2014 */ #pragma once #include #include #include #include #include #include #include "State.h" namespace dev { namespace eth { struct WorkPackage { h256 boundary; h256 headerHash; ///< When h256() means "pause until notified a new work package is available". h256 seedHash; }; static const WorkPackage NullWorkPackage; /** * @brief Describes the progress of a mining operation. */ struct MineProgress { void combine(MineProgress const& _m) { requirement = std::max(requirement, _m.requirement); best = std::min(best, _m.best); current = std::max(current, _m.current); hashes += _m.hashes; ms = std::max(ms, _m.ms); } double requirement = 0; ///< The PoW requirement - as the second logarithm of the minimum acceptable hash. double best = 1e99; ///< The PoW achievement - as the second logarithm of the minimum found hash. double current = 0; ///< The most recent PoW achievement - as the second logarithm of the presently found hash. unsigned hashes = 0; ///< Total number of hashes computed. unsigned ms = 0; ///< Total number of milliseconds of mining thus far. }; /** * @brief Class for hosting one or more Miners. * @warning Must be implemented in a threadsafe manner since it will be called from multiple * miner threads. */ class MinerHost { public: // ============================= NEW API ============================= virtual WorkPackage const& getWork() const { return NullWorkPackage; } // ============================= OLD API ============================= virtual void setupState(State& _s) = 0; ///< Reset the given State object to the one that should be being mined. virtual void onProgressed() {} ///< Called once some progress has been made. virtual void onComplete() {} ///< Called once a block is found. virtual bool force() const = 0; ///< @returns true iff the Miner should mine regardless of the number of transactions. virtual bool turbo() const = 0; ///< @returns true iff the Miner should use GPU if possible. }; class Miner { public: virtual ~Miner(); virtual void noteStateChange() = 0; virtual bool isComplete() const = 0; virtual bytes const& blockData() const = 0; }; class AsyncMiner: public Miner { public: /// Null constructor. AsyncMiner(): m_host(nullptr) {} /// Constructor. AsyncMiner(MinerHost* _host, unsigned _id = 0): m_host(_host), m_id(_id) {} /// Setup its basics. void setup(MinerHost* _host, unsigned _id = 0) { m_host = _host; m_id = _id; } /// Start mining. virtual void start() {} /// Stop mining. virtual void stop() {} /// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force(). virtual bool isRunning() const { return false; } protected: MinerHost* m_host = nullptr; ///< Our host. unsigned m_id = 0; ///< Our unique id. }; /** * @brief Implements Miner. * To begin mining, use start() & stop(). noteStateChange() can be used to reset the mining and set up the * State object according to the host. Use isRunning() to determine if the miner has been start()ed. * Use isComplete() to determine if the miner has finished mining. * * blockData() can be used to retrieve the complete block, ready for insertion into the BlockChain. * * Information on the mining can be queried through miningProgress() and miningHistory(). * @threadsafe * @todo Signal Miner to restart once with condition variables. */ class LocalMiner: public AsyncMiner, Worker { public: /// Null constructor. LocalMiner() {} /// Constructor. LocalMiner(MinerHost* _host, unsigned _id = 0); /// Move-constructor. LocalMiner(LocalMiner&& _m): Worker((Worker&&)_m) { std::swap(m_host, _m.m_host); std::swap(m_pow, _m.m_pow); } /// Move-assignment. LocalMiner& operator=(LocalMiner&& _m) { Worker::operator=((Worker&&)_m); std::swap(m_host, _m.m_host); std::swap(m_pow, _m.m_pow); return *this; } /// Destructor. Stops miner. ~LocalMiner() { stop(); } /// Setup its basics. void setup(MinerHost* _host, unsigned _id = 0); /// Start mining. void start() { startWorking(); } /// Stop mining. void stop() { stopWorking(); } /// Call to notify Miner of a state change. virtual void noteStateChange() override { m_miningStatus = Preparing; } /// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force(). bool isRunning() const override { return isWorking(); } /// @returns true if mining is complete. virtual bool isComplete() const override { return m_miningStatus == Mined; } /// @returns the internal State object. virtual bytes const& blockData() const override { return m_mineState.blockData(); } /// Check the progress of the mining. MineProgress miningProgress() const { Guard l(x_mineInfo); return m_mineProgress; } /// Get and clear the mining history. std::list miningHistory() { Guard l(x_mineInfo); auto ret = m_mineHistory; m_mineHistory.clear(); return ret; } /// @returns the state on which we mined. State const& state() const { return m_mineState; } private: /// Do some work on the mining. virtual void doWork(); enum MiningStatus { Waiting, Preparing, Mining, Mined, Stopping, Stopped }; MiningStatus m_miningStatus = Waiting; ///< TODO: consider mutex/atomic variable. State m_mineState; ///< The state on which we are mining, generally equivalent to m_postMine. std::unique_ptr m_pow; ///< Our miner. mutable Mutex x_mineInfo; ///< Lock for the mining progress & history. MineProgress m_mineProgress; ///< What's our progress? std::list m_mineHistory; ///< What the history of our mining? }; /** * @brief A collective of Miners. * Miners ask for work, then submit proofs * @threadsafe */ class Farm: public MinerHost { public: /** * @brief Sets the current mining mission. * @param _bi The block (header) we wish to be mining. */ void setWork(BlockInfo const& _bi); /** * @brief (Re)start miners for CPU only. * @returns true if started properly. */ bool startCPU(); /** * @brief (Re)start miners for GPU only. * @returns true if started properly. */ bool startGPU(); /** * @brief Stop all mining activities. */ void stop(); /** * @brief Get information on the progress of mining this work package. * @return The progress with mining so far. */ MineProgress const& mineProgress() const { ReadGuard l(x_progress); return m_progress; } protected: /** * @brief Called by a Miner to retrieve a work package. Reimplemented from MinerHost. * @return The work package to solve. */ virtual WorkPackage const& getWork() const override { ReadGuard l(x_work); return m_work; } /** * @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 . */ virtual bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp) = 0; private: mutable SharedMutex x_miners; std::vector> m_miners; mutable SharedMutex x_progress; MineProgress m_progress; mutable SharedMutex x_work; WorkPackage m_work; }; } }