Browse Source

Miner class.

Version bump.
cl-refactor
Gav Wood 11 years ago
parent
commit
5034f35410
  1. 2
      libethcore/CommonEth.cpp
  2. 2
      libethential/Common.cpp
  3. 137
      libethereum/Client.cpp
  4. 47
      libethereum/Client.h
  5. 104
      libethereum/Miner.cpp
  6. 121
      libethereum/Miner.h

2
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<pair<u256, string>> g_units =

2
libethential/Common.cpp

@ -27,6 +27,6 @@ using namespace eth;
namespace eth
{
char const* EthVersion = "0.6.4";
char const* EthVersion = "0.6.5";
}

137
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

47
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<MineInfo> miningHistory() { Guard l(x_mineProgress); auto ret = m_mineHistory; m_mineHistory.clear(); return ret; }
std::list<MineInfo> 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<std::thread> m_work; ///< The work thread.
std::atomic<ClientWorkState> m_workState;
std::unique_ptr<std::thread> m_workMine;///< The work thread.
std::atomic<ClientWorkState> 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<MineInfo> m_mineHistory;
mutable unsigned m_pendingCount = 0;
mutable std::mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters;

104
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 <http://www.gnu.org/licenses/>.
*/
/** @file Miner.cpp
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @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));
}
}

121
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 <http://www.gnu.org/licenses/>.
*/
/** @file Miner.h
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <thread>
#include <list>
#include <atomic>
#include <libethential/Common.h>
#include <libethcore/CommonEth.h>
#include <libethcore/Dagger.h>
#include <libethcore/OverlayDB.h>
#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<MineInfo> 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<std::thread> 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<MineInfo> m_mineHistory; ///< What the history of our mining?
};
}
Loading…
Cancel
Save