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();