diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index db1c9c636..8c0e5518e 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -29,7 +29,7 @@ using namespace eth; //#define ETH_ADDRESS_DEBUG 1 -const unsigned eth::c_protocolVersion = 28; +const unsigned eth::c_protocolVersion = 29; const unsigned eth::c_databaseVersion = 1; static const vector> g_units = diff --git a/libethential/Common.cpp b/libethential/Common.cpp index 13cea2175..524a8f62a 100644 --- a/libethential/Common.cpp +++ b/libethential/Common.cpp @@ -27,6 +27,6 @@ using namespace eth; namespace eth { -char const* EthVersion = "0.6.4"; +char const* EthVersion = "0.6.5"; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index ceb742bf7..9c4576919 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -69,7 +69,7 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)), m_preMine(_us, m_stateDB), m_postMine(_us, m_stateDB), - m_workState(Deleted) + m_miner(this) { if (_dbPath.size()) Defaults::setDBPath(_dbPath); @@ -127,7 +127,7 @@ void Client::clearPending() appendFromNewPending(m_postMine.bloom(i), changeds); changeds.insert(PendingChangedFilter); m_postMine = m_preMine; - m_miningStatus = Preparing; + m_miner.restart(); noteChanged(changeds); } @@ -301,35 +301,35 @@ void Client::connect(std::string const& _seedHost, unsigned short _port) m_net->connect(_seedHost, _port); } -void Client::startMining() +void Client::onComplete(State& _s) { - static const char* c_threadName = "miner"; - - if (!m_workMine) - m_workMine.reset(new thread([&]() - { - setThreadName(c_threadName); - m_workMineState.store(Active, std::memory_order_release); - m_miningStatus = Preparing; - while (m_workMineState.load(std::memory_order_acquire) != Deleting) - workMine(); - m_workMineState.store(Deleted, std::memory_order_release); - })); - - ensureWorking(); + // Must lock stateDB here since we're actually pushing out to the database. + WriteGuard l(x_stateDB); + cwork << "COMPLETE MINE"; + _s.completeMine(); } -void Client::stopMining() +void Client::setupState(State& _s) { - if (m_workMine) { - if (m_workMineState.load(std::memory_order_acquire) == Active) - m_workMineState.store(Deleting, std::memory_order_release); - while (m_workMineState.load(std::memory_order_acquire) != Deleted) - this_thread::sleep_for(chrono::milliseconds(10)); - m_workMine->join(); + ReadGuard l(x_stateDB); + cwork << "SETUP MINE"; + _s = m_postMine; } - m_workMine.reset(nullptr); + if (m_paranoia) + { + if (_s.amIJustParanoid(m_bc)) + { + cnote << "I'm just paranoid. Block is fine."; + _s.commitToMine(m_bc); + } + else + { + cwarn << "I'm not just paranoid. Cannot mine. Please file a bug report."; + } + } + else + _s.commitToMine(m_bc); } void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) @@ -423,86 +423,19 @@ void Client::workNet() this_thread::sleep_for(chrono::milliseconds(1)); } -void Client::workMine() -{ - // Do some mining. - if ((m_pendingCount || m_forceMining) && m_miningStatus != Mined) - { - // TODO: Separate "Miner" object. - if (m_miningStatus == Preparing) - { - m_miningStatus = Mining; - - { - ReadGuard l(x_stateDB); - m_mineState = m_postMine; - } - - { - Guard l(x_mineProgress); - m_mineProgress.best = (double)-1; - m_mineProgress.hashes = 0; - m_mineProgress.ms = 0; - } - - if (m_paranoia) - { - if (m_mineState.amIJustParanoid(m_bc)) - { - cnote << "I'm just paranoid. Block is fine."; - m_mineState.commitToMine(m_bc); - } - else - { - cwarn << "I'm not just paranoid. Cannot mine. Please file a bug report."; - } - } - else - m_mineState.commitToMine(m_bc); - } - - cwork << "MINE"; - - // Mine for a while. - MineInfo mineInfo = m_mineState.mine(100, m_turboMining); - - { - Guard l(x_mineProgress); - m_mineProgress.best = min(m_mineProgress.best, mineInfo.best); - m_mineProgress.current = mineInfo.best; - m_mineProgress.requirement = mineInfo.requirement; - m_mineProgress.ms += 100; - m_mineProgress.hashes += mineInfo.hashes; - m_mineHistory.push_back(mineInfo); - } - if (mineInfo.completed) - { - // Import block. - { - // Must lock stateDB here since we're actually pushing out to the database. - WriteGuard l(x_stateDB); - cwork << "COMPLETE MINE"; - m_mineState.completeMine(); - } - m_miningStatus = Mined; - } - } - else - { - this_thread::sleep_for(chrono::milliseconds(100)); - } - -} - void Client::work() { cworkin << "WORK"; h256Set changeds; - if (m_miningStatus == Mined) + if (m_miner.isComplete()) { cwork << "CHAIN <== postSTATE"; - h256s hs = m_bc.attemptImport(m_mineState.blockData(), m_stateDB); + h256s hs; + { + ReadGuard l(x_stateDB); + hs = m_bc.attemptImport(m_miner.state().blockData(), m_stateDB); + } if (hs.size()) { for (auto h: hs) @@ -510,7 +443,7 @@ void Client::work() changeds.insert(ChainChangedFilter); //changeds.insert(PendingChangedFilter); // if we mined the new block, then we've probably reset the pending transactions. } - m_miningStatus = Preparing; + m_miner.restart(); } // Synchronise state to block chain. @@ -522,7 +455,6 @@ void Client::work() // Resynchronise state with block chain & trans { WriteGuard l(x_stateDB); - cwork << "BQ ==> CHAIN ==> STATE"; OverlayDB db = m_stateDB; x_stateDB.unlock(); @@ -560,15 +492,16 @@ void Client::work() cnote << "Additional transaction ready: Restarting mining operation."; rsm = true; } - m_pendingCount = m_postMine.pending().size(); if (rsm) - m_miningStatus = Preparing; + m_miner.restart(); } cwork << "noteChanged" << changeds.size() << "items"; noteChanged(changeds); cworkout << "WORK"; + + this_thread::sleep_for(chrono::milliseconds(100)); } unsigned Client::numberOf(int _n) const diff --git a/libethereum/Client.h b/libethereum/Client.h index ac0d07199..66601c656 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -35,19 +35,11 @@ #include "TransactionQueue.h" #include "State.h" #include "PeerNetwork.h" +#include "Miner.h" namespace eth { -struct MineProgress -{ - double requirement; - double best; - double current; - uint hashes; - uint ms; -}; - class Client; enum ClientWorkState @@ -164,8 +156,10 @@ struct WorkChannel: public LogChannel { static const char* name() { return "-W-" /** * @brief Main API hub for interfacing with Ethereum. */ -class Client +class Client: public MinerHost { + friend class Miner; + public: /// Constructor. explicit Client(std::string const& _clientVersion, Address _us = Address(), std::string const& _dbPath = std::string(), bool _forceClean = false); @@ -278,7 +272,7 @@ public: // Mining stuff: /// Check block validity prior to mining. - bool paranoia() const { return m_paranoia; } + bool miningParanoia() const { return m_paranoia; } /// Change whether we check block validity prior to mining. void setParanoia(bool _p) { m_paranoia = _p; } /// Set the coinbase address. @@ -287,18 +281,16 @@ public: Address address() const { return m_preMine.address(); } /// Start mining. /// NOT thread-safe - void startMining(); + void startMining() { m_miner.start(); ensureWorking(); } /// Stop mining. /// NOT thread-safe - void stopMining(); + void stopMining() { m_miner.stop(); } /// Are we mining now? - bool isMining() { return !!m_workMine; } - /// Register a callback for information concerning mining. - /// This callback will be in an arbitrary thread, blocking progress. JUST COPY THE DATA AND GET OUT. + bool isMining() { return m_miner.isRunning(); } /// Check the progress of the mining. - MineProgress miningProgress() const { Guard l(x_mineProgress); return m_mineProgress; } + MineProgress miningProgress() const { return m_miner.miningProgress(); } /// Get and clear the mining history. - std::list miningHistory() { Guard l(x_mineProgress); auto ret = m_mineHistory; m_mineHistory.clear(); return ret; } + std::list miningHistory() { return m_miner.miningHistory(); } bool forceMining() const { return m_forceMining; } void setForceMining(bool _enable) { m_forceMining = _enable; } @@ -320,8 +312,11 @@ private: /// Do some work on the network. void workNet(); - /// Do some work on the mining. - void workMine(); + /// Overrides for being a mining host. + virtual void onComplete(State& _s); + virtual void setupState(State& _s); + virtual bool turbo() const { return m_turboMining; } + virtual bool force() const { return m_forceMining; } /// Collate the changed filters for the bloom filter of the given pending transaction. /// Insert any filters that are activated into @a o_changed. @@ -360,18 +355,10 @@ private: std::unique_ptr m_work; ///< The work thread. std::atomic m_workState; - std::unique_ptr m_workMine;///< The work thread. - std::atomic m_workMineState; - enum MiningStatus { Preparing, Mining, Mined }; - MiningStatus m_miningStatus = Preparing;///< TODO: consider mutex/atomic variable. - State m_mineState; ///< The state on which we are mining, generally equivalent to m_postMine. - bool m_paranoia = false; + Miner m_miner; + bool m_paranoia = false; ///< Should we be paranoid about our state? bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping. bool m_forceMining = false; ///< Mine even when there are no transactions pending? - mutable std::mutex x_mineProgress; ///< Lock for the mining progress & history. - MineProgress m_mineProgress; - std::list m_mineHistory; - mutable unsigned m_pendingCount = 0; mutable std::mutex m_filterLock; std::map m_filters; diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp new file mode 100644 index 000000000..ef6c71a20 --- /dev/null +++ b/libethereum/Miner.cpp @@ -0,0 +1,104 @@ +/* + 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.cpp + * @author Alex Leverington + * @author Gav Wood + * @date 2014 + */ + +#include "Miner.h" +#include "State.h" +using namespace std; +using namespace eth; + +Miner::Miner(MinerHost* _host, unsigned _id): + m_host(_host), + m_id(_id) +{ +} + +void Miner::start() +{ + Guard l(x_work); + if (!m_work) + { + m_stop = false; + m_work.reset(new thread([&]() + { + setThreadName(("miner-" + toString(m_id)).c_str()); + m_miningStatus = Preparing; + while (!m_stop) + work(); + })); + } +} + +void Miner::stop() +{ + Guard l(x_work); + if (m_work) + { + m_stop = true; + m_work->join(); + m_work.reset(nullptr); + } +} + +void Miner::work() +{ + // Do some mining. + if ((m_pendingCount || m_host->force()) && m_miningStatus != Mined) + { + // TODO: Separate "Miner" object. + if (m_miningStatus == Preparing) + { + m_miningStatus = Mining; + + m_host->setupState(m_mineState); + m_pendingCount = m_mineState.pending().size(); + + { + Guard l(x_mineInfo); + m_mineProgress.best = (double)-1; + m_mineProgress.hashes = 0; + m_mineProgress.ms = 0; + } + } + + // Mine for a while. + MineInfo mineInfo = m_mineState.mine(100, m_host->turbo()); + + { + Guard l(x_mineInfo); + m_mineProgress.best = min(m_mineProgress.best, mineInfo.best); + m_mineProgress.current = mineInfo.best; + m_mineProgress.requirement = mineInfo.requirement; + m_mineProgress.ms += 100; + m_mineProgress.hashes += mineInfo.hashes; + m_mineHistory.push_back(mineInfo); + } + if (mineInfo.completed) + { + m_host->onComplete(m_mineState); + m_miningStatus = Mined; + } + } + else + { + this_thread::sleep_for(chrono::milliseconds(100)); + } +} diff --git a/libethereum/Miner.h b/libethereum/Miner.h new file mode 100644 index 000000000..4ea55869b --- /dev/null +++ b/libethereum/Miner.h @@ -0,0 +1,121 @@ +/* + 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 +#include "State.h" + +namespace eth +{ + +class BlockChain; +class Client; + +struct MineProgress +{ + double requirement; + double best; + double current; + uint hashes; + uint ms; +}; + +class MinerHost +{ +public: + virtual void setupState(State& _s) = 0; ///< Reset the given State object to the one that should be being mined. + virtual void onComplete(State& _s) = 0; ///< Completed the mine! + virtual bool turbo() const = 0; + virtual bool force() const = 0; +}; + +/** + * @brief Implements Miner. + * The miner will start a thread when there is work provided by @fn restart(). + * The _progressCb callback is called every ~100ms or when a block is found. + * @fn completeMine() is to be called once a block is found. + * If miner is not restarted from _progressCb the thread will terminate. + * @threadsafe + * @todo signal from child->parent thread to wait on exit; refactor redundant dagger/miner stats + */ +class Miner +{ +public: + /// Constructor. Starts miner. + Miner(MinerHost* _host, unsigned _id = 0); + + /// Destructor. Stops miner. + ~Miner() { stop(); } + + /// Start mining. + void start(); + + /// Stop mining. + void stop(); + + /// Restart mining. + void restart() { m_miningStatus = Preparing; } + + /// @returns if mining + bool isRunning() { return !!m_work; } + + /// @returns true if mining is complete. + bool isComplete() const { return m_miningStatus == Mined; } + + /// @returns the internal State object. + State& state() { return m_mineState; } + + /// 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; } + +private: + /// Do some work on the mining. + void work(); + + MinerHost* m_host; ///< Our host. + unsigned m_id; ///< Our identity; + + std::mutex x_work; ///< Mutex protecting the creation of the work thread. + std::unique_ptr m_work; ///< The work thread. + bool m_stop = false; ///< Stop working? + + enum MiningStatus { Preparing, Mining, Mined }; + MiningStatus m_miningStatus = Preparing;///< TODO: consider mutex/atomic variable. + State m_mineState; ///< The state on which we are mining, generally equivalent to m_postMine. + mutable unsigned m_pendingCount = 0; ///< How many pending transactions are there in m_mineState? + + mutable std::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? +}; + +}