diff --git a/alethzero/Main.ui b/alethzero/Main.ui
index 8e48793c9..1fd9669e9 100644
--- a/alethzero/Main.ui
+++ b/alethzero/Main.ui
@@ -176,6 +176,7 @@
+
@@ -1685,6 +1686,11 @@ font-size: 14pt
Retry Unknown Parent Blocks
+
+
+ In&ject Block
+
+
diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp
index 270996a30..64fca6a05 100644
--- a/alethzero/MainWin.cpp
+++ b/alethzero/MainWin.cpp
@@ -164,7 +164,7 @@ Main::Main(QWidget *parent) :
statusBar()->addPermanentWidget(ui->chainStatus);
statusBar()->addPermanentWidget(ui->blockCount);
- ui->blockCount->setText(QString("PV%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(QString::fromStdString(ProofOfWork::name())).arg(ProofOfWork::revision()).arg(dev::Version));
+ ui->blockCount->setText(QString("PV%1.%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(eth::c_minorProtocolVersion).arg(c_databaseVersion).arg(QString::fromStdString(ProofOfWork::name())).arg(ProofOfWork::revision()).arg(dev::Version));
connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved()));
@@ -1448,6 +1448,25 @@ void Main::on_inject_triggered()
}
}
+void Main::on_injectBlock_triggered()
+{
+ QString s = QInputDialog::getText(this, "Inject Block", "Enter block dump in hex");
+ try
+ {
+ bytes b = fromHex(s.toStdString(), WhenError::Throw);
+ ethereum()->injectBlock(b);
+ }
+ catch (BadHexCharacter& _e)
+ {
+ cwarn << "invalid hex character, transaction rejected";
+ cwarn << boost::diagnostic_information(_e);
+ }
+ catch (...)
+ {
+ cwarn << "block rejected";
+ }
+}
+
void Main::on_blocks_currentItemChanged()
{
ui->info->clear();
diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h
index a8579ed01..1a53ec62b 100644
--- a/alethzero/MainWin.h
+++ b/alethzero/MainWin.h
@@ -159,6 +159,7 @@ private slots:
void on_killBlockchain_triggered();
void on_clearPending_triggered();
void on_inject_triggered();
+ void on_injectBlock_triggered();
void on_forceMining_triggered();
void on_usePrivate_triggered();
void on_turboMining_triggered();
diff --git a/cmake/scripts/jsonrpcstub.cmake b/cmake/scripts/jsonrpcstub.cmake
index 39f850e53..64ca7864c 100644
--- a/cmake/scripts/jsonrpcstub.cmake
+++ b/cmake/scripts/jsonrpcstub.cmake
@@ -42,4 +42,3 @@ else()
replace_if_different("${SERVER_TMPFILE}" "${SERVER_OUTFILE}")
replace_if_different("${CLIENT_TMPFILE}" "${CLIENT_OUTFILE}")
endif()
-
diff --git a/eth/Farm.h b/eth/Farm.h
index c061449e3..ae2ef5a7d 100644
--- a/eth/Farm.h
+++ b/eth/Farm.h
@@ -22,11 +22,12 @@ class Farm : public jsonrpc::Client
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
- bool eth_submitWork(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
+ bool eth_submitWork(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
+ p.append(param3);
Json::Value result = this->CallMethod("eth_submitWork",p);
if (result.isBool())
return result.asBool();
diff --git a/eth/farm.json b/eth/farm.json
index 24f0c163e..1f4142d00 100644
--- a/eth/farm.json
+++ b/eth/farm.json
@@ -1,4 +1,4 @@
[
{ "name": "eth_getWork", "params": [], "order": [], "returns": []},
- { "name": "eth_submitWork", "params": ["", ""], "order": [], "returns": true}
+ { "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true}
]
diff --git a/eth/main.cpp b/eth/main.cpp
index 5de9290ae..0344e66d6 100644
--- a/eth/main.cpp
+++ b/eth/main.cpp
@@ -121,7 +121,7 @@ void help()
<< " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl
#endif
<< " -K,--kill First kill the blockchain." << endl
- << " -R,--rebuild First rebuild the blockchain from the existing database." << endl
+ << " -R,--rebuild Rebuild the blockchain from the existing database." << endl
<< " -s,--secret Set the secret key for use with send command (default: auto)." << endl
<< " -S,--session-secret Set the secret key for use with send command, for this session only." << endl
<< "Client transacting:" << endl
@@ -134,6 +134,7 @@ void help()
<< " -f,--force-mining Mine even when there are no transactions to mine (default: off)" << endl
<< " -C,--cpu When mining, use the CPU." << endl
<< " -G,--opencl When mining use the GPU via OpenCL." << endl
+ << " --opencl-platform When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl
<< " --opencl-device When mining using -G/--opencl use OpenCL device n (default: 0)." << endl
<< "Client networking:" << endl
<< " --client-name Add a name to your client's version string (default: blank)." << endl
@@ -338,6 +339,9 @@ void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, u
exit(0);
}
+struct HappyChannel: public LogChannel { static const char* name() { return ":-D"; } static const int verbosity = 1; };
+struct SadChannel: public LogChannel { static const char* name() { return ":-("; } static const int verbosity = 1; };
+
void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
{
(void)_m;
@@ -346,9 +350,7 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
#if ETH_JSONRPC || !ETH_TRUE
jsonrpc::HttpClient client(_remote);
Farm rpc(client);
-
GenericFarm f;
-
if (_m == MinerType::CPU)
f.startCPU();
else if (_m == MinerType::GPU)
@@ -356,29 +358,47 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
ProofOfWork::WorkPackage current;
while (true)
- {
- bool completed = false;
- ProofOfWork::Solution solution;
- f.onSolutionFound([&](ProofOfWork::Solution sol)
- {
- solution = sol;
- return completed = true;
- });
- for (unsigned i = 0; !completed; ++i)
+ try
{
- Json::Value v = rpc.eth_getWork();
- h256 hh(v[0].asString());
- if (hh != current.headerHash)
+ bool completed = false;
+ ProofOfWork::Solution solution;
+ f.onSolutionFound([&](ProofOfWork::Solution sol)
+ {
+ solution = sol;
+ return completed = true;
+ });
+ for (unsigned i = 0; !completed; ++i)
{
- current.headerHash = hh;
- current.seedHash = h256(v[1].asString());
- current.boundary = h256(v[2].asString());
- f.setWork(current);
+ if (current)
+ cnote << "Mining on PoWhash" << current.headerHash.abridged() << ": " << f.miningProgress();
+ else
+ cnote << "Getting work package...";
+ Json::Value v = rpc.eth_getWork();
+ h256 hh(v[0].asString());
+ if (hh != current.headerHash)
+ {
+ current.headerHash = hh;
+ current.seedHash = h256(v[1].asString());
+ current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight);
+ cnote << "Got work package:" << current.headerHash.abridged() << " < " << current.boundary;
+ f.setWork(current);
+ }
+ this_thread::sleep_for(chrono::milliseconds(_recheckPeriod));
}
- this_thread::sleep_for(chrono::milliseconds(_recheckPeriod));
+ cnote << "Solution found; submitting [" << solution.nonce << "," << current.headerHash.abridged() << "," << solution.mixHash.abridged() << "] to" << _remote << "...";
+ bool ok = rpc.eth_submitWork("0x" + toString(solution.nonce), "0x" + toString(current.headerHash), "0x" + toString(solution.mixHash));
+ if (ok)
+ clog(HappyChannel) << "Submitted and accepted.";
+ else
+ clog(SadChannel) << "Not accepted.";
+ current.reset();
+ }
+ catch (jsonrpc::JsonRpcException&)
+ {
+ for (auto i = 3; --i; this_thread::sleep_for(chrono::seconds(1)))
+ cerr << "JSON-RPC problem. Probably couldn't connect. Retrying in " << i << "... \r";
+ cerr << endl;
}
- rpc.eth_submitWork("0x" + toString(solution.nonce), "0x" + toString(solution.mixHash));
- }
#endif
exit(0);
}
@@ -394,6 +414,7 @@ int main(int argc, char** argv)
/// Mining options
MinerType minerType = MinerType::CPU;
+ unsigned openclPlatform = 0;
unsigned openclDevice = 0;
/// File name for import/export.
@@ -511,6 +532,15 @@ int main(int argc, char** argv)
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
+ else if (arg == "--opencl-platform" && i + 1 < argc)
+ try {
+ openclPlatform= stol(argv[++i]);
+ }
+ catch (...)
+ {
+ cerr << "Bad " << arg << " option: " << argv[i] << endl;
+ return -1;
+ }
else if (arg == "--opencl-device" && i + 1 < argc)
try {
openclDevice = stol(argv[++i]);
@@ -598,7 +628,7 @@ int main(int argc, char** argv)
}
else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill")
killChain = WithExisting::Kill;
- else if (arg == "-B" || arg == "--rebuild")
+ else if (arg == "-R" || arg == "--rebuild")
killChain = WithExisting::Verify;
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
{
@@ -809,6 +839,7 @@ int main(int argc, char** argv)
if (sessionSecret)
sigKey = KeyPair(sessionSecret);
+ ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform);
ProofOfWork::GPUMiner::setDefaultDevice(openclDevice);
// Two codepaths is necessary since named block require database, but numbered
@@ -926,6 +957,7 @@ int main(int argc, char** argv)
{
c->setGasPricer(gasPricer);
c->setForceMining(forceMining);
+ c->setTurboMining(minerType == MinerType::GPU);
c->setAddress(coinbase);
}
diff --git a/exp/CMakeLists.txt b/exp/CMakeLists.txt
index d2a0e9ead..bfcd0e658 100644
--- a/exp/CMakeLists.txt
+++ b/exp/CMakeLists.txt
@@ -3,6 +3,7 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
+include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${LEVELDB_INCLUDE_DIRS})
@@ -24,9 +25,9 @@ target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} ethereum)
target_link_libraries(${EXECUTABLE} p2p)
if (ETHASHCL)
-target_link_libraries(${EXECUTABLE} ethash-cl)
-target_link_libraries(${EXECUTABLE} ethash)
-target_link_libraries(${EXECUTABLE} OpenCL)
+ target_link_libraries(${EXECUTABLE} ethash-cl)
+ target_link_libraries(${EXECUTABLE} ethash)
+ target_link_libraries(${EXECUTABLE} OpenCL)
endif()
install( TARGETS ${EXECUTABLE} DESTINATION bin)
diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp
index 7cdc433f3..1f0d188fa 100644
--- a/libdevcore/Common.cpp
+++ b/libdevcore/Common.cpp
@@ -27,7 +27,7 @@ using namespace dev;
namespace dev
{
-char const* Version = "0.9.9";
+char const* Version = "0.9.12";
}
diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h
index 3889f6171..478383b25 100644
--- a/libdevcore/CommonIO.h
+++ b/libdevcore/CommonIO.h
@@ -45,7 +45,8 @@ namespace dev
/// Retrieve and returns the contents of the given file. If the file doesn't exist or isn't readable, returns an empty bytes.
bytes contents(std::string const& _file);
std::string contentsString(std::string const& _file);
-/// Retrieve and returns the allocated contents of the given file. If the file doesn't exist or isn't readable, returns nullptr. Don't forget to delete [] when finished.
+/// Retrieve and returns the allocated contents of the given file; if @_dest is given, don't allocate, use it directly.
+/// If the file doesn't exist or isn't readable, returns bytesRef(). Don't forget to delete [] the returned value's data when finished.
bytesRef contentsNew(std::string const& _file, bytesRef _dest = bytesRef());
/// Write the given binary data into the given file, replacing the file if it pre-exists.
@@ -76,7 +77,7 @@ template inline std::ostream& operator<<(std::ostream& _out,
template inline std::ostream& operator<<(std::ostream& _out, std::multimap const& _e);
template _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p);
-template inline std::string toString(std::chrono::time_point const& _e, std::string _format = "")
+template inline std::string toString(std::chrono::time_point const& _e, std::string _format = "%F %T")
{
unsigned long milliSecondsSinceEpoch = std::chrono::duration_cast(_e.time_since_epoch()).count();
auto const durationSinceEpoch = std::chrono::milliseconds(milliSecondsSinceEpoch);
diff --git a/libdevcore/Log.cpp b/libdevcore/Log.cpp
index 7196ea358..0edc3e039 100644
--- a/libdevcore/Log.cpp
+++ b/libdevcore/Log.cpp
@@ -23,6 +23,7 @@
#include
#include
+#include
#include "Guards.h"
using namespace std;
using namespace dev;
@@ -31,13 +32,87 @@ using namespace dev;
int dev::g_logVerbosity = 5;
map dev::g_logOverride;
-ThreadLocalLogName dev::t_logThreadName("main");
+/// Associate a name with each thread for nice logging.
+struct ThreadLocalLogName
+{
+ ThreadLocalLogName(std::string const& _name) { m_name.reset(new string(_name)); }
+ boost::thread_specific_ptr m_name;
+};
+
+/// Associate a name with each thread for nice logging.
+struct ThreadLocalLogContext
+{
+ ThreadLocalLogContext() = default;
+
+ void push(std::string const& _name)
+ {
+ if (!m_contexts.get())
+ m_contexts.reset(new vector);
+ m_contexts->push_back(_name);
+ }
+
+ void pop()
+ {
+ m_contexts->pop_back();
+ }
+
+ string join(string const& _prior)
+ {
+ string ret;
+ if (m_contexts.get())
+ for (auto const& i: *m_contexts)
+ ret += _prior + i;
+ return ret;
+ }
+
+ boost::thread_specific_ptr> m_contexts;
+};
+
+ThreadLocalLogContext g_logThreadContext;
+
+ThreadLocalLogName g_logThreadName("main");
+
+void dev::ThreadContext::push(string const& _n)
+{
+ g_logThreadContext.push(_n);
+}
+
+void dev::ThreadContext::pop()
+{
+ g_logThreadContext.pop();
+}
+
+string dev::ThreadContext::join(string const& _prior)
+{
+ return g_logThreadContext.join(_prior);
+}
// foward declare without all of Windows.h
#ifdef _WIN32
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);
#endif
+string dev::getThreadName()
+{
+#ifdef __linux__
+ char buffer[128];
+ pthread_getname_np(pthread_self(), buffer, 127);
+ buffer[127] = 0;
+ return buffer;
+#else
+ return g_logThreadName.m_name.get() ? *g_logThreadName.m_name.get() : "";
+#endif
+}
+
+void dev::setThreadName(string const& _n)
+{
+#ifdef __linux__
+ pthread_setname_np(pthread_self(), _n.c_str());
+#else
+ g_logThreadName.m_name.reset(new std::string(_n));
+#endif
+}
+
void dev::simpleDebugOut(std::string const& _s, char const*)
{
static Mutex s_lock;
@@ -55,4 +130,3 @@ void dev::simpleDebugOut(std::string const& _s, char const*)
}
std::function dev::g_logPost = simpleDebugOut;
-
diff --git a/libdevcore/Log.h b/libdevcore/Log.h
index 2e111332c..812ec0886 100644
--- a/libdevcore/Log.h
+++ b/libdevcore/Log.h
@@ -53,18 +53,24 @@ extern std::function g_logPost;
/// or equal to the currently output verbosity (g_logVerbosity).
extern std::map g_logOverride;
-/// Associate a name with each thread for nice logging.
-struct ThreadLocalLogName
+#define ETH_THREAD_CONTEXT(name) for (std::pair __eth_thread_context(name, true); p.second; p.second = false)
+
+class ThreadContext
{
- ThreadLocalLogName(std::string _name) { m_name.reset(new std::string(_name)); };
- boost::thread_specific_ptr m_name;
+public:
+ ThreadContext(std::string const& _info) { push(_info); }
+ ~ThreadContext() { pop(); }
+
+ static void push(std::string const& _n);
+ static void pop();
+ static std::string join(std::string const& _prior);
};
-/// The current thread's name.
-extern ThreadLocalLogName t_logThreadName;
+/// Set the current thread's log name.
+void setThreadName(std::string const& _n);
/// Set the current thread's log name.
-inline void setThreadName(char const* _n) { t_logThreadName.m_name.reset(new std::string(_n)); }
+std::string getThreadName();
/// The default logging channels. Each has an associated verbosity and three-letter prefix (name() ).
/// Channels should inherit from LogChannel and define name() and verbosity.
@@ -92,7 +98,7 @@ public:
char buf[24];
if (strftime(buf, 24, "%X", localtime(&rawTime)) == 0)
buf[0] = '\0'; // empty if case strftime fails
- m_sstr << Id::name() << " [ " << buf << " | " << (t_logThreadName.m_name.get() ? *t_logThreadName.m_name.get() : std::string("")) << (_term ? " ] " : "");
+ m_sstr << Id::name() << " [ " << buf << " | " << getThreadName() << ThreadContext::join(" | ") << (_term ? " ] " : "");
}
}
diff --git a/libdevcore/RLP.cpp b/libdevcore/RLP.cpp
index 994aac265..25e843c77 100644
--- a/libdevcore/RLP.cpp
+++ b/libdevcore/RLP.cpp
@@ -111,10 +111,24 @@ unsigned RLP::actualSize() const
return 0;
}
+void RLP::requireGood() const
+{
+ if (isNull())
+ BOOST_THROW_EXCEPTION(BadRLP());
+ byte n = m_data[0];
+ if (n != c_rlpDataImmLenStart + 1)
+ return;
+ if (m_data.size() < 2)
+ BOOST_THROW_EXCEPTION(BadRLP());
+ if (m_data[1] < c_rlpDataImmLenStart)
+ BOOST_THROW_EXCEPTION(BadRLP());
+}
+
bool RLP::isInt() const
{
if (isNull())
return false;
+ requireGood();
byte n = m_data[0];
if (n < c_rlpDataImmLenStart)
return !!n;
@@ -141,6 +155,7 @@ unsigned RLP::length() const
{
if (isNull())
return 0;
+ requireGood();
unsigned ret = 0;
byte n = m_data[0];
if (n < c_rlpDataImmLenStart)
diff --git a/libdevcore/RLP.h b/libdevcore/RLP.h
index caaf10b6a..c99d1a358 100644
--- a/libdevcore/RLP.h
+++ b/libdevcore/RLP.h
@@ -253,6 +253,7 @@ public:
/// Converts to int of type given; if isString(), decodes as big-endian bytestream. @returns 0 if not an int or string.
template _T toInt(int _flags = Strict) const
{
+ requireGood();
if ((!isInt() && !(_flags & AllowNonCanon)) || isList() || isNull())
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
@@ -273,6 +274,7 @@ public:
template _N toHash(int _flags = Strict) const
{
+ requireGood();
if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)) || (length() < _N::size && (_flags & FailIfTooSmall)))
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
@@ -290,7 +292,7 @@ public:
RLPs toList() const;
/// @returns the data payload. Valid for all types.
- bytesConstRef payload() const { return m_data.cropped(payloadOffset()); }
+ bytesConstRef payload() const { auto l = length(); if (l > m_data.size()) throw BadRLP(); return m_data.cropped(payloadOffset(), l); }
/// @returns the theoretical size of this item.
/// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work.
@@ -300,6 +302,9 @@ private:
/// Disable construction from rvalue
explicit RLP(bytes const&&) {}
+ /// Throws if is non-canonical data (i.e. single byte done in two bytes that could be done in one).
+ void requireGood() const;
+
/// Single-byte data payload.
bool isSingleByte() const { return !isNull() && m_data[0] < c_rlpDataImmLenStart; }
diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp
index 8c1fbb9c7..7fe320420 100644
--- a/libdevcore/Worker.cpp
+++ b/libdevcore/Worker.cpp
@@ -27,52 +27,76 @@
using namespace std;
using namespace dev;
-void Worker::startWorking(IfRunning _ir)
+void Worker::startWorking()
{
// cnote << "startWorking for thread" << m_name;
Guard l(x_work);
-
- if (m_work && m_work->joinable())
- try {
- if (_ir == IfRunning::Detach)
- m_work->detach();
- else if (_ir == IfRunning::Join)
- m_work->join();
- else
- return;
- } catch (...) {}
- cnote << "Spawning" << m_name;
- m_stop = false;
- m_work.reset(new thread([&]()
+ m_state = WorkerState::Starting;
+ if (!m_work)
{
- setThreadName(m_name.c_str());
- startedWorking();
- workLoop();
- m_work->detach();
- cnote << "Finishing up worker thread";
- doneWorking();
- }));
+ m_work.reset(new thread([&]()
+ {
+ setThreadName(m_name.c_str());
+ while (m_state != WorkerState::Killing)
+ {
+ WorkerState ex = WorkerState::Starting;
+ m_state.compare_exchange_strong(ex, WorkerState::Started);
+
+ startedWorking();
+ cnote << "Entering work loop...";
+ workLoop();
+ cnote << "Finishing up worker thread...";
+ doneWorking();
+
+// ex = WorkerState::Stopping;
+// m_state.compare_exchange_strong(ex, WorkerState::Stopped);
+
+ ex = m_state.exchange(WorkerState::Stopped);
+ if (ex == WorkerState::Killing)
+ m_state.exchange(ex);
+
+ while (m_state == WorkerState::Stopped)
+ this_thread::sleep_for(chrono::milliseconds(20));
+ }
+ }));
+ cnote << "Spawning" << m_name;
+ }
+ while (m_state != WorkerState::Started)
+ this_thread::sleep_for(chrono::microseconds(20));
}
void Worker::stopWorking()
{
// cnote << "stopWorking for thread" << m_name;
- Guard l(x_work);
- if (!m_work || !m_work->joinable())
- return;
- cnote << "Stopping" << m_name;
- m_stop = true;
- try {
- m_work->join();
- }
- catch (...) {}
- m_work.reset();
- cnote << "Stopped" << m_name;
+ ETH_GUARDED(x_work)
+ if (m_work)
+ {
+ cnote << "Stopping" << m_name;
+ WorkerState ex = WorkerState::Started;
+ m_state.compare_exchange_strong(ex, WorkerState::Stopping);
+
+ while (m_state != WorkerState::Stopped)
+ this_thread::sleep_for(chrono::microseconds(20));
+ }
+}
+
+void Worker::terminate()
+{
+// cnote << "stopWorking for thread" << m_name;
+ ETH_GUARDED(x_work)
+ if (m_work)
+ {
+ cnote << "Terminating" << m_name;
+ m_state.exchange(WorkerState::Killing);
+
+ m_work->join();
+ m_work.reset();
+ }
}
void Worker::workLoop()
{
- while (!m_stop)
+ while (m_state == WorkerState::Started)
{
if (m_idleWaitMs)
this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs));
diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h
index 287ff6d6f..fbc4d7042 100644
--- a/libdevcore/Worker.h
+++ b/libdevcore/Worker.h
@@ -36,6 +36,15 @@ enum class IfRunning
Detach
};
+enum class WorkerState
+{
+ Starting,
+ Started,
+ Stopping,
+ Stopped,
+ Killing
+};
+
class Worker
{
protected:
@@ -47,19 +56,19 @@ protected:
/// Move-assignment.
Worker& operator=(Worker&& _m) { std::swap(m_name, _m.m_name); return *this; }
- virtual ~Worker() { stopWorking(); }
+ virtual ~Worker() { terminate(); }
/// Allows changing worker name if work is stopped.
void setName(std::string _n) { if (!isWorking()) m_name = _n; }
/// Starts worker thread; causes startedWorking() to be called.
- void startWorking(IfRunning _ir = IfRunning::Fail);
+ void startWorking();
/// Stop worker thread; causes call to stopWorking().
void stopWorking();
-
+
/// Returns if worker thread is present.
- bool isWorking() const { Guard l(x_work); return !!m_work; }
+ bool isWorking() const { Guard l(x_work); return m_state == WorkerState::Started; }
/// Called after thread is started from startWorking().
virtual void startedWorking() {}
@@ -69,21 +78,25 @@ protected:
/// Overrides doWork(); should call shouldStop() often and exit when true.
virtual void workLoop();
- bool shouldStop() const { return m_stop; }
+ bool shouldStop() const { return m_state != WorkerState::Started; }
/// Called when is to be stopped, just prior to thread being joined.
virtual void doneWorking() {}
/// Blocks caller into worker thread has finished.
- void join() const { Guard l(x_work); try { if (m_work) m_work->join(); } catch (...) {} }
+// void join() const { Guard l(x_work); try { if (m_work) m_work->join(); } catch (...) {} }
private:
+ /// Stop and never start again.
+ void terminate();
+
std::string m_name;
+
unsigned m_idleWaitMs = 0;
mutable Mutex x_work; ///< Lock for the network existance.
std::unique_ptr m_work; ///< The network thread.
- bool m_stop = false;
+ std::atomic m_state = {WorkerState::Starting};
};
}
diff --git a/libdevcrypto/OverlayDB.cpp b/libdevcrypto/OverlayDB.cpp
index 4bd698f57..5f8aea667 100644
--- a/libdevcrypto/OverlayDB.cpp
+++ b/libdevcrypto/OverlayDB.cpp
@@ -34,13 +34,6 @@ OverlayDB::~OverlayDB()
cnote << "Closing state DB";
}
-void OverlayDB::setDB(ldb::DB* _db, bool _clearOverlay)
-{
- m_db = std::shared_ptr(_db);
- if (_clearOverlay)
- m_over.clear();
-}
-
void OverlayDB::commit()
{
if (m_db)
diff --git a/libdevcrypto/OverlayDB.h b/libdevcrypto/OverlayDB.h
index d027afbd4..7f7736ac1 100644
--- a/libdevcrypto/OverlayDB.h
+++ b/libdevcrypto/OverlayDB.h
@@ -42,7 +42,6 @@ public:
~OverlayDB();
ldb::DB* db() const { return m_db.get(); }
- void setDB(ldb::DB* _db, bool _clearOverlay = true);
void commit();
void rollback();
diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp
index 172123439..53eabe349 100644
--- a/libethash-cl/ethash_cl_miner.cpp
+++ b/libethash-cl/ethash_cl_miner.cpp
@@ -57,7 +57,7 @@ ethash_cl_miner::ethash_cl_miner()
{
}
-std::string ethash_cl_miner::platform_info()
+std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId)
{
std::vector platforms;
cl::Platform::get(&platforms);
@@ -67,21 +67,22 @@ std::string ethash_cl_miner::platform_info()
return std::string();
}
- // get GPU device of the default platform
+ // get GPU device of the selected platform
std::vector devices;
- platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
+ unsigned platform_num = std::min(_platformId, platforms.size() - 1);
+ platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
debugf("No OpenCL devices found.\n");
return std::string();
}
- // use default device
- unsigned device_num = 0;
+ // use selected default device
+ unsigned device_num = std::min(_deviceId, devices.size() - 1);
cl::Device& device = devices[device_num];
std::string device_version = device.getInfo();
- return "{ \"platform\": \"" + platforms[0].getInfo() + "\", \"device\": \"" + device.getInfo() + "\", \"version\": \"" + device_version + "\" }";
+ return "{ \"platform\": \"" + platforms[platform_num].getInfo() + "\", \"device\": \"" + device.getInfo() + "\", \"version\": \"" + device_version + "\" }";
}
void ethash_cl_miner::finish()
@@ -92,7 +93,7 @@ void ethash_cl_miner::finish()
}
}
-bool ethash_cl_miner::init(ethash_params const& params, std::function _fillDAG, unsigned workgroup_size, unsigned _deviceId)
+bool ethash_cl_miner::init(ethash_params const& params, std::function _fillDAG, unsigned workgroup_size, unsigned _platformId, unsigned _deviceId)
{
// store params
m_params = params;
@@ -106,12 +107,15 @@ bool ethash_cl_miner::init(ethash_params const& params, std::function().c_str());
+ // use selected platform
+
+ _platformId = std::min(_platformId, platforms.size() - 1);
+
+ fprintf(stderr, "Using platform: %s\n", platforms[_platformId].getInfo().c_str());
// get GPU device of the default platform
std::vector devices;
- platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
+ platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
debugf("No OpenCL devices found.\n");
diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h
index 90793ae97..3046f037b 100644
--- a/libethash-cl/ethash_cl_miner.h
+++ b/libethash-cl/ethash_cl_miner.h
@@ -31,8 +31,8 @@ public:
public:
ethash_cl_miner();
- bool init(ethash_params const& params, std::function _fillDAG, unsigned workgroup_size = 64, unsigned _deviceId = 0);
- static std::string platform_info();
+ bool init(ethash_params const& params, std::function _fillDAG, unsigned workgroup_size = 64, unsigned _platformId = 0, unsigned _deviceId = 0);
+ static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0);
void finish();
void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count);
diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp
index b45bdc57e..e9ce070dc 100644
--- a/libethcore/BlockInfo.cpp
+++ b/libethcore/BlockInfo.cpp
@@ -77,7 +77,7 @@ h256 const& BlockInfo::hash() const
h256 const& BlockInfo::boundary() const
{
- if (!m_boundary)
+ if (!m_boundary && difficulty)
m_boundary = (h256)(u256)((bigint(1) << 256) / difficulty);
return m_boundary;
}
diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp
index a0ceb389e..56120471b 100644
--- a/libethcore/Common.cpp
+++ b/libethcore/Common.cpp
@@ -34,7 +34,7 @@ namespace eth
{
const unsigned c_protocolVersion = 60;
-const unsigned c_minorProtocolVersion = 0;
+const unsigned c_minorProtocolVersion = 1;
const unsigned c_databaseBaseVersion = 9;
#if ETH_FATDB
const unsigned c_databaseVersionModifier = 1;
diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp
index 977149e7a..7ff35fd2b 100644
--- a/libethcore/Ethash.cpp
+++ b/libethcore/Ethash.cpp
@@ -237,7 +237,7 @@ protected:
return true;
}
}
- return false;
+ return m_owner->shouldStop();
}
virtual bool searched(uint64_t _startNonce, uint32_t _count) override
@@ -246,7 +246,7 @@ protected:
// std::cerr << "Searched " << _count << " from " << _startNonce << std::endl;
m_owner->accumulateHashes(_count);
m_last = _startNonce + _count;
- if (m_abort)
+ if (m_abort || m_owner->shouldStop())
{
m_aborted = true;
return true;
@@ -262,10 +262,12 @@ private:
Ethash::GPUMiner* m_owner = nullptr;
};
+unsigned Ethash::GPUMiner::s_platformId = 0;
unsigned Ethash::GPUMiner::s_deviceId = 0;
Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci):
Miner(_ci),
+ Worker("gpuminer"),
m_hook(new EthashCLHook(this))
{
}
@@ -295,21 +297,27 @@ void Ethash::GPUMiner::kickOff()
void Ethash::GPUMiner::workLoop()
{
// take local copy of work since it may end up being overwritten by kickOff/pause.
- WorkPackage w = work();
- if (!m_miner || m_minerSeed != w.seedHash)
- {
- m_minerSeed = w.seedHash;
+ try {
+ WorkPackage w = work();
+ if (!m_miner || m_minerSeed != w.seedHash)
+ {
+ m_minerSeed = w.seedHash;
- delete m_miner;
- m_miner = new ethash_cl_miner;
+ delete m_miner;
+ m_miner = new ethash_cl_miner;
- auto p = EthashAux::params(m_minerSeed);
- auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); };
- m_miner->init(p, cb, 32, s_deviceId);
- }
+ auto p = EthashAux::params(m_minerSeed);
+ auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); };
+ m_miner->init(p, cb, 32, s_platformId, s_deviceId);
+ }
- uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
- m_miner->search(w.headerHash.data(), upper64OfBoundary, *m_hook);
+ uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
+ m_miner->search(w.headerHash.data(), upper64OfBoundary, *m_hook);
+ }
+ catch (...)
+ {
+ cwarn << "Error GPU mining. GPU memory fragmentation?";
+ }
}
void Ethash::GPUMiner::pause()
@@ -320,7 +328,7 @@ void Ethash::GPUMiner::pause()
std::string Ethash::GPUMiner::platformInfo()
{
- return ethash_cl_miner::platform_info();
+ return ethash_cl_miner::platform_info(s_platformId, s_deviceId);
}
#endif
diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h
index 077da4460..2bbe7d649 100644
--- a/libethcore/Ethash.h
+++ b/libethcore/Ethash.h
@@ -86,6 +86,7 @@ public:
static unsigned instances() { return std::thread::hardware_concurrency(); }
static std::string platformInfo();
+ static void setDefaultPlatform(unsigned) {}
static void setDefaultDevice(unsigned) {}
protected:
@@ -113,6 +114,7 @@ public:
static unsigned instances() { return 1; }
static std::string platformInfo();
+ static void setDefaultPlatform(unsigned _id) { s_platformId = _id; }
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; }
protected:
@@ -129,6 +131,7 @@ public:
ethash_cl_miner* m_miner = nullptr;
h256 m_minerSeed; ///< Last seed in m_miner
+ static unsigned s_platformId;
static unsigned s_deviceId;
};
#else
diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp
index 465ae17da..750d80082 100644
--- a/libethcore/EthashAux.cpp
+++ b/libethcore/EthashAux.cpp
@@ -75,11 +75,11 @@ h256 EthashAux::seedHash(unsigned _number)
n = get()->m_seedHashes.size() - 1;
}
get()->m_seedHashes.resize(epoch + 1);
- cdebug << "Searching for seedHash of epoch " << epoch;
+// cdebug << "Searching for seedHash of epoch " << epoch;
for (; n <= epoch; ++n, ret = sha3(ret))
{
get()->m_seedHashes[n] = ret;
- cdebug << "Epoch" << n << "is" << ret.abridged();
+// cdebug << "Epoch" << n << "is" << ret.abridged();
}
}
return get()->m_seedHashes[epoch];
@@ -95,7 +95,7 @@ ethash_params EthashAux::params(h256 const& _seedHash)
}
catch (...)
{
- cdebug << "Searching for seedHash " << _seedHash.abridged();
+// cdebug << "Searching for seedHash " << _seedHash.abridged();
for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {}
if (epoch == 2048)
{
@@ -138,12 +138,12 @@ EthashAux::LightAllocation::~LightAllocation()
}
-EthashAux::FullType EthashAux::full(BlockInfo const& _header, bytesRef _dest)
+EthashAux::FullType EthashAux::full(BlockInfo const& _header, bytesRef _dest, bool _createIfMissing)
{
- return full(_header.seedHash(), _dest);
+ return full(_header.seedHash(), _dest, _createIfMissing);
}
-EthashAux::FullType EthashAux::full(h256 const& _seedHash, bytesRef _dest)
+EthashAux::FullType EthashAux::full(h256 const& _seedHash, bytesRef _dest, bool _createIfMissing)
{
RecursiveGuard l(get()->x_this);
FullType ret = get()->m_fulls[_seedHash].lock();
@@ -180,6 +180,8 @@ EthashAux::FullType EthashAux::full(h256 const& _seedHash, bytesRef _dest)
bytesRef r = contentsNew(memoFile, _dest);
if (!r)
{
+ if (!_createIfMissing)
+ return FullType();
// file didn't exist.
if (_dest)
// buffer was passed in - no insertion into cache nor need to allocate
@@ -221,8 +223,7 @@ Ethash::Result EthashAux::LightAllocation::compute(h256 const& _seedHash, h256 c
Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce)
{
- // TODO: should be EthashAux::get()->haveFull(_seedHash)
- if (auto dag = EthashAux::get()->full(_seedHash))
+ if (auto dag = EthashAux::get()->full(_seedHash, bytesRef(), false))
return dag->compute(_seedHash, _headerHash, _nonce);
return EthashAux::get()->light(_seedHash)->compute(_seedHash, _headerHash, _nonce);
}
diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h
index 98a6554ad..40dd88e16 100644
--- a/libethcore/EthashAux.h
+++ b/libethcore/EthashAux.h
@@ -60,8 +60,8 @@ public:
static ethash_params params(unsigned _n);
static LightType light(BlockInfo const& _header);
static LightType light(h256 const& _seedHash);
- static FullType full(BlockInfo const& _header, bytesRef _dest = bytesRef());
- static FullType full(h256 const& _header, bytesRef _dest = bytesRef());
+ static FullType full(BlockInfo const& _header, bytesRef _dest = bytesRef(), bool _createIfMissing = true);
+ static FullType full(h256 const& _header, bytesRef _dest = bytesRef(), bool _createIfMissing = true);
static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); }
static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce);
diff --git a/libethcore/Miner.h b/libethcore/Miner.h
index 71e952d5c..3a68491ff 100644
--- a/libethcore/Miner.h
+++ b/libethcore/Miner.h
@@ -24,7 +24,9 @@
#include
#include
#include
+#include
#include
+#include
#include
#include
@@ -105,8 +107,12 @@ public:
}
if (!!_work)
{
+ boost::timer t;
pause();
+ cdebug << "pause took" << t.elapsed();
+ t.restart();
kickOff();
+ cdebug << "kickOff took" << t.elapsed();
}
else if (!_work && !!old)
pause();
diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp
index 01cd876ab..6a483a228 100644
--- a/libethereum/BlockChain.cpp
+++ b/libethereum/BlockChain.cpp
@@ -167,6 +167,7 @@ void BlockChain::open(std::string const& _path, WithExisting _we)
std::string l;
m_extrasDB->Get(m_readOptions, ldb::Slice("best"), &l);
m_lastBlockHash = l.empty() ? m_genesisHash : *(h256*)l.data();
+ m_lastBlockNumber = number(m_lastBlockHash);
cnote << "Opened blockchain DB. Latest: " << currentHash();
}
@@ -177,6 +178,7 @@ void BlockChain::close()
delete m_extrasDB;
delete m_blocksDB;
m_lastBlockHash = m_genesisHash;
+ m_lastBlockNumber = 0;
m_details.clear();
m_blocks.clear();
}
@@ -185,26 +187,27 @@ void BlockChain::close()
void BlockChain::rebuild(std::string const& _path, std::function const& _progress)
{
+ std::string path = _path.empty() ? Defaults::get()->m_dbPath : _path;
+
#if ETH_PROFILING_GPERF
ProfilerStart("BlockChain_rebuild.log");
#endif
-// unsigned originalNumber = (unsigned)BlockInfo(oldBlock(m_lastBlockHash)).number;
- unsigned originalNumber = number();
+ unsigned originalNumber = m_lastBlockNumber;
// Keep extras DB around, but under a temp name
delete m_extrasDB;
m_extrasDB = nullptr;
- IGNORE_EXCEPTIONS(boost::filesystem::remove_all(_path + "/details.old"));
- boost::filesystem::rename(_path + "/details", _path + "/details.old");
+ IGNORE_EXCEPTIONS(boost::filesystem::remove_all(path + "/details.old"));
+ boost::filesystem::rename(path + "/details", path + "/details.old");
ldb::DB* oldExtrasDB;
ldb::Options o;
o.create_if_missing = true;
- ldb::DB::Open(o, _path + "/details.old", &oldExtrasDB);
- ldb::DB::Open(o, _path + "/details", &m_extrasDB);
+ ldb::DB::Open(o, path + "/details.old", &oldExtrasDB);
+ ldb::DB::Open(o, path + "/details", &m_extrasDB);
// Open a fresh state DB
- State s(State::openDB(_path, WithExisting::Kill), BaseState::CanonGenesis);
+ State s(State::openDB(path, WithExisting::Kill), BaseState::CanonGenesis);
// Clear all memos ready for replay.
m_details.clear();
@@ -215,8 +218,13 @@ void BlockChain::rebuild(std::string const& _path, std::functionPut(m_writeOptions, toSlice(m_lastBlockHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[m_lastBlockHash].rlp()));
+
+ h256 lastHash = m_lastBlockHash;
boost::timer t;
for (unsigned d = 1; d < originalNumber; ++d)
{
@@ -238,7 +246,7 @@ void BlockChain::rebuild(std::string const& _path, std::function
@@ -308,7 +316,7 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
fresh += r.first;
dead += r.second;
}
- catch (UnknownParent)
+ catch (dev::eth::UnknownParent)
{
cwarn << "ODD: Import queue contains block with unknown parent." << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the right order.
@@ -396,20 +404,22 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
if (!pd)
{
auto pdata = pd.rlp();
- cwarn << "Odd: details is returning false despite block known:" << RLP(pdata);
+ clog(BlockChainDebug) << "Details is returning false despite block known:" << RLP(pdata);
auto parentBlock = block(bi.parentHash);
- cwarn << "Block:" << RLP(parentBlock);
+ clog(BlockChainDebug) << "Block:" << RLP(parentBlock);
+ clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
+ exit(-1);
}
// Check it's not crazy
if (bi.timestamp > (u256)time(0))
{
- clog(BlockChainNote) << bi.hash() << ": Future time " << bi.timestamp << " (now at " << time(0) << ")";
+ clog(BlockChainChat) << bi.hash() << ": Future time " << bi.timestamp << " (now at " << time(0) << ")";
// Block has a timestamp in the future. This is no good.
BOOST_THROW_EXCEPTION(FutureTime());
}
- clog(BlockChainNote) << "Attempting import of " << bi.hash().abridged() << "...";
+ clog(BlockChainChat) << "Attempting import of " << bi.hash().abridged() << "...";
#if ETH_TIMED_IMPORTS
preliminaryChecks = t.elapsed();
@@ -527,12 +537,6 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
{
unsigned commonIndex;
tie(route, common, commonIndex) = treeRoute(last, bi.hash());
- {
- WriteGuard l(x_lastBlockHash);
- m_lastBlockHash = bi.hash();
- }
-
- m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&(bi.hash()), 32));
// Most of the time these two will be equal - only when we're doing a chain revert will they not be
if (common != last)
@@ -593,6 +597,14 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp()));
}
+ // FINALLY! change our best hash.
+ {
+ WriteGuard l(x_lastBlockHash);
+ m_lastBlockHash = bi.hash();
+ m_lastBlockNumber = (unsigned)bi.number;
+ m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&(bi.hash()), 32));
+ }
+
clog(BlockChainNote) << " Imported and best" << td << " (#" << bi.number << "). Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(route);
noteCanonChanged();
@@ -605,7 +617,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
}
else
{
- clog(BlockChainNote) << " Imported but not best (oTD:" << details(last).totalDifficulty << " > TD:" << td << ")";
+ clog(BlockChainChat) << " Imported but not best (oTD:" << details(last).totalDifficulty << " > TD:" << td << ")";
}
#if ETH_TIMED_IMPORTS
@@ -618,6 +630,14 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
cnote << "checkBest:" << checkBest;
#endif
+ if (isKnown(bi.hash()) && !details(bi.hash()))
+ {
+ clog(BlockChainDebug) << "Known block just inserted has no details.";
+ clog(BlockChainDebug) << "Block:" << bi;
+ clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
+ exit(-1);
+ }
+
h256s fresh;
h256s dead;
bool isOld = true;
@@ -944,14 +964,26 @@ bool BlockChain::isKnown(h256 const& _hash) const
{
if (_hash == m_genesisHash)
return true;
+
+ BlockInfo bi;
+
{
ReadGuard l(x_blocks);
- if (m_blocks.count(_hash))
- return true;
+ auto it = m_blocks.find(_hash);
+ if (it != m_blocks.end())
+ bi = BlockInfo(it->second, CheckNothing, _hash);
}
- string d;
- m_blocksDB->Get(m_readOptions, toSlice(_hash), &d);
- return !!d.size();
+
+ if (!bi)
+ {
+ string d;
+ m_blocksDB->Get(m_readOptions, toSlice(_hash), &d);
+ if (!d.size())
+ return false;
+ bi = BlockInfo(bytesConstRef(&d), CheckNothing, _hash);
+ }
+
+ return bi.number <= m_lastBlockNumber; // TODO: m_lastBlockNumber
}
bytes BlockChain::block(h256 const& _hash) const
diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h
index 2c3ef40a8..be5b931ee 100644
--- a/libethereum/BlockChain.h
+++ b/libethereum/BlockChain.h
@@ -56,9 +56,10 @@ struct AlreadyHaveBlock: virtual Exception {};
struct UnknownParent: virtual Exception {};
struct FutureTime: virtual Exception {};
-struct BlockChainChat: public LogChannel { static const char* name() { return "-B-"; } static const int verbosity = 7; };
-struct BlockChainNote: public LogChannel { static const char* name() { return "=B="; } static const int verbosity = 4; };
+struct BlockChainChat: public LogChannel { static const char* name() { return "-B-"; } static const int verbosity = 5; };
+struct BlockChainNote: public LogChannel { static const char* name() { return "=B="; } static const int verbosity = 3; };
struct BlockChainWarn: public LogChannel { static const char* name() { return "=B="; } static const int verbosity = 1; };
+struct BlockChainDebug: public LogChannel { static const char* name() { return "#B#"; } static const int verbosity = 0; };
// TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::map const& genesisState();
@@ -98,7 +99,8 @@ public:
/// To be called from main loop every 100ms or so.
void process();
- /// Sync the chain with any incoming blocks. All blocks should, if processed in order
+ /// Sync the chain with any incoming blocks. All blocks should, if processed in order.
+ /// @returns fresh blocks, dead blocks and true iff there are additional blocks to be processed waiting.
std::tuple sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max);
/// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB.
@@ -183,7 +185,7 @@ public:
/// Get a number for the given hash (or the most recent mined if none given). Thread-safe.
unsigned number(h256 const& _hash) const { return details(_hash).number; }
- unsigned number() const { return number(currentHash()); }
+ unsigned number() const { return m_lastBlockNumber; }
/// Get a given block (RLP format). Thread-safe.
h256 currentHash() const { ReadGuard l(x_lastBlockHash); return m_lastBlockHash; }
@@ -313,6 +315,7 @@ private:
/// Hash of the last (valid) block on the longest chain.
mutable boost::shared_mutex x_lastBlockHash;
h256 m_lastBlockHash;
+ unsigned m_lastBlockNumber = 0;
/// Genesis block info.
h256 m_genesisHash;
diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp
index 70ea8753b..4fd63aa86 100644
--- a/libethereum/BlockQueue.cpp
+++ b/libethereum/BlockQueue.cpp
@@ -74,7 +74,11 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (bi.timestamp > (u256)time(0)/* && !_isOurs*/)
{
m_future.insert(make_pair((unsigned)bi.timestamp, _block.toBytes()));
- cblockq << "OK - queued for future.";
+ char buf[24];
+ time_t bit = (unsigned)bi.timestamp;
+ if (strftime(buf, 24, "%X", localtime(&bit)) == 0)
+ buf[0] = '\0'; // empty if case strftime fails
+ cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf;
return ImportResult::FutureTime;
}
else
@@ -132,12 +136,32 @@ bool BlockQueue::doneDrain(h256s const& _bad)
void BlockQueue::tick(BlockChain const& _bc)
{
- unsigned t = time(0);
- for (auto i = m_future.begin(); i != m_future.end() && i->first < t; ++i)
- import(&(i->second), _bc);
+ vector todo;
+ {
+ UpgradableGuard l(m_lock);
+ if (m_future.empty())
+ return;
- WriteGuard l(m_lock);
- m_future.erase(m_future.begin(), m_future.upper_bound(t));
+ cblockq << "Checking past-future blocks...";
+
+ unsigned t = time(0);
+ if (t <= m_future.begin()->first)
+ return;
+
+ cblockq << "Past-future blocks ready.";
+
+ {
+ UpgradeGuard l2(l);
+ auto end = m_future.lower_bound(t);
+ for (auto i = m_future.begin(); i != end; ++i)
+ todo.push_back(move(i->second));
+ m_future.erase(m_future.begin(), end);
+ }
+ }
+ cblockq << "Importing" << todo.size() << "past-future blocks.";
+
+ for (auto const& b: todo)
+ import(&b, _bc);
}
template T advanced(T _t, unsigned _n)
@@ -146,6 +170,21 @@ template T advanced(T _t, unsigned _n)
return _t;
}
+QueueStatus BlockQueue::blockStatus(h256 const& _h) const
+{
+ ReadGuard l(m_lock);
+ return
+ m_readySet.count(_h) ?
+ QueueStatus::Ready :
+ m_drainingSet.count(_h) ?
+ QueueStatus::Importing :
+ m_unknownSet.count(_h) ?
+ QueueStatus::UnknownParent :
+ m_knownBad.count(_h) ?
+ QueueStatus::Bad :
+ QueueStatus::Unknown;
+}
+
void BlockQueue::drain(std::vector& o_out, unsigned _max)
{
WriteGuard l(m_lock);
diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h
index 631877292..1932b2f66 100644
--- a/libethereum/BlockQueue.h
+++ b/libethereum/BlockQueue.h
@@ -46,6 +46,15 @@ struct BlockQueueStatus
size_t bad;
};
+enum class QueueStatus
+{
+ Ready,
+ Importing,
+ UnknownParent,
+ Bad,
+ Unknown
+};
+
/**
* @brief A queue of blocks. Sits between network or other I/O and the BlockChain.
* Sorts them ready for blockchain insertion (with the BlockChain::sync() method).
@@ -86,6 +95,9 @@ public:
/// Get some infomration on the current status.
BlockQueueStatus status() const { ReadGuard l(m_lock); return BlockQueueStatus{m_ready.size(), m_future.size(), m_unknown.size(), m_knownBad.size()}; }
+ /// Get some infomration on the given block's status regarding us.
+ QueueStatus blockStatus(h256 const& _h) const;
+
template Handler onReady(T const& _t) { return m_onReady.add(_t); }
private:
diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp
index b9df1cf90..7ab4a99f4 100644
--- a/libethereum/Client.cpp
+++ b/libethereum/Client.cpp
@@ -117,6 +117,13 @@ void BasicGasPricer::update(BlockChain const& _bc)
}
}
+std::ostream& dev::eth::operator<<(std::ostream& _out, ActivityReport const& _r)
+{
+ _out << "Since " << toString(_r.since) << " (" << std::chrono::duration_cast(std::chrono::system_clock::now() - _r.since).count();
+ _out << "): " << _r.ticks << "ticks";
+ return _out;
+}
+
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Worker("eth"),
m_vc(_dbPath),
@@ -197,16 +204,17 @@ void Client::startedWorking()
// Synchronise the state according to the head of the block chain.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
cdebug << "startedWorking()";
- WriteGuard l(x_stateDB);
cdebug << m_bc.number() << m_bc.currentHash();
-
cdebug << "Pre:" << m_preMine.info();
cdebug << "Post:" << m_postMine.info();
cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce);
- m_preMine.sync(m_bc);
- m_postMine = m_preMine;
+ ETH_WRITE_GUARDED(x_preMine)
+ m_preMine.sync(m_bc);
+ ETH_WRITE_GUARDED(x_postMine)
+ ETH_READ_GUARDED(x_preMine)
+ m_postMine = m_preMine;
cdebug << "Pre:" << m_preMine.info();
cdebug << "Post:" << m_postMine.info();
@@ -217,13 +225,18 @@ void Client::doneWorking()
{
// Synchronise the state according to the head of the block chain.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
- WriteGuard l(x_stateDB);
- m_preMine.sync(m_bc);
- m_postMine = m_preMine;
+ ETH_WRITE_GUARDED(x_preMine)
+ m_preMine.sync(m_bc);
+ ETH_WRITE_GUARDED(x_postMine)
+ ETH_READ_GUARDED(x_preMine)
+ m_postMine = m_preMine;
}
void Client::killChain()
{
+ WriteGuard l(x_postMine);
+ WriteGuard l2(x_preMine);
+
bool wasMining = isMining();
if (wasMining)
stopMining();
@@ -235,8 +248,8 @@ void Client::killChain()
m_preMine = State();
m_postMine = State();
+// ETH_WRITE_GUARDED(x_stateDB) // no point doing this yet since we can't control where else it's open yet.
{
- WriteGuard l(x_stateDB);
m_stateDB = OverlayDB();
m_stateDB = State::openDB(Defaults::dbPath(), WithExisting::Kill);
}
@@ -258,15 +271,16 @@ void Client::killChain()
void Client::clearPending()
{
h256Set changeds;
+ ETH_WRITE_GUARDED(x_postMine)
{
- WriteGuard l(x_stateDB);
if (!m_postMine.pending().size())
return;
// for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
// appendFromNewPending(m_postMine.logBloom(i), changeds);
changeds.insert(PendingChangedFilter);
m_tq.clear();
- m_postMine = m_preMine;
+ ETH_READ_GUARDED(x_preMine)
+ m_postMine = m_preMine;
}
startMining();
@@ -338,11 +352,15 @@ void Client::setForceMining(bool _enable)
MiningProgress Client::miningProgress() const
{
+ if (m_farm.isMining())
+ return m_farm.miningProgress();
return MiningProgress();
}
uint64_t Client::hashrate() const
{
+ if (m_farm.isMining())
+ return m_farm.miningProgress().rate();
return 0;
}
@@ -364,29 +382,6 @@ std::list Client::miningHistory()
return ret;
}
-/*void Client::setupState(State& _s)
-{
- {
- ReadGuard l(x_stateDB);
- cwork << "SETUP MINE";
- _s = m_postMine;
- }
- 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);
-}*/
-
ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice, Address const& _from)
{
ExecutionResult ret;
@@ -394,11 +389,9 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
{
State temp;
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
- {
- ReadGuard l(x_stateDB);
+ ETH_READ_GUARDED(x_postMine)
temp = m_postMine;
- temp.addBalance(_from, _value + _gasPrice * _gas);
- }
+ temp.addBalance(_from, _value + _gasPrice * _gas);
Executive e(temp, LastHashes(), 0);
if (!e.call(_dest, _dest, _from, _value, _gasPrice, &_data, _gas, _from))
e.go();
@@ -413,6 +406,10 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
ProofOfWork::WorkPackage Client::getWork()
{
+ // lock the work so a later submission isn't invalidated by processing a transaction elsewhere.
+ // this will be reset as soon as a new block arrives, allowing more transactions to be processed.
+ m_lastGetWork = chrono::system_clock::now();
+ m_remoteWorking = true;
return ProofOfWork::package(m_miningInfo);
}
@@ -420,10 +417,11 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution)
{
bytes newBlock;
{
- WriteGuard l(x_stateDB);
+ WriteGuard l(x_postMine);
if (!m_postMine.completeMine(_solution))
return false;
newBlock = m_postMine.blockData();
+ // OPTIMISE: very inefficient to not utilise the existing OverlayDB in m_postMine that contains all trie changes.
}
m_bq.import(&newBlock, m_bc, true);
/*
@@ -439,13 +437,9 @@ void Client::syncBlockQueue()
cwork << "BQ ==> CHAIN ==> STATE";
{
- WriteGuard l(x_stateDB);
- OverlayDB db = m_stateDB;
- ETH_WRITE_UNGUARDED(x_stateDB)
- tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100);
+ tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, 100);
if (ir.first.empty())
return;
- m_stateDB = db;
}
onChainChanged(ir);
}
@@ -458,25 +452,26 @@ void Client::syncTransactionQueue()
h256Set changeds;
TransactionReceipts newPendingReceipts;
- ETH_WRITE_GUARDED(x_stateDB)
- newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp);
+ ETH_WRITE_GUARDED(x_postMine)
+ tie(newPendingReceipts, m_syncTransactionQueue) = m_postMine.sync(m_bc, m_tq, *m_gp);
- if (newPendingReceipts.size())
- {
+ if (newPendingReceipts.empty())
+ return;
+
+ ETH_READ_GUARDED(x_postMine)
for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
- changeds.insert(PendingChangedFilter);
+ changeds.insert(PendingChangedFilter);
- // TODO: Tell farm about new transaction (i.e. restartProofOfWork mining).
- onPostStateChanged();
+ // Tell farm about new transaction (i.e. restartProofOfWork mining).
+ onPostStateChanged();
- // Tell watches about the new transactions.
- noteChanged(changeds);
+ // Tell watches about the new transactions.
+ noteChanged(changeds);
- // Tell network about the new transactions.
- if (auto h = m_host.lock())
- h->noteNewTransactions();
- }
+ // Tell network about the new transactions.
+ if (auto h = m_host.lock())
+ h->noteNewTransactions();
}
void Client::onChainChanged(ImportRoute const& _ir)
@@ -488,7 +483,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
for (auto const& t: m_bc.transactions(h))
{
clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None);
- m_tq.import(t);
+ m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
}
}
@@ -514,34 +509,43 @@ void Client::onChainChanged(ImportRoute const& _ir)
// RESTART MINING
// LOCKS REALLY NEEDED?
- ETH_WRITE_GUARDED(x_stateDB)
- if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address())
- {
- if (isMining())
- cnote << "New block on chain.";
+ bool preChanged = false;
+ ETH_WRITE_GUARDED(x_preMine)
+ preChanged = m_preMine.sync(m_bc);
+ if (preChanged || m_postMine.address() != m_preMine.address())
+ {
+ if (isMining())
+ cnote << "New block on chain.";
- m_postMine = m_preMine;
- changeds.insert(PendingChangedFilter);
+ ETH_WRITE_GUARDED(x_postMine)
+ ETH_READ_GUARDED(x_preMine)
+ m_postMine = m_preMine;
+ changeds.insert(PendingChangedFilter);
- ETH_WRITE_UNGUARDED(x_stateDB)
- onPostStateChanged();
- }
+ onPostStateChanged();
+ }
noteChanged(changeds);
}
+bool Client::remoteActive() const
+{
+ return chrono::system_clock::now() - m_lastGetWork < chrono::seconds(30);
+}
+
void Client::onPostStateChanged()
{
cnote << "Post state changed: Restarting mining...";
- if (isMining())
+ if (isMining() || remoteActive())
{
{
- WriteGuard l(x_stateDB);
+ WriteGuard l(x_postMine);
m_postMine.commitToMine(m_bc);
m_miningInfo = m_postMine.info();
}
m_farm.setWork(m_miningInfo);
}
+ m_remoteWorking = false;
}
void Client::startMining()
@@ -578,25 +582,29 @@ void Client::doWork()
// TODO: Use condition variable rather than this rubbish.
bool t = true;
- if (m_syncTransactionQueue.compare_exchange_strong(t, false))
- syncTransactionQueue();
-
- t = true;
if (m_syncBlockQueue.compare_exchange_strong(t, false))
syncBlockQueue();
+ t = true;
+ if (m_syncTransactionQueue.compare_exchange_strong(t, false) && !m_remoteWorking)
+ syncTransactionQueue();
+
tick();
- this_thread::sleep_for(chrono::milliseconds(20));
+ if (!m_syncBlockQueue && !m_syncTransactionQueue)
+ this_thread::sleep_for(chrono::milliseconds(20));
}
void Client::tick()
{
if (chrono::system_clock::now() - m_lastTick > chrono::seconds(1))
{
+ m_report.ticks++;
checkWatchGarbage();
m_bq.tick(m_bc);
m_lastTick = chrono::system_clock::now();
+ if (m_report.ticks == 15)
+ cnote << activityReport();
}
}
@@ -606,15 +614,13 @@ void Client::checkWatchGarbage()
{
// watches garbage collection
vector toUninstall;
- {
- Guard l(x_filtersWatches);
+ ETH_GUARDED(x_filtersWatches)
for (auto key: keysOf(m_watches))
if (m_watches[key].lastPoll != chrono::system_clock::time_point::max() && chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20))
{
toUninstall.push_back(key);
cnote << "GC: Uninstall" << key << "(" << chrono::duration_cast(chrono::system_clock::now() - m_watches[key].lastPoll).count() << "s old)";
}
- }
for (auto i: toUninstall)
uninstallWatch(i);
@@ -627,7 +633,6 @@ void Client::checkWatchGarbage()
State Client::asOf(h256 const& _block) const
{
- ReadGuard l(x_stateDB);
return State(m_stateDB, bc(), _block);
}
@@ -638,19 +643,16 @@ void Client::prepareForTransaction()
State Client::state(unsigned _txi, h256 _block) const
{
- ReadGuard l(x_stateDB);
return State(m_stateDB, m_bc, _block).fromPending(_txi);
}
eth::State Client::state(h256 _block) const
{
- ReadGuard l(x_stateDB);
return State(m_stateDB, m_bc, _block);
}
eth::State Client::state(unsigned _txi) const
{
- ReadGuard l(x_stateDB);
return m_postMine.fromPending(_txi);
}
diff --git a/libethereum/Client.h b/libethereum/Client.h
index 1dfa45d7e..b1cfaf4ac 100644
--- a/libethereum/Client.h
+++ b/libethereum/Client.h
@@ -97,6 +97,14 @@ struct ClientChat: public LogChannel { static const char* name() { return "=C=";
struct ClientTrace: public LogChannel { static const char* name() { return "-C-"; } static const int verbosity = 7; };
struct ClientDetail: public LogChannel { static const char* name() { return " C "; } static const int verbosity = 14; };
+struct ActivityReport
+{
+ unsigned ticks = 0;
+ std::chrono::system_clock::time_point since = std::chrono::system_clock::now();
+};
+
+std::ostream& operator<<(std::ostream& _out, ActivityReport const& _r);
+
/**
* @brief Main API hub for interfacing with Ethereum.
*/
@@ -144,7 +152,7 @@ public:
dev::eth::State state(unsigned _txi) const;
/// Get the object representing the current state of Ethereum.
- dev::eth::State postState() const { ReadGuard l(x_stateDB); return m_postMine; }
+ dev::eth::State postState() const { ReadGuard l(x_postMine); return m_postMine; }
/// Get the object representing the current canonical blockchain.
CanonBlockChain const& blockChain() const { return m_bc; }
/// Get some information on the block queue.
@@ -152,7 +160,7 @@ public:
// Mining stuff:
- void setAddress(Address _us) { WriteGuard l(x_stateDB); m_preMine.setAddress(_us); }
+ void setAddress(Address _us) { WriteGuard l(x_preMine); m_preMine.setAddress(_us); }
/// Check block validity prior to mining.
bool miningParanoia() const { return m_paranoia; }
@@ -204,6 +212,8 @@ public:
void killChain();
/// Retries all blocks with unknown parents.
void retryUnkonwn() { m_bq.retryAllUnknown(); }
+ /// Get a report of activity.
+ ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
protected:
/// InterfaceStub methods
@@ -214,8 +224,8 @@ protected:
/// Works properly with LatestBlock and PendingBlock.
using ClientBase::asOf;
virtual State asOf(h256 const& _block) const override;
- virtual State preMine() const override { ReadGuard l(x_stateDB); return m_preMine; }
- virtual State postMine() const override { ReadGuard l(x_stateDB); return m_postMine; }
+ virtual State preMine() const override { ReadGuard l(x_preMine); return m_preMine; }
+ virtual State postMine() const override { ReadGuard l(x_postMine); return m_postMine; }
virtual void prepareForTransaction() override;
/// Collate the changed filters for the bloom filter of the given pending transaction.
@@ -271,11 +281,15 @@ private:
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
std::shared_ptr m_gp; ///< The gas pricer.
- mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine.
OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
+ mutable SharedMutex x_preMine; ///< Lock on the OverlayDB and other attributes of m_preMine.
State m_preMine; ///< The present state of the client.
+ mutable SharedMutex x_postMine; ///< Lock on the OverlayDB and other attributes of m_postMine.
State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
BlockInfo m_miningInfo; ///< The header we're attempting to mine on (derived from m_postMine).
+ bool remoteActive() const; ///< Is there an active and valid remote worker?
+ bool m_remoteWorking = false; ///< Has the remote worker recently been reset?
+ std::chrono::system_clock::time_point m_lastGetWork = std::chrono::system_clock::time_point::min(); ///< Is there an active and valid remote worker?
std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock.
@@ -293,6 +307,8 @@ private:
mutable std::chrono::system_clock::time_point m_lastTick = std::chrono::system_clock::now();
///< When did we last tick()?
+ ActivityReport m_report;
+
// TODO!!!!!! REPLACE WITH A PROPER X-THREAD ASIO SIGNAL SYSTEM (could just be condition variables)
std::atomic m_syncTransactionQueue = {false};
std::atomic m_syncBlockQueue = {false};
diff --git a/libethereum/CommonNet.h b/libethereum/CommonNet.h
index 2ee260650..2083e9919 100644
--- a/libethereum/CommonNet.h
+++ b/libethereum/CommonNet.h
@@ -37,13 +37,13 @@ namespace eth
{
#if ETH_DEBUG
-static const unsigned c_maxHashes = 64; ///< Maximum number of hashes BlockHashes will ever send.
-static const unsigned c_maxHashesAsk = 64; ///< Maximum number of hashes GetBlockHashes will ever ask for.
-static const unsigned c_maxBlocks = 32; ///< Maximum number of blocks Blocks will ever send.
-static const unsigned c_maxBlocksAsk = 32; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
+static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
+static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
+static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
+static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
#else
-static const unsigned c_maxHashes = 256; ///< Maximum number of hashes BlockHashes will ever send.
-static const unsigned c_maxHashesAsk = 256; ///< Maximum number of hashes GetBlockHashes will ever ask for.
+static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
+static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
#endif
diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp
index 6ef293d5a..fde2ae745 100644
--- a/libethereum/EthereumHost.cpp
+++ b/libethereum/EthereumHost.cpp
@@ -189,7 +189,7 @@ void EthereumHost::maintainTransactions()
for (auto const& i: ts)
{
bool unsent = !m_transactionsSent.count(i.first);
- for (auto const& p: randomSelection(25, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }))
+ for (auto const& p: randomSelection(100, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }))
peerTransactions[p].push_back(i.first);
}
for (auto const& t: ts)
@@ -242,18 +242,28 @@ std::vector> EthereumHost::randomSelection(unsigne
void EthereumHost::maintainBlocks(h256 _currentHash)
{
// Send any new blocks.
- if (m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty)
+ auto detailsFrom = m_chain.details(m_latestBlockSent);
+ auto detailsTo = m_chain.details(_currentHash);
+ if (detailsFrom.totalDifficulty < detailsTo.totalDifficulty)
{
- clog(NetMessageSummary) << "Sending a new block (current is" << _currentHash << ", was" << m_latestBlockSent << ")";
-
- for (auto const& p: randomSelection(25, [&](EthereumPeer* p){return !p->m_knownBlocks.count(_currentHash); }))
+ if (diff(detailsFrom.number, detailsTo.number) < 20)
{
- RLPStream ts;
- p->prep(ts, NewBlockPacket, 2).appendRaw(m_chain.block(), 1).append(m_chain.details().totalDifficulty);
+ // don't be sending more than 20 "new" blocks. if there are any more we were probably waaaay behind.
+ clog(NetMessageSummary) << "Sending a new block (current is" << _currentHash << ", was" << m_latestBlockSent << ")";
+
+ h256s blocks = get<0>(m_chain.treeRoute(m_latestBlockSent, _currentHash, false, false, true));
+
+ for (auto const& p: randomSelection(100, [&](EthereumPeer* p){return !p->m_knownBlocks.count(_currentHash); }))
+ for (auto const& b: blocks)
+ if (!p->m_knownBlocks.count(b))
+ {
+ RLPStream ts;
+ p->prep(ts, NewBlockPacket, 2).appendRaw(m_chain.block(b), 1).append(m_chain.details(b).totalDifficulty);
- Guard l(p->x_knownBlocks);
- p->sealAndSend(ts);
- p->m_knownBlocks.clear();
+ Guard l(p->x_knownBlocks);
+ p->sealAndSend(ts);
+ p->m_knownBlocks.clear();
+ }
}
m_latestBlockSent = _currentHash;
}
diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp
index 3da4402ac..d730771a0 100644
--- a/libethereum/EthereumPeer.cpp
+++ b/libethereum/EthereumPeer.cpp
@@ -34,11 +34,6 @@ using namespace dev;
using namespace dev::eth;
using namespace p2p;
-#if defined(clogS)
-#undef clogS
-#endif
-#define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] "
-
EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i):
Capability(_s, _h, _i),
m_sub(host()->m_man)
@@ -48,7 +43,7 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i):
EthereumPeer::~EthereumPeer()
{
- clogS(NetMessageSummary) << "Aborting Sync :-(";
+ clog(NetMessageSummary) << "Aborting Sync :-(";
abortSync();
}
@@ -81,7 +76,7 @@ string toString(Asking _a)
void EthereumPeer::transition(Asking _a, bool _force)
{
- clogS(NetMessageSummary) << "Transition!" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
+ clog(NetMessageSummary) << "Transition!" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
if (m_asking == Asking::State && _a != Asking::State)
m_requireTransactions = true;
@@ -108,7 +103,7 @@ void EthereumPeer::transition(Asking _a, bool _force)
if (m_asking == Asking::State || m_asking == Asking::Nothing)
{
if (isSyncing())
- clogS(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
+ clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
m_syncingLatestHash = m_latestHash;
m_syncingTotalDifficulty = m_totalDifficulty;
@@ -123,10 +118,10 @@ void EthereumPeer::transition(Asking _a, bool _force)
else if (m_asking == Asking::Hashes)
{
if (!isSyncing())
- clogS(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
+ clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
setAsking(_a, true);
- prep(s, GetBlockHashesPacket, 2) << m_syncingNeededBlocks.back() << c_maxHashesAsk;
+ prep(s, GetBlockHashesPacket, 2) << m_syncingLastReceivedHash << c_maxHashesAsk;
sealAndSend(s);
return;
}
@@ -136,13 +131,13 @@ void EthereumPeer::transition(Asking _a, bool _force)
if (m_asking == Asking::Hashes)
{
if (!isSyncing())
- clogS(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
+ clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
if (shouldGrabBlocks())
{
clog(NetNote) << "Difficulty of hashchain HIGHER. Grabbing" << m_syncingNeededBlocks.size() << "blocks [latest now" << m_syncingLatestHash.abridged() << ", was" << host()->m_latestBlockSent.abridged() << "]";
host()->m_man.resetToChain(m_syncingNeededBlocks);
- host()->m_latestBlockSent = m_syncingLatestHash;
+// host()->m_latestBlockSent = m_syncingLatestHash;
}
else
{
@@ -174,7 +169,7 @@ void EthereumPeer::transition(Asking _a, bool _force)
{
if (m_asking == Asking::Blocks)
{
- clogS(NetNote) << "Finishing blocks fetch...";
+ clog(NetNote) << "Finishing blocks fetch...";
// a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry.
if (isSyncing())
@@ -187,7 +182,7 @@ void EthereumPeer::transition(Asking _a, bool _force)
}
else if (m_asking == Asking::Hashes)
{
- clogS(NetNote) << "Finishing hashes fetch...";
+ clog(NetNote) << "Finishing hashes fetch...";
setAsking(Asking::Nothing, false);
}
@@ -202,7 +197,7 @@ void EthereumPeer::transition(Asking _a, bool _force)
return;
}
- clogS(NetWarn) << "Invalid state transition:" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
+ clog(NetWarn) << "Invalid state transition:" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
}
void EthereumPeer::setAsking(Asking _a, bool _isSyncing)
@@ -270,14 +265,14 @@ void EthereumPeer::attemptSync()
{
if (m_asking != Asking::Nothing)
{
- clogS(NetAllDetail) << "Can't synced with this peer - outstanding asks.";
+ clog(NetAllDetail) << "Can't synced with this peer - outstanding asks.";
return;
}
// if already done this, then ignore.
if (!needsSyncing())
{
- clogS(NetAllDetail) << "Already synced with this peer.";
+ clog(NetAllDetail) << "Already synced with this peer.";
return;
}
@@ -285,16 +280,16 @@ void EthereumPeer::attemptSync()
unsigned n = host()->m_chain.number();
u256 td = host()->m_chain.details().totalDifficulty;
- clogS(NetAllDetail) << "Attempt chain-grab? Latest:" << c.abridged() << ", number:" << n << ", TD:" << td << " versus " << m_totalDifficulty;
+ clog(NetAllDetail) << "Attempt chain-grab? Latest:" << c.abridged() << ", number:" << n << ", TD:" << td << " versus " << m_totalDifficulty;
if (td >= m_totalDifficulty)
{
- clogS(NetAllDetail) << "No. Our chain is better.";
+ clog(NetAllDetail) << "No. Our chain is better.";
resetNeedsSyncing();
transition(Asking::Nothing);
}
else
{
- clogS(NetAllDetail) << "Yes. Their chain is better.";
+ clog(NetAllDetail) << "Yes. Their chain is better.";
transition(Asking::Hashes);
}
}
@@ -315,7 +310,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
m_latestHash = _r[3].toHash();
auto genesisHash = _r[4].toHash();
- clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged();
+ clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged();
if (genesisHash != host()->m_chain.genesisHash())
disable("Invalid genesis hash");
@@ -334,7 +329,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case GetTransactionsPacket: break; // DEPRECATED.
case TransactionsPacket:
{
- clogS(NetAllDetail) << "Transactions (" << dec << _r.itemCount() << "entries)";
+ clog(NetAllDetail) << "Transactions (" << dec << _r.itemCount() << "entries)";
Guard l(x_knownTransactions);
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
@@ -363,7 +358,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{
h256 later = _r[0].toHash();
unsigned limit = _r[1].toInt();
- clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")";
+ clog(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")";
unsigned c = min(host()->m_chain.number(later), limit);
@@ -378,7 +373,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case BlockHashesPacket:
{
- clogS(NetMessageSummary) << "BlockHashes (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreHashes");
+ clog(NetMessageSummary) << "BlockHashes (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreHashes");
if (m_asking != Asking::Hashes)
{
@@ -390,37 +385,67 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
transition(Asking::Blocks);
return true;
}
+ unsigned knowns = 0;
+ unsigned unknowns = 0;
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
addRating(1);
auto h = _r[i].toHash();
- if (host()->m_chain.isKnown(h))
+ auto status = host()->m_bq.blockStatus(h);
+ if (status == QueueStatus::Importing || status == QueueStatus::Ready || host()->m_chain.isKnown(h))
{
+ clog(NetMessageSummary) << "block hash ready:" << h << ". Start blocks download...";
transition(Asking::Blocks);
return true;
}
- else
+ else if (status == QueueStatus::Bad)
+ {
+ cwarn << "block hash bad!" << h << ". Bailing...";
+ transition(Asking::Nothing);
+ return true;
+ }
+ else if (status == QueueStatus::Unknown)
+ {
+ unknowns++;
m_syncingNeededBlocks.push_back(h);
+ }
+ else
+ knowns++;
+ m_syncingLastReceivedHash = h;
}
+ clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLastReceivedHash.abridged();
// run through - ask for more.
transition(Asking::Hashes);
break;
}
case GetBlocksPacket:
{
- clogS(NetMessageSummary) << "GetBlocks (" << dec << _r.itemCount() << "entries)";
+ unsigned count = _r.itemCount();
+ clog(NetMessageSummary) << "GetBlocks (" << dec << count << "entries)";
+
+ if (!count)
+ {
+ clog(NetImpolite) << "Zero-entry GetBlocks: Not replying.";
+ addRating(-10);
+ break;
+ }
// return the requested blocks.
bytes rlp;
unsigned n = 0;
- for (unsigned i = 0; i < _r.itemCount() && i <= c_maxBlocks; ++i)
+ for (unsigned i = 0; i < min(count, c_maxBlocks); ++i)
{
- auto b = host()->m_chain.block(_r[i].toHash());
- if (b.size())
+ auto h = _r[i].toHash();
+ if (host()->m_chain.isKnown(h))
{
- rlp += b;
+ rlp += host()->m_chain.block(_r[i].toHash());
++n;
}
}
+ if (count > 20 && n == 0)
+ clog(NetWarn) << "all" << count << "unknown blocks requested; peer on different chain?";
+ else
+ clog(NetMessageSummary) << n << "blocks known and returned;" << (min(count, c_maxBlocks) - n) << "blocks unknown;" << (count > c_maxBlocks ? count - c_maxBlocks : 0) << "blocks ignored";
+
addRating(0);
RLPStream s;
prep(s, BlocksPacket, n).appendRaw(rlp, n);
@@ -429,10 +454,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case BlocksPacket:
{
- clogS(NetMessageSummary) << "Blocks (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreBlocks");
+ clog(NetMessageSummary) << "Blocks (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreBlocks");
if (m_asking != Asking::Blocks)
- clogS(NetWarn) << "Unexpected Blocks received!";
+ clog(NetWarn) << "Unexpected Blocks received!";
if (_r.itemCount() == 0)
{
@@ -476,6 +501,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case ImportResult::UnknownParent:
unknown++;
break;
+
+ default:;
}
}
else
@@ -485,7 +512,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
}
- clogS(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
+ clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
if (m_asking == Asking::Blocks)
{
@@ -499,7 +526,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case NewBlockPacket:
{
auto h = BlockInfo::headerHash(_r[0].data());
- clogS(NetMessageSummary) << "NewBlock: " << h.abridged();
+ clog(NetMessageSummary) << "NewBlock: " << h.abridged();
if (_r.itemCount() != 2)
disable("NewBlock without 2 data fields.");
@@ -524,9 +551,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
break;
case ImportResult::UnknownParent:
- clogS(NetMessageSummary) << "Received block with no known parent. Resyncing...";
+ clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
setNeedsSyncing(h, _r[1].toInt());
break;
+ default:;
}
Guard l(x_knownBlocks);
@@ -540,11 +568,11 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
catch (Exception const& _e)
{
- clogS(NetWarn) << "Peer causing an Exception:" << _e.what() << _r;
+ clog(NetWarn) << "Peer causing an Exception:" << _e.what() << _r;
}
catch (std::exception const& _e)
{
- clogS(NetWarn) << "Peer causing an exception:" << _e.what() << _r;
+ clog(NetWarn) << "Peer causing an exception:" << _e.what() << _r;
}
return true;
diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h
index da144134b..a80d5dadd 100644
--- a/libethereum/EthereumPeer.h
+++ b/libethereum/EthereumPeer.h
@@ -129,6 +129,7 @@ private:
/// This is built as we ask for hashes. Once no more hashes are given, we present this to the
/// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks.
h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer.
+ h256 m_syncingLastReceivedHash; ///< Hash more recently received from peer.
h256 m_syncingLatestHash; ///< Peer's latest block's hash, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Peer's latest block's total difficulty, as of the current sync.
diff --git a/libethereum/Farm.h b/libethereum/Farm.h
index 6263faf1b..fda9d64c3 100644
--- a/libethereum/Farm.h
+++ b/libethereum/Farm.h
@@ -155,12 +155,15 @@ private:
{
if (m_onSolutionFound && m_onSolutionFound(_s))
{
- WriteGuard ul(x_minerWork);
- for (std::shared_ptr const& m: m_miners)
- if (m.get() != _m)
- m->setWork();
- m_work.reset();
- return true;
+ 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;
}
diff --git a/libethereum/State.cpp b/libethereum/State.cpp
index 65f267b0f..598dac37d 100644
--- a/libethereum/State.cpp
+++ b/libethereum/State.cpp
@@ -276,6 +276,7 @@ bool State::sync(BlockChain const& _bc)
bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi, ImportRequirements::value _ir)
{
+ (void)_ir;
bool ret = false;
// BLOCK
BlockInfo bi = _bi ? _bi : _bc.info(_block);
@@ -315,6 +316,24 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi, Impor
// No change since last sync.
// Carry on as we were.
}
+ else
+ {
+ // New blocks available, or we've switched to a different branch. All change.
+ // Find most recent state dump and replay what's left.
+ // (Most recent state dump might end up being genesis.)
+
+ if (m_db.lookup(bi.stateRoot).empty())
+ {
+ cwarn << "Unable to sync to" << bi.hash().abridged() << "; state root" << bi.stateRoot.abridged() << "not found in database.";
+ cwarn << "Database corrupt: contains block without stateRoot:" << bi;
+ cwarn << "Bailing.";
+ exit(-1);
+ }
+ m_previousBlock = bi;
+ resetCurrent();
+ ret = true;
+ }
+#if ALLOW_REBUILD
else
{
// New blocks available, or we've switched to a different branch. All change.
@@ -352,6 +371,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi, Impor
resetCurrent();
ret = true;
}
+#endif
return ret;
}
@@ -441,41 +461,19 @@ void State::resetCurrent()
paranoia("begin resetCurrent", true);
}
-bool State::cull(TransactionQueue& _tq) const
-{
- bool ret = false;
- auto ts = _tq.transactions();
- for (auto const& i: ts)
- {
- if (!m_transactionSet.count(i.first))
- {
- try
- {
- if (i.second.nonce() < transactionsFrom(i.second.sender()))
- {
- _tq.drop(i.first);
- ret = true;
- }
- }
- catch (...)
- {
- _tq.drop(i.first);
- ret = true;
- }
- }
- }
- return ret;
-}
-
-TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, bool* o_transactionQueueChanged)
+pair State::sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned msTimeout)
{
// TRANSACTIONS
- TransactionReceipts ret;
+ pair ret;
+ ret.second = false;
+
auto ts = _tq.transactions();
LastHashes lh;
- for (int goodTxs = 1; goodTxs;)
+ auto deadline = chrono::steady_clock::now() + chrono::milliseconds(msTimeout);
+
+ for (int goodTxs = 1; goodTxs; )
{
goodTxs = 0;
for (auto const& i: ts)
@@ -489,51 +487,67 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga
if (lh.empty())
lh = _bc.lastHashes();
execute(lh, i.second);
- ret.push_back(m_receipts.back());
+ ret.first.push_back(m_receipts.back());
_tq.noteGood(i);
++goodTxs;
// cnote << "TX took:" << t.elapsed() * 1000;
}
+ else if (i.second.gasPrice() < _gp.ask(*this) * 9 / 10)
+ {
+ // less than 90% of our ask price for gas. drop.
+ cnote << i.first.abridged() << "Dropping El Cheapo transaction (<90% of ask price)";
+ _tq.drop(i.first);
+ }
}
-#if ETH_DEBUG
catch (InvalidNonce const& in)
{
- bigint const* req = boost::get_error_info(in);
- bigint const* got = boost::get_error_info(in);
+ bigint const& req = *boost::get_error_info(in);
+ bigint const& got = *boost::get_error_info(in);
- if (*req > *got)
+ if (req > got)
{
// too old
+ cnote << i.first.abridged() << "Dropping old transaction (nonce too low)";
+ _tq.drop(i.first);
+ }
+ else if (got > req + 5)
+ {
+ // too new
+ cnote << i.first.abridged() << "Dropping new transaction (> 5 nonces ahead)";
_tq.drop(i.first);
- if (o_transactionQueueChanged)
- *o_transactionQueueChanged = true;
}
else
_tq.setFuture(i);
}
catch (BlockGasLimitReached const& e)
{
- _tq.setFuture(i);
+ bigint const& got = *boost::get_error_info(e);
+ if (got > m_currentBlock.gasLimit)
+ {
+ cnote << i.first.abridged() << "Dropping over-gassy transaction (gas > block's gas limit)";
+ _tq.drop(i.first);
+ }
+ else
+ _tq.setFuture(i);
}
-#endif
catch (Exception const& _e)
{
// Something else went wrong - drop it.
+ cnote << i.first.abridged() << "Dropping invalid transaction:" << diagnostic_information(_e);
_tq.drop(i.first);
- if (o_transactionQueueChanged)
- *o_transactionQueueChanged = true;
- cnote << "Dropping invalid transaction:";
- cnote << diagnostic_information(_e);
}
catch (std::exception const&)
{
// Something else went wrong - drop it.
_tq.drop(i.first);
- if (o_transactionQueueChanged)
- *o_transactionQueueChanged = true;
- cnote << "Transaction caused low-level exception :(";
+ cnote << i.first.abridged() << "Transaction caused low-level exception :(";
}
}
+ if (chrono::steady_clock::now() > deadline)
+ {
+ ret.second = true;
+ break;
+ }
}
return ret;
}
@@ -649,7 +663,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
uncle.verifyParent(uncleParent);
nonces.insert(uncle.nonce);
- tdIncrease += uncle.difficulty;
+// tdIncrease += uncle.difficulty;
rewarded.push_back(uncle);
}
@@ -690,13 +704,15 @@ void State::cleanup(bool _fullCommit)
paranoia("immediately before database commit", true);
// Commit the new trie to disk.
+ clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot.abridged() << "=" << rootHash().abridged() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
m_db.commit();
+ clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot.abridged() << "=" << rootHash().abridged() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
paranoia("immediately after database commit", true);
m_previousBlock = m_currentBlock;
m_currentBlock.populateFromParent(m_previousBlock);
- cdebug << "finalising enactment. current -> previous, hash is" << m_previousBlock.hash().abridged();
+ clog(StateTrace) << "finalising enactment. current -> previous, hash is" << m_previousBlock.hash().abridged();
}
else
m_db.rollback();
diff --git a/libethereum/State.h b/libethereum/State.h
index c7ad1e0e6..28b005243 100644
--- a/libethereum/State.h
+++ b/libethereum/State.h
@@ -200,14 +200,9 @@ public:
/// Only valid after mine() returns true.
bytes const& blockData() const { return m_currentBytes; }
- // TODO: Cleaner interface.
/// Sync our transactions, killing those from the queue that we have and assimilating those that we don't.
- /// @returns a list of receipts one for each transaction placed from the queue into the state.
- /// @a o_transactionQueueChanged boolean pointer, the value of which will be set to true if the transaction queue
- /// changed and the pointer is non-null
- TransactionReceipts sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, bool* o_transactionQueueChanged = nullptr);
- /// Like sync but only operate on _tq, killing the invalid/old ones.
- bool cull(TransactionQueue& _tq) const;
+ /// @returns a list of receipts one for each transaction placed from the queue into the state and bool, true iff there are more transactions to be processed.
+ std::pair sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned _msTimeout = 100);
/// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly.
diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp
index 506de2d2f..c23fd8db4 100644
--- a/libethereum/TransactionQueue.cpp
+++ b/libethereum/TransactionQueue.cpp
@@ -28,7 +28,7 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
-ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb)
+ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb, IfDropped _ik)
{
// Check if we already know this transaction.
h256 h = sha3(_transactionRLP);
@@ -39,6 +39,9 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallb
if (m_known.count(h))
return ImportResult::AlreadyKnown;
+ if (m_dropped.count(h) && _ik == IfDropped::Ignore)
+ return ImportResult::AlreadyInChain;
+
try
{
// Check validity of _transactionRLP as a transaction. To do this we just deserialise and attempt to determine the sender.
@@ -88,7 +91,7 @@ void TransactionQueue::noteGood(std::pair const& _t)
m_unknown.erase(r.first, r.second);
}
-void TransactionQueue::drop(h256 _txHash)
+void TransactionQueue::drop(h256 const& _txHash)
{
UpgradableGuard l(m_lock);
@@ -96,6 +99,7 @@ void TransactionQueue::drop(h256 _txHash)
return;
UpgradeGuard ul(l);
+ m_dropped.insert(_txHash);
m_known.erase(_txHash);
if (m_current.count(_txHash))
diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h
index c3df00d25..e2d1c3aee 100644
--- a/libethereum/TransactionQueue.h
+++ b/libethereum/TransactionQueue.h
@@ -22,7 +22,6 @@
#pragma once
#include
-#include
#include
#include
#include
@@ -39,6 +38,8 @@ class BlockChain;
struct TransactionQueueChannel: public LogChannel { static const char* name() { return "->Q"; } static const int verbosity = 4; };
#define ctxq dev::LogOutputStream()
+enum class IfDropped { Ignore, Retry };
+
/**
* @brief A queue of Transactions, each stored as RLP.
* @threadsafe
@@ -48,10 +49,10 @@ class TransactionQueue
public:
using ImportCallback = std::function;
- ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback()) { return import(&_tx, _cb); }
- ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback());
+ ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback(), IfDropped _ik = IfDropped::Ignore) { return import(&_tx, _cb, _ik); }
+ ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback(), IfDropped _ik = IfDropped::Ignore);
- void drop(h256 _txHash);
+ void drop(h256 const& _txHash);
std::map transactions() const { ReadGuard l(m_lock); return m_current; }
std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_unknown.size()); }
@@ -63,11 +64,12 @@ public:
template Handler onReady(T const& _t) { return m_onReady.add(_t); }
private:
- mutable boost::shared_mutex m_lock; ///< General lock.
+ mutable SharedMutex m_lock; ///< General lock.
std::set m_known; ///< Hashes of transactions in both sets.
std::map m_current; ///< Map of SHA3(tx) to tx.
std::multimap> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX.
std::map> m_callbacks; ///< Called once.
+ std::set m_dropped; ///< Transactions that have previously been dropped.
Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast.
};
diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp
index bf4ea2145..ae8567e2a 100644
--- a/libevmcore/Assembly.cpp
+++ b/libevmcore/Assembly.cpp
@@ -24,7 +24,7 @@
#include
#include
#include
-
+#include
using namespace std;
using namespace dev;
using namespace dev::eth;
@@ -65,6 +65,13 @@ void Assembly::append(Assembly const& _a, int _deposit)
}
}
+string Assembly::out() const
+{
+ stringstream ret;
+ stream(ret);
+ return ret.str();
+}
+
unsigned Assembly::bytesRequired() const
{
for (unsigned br = 1;; ++br)
@@ -101,7 +108,7 @@ string Assembly::getLocationFromSources(StringMap const& _sourceCodes, SourceLoc
return move(cut);
}
-ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const
+ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const
{
_out << _prefix << ".code:" << endl;
for (AssemblyItem const& i: m_items)
@@ -157,6 +164,115 @@ ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const&
return _out;
}
+Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) const
+{
+ Json::Value value;
+ value["name"] = _name;
+ value["begin"] = _begin;
+ value["end"] = _end;
+ if (!_value.empty())
+ value["value"] = _value;
+ if (!_jumpType.empty())
+ value["jumpType"] = _jumpType;
+ return value;
+}
+
+string toStringInHex(u256 _value)
+{
+ std::stringstream hexStr;
+ hexStr << hex << _value;
+ return hexStr.str();
+}
+
+Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes) const
+{
+ Json::Value root;
+
+ Json::Value collection(Json::arrayValue);
+ for (AssemblyItem const& i: m_items)
+ {
+ switch (i.type())
+ {
+ case Operation:
+ collection.append(
+ createJsonValue(instructionInfo(i.instruction()).name, i.getLocation().start, i.getLocation().end, i.getJumpTypeAsString()));
+ break;
+ case Push:
+ collection.append(
+ createJsonValue("PUSH", i.getLocation().start, i.getLocation().end, toStringInHex(i.data()), i.getJumpTypeAsString()));
+ break;
+ case PushString:
+ collection.append(
+ createJsonValue("PUSH tag", i.getLocation().start, i.getLocation().end, m_strings.at((h256)i.data())));
+ break;
+ case PushTag:
+ collection.append(
+ createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
+ break;
+ case PushSub:
+ collection.append(
+ createJsonValue("PUSH [$]", i.getLocation().start, i.getLocation().end, dev::toString(h256(i.data()))));
+ break;
+ case PushSubSize:
+ collection.append(
+ createJsonValue("PUSH #[$]", i.getLocation().start, i.getLocation().end, dev::toString(h256(i.data()))));
+ break;
+ case PushProgramSize:
+ collection.append(
+ createJsonValue("PUSHSIZE", i.getLocation().start, i.getLocation().end));
+ break;
+ case Tag:
+ {
+ collection.append(
+ createJsonValue("tag", i.getLocation().start, i.getLocation().end, string(i.data())));
+ collection.append(
+ createJsonValue("JUMDEST", i.getLocation().start, i.getLocation().end));
+ }
+ break;
+ case PushData:
+ {
+ Json::Value pushData;
+ pushData["name"] = "PUSH hex";
+ collection.append(createJsonValue("PUSH hex", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
+ }
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InvalidOpcode());
+ }
+ }
+
+ root[".code"] = collection;
+
+ if (!m_data.empty() || !m_subs.empty())
+ {
+ Json::Value data;
+ for (auto const& i: m_data)
+ if (u256(i.first) >= m_subs.size())
+ data[toStringInHex((u256)i.first)] = toHex(i.second);
+
+ for (size_t i = 0; i < m_subs.size(); ++i)
+ {
+ std::stringstream hexStr;
+ hexStr << hex << i;
+ data[hexStr.str()] = m_subs[i].stream(_out, "", _sourceCodes, true);
+ }
+ root[".data"] = data;
+ _out << root;
+ }
+ return root;
+}
+
+Json::Value Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes, bool _inJsonFormat) const
+{
+ if (_inJsonFormat)
+ return streamAsmJson(_out, _sourceCodes);
+ else
+ {
+ streamAsm(_out, _prefix, _sourceCodes);
+ return Json::Value();
+ }
+}
+
AssemblyItem const& Assembly::append(AssemblyItem const& _i)
{
m_deposit += _i.deposit();
diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h
index 2744af900..4ac873682 100644
--- a/libevmcore/Assembly.h
+++ b/libevmcore/Assembly.h
@@ -29,7 +29,12 @@
#include
#include
#include "Exceptions.h"
+#include
+namespace Json
+{
+class Value;
+}
namespace dev
{
namespace eth
@@ -76,7 +81,7 @@ public:
void popTo(int _deposit) { while (m_deposit > _deposit) append(Instruction::POP); }
void injectStart(AssemblyItem const& _i);
- std::string out() const { std::stringstream ret; stream(ret); return ret.str(); }
+ std::string out() const;
int deposit() const { return m_deposit; }
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
@@ -86,13 +91,24 @@ public:
bytes assemble() const;
Assembly& optimise(bool _enable);
- std::ostream& stream(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const;
+ Json::Value stream(
+ std::ostream& _out,
+ std::string const& _prefix = "",
+ const StringMap &_sourceCodes = StringMap(),
+ bool _inJsonFormat = false
+ ) const;
protected:
std::string getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const;
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
unsigned bytesRequired() const;
+private:
+ Json::Value streamAsmJson(std::ostream& _out, const StringMap &_sourceCodes) const;
+ std::ostream& streamAsm(std::ostream& _out, std::string const& _prefix, StringMap const& _sourceCodes) const;
+ Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()) const;
+
+protected:
unsigned m_usedTags = 0;
AssemblyItems m_items;
mutable std::map m_data;
diff --git a/libevmcore/CMakeLists.txt b/libevmcore/CMakeLists.txt
index 6a834936b..83a4e115c 100644
--- a/libevmcore/CMakeLists.txt
+++ b/libevmcore/CMakeLists.txt
@@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST)
+include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})
diff --git a/liblll/CMakeLists.txt b/liblll/CMakeLists.txt
index e60522a7c..b865d4afe 100644
--- a/liblll/CMakeLists.txt
+++ b/liblll/CMakeLists.txt
@@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST)
+include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})
diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp
index f59fd8cdd..ecc458730 100644
--- a/libp2p/Capability.cpp
+++ b/libp2p/Capability.cpp
@@ -27,19 +27,14 @@ using namespace std;
using namespace dev;
using namespace dev::p2p;
-#if defined(clogS)
-#undef clogS
-#endif
-#define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] "
-
Capability::Capability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset): m_session(_s), m_host(_h), m_idOffset(_idOffset)
{
- clogS(NetConnect) << "New session for capability" << m_host->name() << "; idOffset:" << m_idOffset;
+ clog(NetConnect) << "New session for capability" << m_host->name() << "; idOffset:" << m_idOffset;
}
void Capability::disable(std::string const& _problem)
{
- clogS(NetWarn) << "DISABLE: Disabling capability '" << m_host->name() << "'. Reason:" << _problem;
+ clog(NetWarn) << "DISABLE: Disabling capability '" << m_host->name() << "'. Reason:" << _problem;
m_enabled = false;
}
diff --git a/libp2p/Common.h b/libp2p/Common.h
index 9ad5bb206..691ef7fb3 100644
--- a/libp2p/Common.h
+++ b/libp2p/Common.h
@@ -77,6 +77,7 @@ struct InvalidHostIPAddress: virtual dev::Exception {};
struct NetWarn: public LogChannel { static const char* name() { return "!N!"; } static const int verbosity = 0; };
struct NetNote: public LogChannel { static const char* name() { return "*N*"; } static const int verbosity = 1; };
+struct NetImpolite: public LogChannel { static const char* name() { return "#!*"; } static const int verbosity = 1; };
struct NetMessageSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 2; };
struct NetConnect: public LogChannel { static const char* name() { return "+N+"; } static const int verbosity = 10; };
struct NetMessageDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 5; };
diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp
index 428b94e7c..e65c6660b 100644
--- a/libp2p/NodeTable.cpp
+++ b/libp2p/NodeTable.cpp
@@ -382,7 +382,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
// h256 + Signature + type + RLP (smallest possible packet is empty neighbours packet which is 3 bytes)
if (_packet.size() < h256::size + Signature::size + 1 + 3)
{
- clog(NodeTableWarn) << "Invalid Message size from " << _from.address().to_string() << ":" << _from.port();
+ clog(NodeTableTriviaSummary) << "Invalid message size from " << _from.address().to_string() << ":" << _from.port();
return;
}
@@ -390,7 +390,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
h256 hashSigned(sha3(hashedBytes));
if (!_packet.cropped(0, h256::size).contentsEqual(hashSigned.asBytes()))
{
- clog(NodeTableWarn) << "Invalid Message hash from " << _from.address().to_string() << ":" << _from.port();
+ clog(NodeTableTriviaSummary) << "Invalid message hash from " << _from.address().to_string() << ":" << _from.port();
return;
}
@@ -402,7 +402,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
Public nodeid(dev::recover(*(Signature const*)sigBytes.data(), sha3(signedBytes)));
if (!nodeid)
{
- clog(NodeTableWarn) << "Invalid Message signature from " << _from.address().to_string() << ":" << _from.port();
+ clog(NodeTableTriviaSummary) << "Invalid message signature from " << _from.address().to_string() << ":" << _from.port();
return;
}
@@ -469,7 +469,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
if (!expected)
{
- clog(NetConnect) << "Dropping unsolicited Neighbours packet from " << _from.address();
+ clog(NetConnect) << "Dropping unsolicited neighbours packet from " << _from.address();
break;
}
@@ -517,7 +517,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
}
default:
- clog(NodeTableWarn) << "Invalid Message, " << hex << packetType << ", received from " << _from.address().to_string() << ":" << dec << _from.port();
+ clog(NodeTableWarn) << "Invalid message, " << hex << packetType << ", received from " << _from.address().to_string() << ":" << dec << _from.port();
return;
}
diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp
index bb9af2ef7..fbf0d9fdf 100644
--- a/libp2p/RLPxHandshake.cpp
+++ b/libp2p/RLPxHandshake.cpp
@@ -249,7 +249,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
bytesRef frame(&m_handshakeInBuffer);
if (!m_io->authAndDecryptFrame(frame))
{
- clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed";
+ clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed";
m_nextState = Error;
transition();
return;
@@ -258,13 +258,13 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
PacketType packetType = (PacketType)(frame[0] == 0x80 ? 0x0 : frame[0]);
if (packetType != 0)
{
- clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type";
+ clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type";
m_nextState = Error;
transition();
return;
}
- clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session.";
+ clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session.";
RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall);
m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint());
}
diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp
index cdc1a18fd..cd4bccbf0 100644
--- a/libp2p/Session.cpp
+++ b/libp2p/Session.cpp
@@ -33,11 +33,6 @@ using namespace std;
using namespace dev;
using namespace dev::p2p;
-#if defined(clogS)
-#undef clogS
-#endif
-#define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] "
-
Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr const& _n, PeerSessionInfo _info):
m_server(_s),
m_io(_io),
@@ -53,7 +48,8 @@ Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr const& _n, Pe
Session::~Session()
{
- clogS(NetMessageSummary) << "Closing Peer Session :-(";
+ ThreadContext tc(info().id.abridged() + " | " + info().clientVersion);
+ clog(NetMessageSummary) << "Closing peer session :-(";
m_peer->m_lastConnected = m_peer->m_lastAttempted - chrono::seconds(1);
// Read-chain finished for one reason or another.
@@ -121,6 +117,8 @@ void Session::ensureNodesRequested()
void Session::serviceNodesRequest()
{
+ ThreadContext tc(info().id.abridged() + "/" + info().clientVersion);
+
if (!m_theyRequestedNodes)
return;
@@ -139,7 +137,7 @@ void Session::serviceNodesRequest()
auto rs = randomSelection(peers, 10);
for (auto const& i: rs)
{
- clogS(NetTriviaDetail) << "Sending peer " << i.id.abridged() << i.endpoint;
+ clog(NetTriviaDetail) << "Sending peer " << i.id.abridged() << i.endpoint;
if (i.endpoint.address.is_v4())
s.appendList(3) << bytesConstRef(i.endpoint.address.to_v4().to_bytes().data(), 4) << i.endpoint.tcpPort << i.id;
else// if (i.second.address().is_v6()) - assumed
@@ -154,7 +152,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
{
m_lastReceived = chrono::steady_clock::now();
- clogS(NetRight) << _t << _r;
+ clog(NetRight) << _t << _r;
try // Generic try-catch block designed to capture RLP format errors - TODO: give decent diagnostics, make a bit more specific over what is caught.
{
switch (_t)
@@ -168,28 +166,28 @@ bool Session::interpret(PacketType _t, RLP const& _r)
else
{
reason = reasonOf(r);
- clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")";
+ clog(NetMessageSummary) << "Disconnect (reason: " << reason << ")";
drop(DisconnectRequested);
}
break;
}
case PingPacket:
{
- clogS(NetTriviaSummary) << "Ping";
+ clog(NetTriviaSummary) << "Ping";
RLPStream s;
sealAndSend(prep(s, PongPacket));
break;
}
case PongPacket:
m_info.lastPing = std::chrono::steady_clock::now() - m_ping;
- clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast(m_info.lastPing).count() << " ms";
+ clog(NetTriviaSummary) << "Latency: " << chrono::duration_cast(m_info.lastPing).count() << " ms";
break;
case GetPeersPacket:
// Disabled for interop testing.
// GetPeers/PeersPacket will be modified to only exchange new nodes which it's peers are interested in.
break;
- clogS(NetTriviaSummary) << "GetPeers";
+ clog(NetTriviaSummary) << "GetPeers";
m_theyRequestedNodes = true;
serviceNodesRequest();
break;
@@ -198,7 +196,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
// GetPeers/PeersPacket will be modified to only exchange new nodes which it's peers are interested in.
break;
- clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
+ clog(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
m_weRequestedNodes = false;
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
@@ -216,7 +214,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt());
NodeId id = _r[i][2].toHash();
- clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")";
+ clog(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")";
if (!isPublicAddress(peerAddress))
goto CONTINUE; // Private address. Ignore.
@@ -239,7 +237,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
// OK passed all our checks. Assume it's good.
addRating(1000);
m_server->addNode(id, NodeIPEndpoint(ep.address(), ep.port(), ep.port()));
- clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")";
+ clog(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")";
CONTINUE:;
LAMEPEER:;
}
@@ -258,7 +256,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
}
catch (std::exception const& _e)
{
- clogS(NetWarn) << "Peer causing an exception:" << _e.what() << _r;
+ clog(NetWarn) << "Peer causing an exception:" << _e.what() << _r;
disconnect(BadProtocol);
return true;
}
@@ -298,11 +296,11 @@ bool Session::checkPacket(bytesConstRef _msg)
void Session::send(bytes&& _msg)
{
- clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1));
+ clog(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1));
bytesConstRef msg(&_msg);
if (!checkPacket(msg))
- clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!";
+ clog(NetWarn) << "INVALID PACKET CONSTRUCTED!";
if (!m_socket.is_open())
return;
@@ -325,10 +323,11 @@ void Session::write()
auto self(shared_from_this());
ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/)
{
+ ThreadContext tc(info().id.abridged() + " | " + info().clientVersion);
// must check queue, as write callback can occur following dropped()
if (ec)
{
- clogS(NetWarn) << "Error sending: " << ec.message();
+ clog(NetWarn) << "Error sending: " << ec.message();
drop(TCPError);
return;
}
@@ -350,7 +349,7 @@ void Session::drop(DisconnectReason _reason)
if (m_socket.is_open())
try
{
- clogS(NetConnect) << "Closing " << m_socket.remote_endpoint() << "(" << reasonOf(_reason) << ")";
+ clog(NetConnect) << "Closing " << m_socket.remote_endpoint() << "(" << reasonOf(_reason) << ")";
boost::system::error_code ec;
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
m_socket.close();
@@ -368,7 +367,7 @@ void Session::drop(DisconnectReason _reason)
void Session::disconnect(DisconnectReason _reason)
{
- clogS(NetConnect) << "Disconnecting (our reason:" << reasonOf(_reason) << ")";
+ clog(NetConnect) << "Disconnecting (our reason:" << reasonOf(_reason) << ")";
StructuredLogger::p2pDisconnected(
m_info.id.abridged(),
m_peer->endpoint, // TODO: may not be 100% accurate
@@ -398,9 +397,10 @@ void Session::doRead()
auto self(shared_from_this());
ba::async_read(m_socket, boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length)
{
+ ThreadContext tc(info().id.abridged() + " | " + info().clientVersion);
if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof)
{
- clogS(NetWarn) << "Error reading: " << ec.message();
+ clog(NetWarn) << "Error reading: " << ec.message();
drop(TCPError);
}
else if (ec && length == 0)
@@ -433,9 +433,10 @@ void Session::doRead()
auto tlen = frameSize + ((16 - (frameSize % 16)) % 16) + h128::size;
ba::async_read(m_socket, boost::asio::buffer(m_data, tlen), [this, self, headerRLP, frameSize, tlen](boost::system::error_code ec, std::size_t length)
{
+ ThreadContext tc(info().id.abridged() + " | " + info().clientVersion);
if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof)
{
- clogS(NetWarn) << "Error reading: " << ec.message();
+ clog(NetWarn) << "Error reading: " << ec.message();
drop(TCPError);
}
else if (ec && length == 0)
@@ -453,7 +454,7 @@ void Session::doRead()
if (!checkPacket(frame))
{
cerr << "Received " << frame.size() << ": " << toHex(frame) << endl;
- clogS(NetWarn) << "INVALID MESSAGE RECEIVED";
+ clog(NetWarn) << "INVALID MESSAGE RECEIVED";
disconnect(BadProtocol);
return;
}
@@ -462,7 +463,7 @@ void Session::doRead()
auto packetType = (PacketType)RLP(frame.cropped(0, 1)).toInt();
RLP r(frame.cropped(1));
if (!interpret(packetType, r))
- clogS(NetWarn) << "Couldn't interpret packet." << RLP(r);
+ clog(NetWarn) << "Couldn't interpret packet." << RLP(r);
}
doRead();
}
diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp
index acf399349..78b83d064 100644
--- a/libsolidity/AST.cpp
+++ b/libsolidity/AST.cpp
@@ -55,6 +55,7 @@ void ContractDefinition::checkTypeRequirements()
checkDuplicateFunctions();
checkIllegalOverrides();
checkAbstractFunctions();
+ checkAbstractConstructors();
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
@@ -181,6 +182,45 @@ void ContractDefinition::checkAbstractFunctions()
}
}
+void ContractDefinition::checkAbstractConstructors()
+{
+ set argumentsNeeded;
+ // check that we get arguments for all base constructors that need it.
+ // If not mark the contract as abstract (not fully implemented)
+
+ vector const& bases = getLinearizedBaseContracts();
+ for (ContractDefinition const* contract: bases)
+ if (FunctionDefinition const* constructor = contract->getConstructor())
+ if (contract != this && !constructor->getParameters().empty())
+ argumentsNeeded.insert(contract);
+
+ for (ContractDefinition const* contract: bases)
+ {
+ if (FunctionDefinition const* constructor = contract->getConstructor())
+ for (auto const& modifier: constructor->getModifiers())
+ {
+ auto baseContract = dynamic_cast(
+ &modifier->getName()->getReferencedDeclaration()
+ );
+ if (baseContract)
+ argumentsNeeded.erase(baseContract);
+ }
+
+
+ for (ASTPointer const& base: contract->getBaseContracts())
+ {
+ auto baseContract = dynamic_cast(
+ &base->getName()->getReferencedDeclaration()
+ );
+ solAssert(baseContract, "");
+ if (!base->getArguments().empty())
+ argumentsNeeded.erase(baseContract);
+ }
+ }
+ if (!argumentsNeeded.empty())
+ setFullyImplemented(false);
+}
+
void ContractDefinition::checkIllegalOverrides() const
{
// TODO unify this at a later point. for this we need to put the constness and the access specifier
@@ -354,7 +394,7 @@ void InheritanceSpecifier::checkTypeRequirements()
ContractDefinition const* base = dynamic_cast(&m_baseName->getReferencedDeclaration());
solAssert(base, "Base contract not available.");
TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes();
- if (parameterTypes.size() != m_arguments.size())
+ if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
@@ -421,8 +461,8 @@ void FunctionDefinition::checkTypeRequirements()
}
for (ASTPointer const& modifier: m_functionModifiers)
modifier->checkTypeRequirements(isConstructor() ?
- dynamic_cast(*getScope()).getBaseContracts() :
- vector>());
+ dynamic_cast(*getScope()).getLinearizedBaseContracts() :
+ vector());
if (m_body)
m_body->checkTypeRequirements();
}
@@ -500,7 +540,7 @@ void ModifierDefinition::checkTypeRequirements()
m_body->checkTypeRequirements();
}
-void ModifierInvocation::checkTypeRequirements(vector> const& _bases)
+void ModifierInvocation::checkTypeRequirements(vector const& _bases)
{
TypePointers argumentTypes;
for (ASTPointer const& argument: m_arguments)
@@ -517,10 +557,10 @@ void ModifierInvocation::checkTypeRequirements(vectorgetParameters();
else
// check parameters for Base constructors
- for (auto const& base: _bases)
- if (declaration == &base->getName()->getReferencedDeclaration())
+ for (ContractDefinition const* base: _bases)
+ if (declaration == base)
{
- if (auto referencedConstructor = dynamic_cast(*declaration).getConstructor())
+ if (auto referencedConstructor = base->getConstructor())
parameters = &referencedConstructor->getParameters();
else
parameters = &emptyParameterList;
@@ -777,7 +817,7 @@ void NewExpression::checkTypeRequirements(TypePointers const*)
if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
if (!m_contract->isFullyImplemented())
- BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an instance of an abstract contract."));
+ BOOST_THROW_EXCEPTION(createTypeError("Trying to create an instance of an abstract contract."));
shared_ptr contractType = make_shared(*m_contract);
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
m_type = make_shared(parameterTypes, TypePointers{contractType},
diff --git a/libsolidity/AST.h b/libsolidity/AST.h
index f0144c7ec..f5f36f6fe 100644
--- a/libsolidity/AST.h
+++ b/libsolidity/AST.h
@@ -287,6 +287,7 @@ private:
void checkDuplicateFunctions() const;
void checkIllegalOverrides() const;
void checkAbstractFunctions();
+ void checkAbstractConstructors();
/// Checks that different functions with external visibility end up having different
/// external argument types (i.e. different signature).
void checkExternalTypeClashes() const;
@@ -382,7 +383,7 @@ class EnumValue: public Declaration
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- TypePointer getType(ContractDefinition const* = nullptr) const;
+ virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
};
/**
@@ -572,7 +573,7 @@ public:
std::vector> const& getArguments() const { return m_arguments; }
/// @param _bases is the list of base contracts for base constructor calls. For modifiers an empty vector should be passed.
- void checkTypeRequirements(std::vector> const& _bases);
+ void checkTypeRequirements(std::vector const& _bases);
private:
ASTPointer m_modifierName;
diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp
index 83954922e..37b577ccd 100644
--- a/libsolidity/Compiler.cpp
+++ b/libsolidity/Compiler.cpp
@@ -136,6 +136,7 @@ void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
FunctionType constructorType(_constructor);
if (!constructorType.getParameterTypes().empty())
{
+ solAssert(m_baseArguments.count(&_constructor), "");
std::vector> const* arguments = m_baseArguments[&_constructor];
solAssert(arguments, "");
for (unsigned i = 0; i < arguments->size(); ++i)
diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h
index 4b1e1b4d6..d476ec684 100644
--- a/libsolidity/Compiler.h
+++ b/libsolidity/Compiler.h
@@ -42,9 +42,10 @@ public:
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
/// @arg _sourceCodes is the map of input files to source code strings
- void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const
+ /// @arg _inJsonFromat shows whether the out should be in Json format
+ void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
{
- m_context.streamAssembly(_stream, _sourceCodes);
+ m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat);
}
/// @returns Assembly items of the normal compiler context
eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); }
diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h
index 53d0a89a4..34a3f97cd 100644
--- a/libsolidity/CompilerContext.h
+++ b/libsolidity/CompilerContext.h
@@ -122,7 +122,11 @@ public:
eth::Assembly const& getAssembly() const { return m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings
- void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.stream(_stream, "", _sourceCodes); }
+ /// @arg _inJsonFormat shows whether the out should be in Json format
+ void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
+ {
+ m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
+ }
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }
diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp
index 1301bfa5d..d6274e2c7 100644
--- a/libsolidity/CompilerStack.cpp
+++ b/libsolidity/CompilerStack.cpp
@@ -157,14 +157,16 @@ bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
return getBytecode();
}
-eth::AssemblyItems const& CompilerStack::getAssemblyItems(string const& _contractName) const
+eth::AssemblyItems const* CompilerStack::getAssemblyItems(string const& _contractName) const
{
- return getContract(_contractName).compiler->getAssemblyItems();
+ Contract const& contract = getContract(_contractName);
+ return contract.compiler ? &getContract(_contractName).compiler->getAssemblyItems() : nullptr;
}
-eth::AssemblyItems const& CompilerStack::getRuntimeAssemblyItems(string const& _contractName) const
+eth::AssemblyItems const* CompilerStack::getRuntimeAssemblyItems(string const& _contractName) const
{
- return getContract(_contractName).compiler->getRuntimeAssemblyItems();
+ Contract const& contract = getContract(_contractName);
+ return contract.compiler ? &getContract(_contractName).compiler->getRuntimeAssemblyItems() : nullptr;
}
bytes const& CompilerStack::getBytecode(string const& _contractName) const
@@ -182,9 +184,13 @@ dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const
return dev::sha3(getRuntimeBytecode(_contractName));
}
-void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes) const
+void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const
{
- getContract(_contractName).compiler->streamAssembly(_outStream, _sourceCodes);
+ Contract const& contract = getContract(_contractName);
+ if (contract.compiler)
+ contract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat);
+ else
+ _outStream << "Contract not fully implemented" << endl;
}
string const& CompilerStack::getInterface(string const& _contractName) const
diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h
index 1cf576ab4..2e7c217d5 100644
--- a/libsolidity/CompilerStack.h
+++ b/libsolidity/CompilerStack.h
@@ -94,16 +94,17 @@ public:
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
/// @returns normal contract assembly items
- eth::AssemblyItems const& getAssemblyItems(std::string const& _contractName = "") const;
+ eth::AssemblyItems const* getAssemblyItems(std::string const& _contractName = "") const;
/// @returns runtime contract assembly items
- eth::AssemblyItems const& getRuntimeAssemblyItems(std::string const& _contractName = "") const;
+ eth::AssemblyItems const* getRuntimeAssemblyItems(std::string const& _contractName = "") const;
/// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
dev::h256 getContractCodeHash(std::string const& _contractName = "") const;
/// Streams a verbose version of the assembly to @a _outStream.
/// @arg _sourceCodes is the map of input files to source code strings
+ /// @arg _inJsonFromat shows whether the out should be in Json format
/// Prerequisite: Successful compilation.
- void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const;
+ void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const;
/// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile.
diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp
index 7fc68c27f..212f3728b 100644
--- a/libweb3jsonrpc/WebThreeStubServerBase.cpp
+++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp
@@ -775,7 +775,7 @@ Json::Value WebThreeStubServerBase::eth_getWork()
return ret;
}
-bool WebThreeStubServerBase::eth_submitWork(string const& _nonce, string const& _mixHash)
+bool WebThreeStubServerBase::eth_submitWork(string const& _nonce, string const&, string const& _mixHash)
{
try
{
diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h
index 22a31a762..9180c8ef7 100644
--- a/libweb3jsonrpc/WebThreeStubServerBase.h
+++ b/libweb3jsonrpc/WebThreeStubServerBase.h
@@ -113,7 +113,7 @@ public:
virtual Json::Value eth_getFilterLogs(std::string const& _filterId);
virtual Json::Value eth_getLogs(Json::Value const& _json);
virtual Json::Value eth_getWork();
- virtual bool eth_submitWork(std::string const& _nonce, std::string const& _mixHash);
+ virtual bool eth_submitWork(std::string const& _nonce, std::string const&, std::string const& _mixHash);
virtual std::string eth_register(std::string const& _address);
virtual bool eth_unregister(std::string const& _accountId);
virtual Json::Value eth_fetchQueuedTransactions(std::string const& _accountId);
diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h
index 0860ecaee..8da47d0fc 100644
--- a/libweb3jsonrpc/abstractwebthreestubserver.h
+++ b/libweb3jsonrpc/abstractwebthreestubserver.h
@@ -53,7 +53,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("eth_getFilterLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getFilterLogsI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_getLogsI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_getWorkI);
- this->bindAndAddMethod(jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI);
+ this->bindAndAddMethod(jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_register", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_registerI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_unregister", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_unregisterI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_fetchQueuedTransactions", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_fetchQueuedTransactionsI);
@@ -250,7 +250,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_submitWork(request[0u].asString(), request[1u].asString());
+ response = this->eth_submitWork(request[0u].asString(), request[1u].asString(), request[2u].asString());
}
inline virtual void eth_registerI(const Json::Value &request, Json::Value &response)
{
@@ -350,7 +350,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer(false) << "| " << std::setw(2) << session()->socketId() << "] "
-
Interface::~Interface()
{
}
diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp
index 97a24e112..ab3576292 100644
--- a/libwhisper/WhisperHost.cpp
+++ b/libwhisper/WhisperHost.cpp
@@ -29,12 +29,7 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
-#if defined(clogS)
-#undef clogS
-#endif
-#define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] "
-
-WhisperHost::WhisperHost()
+WhisperHost::WhisperHost(): Worker("shh")
{
}
diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp
index 53ea91a9e..9bef25140 100644
--- a/libwhisper/WhisperPeer.cpp
+++ b/libwhisper/WhisperPeer.cpp
@@ -29,11 +29,6 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
-#if defined(clogS)
-#undef clogS
-#endif
-#define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] "
-
WhisperPeer::WhisperPeer(Session* _s, HostCapabilityFace* _h, unsigned _i): Capability(_s, _h, _i)
{
RLPStream s;
@@ -57,7 +52,7 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r)
{
auto protocolVersion = _r[0].toInt();
- clogS(NetMessageSummary) << "Status: " << protocolVersion;
+ clog(NetMessageSummary) << "Status: " << protocolVersion;
if (protocolVersion != version())
disable("Invalid protocol version.");
diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp
index 3478a88fa..dad1ced62 100644
--- a/mix/ClientModel.cpp
+++ b/mix/ClientModel.cpp
@@ -498,6 +498,8 @@ QVariant ClientModel::formatStorageValue(SolidityType const& _type, map 0
tooltip: qsTr("Clear")
onTriggered: {
- logsModel.clear();
+ logsPane.clear()
}
}
}
diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml
index d36b6fa75..0c01caeb1 100644
--- a/mix/qml/StatusPane.qml
+++ b/mix/qml/StatusPane.qml
@@ -55,6 +55,12 @@ Rectangle {
currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "error" }
}
+ function clear()
+ {
+ status.state = "";
+ status.text = "";
+ }
+
StatusPaneStyle {
id: statusPaneStyle
}
@@ -359,6 +365,7 @@ Rectangle {
LogsPane
{
id: logPane;
+ statusPane: statusHeader
onContentXPosChanged:
{
parent.move();
diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml
index 86f8b2a74..16ed3e9bf 100644
--- a/mix/qml/TransactionLog.qml
+++ b/mix/qml/TransactionLog.qml
@@ -151,7 +151,7 @@ Item {
Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) {
var item = logTable.model.get(currentRow);
- appContext.toClipboard(item.returned);
+ clipboard.text = item.returned;
}
}
}
diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml
index 6db727e3b..72cb88401 100644
--- a/mix/qml/WebPreview.qml
+++ b/mix/qml/WebPreview.qml
@@ -317,7 +317,7 @@ Item {
experimental.settings.localContentCanAccessRemoteUrls: true
onJavaScriptConsoleMessage: {
console.log(sourceID + ":" + lineNumber + ": " + message);
- webPreview.javaScriptMessage(level, sourceID, lineNumber, message);
+ webPreview.javaScriptMessage(level, sourceID, lineNumber - 1, message);
}
onLoadingChanged: {
if (!loading) {
diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml
index 73cff824e..778e4dc20 100644
--- a/mix/test/qml/TestMain.qml
+++ b/mix/test/qml/TestMain.qml
@@ -58,11 +58,20 @@ TestCase
ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
}
+ function waitForMining()
+ {
+ while (mainApplication.clientModel.mining)
+ ts.waitForSignal(mainApplication.clientModel, "miningComplete()", 5000);
+ wait(1); //allow events to propagate 2 times for transaction log to be updated
+ wait(1);
+ }
function waitForExecution()
{
while (mainApplication.clientModel.running)
ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000);
+ wait(1); //allow events to propagate 2 times for transaction log to be updated
+ wait(1);
}
function editHtml(c)
@@ -89,6 +98,7 @@ TestCase
function test_dbg_vm() { TestDebugger.test_vmDebugging(); }
function test_miner_getDefaultiner() { TestMiner.test_getDefaultMiner(); }
function test_miner_selectMiner() { TestMiner.test_selectMiner(); }
+ function test_miner_mine() { TestMiner.test_mine(); }
function test_project_contractRename() { TestProject.test_contractRename(); }
}
diff --git a/mix/test/qml/js/TestDebugger.js b/mix/test/qml/js/TestDebugger.js
index 706601d18..f8453df78 100644
--- a/mix/test/qml/js/TestDebugger.js
+++ b/mix/test/qml/js/TestDebugger.js
@@ -91,17 +91,20 @@ function test_arrayParametersAndStorage()
" {\r" +
" m = x;\r" +
" s = 5;\r" +
+ " signed = 6534;\r" +
" }\r" +
" \r" +
" function setMV(uint72[5] x) external\r" +
" {\r" +
" mv = x;\r" +
" s = 42;\r" +
+ " signed = -534;\r" +
" }\r" +
" \r" +
" uint256[] m;\r" +
" uint72[5] mv;\r" +
" uint256 s;\r" +
+ " int48 signed;\r" +
" }\r");
mainApplication.projectModel.stateListModel.editState(0);
@@ -127,11 +130,13 @@ function test_arrayParametersAndStorage()
mainApplication.mainContent.rightPane.debugSlider.value = mainApplication.mainContent.rightPane.debugSlider.maximumValue;
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "m", ["4","5","6","2","10"]);
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "s", "5");
+ tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "signed", "6534");
//debug setMV
mainApplication.clientModel.debugRecord(4);
mainApplication.mainContent.rightPane.debugSlider.value = mainApplication.mainContent.rightPane.debugSlider.maximumValue - 1;
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "mv", ["13","35","1","4","0"]);
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "s", "42");
+ tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "signed", "-534");
tryCompare(mainApplication.mainContent.rightPane.solCallStack.listModel, 0, "setMV");
}
diff --git a/mix/test/qml/js/TestMiner.js b/mix/test/qml/js/TestMiner.js
index a24b0ce19..9d98c9f24 100644
--- a/mix/test/qml/js/TestMiner.js
+++ b/mix/test/qml/js/TestMiner.js
@@ -18,3 +18,13 @@ function test_selectMiner()
compare(state.miner.secret, account.secret);
}
+function test_mine()
+{
+ newProject();
+ mainApplication.mainContent.startQuickDebugging();
+ waitForExecution();
+ mainApplication.clientModel.mine();
+ waitForMining();
+ tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "contract", " - Block - ");
+}
+
diff --git a/mix/test/qml/js/TestProject.js b/mix/test/qml/js/TestProject.js
index 444760ea3..0d20ec9a8 100644
--- a/mix/test/qml/js/TestProject.js
+++ b/mix/test/qml/js/TestProject.js
@@ -1,11 +1,12 @@
function test_contractRename()
{
newProject();
+ waitForExecution();
tryCompare(mainApplication.mainContent.projectNavigator.sections.itemAt(0).model.get(0), "name", "Contract");
editContract("contract Renamed {}");
mainApplication.mainContent.startQuickDebugging();
waitForExecution();
- wait(1000);
+ tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(2), "contract", "Renamed");
tryCompare(mainApplication.mainContent.projectNavigator.sections.itemAt(0).model.get(0), "name", "Renamed");
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.editTransaction(2);
@@ -14,5 +15,4 @@ function test_contractRename()
tryCompare(transactionDialog, "functionId", "Renamed");
transactionDialog.close();
mainApplication.projectModel.stateDialog.close();
- tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(2), "contract", "Renamed");
}
diff --git a/neth/main.cpp b/neth/main.cpp
index 4c38da0a4..31df2ffcb 100644
--- a/neth/main.cpp
+++ b/neth/main.cpp
@@ -626,6 +626,7 @@ int main(int argc, char** argv)
logwin = newwin(height * 2 / 5 - 2, width * 2 / 3, qheight, 0);
nc::nc_window_streambuf outbuf(logwin, std::cout);
+ nc::nc_window_streambuf eoutbuf(logwin, std::cerr);
consolewin = newwin(qheight, width / 4, 0, 0);
nc::nc_window_streambuf coutbuf(consolewin, ccout);
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index 6ed90cdea..182015709 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -55,6 +55,7 @@ namespace solidity
static string const g_argAbiStr = "json-abi";
static string const g_argSolAbiStr = "sol-abi";
static string const g_argAsmStr = "asm";
+static string const g_argAsmJsonStr = "asm-json";
static string const g_argAstStr = "ast";
static string const g_argAstJson = "ast-json";
static string const g_argBinaryStr = "binary";
@@ -80,10 +81,15 @@ static bool needStdout(po::variables_map const& _args)
{
return
- argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argSolAbiStr) ||
- argToStdout(_args, g_argNatspecUserStr) || argToStdout(_args, g_argAstJson) ||
- argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) ||
- argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr);
+ argToStdout(_args, g_argAbiStr) ||
+ argToStdout(_args, g_argSolAbiStr) ||
+ argToStdout(_args, g_argNatspecUserStr) ||
+ argToStdout(_args, g_argAstJson) ||
+ argToStdout(_args, g_argNatspecDevStr) ||
+ argToStdout(_args, g_argAsmStr) ||
+ argToStdout(_args, g_argAsmJsonStr) ||
+ argToStdout(_args, g_argOpcodesStr) ||
+ argToStdout(_args, g_argBinaryStr);
}
static inline bool outputToFile(OutputType type)
@@ -215,23 +221,25 @@ bool CommandLineInterface::parseArguments(int argc, char** argv)
("add-std", po::value()->default_value(false), "Add standard contracts")
("input-file", po::value>(), "input file")
(g_argAstStr.c_str(), po::value()->value_name("stdout|file|both"),
- "Request to output the AST of the contract.")
+ "Request to output the AST of the contract.")
(g_argAstJson.c_str(), po::value()->value_name("stdout|file|both"),
- "Request to output the AST of the contract in JSON format.")
+ "Request to output the AST of the contract in JSON format.")
(g_argAsmStr.c_str(), po::value()->value_name("stdout|file|both"),
- "Request to output the EVM assembly of the contract.")
+ "Request to output the EVM assembly of the contract.")
+ (g_argAsmJsonStr.c_str(), po::value()->value_name("stdout|file|both"),
+ "Request to output the EVM assembly of the contract in JSON format.")
(g_argOpcodesStr.c_str(), po::value()->value_name("stdout|file|both"),
- "Request to output the Opcodes of the contract.")
+ "Request to output the Opcodes of the contract.")
(g_argBinaryStr.c_str(), po::value()->value_name("stdout|file|both"),
- "Request to output the contract in binary (hexadecimal).")
+ "Request to output the contract in binary (hexadecimal).")
(g_argAbiStr.c_str(), po::value()->value_name("stdout|file|both"),
- "Request to output the contract's JSON ABI interface.")
+ "Request to output the contract's JSON ABI interface.")
(g_argSolAbiStr.c_str(), po::value()->value_name("stdout|file|both"),
- "Request to output the contract's Solidity ABI interface.")
+ "Request to output the contract's Solidity ABI interface.")
(g_argNatspecUserStr.c_str(), po::value()->value_name("stdout|file|both"),
- "Request to output the contract's Natspec user documentation.")
+ "Request to output the contract's Natspec user documentation.")
(g_argNatspecDevStr.c_str(), po::value()->value_name("stdout|file|both"),
- "Request to output the contract's Natspec developer documentation.");
+ "Request to output the contract's Natspec developer documentation.");
// All positional options should be interpreted as input files
po::positional_options_description p;
@@ -411,19 +419,19 @@ void CommandLineInterface::actOnInput()
cout << endl << "======= " << contract << " =======" << endl;
// do we need EVM assembly?
- if (m_args.count(g_argAsmStr))
+ if (m_args.count(g_argAsmStr) || m_args.count(g_argAsmJsonStr))
{
- auto choice = m_args[g_argAsmStr].as();
+ auto choice = m_args.count(g_argAsmStr) ? m_args[g_argAsmStr].as() : m_args[g_argAsmJsonStr].as();
if (outputToStdout(choice))
{
cout << "EVM assembly:" << endl;
- m_compiler->streamAssembly(cout, contract, m_sourceCodes);
+ m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
}
if (outputToFile(choice))
{
- ofstream outFile(contract + ".evm");
- m_compiler->streamAssembly(outFile, contract, m_sourceCodes);
+ ofstream outFile(contract + (m_args.count(g_argAsmJsonStr) ? "_evm.json" : ".evm"));
+ m_compiler->streamAssembly(outFile, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
outFile.close();
}
}
diff --git a/solc/docker_emscripten/Dockerfile b/solc/docker_emscripten/Dockerfile
index 06467a2c5..1ad1875d7 100644
--- a/solc/docker_emscripten/Dockerfile
+++ b/solc/docker_emscripten/Dockerfile
@@ -43,7 +43,14 @@ RUN sed -i 's/$(archiver\[1\])/\/home\/user\/emsdk_portable\/emscripten\/master\
RUN sed -i 's/$(ranlib\[1\])/\/home\/user\/emsdk_portable\/emscripten\/master\/emranlib/g' ./tools/build/src/tools/gcc.jam
RUN ./b2 link=static variant=release threading=single runtime-link=static thread system regex
-# Build soljs
+# Json-CPP
+WORKDIR /home/user
+RUN git clone https://github.com/open-source-parsers/jsoncpp.git
+WORKDIR /home/user/jsoncpp
+RUN emcmake cmake -DJSONCPP_LIB_BUILD_STATIC=ON -DJSONCPP_LIB_BUILD_SHARED=OFF -DJSONCPP_WITH_TESTS=OFF -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF -G "Unix Makefiles" .
+RUN emmake make
+
+## Build soljs
WORKDIR /home/user
ADD https://api.github.com/repos/ethereum/cpp-ethereum/git/refs/heads/develop unused.txt
RUN git clone --depth=1 https://github.com/ethereum/cpp-ethereum
@@ -55,8 +62,10 @@ RUN git remote add -f solidityjs https://github.com/chriseth/cpp-ethereum
# TODO this should be a proper merge but somehow causes problems
# NOTE that we only get the latest commit of that branch
RUN git cherry-pick solidityjs/solidity-js
-RUN emcmake cmake -DETH_STATIC=1 -DONLY_SOLIDITY=1 -DHEADLESS=1 -DCMAKE_CXX_COMPILER=/home/user/emsdk_portable/emscripten/master/em++ -DCMAKE_C_COMPILER=/home/user/emsdk_portable/emscripten/master/emcc
+RUN emcmake cmake -DETH_STATIC=1 -DSOLIDITY=ON -DGUI=0 -DCMAKE_CXX_COMPILER=/home/user/emsdk_portable/emscripten/master/em++ -DCMAKE_C_COMPILER=/home/user/emsdk_portable/emscripten/master/emcc
RUN emmake make -j 6 soljs
-ENTRYPOINT cat soljs/soljs.js
+WORKDIR /home/user/cpp-ethereum/soljs
+# somehow it does not work to pipe out both files
+#ENTRYPOINT tar -c soljs.js soljs.js.mem | base64
diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp
index fdbe46d09..6bb210056 100644
--- a/test/SolidityNameAndTypeResolution.cpp
+++ b/test/SolidityNameAndTypeResolution.cpp
@@ -407,6 +407,39 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
+BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_optional)
+{
+ ASTPointer sourceUnit;
+ char const* text = R"(
+ contract BaseBase { function BaseBase(uint j); }
+ contract base is BaseBase { function foo(); }
+ contract derived is base {
+ function derived(uint i) BaseBase(i){}
+ function foo() {}
+ }
+ )";
+ ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(text), "Parsing and name resolving failed");
+}
+
+BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided)
+{
+ ASTPointer sourceUnit;
+ char const* text = R"(
+ contract BaseBase { function BaseBase(uint j); }
+ contract base is BaseBase { function foo(); }
+ contract derived is base {
+ function derived(uint i) {}
+ function foo() {}
+ }
+ )";
+ ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name resolving failed");
+ std::vector> nodes = sourceUnit->getNodes();
+ BOOST_CHECK_EQUAL(nodes.size(), 3);
+ ContractDefinition* derived = dynamic_cast(nodes[2].get());
+ BOOST_CHECK(derived);
+ BOOST_CHECK(!derived->isFullyImplemented());
+}
+
BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
{
ASTPointer sourceUnit;
@@ -665,7 +698,7 @@ BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments)
contract A { function A(uint a) { } }
contract B is A { }
)";
- BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
+ ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed");
}
BOOST_AUTO_TEST_CASE(base_constructor_arguments_override)
@@ -674,7 +707,7 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments_override)
contract A { function A(uint a) { } }
contract B is A { }
)";
- BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
+ ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed");
}
BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion)
diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp
index 92a8258ac..ed844e961 100644
--- a/test/TestHelper.cpp
+++ b/test/TestHelper.cpp
@@ -123,7 +123,7 @@ json_spirit::mObject& ImportTest::makeAllFieldsHex(json_spirit::mObject& _o)
{
static const set hashes {"bloom" , "coinbase", "hash", "mixHash", "parentHash", "receiptTrie",
"stateRoot", "transactionsTrie", "uncleHash", "currentCoinbase",
- "previousHash", "to", "address", "caller", "origin", "secretKey"};
+ "previousHash", "to", "address", "caller", "origin", "secretKey", "data"};
for (auto& i: _o)
{
@@ -140,7 +140,7 @@ json_spirit::mObject& ImportTest::makeAllFieldsHex(json_spirit::mObject& _o)
str = value.get_str();
else continue;
- _o[key] = (str.substr(0, 2) == "0x") ? str : "0x" + toHex(toCompactBigEndian(toInt(str)));
+ _o[key] = (str.substr(0, 2) == "0x") ? str : "0x" + toHex(toCompactBigEndian(toInt(str), 1));
}
return _o;
}
@@ -363,8 +363,8 @@ json_spirit::mObject fillJsonWithState(State _state)
for (auto const& a: _state.addresses())
{
json_spirit::mObject o;
- o["balance"] = "0x" + toHex(toCompactBigEndian(_state.balance(a.first)));
- o["nonce"] = "0x" + toHex(toCompactBigEndian(_state.transactionsFrom(a.first)));
+ o["balance"] = "0x" + toHex(toCompactBigEndian(_state.balance(a.first), 1));
+ o["nonce"] = "0x" + toHex(toCompactBigEndian(_state.transactionsFrom(a.first), 1));
{
json_spirit::mObject store;
for (auto const& s: _state.storage(a.first))
diff --git a/test/blockchain.cpp b/test/blockchain.cpp
index b144abe62..ec8fb7539 100644
--- a/test/blockchain.cpp
+++ b/test/blockchain.cpp
@@ -625,11 +625,11 @@ void writeBlockHeaderToJson(mObject& _o, BlockInfo const& _bi)
_o["transactionsTrie"] = toString(_bi.transactionsRoot);
_o["receiptTrie"] = toString(_bi.receiptsRoot);
_o["bloom"] = toString(_bi.logBloom);
- _o["difficulty"] = "0x" + toHex(toCompactBigEndian(_bi.difficulty));
- _o["number"] = "0x" + toHex(toCompactBigEndian(_bi.number));
- _o["gasLimit"] = "0x" + toHex(toCompactBigEndian(_bi.gasLimit));
- _o["gasUsed"] = "0x" + toHex(toCompactBigEndian(_bi.gasUsed));
- _o["timestamp"] = "0x" + toHex(toCompactBigEndian(_bi.timestamp));
+ _o["difficulty"] = "0x" + toHex(toCompactBigEndian(_bi.difficulty), 1);
+ _o["number"] = "0x" + toHex(toCompactBigEndian(_bi.number), 1);
+ _o["gasLimit"] = "0x" + toHex(toCompactBigEndian(_bi.gasLimit), 1);
+ _o["gasUsed"] = "0x" + toHex(toCompactBigEndian(_bi.gasUsed), 1);
+ _o["timestamp"] = "0x" + toHex(toCompactBigEndian(_bi.timestamp), 1);
_o["extraData"] ="0x" + toHex(_bi.extraData);
_o["mixHash"] = toString(_bi.mixHash);
_o["nonce"] = toString(_bi.nonce);
diff --git a/test/stateOriginal.cpp b/test/stateOriginal.cpp
index e1a3c7c4a..7f3371484 100644
--- a/test/stateOriginal.cpp
+++ b/test/stateOriginal.cpp
@@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(Complex)
CanonBlockChain bc;
cout << bc;
- State s(stateDB, BaseState::Empty, myMiner.address());
+ State s(stateDB, BaseState::CanonGenesis, myMiner.address());
cout << s;
// Sync up - this won't do much until we use the last state.
@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(Complex)
cout << s;
// Inject a transaction to transfer funds from miner to me.
- Transaction t(1000, 10000, 10000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret());
+ Transaction t(1000, 10000, 100000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret());
assert(t.sender() == myMiner.address());
s.execute(bc.lastHashes(), t);
diff --git a/test/webthreestubclient.h b/test/webthreestubclient.h
index c1fdc3411..fd71bfb5d 100644
--- a/test/webthreestubclient.h
+++ b/test/webthreestubclient.h
@@ -434,11 +434,12 @@ class WebThreeStubClient : public jsonrpc::Client
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
- bool eth_submitWork(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
+ bool eth_submitWork(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
+ p.append(param3);
Json::Value result = this->CallMethod("eth_submitWork",p);
if (result.isBool())
return result.asBool();