From 7ce4524ac09af35c94100e7cf11ab24f3c4e02f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 25 Mar 2015 12:13:44 +0100 Subject: [PATCH 01/45] Test execution stats improvements --stats can have optional values: --stats=out prints all test timings to standard output, --stats= prints all test timings to a file in tab-separated format. Stats are now kept in vector to allow duplicated values. --- test/Stats.cpp | 33 +++++++++++++++++++++++++-------- test/Stats.h | 13 ++++++++++--- test/TestHelper.cpp | 19 ++++++++++++++++--- test/TestHelper.h | 4 +++- test/state.cpp | 5 +---- test/vm.cpp | 5 +---- 6 files changed, 56 insertions(+), 23 deletions(-) diff --git a/test/Stats.cpp b/test/Stats.cpp index fa615cb50..e76d1ee00 100644 --- a/test/Stats.cpp +++ b/test/Stats.cpp @@ -19,6 +19,7 @@ #include #include +#include namespace dev { @@ -31,6 +32,11 @@ Stats& Stats::get() return instance; } +void Stats::suiteStarted(std::string const& _name) +{ + m_currentSuite = _name; +} + void Stats::testStarted(std::string const& _name) { m_currentTest = _name; @@ -39,7 +45,7 @@ void Stats::testStarted(std::string const& _name) void Stats::testFinished() { - m_stats[clock::now() - m_tp] = std::move(m_currentTest); + m_stats.push_back({clock::now() - m_tp, m_currentSuite + "/" + m_currentTest}); } std::ostream& operator<<(std::ostream& out, Stats::clock::duration const& d) @@ -52,31 +58,42 @@ Stats::~Stats() if (m_stats.empty()) return; + std::sort(m_stats.begin(), m_stats.end(), [](Stats::Item const& a, Stats::Item const& b){ + return a.duration < b.duration; + }); + auto& out = std::cout; auto itr = m_stats.begin(); auto min = *itr; auto max = *m_stats.rbegin(); std::advance(itr, m_stats.size() / 2); auto med = *itr; - auto tot = std::accumulate(m_stats.begin(), m_stats.end(), clock::duration{}, [](clock::duration const& a, stats_t::value_type const& v) + auto tot = std::accumulate(m_stats.begin(), m_stats.end(), clock::duration{}, [](clock::duration const& a, Stats::Item const& v) { - return a + v.first; + return a + v.duration; }); out << "\nSTATS:\n\n" << std::setfill(' '); - if (Options::get().statsFull) + if (Options::get().statsOutFile == "out") { for (auto&& s: m_stats) - out << " " << std::setw(40) << std::left << s.second.substr(0, 40) << s.first << " \n"; + out << " " << std::setw(40) << std::left << s.name.substr(0, 40) << s.duration << " \n"; out << "\n"; } + else if (!Options::get().statsOutFile.empty()) + { + // Output stats to file + std::ofstream file{Options::get().statsOutFile}; + for (auto&& s: m_stats) + file << s.name << "\t" << std::chrono::duration_cast(s.duration).count() << "\n"; + } out << " tot: " << tot << "\n" << " avg: " << (tot / m_stats.size()) << "\n\n" - << " min: " << min.first << " (" << min.second << ")\n" - << " med: " << med.first << " (" << med.second << ")\n" - << " max: " << max.first << " (" << max.second << ")\n"; + << " min: " << min.duration << " (" << min.name << ")\n" + << " med: " << med.duration << " (" << med.name << ")\n" + << " max: " << max.duration << " (" << max.name << ")\n"; } } diff --git a/test/Stats.h b/test/Stats.h index 7692a2b30..9b40c5fce 100644 --- a/test/Stats.h +++ b/test/Stats.h @@ -18,7 +18,7 @@ #pragma once #include -#include +#include #include "TestHelper.h" @@ -31,19 +31,26 @@ class Stats: public Listener { public: using clock = std::chrono::high_resolution_clock; - using stats_t = std::map; + + struct Item + { + clock::duration duration; + std::string name; + }; static Stats& get(); ~Stats(); + void suiteStarted(std::string const& _name) override; void testStarted(std::string const& _name) override; void testFinished() override; private: clock::time_point m_tp; + std::string m_currentSuite; std::string m_currentTest; - stats_t m_stats; + std::vector m_stats; }; } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index b29c5dc3a..b3e64f47b 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -29,6 +29,7 @@ #include #include #include +#include "Stats.h" using namespace std; using namespace dev::eth; @@ -447,6 +448,9 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun string testPath = getTestPath(); testPath += _testPathAppendix; + if (Options::get().stats) + Listener::registerListener(Stats::get()); + if (Options::get().fillTests) { try @@ -478,6 +482,7 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun string s = asString(dev::contents(testPath + "/" + _name + ".json")); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + testPath + "/" + _name + ".json is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"); json_spirit::read_string(s, v); + Listener::notifySuiteStarted(_name); doTests(v, false); } catch (Exception const& _e) @@ -551,10 +556,12 @@ Options::Options() vmtrace = true; else if (arg == "--filltests") fillTests = true; - else if (arg == "--stats") + else if (arg.compare(0, 7, "--stats") == 0) + { stats = true; - else if (arg == "--stats=full") - stats = statsFull = true; + if (arg.size() > 7) + statsOutFile = arg.substr(8); // skip '=' char + } else if (arg == "--performance") performance = true; else if (arg == "--quadratic") @@ -602,6 +609,12 @@ void Listener::registerListener(Listener& _listener) g_listener = &_listener; } +void Listener::notifySuiteStarted(std::string const& _name) +{ + if (g_listener) + g_listener->suiteStarted(_name); +} + void Listener::notifyTestStarted(std::string const& _name) { if (g_listener) diff --git a/test/TestHelper.h b/test/TestHelper.h index ade20f5e4..05e01792d 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -164,7 +164,7 @@ public: bool vmtrace = false; ///< Create EVM execution tracer // TODO: Link with log verbosity? bool fillTests = false; ///< Create JSON test files from execution results bool stats = false; ///< Execution time stats - bool statsFull = false; ///< Output full stats - execution times for every test + std::string statsOutFile; ///< Stats output file. "out" for standard output /// Test selection /// @{ @@ -191,10 +191,12 @@ class Listener public: virtual ~Listener() = default; + virtual void suiteStarted(std::string const&) {} virtual void testStarted(std::string const& _name) = 0; virtual void testFinished() = 0; static void registerListener(Listener& _listener); + static void notifySuiteStarted(std::string const& _name); static void notifyTestStarted(std::string const& _name); static void notifyTestFinished(); diff --git a/test/state.cpp b/test/state.cpp index 4ab59f7a1..8168693b1 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -31,7 +31,6 @@ #include #include #include "TestHelper.h" -#include "Stats.h" using namespace std; using namespace json_spirit; @@ -42,9 +41,6 @@ namespace dev { namespace test { void doStateTests(json_spirit::mValue& v, bool _fillin) { - if (Options::get().stats) - Listener::registerListener(Stats::get()); - for (auto& i: v.get_obj()) { std::cout << " " << i.first << "\n"; @@ -255,6 +251,7 @@ BOOST_AUTO_TEST_CASE(stRandom) string s = asString(dev::contents(path.string())); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Content of " + path.string() + " is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"); json_spirit::read_string(s, v); + test::Listener::notifySuiteStarted(path.filename().string()); dev::test::doStateTests(v, false); } catch (Exception const& _e) diff --git a/test/vm.cpp b/test/vm.cpp index 2bdafb270..cffbaa64d 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -25,7 +25,6 @@ #include #include #include "vm.h" -#include "Stats.h" using namespace std; using namespace json_spirit; @@ -311,9 +310,6 @@ namespace dev { namespace test { void doVMTests(json_spirit::mValue& v, bool _fillin) { - if (Options::get().stats) - Listener::registerListener(Stats::get()); - for (auto& i: v.get_obj()) { std::cout << " " << i.first << "\n"; @@ -549,6 +545,7 @@ BOOST_AUTO_TEST_CASE(vmRandom) string s = asString(dev::contents(path.string())); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Content of " + path.string() + " is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"); json_spirit::read_string(s, v); + test::Listener::notifySuiteStarted(path.filename().string()); doVMTests(v, false); } catch (Exception const& _e) From d1f9f0290faca526311a89dac007ed083ecf9bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 25 Mar 2015 15:12:54 +0100 Subject: [PATCH 02/45] Add option to preload cache --- evmjit/libevmjit/Cache.cpp | 31 ++++++++++++++++++++++++++++ evmjit/libevmjit/Cache.h | 12 ++++++++++- evmjit/libevmjit/ExecutionEngine.cpp | 17 ++++++++++++--- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp index d3cda1098..47a6386e9 100644 --- a/evmjit/libevmjit/Cache.cpp +++ b/evmjit/libevmjit/Cache.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,36 @@ void Cache::clear() fs::remove(it->path()); } +void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache) +{ + // TODO: Cache dir should be in one place + using namespace llvm::sys; + llvm::SmallString<256> cachePath; + path::system_temp_directory(false, cachePath); + path::append(cachePath, "evm_objs"); + + // Disable listener + auto listener = g_listener; + g_listener = nullptr; + + std::error_code err; + for (auto it = fs::directory_iterator{cachePath.str(), err}; it != fs::directory_iterator{}; it.increment(err)) + { + auto name = it->path().substr(cachePath.size() + 1); + if (auto module = getObject(name)) + { + DLOG(cache) << "Preload: " << name << "\n"; + _ee.addModule(module.get()); + module.release(); + auto addr = _ee.getFunctionAddress(name); + assert(addr); + _funcCache[std::move(name)] = addr; + } + } + + g_listener = listener; +} + std::unique_ptr Cache::getObject(std::string const& id) { if (g_mode != CacheMode::on && g_mode != CacheMode::read) diff --git a/evmjit/libevmjit/Cache.h b/evmjit/libevmjit/Cache.h index b0d26d080..f6a0a3400 100644 --- a/evmjit/libevmjit/Cache.h +++ b/evmjit/libevmjit/Cache.h @@ -1,9 +1,15 @@ #pragma once #include +#include #include +namespace llvm +{ + class ExecutionEngine; +} + namespace dev { namespace eth @@ -18,7 +24,8 @@ enum class CacheMode off, read, write, - clear + clear, + preload }; class ObjectCache : public llvm::ObjectCache @@ -43,6 +50,9 @@ public: /// Clears cache storage static void clear(); + + /// Loads all available cached objects to ExecutionEngine + static void preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache); }; } diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index ce1f530d7..0ed4a65b5 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -76,6 +76,7 @@ cl::opt g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"}, clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), + clEnumValN(CacheMode::preload, "p", "Preload all cached objects."), clEnumValEnd)}; cl::opt g_stats{"st", cl::desc{"Statistics"}}; cl::opt g_dump{"dump", cl::desc{"Dump LLVM IR module"}}; @@ -111,8 +112,15 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) std::unique_ptr listener{new ExecStats}; listener->stateChanged(ExecState::Started); + bool preloadCache = g_cache == CacheMode::preload; + if (preloadCache) + g_cache = CacheMode::on; + + // TODO: Do not pseudo-init the cache every time auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr; + static std::unordered_map funcCache; + static std::unique_ptr ee; if (!ee) { @@ -138,6 +146,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) return ReturnCode::LLVMConfigError; module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module ee->setObjectCache(objectCache); + + if (preloadCache) + Cache::preload(*ee, funcCache); } static StatsCollector statsCollector; @@ -146,10 +157,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) m_runtime.init(_data, _env); EntryFuncPtr entryFuncPtr = nullptr; - static std::unordered_map funcCache; auto it = funcCache.find(mainFuncName); if (it != funcCache.end()) - entryFuncPtr = it->second; + entryFuncPtr = (EntryFuncPtr) it->second; if (!entryFuncPtr) { @@ -177,7 +187,8 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) if (!CHECK(entryFuncPtr)) return ReturnCode::LLVMLinkError; - funcCache[mainFuncName] = entryFuncPtr; + if (it == funcCache.end()) + funcCache[mainFuncName] = (uint64_t) entryFuncPtr; listener->stateChanged(ExecState::Execution); auto returnCode = entryFuncPtr(&m_runtime); From 95dc6d21b68c21051852f2b576b9d08854da369f Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 26 Mar 2015 12:19:21 +0100 Subject: [PATCH 03/45] testing part 1 --- mix/CMakeLists.txt | 44 +- mix/MixApplication.cpp | 71 +++- mix/MixApplication.h | 17 + mix/main.cpp | 15 +- mix/qml.qrc | 2 +- mix/qml/Application.qml | 397 ++++++++++++++++++ mix/test.qrc | 6 + mix/test/TestMain.qml | 51 +++ .../TestTransactionDebug.qml} | 0 9 files changed, 567 insertions(+), 36 deletions(-) create mode 100644 mix/qml/Application.qml create mode 100644 mix/test.qrc create mode 100644 mix/test/TestMain.qml rename mix/{qml/main.qml => test/TestTransactionDebug.qml} (100%) diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index d57dafcac..4cb285b85 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -16,7 +16,7 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(BEFORE ..) find_package (Qt5WebEngine QUIET) -qt5_add_resources(UI_RESOURCES res.qrc qml.qrc) +qt5_add_resources(UI_RESOURCES res.qrc qml.qrc test.qrc) file(GLOB HEADERS "*.h") @@ -37,32 +37,18 @@ eth_add_executable(${EXECUTABLE} UI_RESOURCES ${UI_RESOURCES} ) -target_link_libraries(${EXECUTABLE} Qt5::Core) -target_link_libraries(${EXECUTABLE} Qt5::Gui) -target_link_libraries(${EXECUTABLE} Qt5::Widgets) -target_link_libraries(${EXECUTABLE} Qt5::Network) -target_link_libraries(${EXECUTABLE} Qt5::Quick) -target_link_libraries(${EXECUTABLE} Qt5::Qml) -target_link_libraries(${EXECUTABLE} webthree) -target_link_libraries(${EXECUTABLE} ethereum) -target_link_libraries(${EXECUTABLE} evm) -target_link_libraries(${EXECUTABLE} ethcore) -target_link_libraries(${EXECUTABLE} devcrypto) -target_link_libraries(${EXECUTABLE} secp256k1) -if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) -target_link_libraries(${EXECUTABLE} serpent) -endif() -target_link_libraries(${EXECUTABLE} lll) -target_link_libraries(${EXECUTABLE} solidity) -target_link_libraries(${EXECUTABLE} evmcore) -target_link_libraries(${EXECUTABLE} devcore) -target_link_libraries(${EXECUTABLE} jsqrc) -target_link_libraries(${EXECUTABLE} web3jsonrpc) +find_package(Qt5QuickTest REQUIRED) +find_package(Qt5Test REQUIRED) +set(LIBRARIES "Qt5::Core;Qt5::Gui;Qt5::Widgets;Qt5::Network;Qt5::Quick;Qt5::Qml;webthree;ethereum;evm;ethcore;devcrypto;solidity;evmcore;devcore;jsqrc;web3jsonrpc") if (${ETH_HAVE_WEBENGINE}) add_definitions(-DETH_HAVE_WEBENGINE) - target_link_libraries(${EXECUTABLE} Qt5::WebEngine) + list(APPEND LIBRARIES "Qt5::WebEngine") endif() + list(APPEND LIBRARIES "Qt5::QuickTest") + list(APPEND LIBRARIES "Qt5::Test") + +target_link_libraries(${EXECUTABLE} ${LIBRARIES}) # eth_install_executable is defined in cmake/EthExecutableHelper.cmake eth_install_executable(${EXECUTABLE} @@ -71,5 +57,15 @@ eth_install_executable(${EXECUTABLE} #add qml asnd stdc files to project tree in Qt creator file(GLOB_RECURSE QMLFILES "qml/*.*") +file(GLOB_RECURSE TESTFILE "test/*.*") file(GLOB_RECURSE SOLFILES "stdc/*.*") -add_custom_target(mix_qml SOURCES ${QMLFILES} ${SOLFILES}) +add_custom_target(mix_qml SOURCES ${QMLFILES} ${SOLFILES} ${TESTFILES}) + + +#test target +#set(TEST_EXECUTABLE mix_test) +#qt5_add_resources(UI_RESOURCES test.qrc) +#add_executable(${TEST_EXECUTABLE} ${UI_RESOURCES} ${SRC_LIST} ${HEADERS}) + + + diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index 028f1cb0b..7eefcc38e 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -23,6 +23,10 @@ #include #include #include +#include +#include +#include +#include #ifdef ETH_HAVE_WEBENGINE #include #endif @@ -37,21 +41,73 @@ using namespace dev::mix; +ApplicationService::ApplicationService() +{ +#ifdef ETH_HAVE_WEBENGINE + QtWebEngine::initialize(); +#endif + QFont f; + m_systemPointSize = f.pointSize(); +} + + +#include + +bool ApplicationService::waitForSignal(QObject* _item, QString _signalName, int _timeout) +{ + QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); + QMetaObject const* mo = _item->metaObject(); + + QStringList methods; + + for(int i = mo->methodOffset(); i < mo->methodCount(); ++i) { + if (mo->method(i).methodType() == QMetaMethod::Signal) { + methods << QString::fromLatin1(mo->method(i).methodSignature()); + } + } + + QElapsedTimer timer; + timer.start(); + + while (!spy.size()) { + int remaining = _timeout - int(timer.elapsed()); + if (remaining <= 0) + break; + QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QTest::qSleep(10); + } + + return spy.size(); +} + MixApplication::MixApplication(int& _argc, char* _argv[]): QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()) +{ + initialize(); + //m_engine->load(QUrl("qrc:/qml/main.qml")); + m_engine->load(QUrl("qrc:/test/TestMain.qml")); + + + if (!m_engine->rootObjects().empty()) + { + QWindow *window = qobject_cast(m_engine->rootObjects().at(0)); + if (window) + window->setIcon(QIcon(":/res/mix_256x256x32.png")); + } +} + + +void MixApplication::initialize() { setOrganizationName(tr("Ethereum")); setOrganizationDomain(tr("ethereum.org")); setApplicationName(tr("Mix")); setApplicationVersion("0.1"); -#ifdef ETH_HAVE_WEBENGINE - QtWebEngine::initialize(); -#endif - QFont f; - m_engine->rootContext()->setContextProperty("systemPointSize", f.pointSize()); qmlRegisterType("org.ethereum.qml.CodeModel", 1, 0, "CodeModel"); qmlRegisterType("org.ethereum.qml.ClientModel", 1, 0, "ClientModel"); + qmlRegisterType("org.ethereum.qml.ApplicationService", 1, 0, "ApplicationService"); qmlRegisterType("org.ethereum.qml.FileIo", 1, 0, "FileIo"); qmlRegisterType("org.ethereum.qml.QEther", 1, 0, "QEther"); qmlRegisterType("org.ethereum.qml.QBigInt", 1, 0, "QBigInt"); @@ -63,10 +119,7 @@ MixApplication::MixApplication(int& _argc, char* _argv[]): qmlRegisterType("HttpServer", 1, 0, "HttpServer"); qRegisterMetaType("CodeModel*"); qRegisterMetaType("ClientModel*"); - - m_engine->load(QUrl("qrc:/qml/main.qml")); - QWindow *window = qobject_cast(m_engine->rootObjects().at(0)); - window->setIcon(QIcon(":/res/mix_256x256x32.png")); + qRegisterMetaType("ClientModel*"); } MixApplication::~MixApplication() diff --git a/mix/MixApplication.h b/mix/MixApplication.h index 49a6e0047..ce3544392 100644 --- a/mix/MixApplication.h +++ b/mix/MixApplication.h @@ -33,12 +33,29 @@ namespace dev namespace mix { +class ApplicationService: public QObject +{ + Q_OBJECT + Q_PROPERTY(int systemPointSize READ systemPointSize CONSTANT) + +public: + ApplicationService(); + int systemPointSize() const { return m_systemPointSize; } +public slots: + bool waitForSignal(QObject* _item, QString _signalName, int _timeout); + +private: + int m_systemPointSize = 0; +}; + + class MixApplication: public QApplication { Q_OBJECT public: MixApplication(int& _argc, char* _argv[]); + static void initialize(); virtual ~MixApplication(); QQmlApplicationEngine* engine() { return m_engine.get(); } diff --git a/mix/main.cpp b/mix/main.cpp index 798520e39..908606bd9 100644 --- a/mix/main.cpp +++ b/mix/main.cpp @@ -24,6 +24,10 @@ #include #include "MixApplication.h" #include "Exceptions.h" + +#include + + using namespace dev::mix; int main(int _argc, char* _argv[]) @@ -43,8 +47,15 @@ int main(int _argc, char* _argv[]) #endif try { - MixApplication app(_argc, _argv); - return app.exec(); + + // + MixApplication::initialize(); + //MixApplication app(_argc, _argv); + return quick_test_main(_argc, _argv, "mix", "/home/arkady/src/cpp-ethereum/mix/test/TestMain.qml"); + + + //MixApplication app(_argc, _argv); + //return app.exec(); } catch (boost::exception const& _e) { diff --git a/mix/qml.qrc b/mix/qml.qrc index bed954741..fd437d881 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -1,6 +1,7 @@ qml/AlertMessageDialog.qml + qml/Application.qml qml/BasicMessage.qml qml/BigIntValue.qml qml/CallStack.qml @@ -59,7 +60,6 @@ qml/js/ProjectModel.js qml/js/QEtherHelper.js qml/js/TransactionHelper.js - qml/main.qml qml/qmldir diff --git a/mix/qml/Application.qml b/mix/qml/Application.qml new file mode 100644 index 000000000..b112cf527 --- /dev/null +++ b/mix/qml/Application.qml @@ -0,0 +1,397 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 +import QtQuick.PrivateWidgets 1.1 +import Qt.labs.settings 1.0 +import org.ethereum.qml.QEther 1.0 +import org.ethereum.qml.CodeModel 1.0 +import org.ethereum.qml.ClientModel 1.0 +import org.ethereum.qml.FileIo 1.0 +import org.ethereum.qml.Clipboard 1.0 +import org.ethereum.qml.ApplicationService 1.0 + +ApplicationWindow { + + id: mainApplication + signal loaded; + visible: true + width: 1200 + height: 800 + minimumWidth: 400 + minimumHeight: 300 + title: qsTr("Mix") + property alias systemPointSize: appService.systemPointSize; + property alias mainContent: mainContent; + property alias codeModel: codeModel; + property alias clientModel: clientModel; + property alias projectModel: projectModel; + property alias appService: appService; + + ApplicationService { + id: appService + } + + CodeModel { + id: codeModel + } + + ClientModel { + id: clientModel + codeModel: codeModel + } + + ProjectModel { + id: projectModel + } + + FileIo { + id: fileIo + } + + Clipboard { + id: clipboard + } + + Connections { + target: mainApplication + onClosing: + { + mainApplication.close(); + close.accepted = false; + } + } + + Component.onCompleted: { + loaded(); + } + + function close() { + projectModel.appIsClosing = true; + if (projectModel.projectPath !== "") + projectModel.closeProject(function() { Qt.quit(); }) + else + Qt.quit(); + } + + menuBar: MenuBar { + Menu { + title: qsTr("File") + MenuItem { action: createProjectAction } + MenuItem { action: openProjectAction } + MenuSeparator {} + MenuItem { action: saveAllFilesAction } + MenuItem { action: saveCurrentDocument } + MenuSeparator {} + MenuItem { action: addExistingFileAction } + MenuItem { action: addNewJsFileAction } + MenuItem { action: addNewHtmlFileAction } + MenuItem { action: addNewCssFileAction } + MenuSeparator {} + MenuItem { action: addNewContractAction } + MenuItem { action: closeProjectAction } + MenuSeparator {} + MenuItem { action: exitAppAction } + } + Menu { + title: qsTr("Deploy") + MenuItem { action: mineAction } + MenuSeparator {} + MenuItem { action: editStatesAction } + MenuSeparator {} + MenuItem { action: deployViaRpcAction } + MenuSeparator {} + MenuItem { action: toggleRunOnLoadAction } + } + Menu { + title: qsTr("Debug") + MenuItem { action: debugRunAction } + MenuSeparator {} + MenuItem { action: toggleAssemblyDebuggingAction } + } + Menu { + title: qsTr("Windows") + MenuItem { action: openNextDocumentAction } + MenuItem { action: openPrevDocumentAction } + MenuSeparator {} + MenuItem { action: toggleProjectNavigatorAction } + MenuItem { action: showHideRightPanelAction } + MenuItem { action: toggleTransactionLogAction } + MenuItem { action: toggleWebPreviewAction } + MenuItem { action: toggleWebPreviewOrientationAction } + //MenuItem { action: toggleCallsInLog } + } + } + + MainContent { + id: mainContent; + anchors.fill: parent + } + + ModalDialog { + objectName: "dialog" + id: dialog + } + + AlertMessageDialog { + objectName: "alertMessageDialog" + id: messageDialog + } + + Settings { + id: mainWindowSettings + property alias mainWidth: mainApplication.width + property alias mainHeight: mainApplication.height + property alias mainX: mainApplication.x + property alias mainY: mainApplication.y + } + + Action { + id: exitAppAction + text: qsTr("Exit") + shortcut: "Ctrl+Q" + onTriggered: + { + mainApplication.close(); + } + } + + Action { + id: mineAction + text: qsTr("New Block") + shortcut: "Ctrl+M" + onTriggered: clientModel.mine(); + enabled: codeModel.hasContract && !clientModel.running && !clientModel.mining + } + + StateList { + id: stateList + } + + Action { + id: editStatesAction + text: qsTr("Edit States") + shortcut: "Ctrl+Alt+E" + onTriggered: stateList.show(); + } + + Connections { + target: projectModel.stateListModel + + function updateRunLabel() + { + debugRunAction.text = qsTr("Deploy") + " \"" + projectModel.stateListModel.defaultStateName() + "\""; + } + + onDefaultStateChanged: updateRunLabel() + onStateListModelReady: updateRunLabel() + } + + Action { + id: debugRunAction + text: qsTr("Deploy") + shortcut: "F5" + onTriggered: mainContent.startQuickDebugging() + enabled: codeModel.hasContract && !clientModel.running + } + + Action { + id: toggleAssemblyDebuggingAction + text: qsTr("Show VM Code") + shortcut: "Ctrl+Alt+V" + onTriggered: mainContent.rightPane.assemblyMode = !mainContent.rightPane.assemblyMode; + checked: mainContent.rightPane.assemblyMode; + enabled: true + } + + Action { + id: toggleWebPreviewAction + text: qsTr("Show Web View") + shortcut: "F2" + checkable: true + checked: mainContent.webViewVisible + onTriggered: mainContent.toggleWebPreview(); + } + + Action { + id: toggleTransactionLogAction + text: qsTr("Show States and Transactions") + shortcut: "Alt+1" + checkable: true + checked: mainContent.rightPane.transactionLog.visible + onTriggered: mainContent.rightPane.transactionLog.visible = !mainContent.rightPane.transactionLog.visible + } + + Action { + id: toggleProjectNavigatorAction + text: qsTr("Show Project Navigator") + shortcut: "Alt+0" + checkable: true + checked: mainContent.projectViewVisible + onTriggered: mainContent.toggleProjectView(); + } + + Action { + id: toggleWebPreviewOrientationAction + text: qsTr("Horizontal Web View") + shortcut: "" + checkable: true + checked: mainContent.webViewHorizontal + onTriggered: mainContent.toggleWebPreviewOrientation(); + } + + Action { + id: toggleRunOnLoadAction + text: qsTr("Load State on Startup") + shortcut: "" + checkable: true + checked: mainContent.runOnProjectLoad + onTriggered: mainContent.runOnProjectLoad = !mainContent.runOnProjectLoad + } + + Action { + id: showHideRightPanelAction + text: qsTr("Show Right View") + shortcut: "F7" + checkable: true + checked: mainContent.rightViewVisible + onTriggered: mainContent.toggleRightView(); + } + + Action { + id: createProjectAction + text: qsTr("&New Project") + shortcut: "Ctrl+N" + enabled: true; + onTriggered: projectModel.createProject(); + } + + Action { + id: openProjectAction + text: qsTr("&Open Project") + shortcut: "Ctrl+O" + enabled: true; + onTriggered: openProjectFileDialog.open() + } + + FileDialog { + id: openProjectFileDialog + visible: false + title: qsTr("Open a Project") + selectFolder: true + onAccepted: { + var path = openProjectFileDialog.fileUrl.toString(); + path += "/"; + projectModel.loadProject(path); + } + } + + Action { + id: addNewJsFileAction + text: qsTr("New JavaScript File") + shortcut: "Ctrl+Alt+J" + enabled: !projectModel.isEmpty + onTriggered: projectModel.newJsFile(); + } + + Action { + id: addNewHtmlFileAction + text: qsTr("New HTML File") + shortcut: "Ctrl+Alt+H" + enabled: !projectModel.isEmpty + onTriggered: projectModel.newHtmlFile(); + } + + Action { + id: addNewCssFileAction + text: qsTr("New CSS File") + shortcut: "Ctrl+Alt+S" + enabled: !projectModel.isEmpty + onTriggered: projectModel.newCssFile(); + } + + Action { + id: addNewContractAction + text: qsTr("New Contract") + shortcut: "Ctrl+Alt+C" + enabled: !projectModel.isEmpty + onTriggered: projectModel.newContract(); + } + + Action { + id: addExistingFileAction + text: qsTr("Add Existing File") + shortcut: "Ctrl+Alt+A" + enabled: !projectModel.isEmpty + onTriggered: addExistingFileDialog.open() + } + + FileDialog { + id: addExistingFileDialog + visible: false + title: qsTr("Add a File") + selectFolder: false + onAccepted: { + var paths = addExistingFileDialog.fileUrls; + projectModel.addExistingFiles(paths); + } + } + + Action { + id: saveAllFilesAction + text: qsTr("Save All") + shortcut: "Ctrl+Shift+A" + enabled: !projectModel.isEmpty + onTriggered: projectModel.saveAll(); + } + + Action { + id: saveCurrentDocument + text: qsTr("Save Current Document") + shortcut: "Ctrl+S" + enabled: !projectModel.isEmpty + onTriggered: projectModel.saveCurrentDocument(); + } + + Action { + id: closeProjectAction + text: qsTr("Close Project") + shortcut: "Ctrl+W" + enabled: !projectModel.isEmpty + onTriggered: projectModel.closeProject(); + } + + Action { + id: openNextDocumentAction + text: qsTr("Next Document") + shortcut: "Ctrl+Tab" + enabled: !projectModel.isEmpty + onTriggered: projectModel.openNextDocument(); + } + + Action { + id: openPrevDocumentAction + text: qsTr("Previous Document") + shortcut: "Ctrl+Shift+Tab" + enabled: !projectModel.isEmpty + onTriggered: projectModel.openPrevDocument(); + } + + Action { + id: toggleBreakpointAction + text: qsTr("Toggle Breakpoint") + shortcut: "F9" + enabled: mainContent.codeEditor.editingContract(); + onTriggered: mainContent.toggleBreakpoint(); + } + + Action { + id: deployViaRpcAction + text: qsTr("Deploy to Network") + shortcut: "Ctrl+Shift+D" + enabled: !projectModel.isEmpty && codeModel.hasContract + onTriggered: projectModel.deployProject(); + } +} diff --git a/mix/test.qrc b/mix/test.qrc new file mode 100644 index 000000000..e7d2271a4 --- /dev/null +++ b/mix/test.qrc @@ -0,0 +1,6 @@ + + + test/TestMain.qml + test/TestTransactionDebug.qml + + diff --git a/mix/test/TestMain.qml b/mix/test/TestMain.qml new file mode 100644 index 000000000..737a2b2a4 --- /dev/null +++ b/mix/test/TestMain.qml @@ -0,0 +1,51 @@ +import QtQuick 2.2 +import QtTest 1.1 +//import Qt.test.qtestroot 1.0 +import "../qml" + + +TestCase +{ + +Item +{ + id: helper + function findChild(item, childId) { + if (item.children) { + var searchString = "button" + + for (var idx in item.children) { + var currentItem = item.children[idx] + + if (currentItem.id.match("^"+childId) === childId) + return currentItem; + + return findChild(currentItem, childId); + } + } + } +} + + +function test_t1() +{ + waitForRendering(mainApplication.mainContent, 10000); + mainApplication.projectModel.createProject(); + var projectDlg = helper.findChild(mainApplication, "newProjectDialog"); + + if (mainApplication.appService.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + console.log("compiled"); +} + +function runTest() +{ + waitForRendering(mainApplication.mainContent, 10000); + console.log("runtest"); + mousePress(mainApplication, 10, 10); +} + +Application { + id: mainApplication +} + +} diff --git a/mix/qml/main.qml b/mix/test/TestTransactionDebug.qml similarity index 100% rename from mix/qml/main.qml rename to mix/test/TestTransactionDebug.qml From 57e88359dc579ac56f5b1272f00fad86cf0b279d Mon Sep 17 00:00:00 2001 From: arkpar Date: Sat, 28 Mar 2015 10:36:47 +0100 Subject: [PATCH 04/45] test framework part2 --- mix/CMakeLists.txt | 23 ++-- mix/ClientModel.cpp | 5 +- mix/ClientModel.h | 2 + mix/MixApplication.cpp | 51 +++------ mix/MixApplication.h | 8 +- mix/QBasicNodeDefinition.cpp | 41 +++++++ mix/QBasicNodeDefinition.h | 12 ++- mix/QVariableDeclaration.cpp | 1 + mix/QVariableDeclaration.h | 5 +- mix/QVariableDefinition.cpp | 36 +++++++ mix/QVariableDefinition.h | 8 +- mix/main.cpp | 29 +---- mix/qml.qrc | 1 - mix/qml/Application.qml | 4 + mix/qml/CallStack.qml | 5 +- mix/qml/CodeEditor.qml | 51 +++++---- mix/qml/CodeEditorStyle.qml | 1 - mix/qml/CodeEditorView.qml | 15 ++- mix/qml/CommonSeparator.qml | 2 +- mix/qml/Debugger.qml | 12 ++- mix/qml/DebuggerPaneStyle.qml | 1 - mix/qml/DefaultLabel.qml | 1 - mix/qml/DeploymentDialog.qml | 4 +- mix/qml/FilesSection.qml | 30 +++--- mix/qml/LogsPane.qml | 72 +++++++------ mix/qml/LogsPaneStyle.qml | 1 - mix/qml/NewProjectDialog.qml | 1 + mix/qml/ProjectFilesStyle.qml | 1 - mix/qml/ProjectList.qml | 21 ++-- mix/qml/ProjectModel.qml | 1 + mix/qml/Splitter.qml | 3 - mix/qml/StateDialog.qml | 6 +- mix/qml/StateDialogStyle.qml | 1 - mix/qml/StatusPane.qml | 6 +- mix/qml/StatusPaneStyle.qml | 1 - mix/qml/Style.qml | 1 - mix/qml/TransactionDialog.qml | 6 +- mix/qml/WebCodeEditor.qml | 16 +-- mix/qml/WebPreview.qml | 20 ++-- mix/qml/WebPreviewStyle.qml | 1 - mix/qml/qmldir | 8 -- mix/test/TestMain.cpp | 50 +++++++++ mix/test/TestMain.qml | 51 --------- mix/test/TestService.cpp | 113 ++++++++++++++++++++ mix/test/TestService.h | 52 +++++++++ mix/test/qml/TestMain.qml | 52 +++++++++ mix/test/{ => qml}/TestTransactionDebug.qml | 0 mix/web.qrc | 2 +- 48 files changed, 566 insertions(+), 268 deletions(-) create mode 100644 mix/QBasicNodeDefinition.cpp create mode 100644 mix/QVariableDefinition.cpp delete mode 100644 mix/qml/qmldir create mode 100644 mix/test/TestMain.cpp delete mode 100644 mix/test/TestMain.qml create mode 100644 mix/test/TestService.cpp create mode 100644 mix/test/TestService.h create mode 100644 mix/test/qml/TestMain.qml rename mix/test/{ => qml}/TestTransactionDebug.qml (100%) diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index 4cb285b85..8137c8eba 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -16,7 +16,7 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(BEFORE ..) find_package (Qt5WebEngine QUIET) -qt5_add_resources(UI_RESOURCES res.qrc qml.qrc test.qrc) +qt5_add_resources(UI_RESOURCES res.qrc qml.qrc) file(GLOB HEADERS "*.h") @@ -37,16 +37,12 @@ eth_add_executable(${EXECUTABLE} UI_RESOURCES ${UI_RESOURCES} ) -find_package(Qt5QuickTest REQUIRED) -find_package(Qt5Test REQUIRED) set(LIBRARIES "Qt5::Core;Qt5::Gui;Qt5::Widgets;Qt5::Network;Qt5::Quick;Qt5::Qml;webthree;ethereum;evm;ethcore;devcrypto;solidity;evmcore;devcore;jsqrc;web3jsonrpc") if (${ETH_HAVE_WEBENGINE}) add_definitions(-DETH_HAVE_WEBENGINE) list(APPEND LIBRARIES "Qt5::WebEngine") endif() - list(APPEND LIBRARIES "Qt5::QuickTest") - list(APPEND LIBRARIES "Qt5::Test") target_link_libraries(${EXECUTABLE} ${LIBRARIES}) @@ -57,15 +53,22 @@ eth_install_executable(${EXECUTABLE} #add qml asnd stdc files to project tree in Qt creator file(GLOB_RECURSE QMLFILES "qml/*.*") -file(GLOB_RECURSE TESTFILE "test/*.*") +file(GLOB_RECURSE TESTFILES "test/qml/*.*") file(GLOB_RECURSE SOLFILES "stdc/*.*") add_custom_target(mix_qml SOURCES ${QMLFILES} ${SOLFILES} ${TESTFILES}) - #test target -#set(TEST_EXECUTABLE mix_test) -#qt5_add_resources(UI_RESOURCES test.qrc) -#add_executable(${TEST_EXECUTABLE} ${UI_RESOURCES} ${SRC_LIST} ${HEADERS}) +find_package(Qt5QuickTest REQUIRED) +find_package(Qt5Test REQUIRED) +set(TEST_EXECUTABLE mix_test) +list(APPEND LIBRARIES "Qt5::QuickTest") +list(APPEND LIBRARIES "Qt5::Test") +list(REMOVE_ITEM SRC_LIST "./main.cpp") +aux_source_directory(test SRC_LIST) +file(GLOB HEADERS "test/*.h") +add_executable(${TEST_EXECUTABLE} ${UI_RESOURCES} ${SRC_LIST} ${HEADERS}) +target_link_libraries(${TEST_EXECUTABLE} ${LIBRARIES}) +set_target_properties(${TEST_EXECUTABLE} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 739eb8d8c..5a344f921 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -90,6 +90,7 @@ ClientModel::ClientModel(): ClientModel::~ClientModel() { + m_runFuture.waitForFinished(); } QString ClientModel::apiCall(QString const& _message) @@ -113,7 +114,7 @@ void ClientModel::mine() m_mining = true; emit miningStarted(); emit miningStateChanged(); - QtConcurrent::run([=]() + m_runFuture = QtConcurrent::run([=]() { try { @@ -206,7 +207,7 @@ void ClientModel::executeSequence(std::vector const& _seque emit runStateChanged(); //run sequence - QtConcurrent::run([=]() + m_runFuture = QtConcurrent::run([=]() { try { diff --git a/mix/ClientModel.h b/mix/ClientModel.h index a5d89d859..9b35af4fd 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "MachineStates.h" namespace dev @@ -203,6 +204,7 @@ private: std::atomic m_running; std::atomic m_mining; + QFuture m_runFuture; std::unique_ptr m_client; std::unique_ptr m_rpcConnector; std::unique_ptr m_web3Server; diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index 7eefcc38e..80929dc49 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -24,9 +24,6 @@ #include #include #include -#include -#include -#include #ifdef ETH_HAVE_WEBENGINE #include #endif @@ -50,45 +47,10 @@ ApplicationService::ApplicationService() m_systemPointSize = f.pointSize(); } - -#include - -bool ApplicationService::waitForSignal(QObject* _item, QString _signalName, int _timeout) -{ - QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); - QMetaObject const* mo = _item->metaObject(); - - QStringList methods; - - for(int i = mo->methodOffset(); i < mo->methodCount(); ++i) { - if (mo->method(i).methodType() == QMetaMethod::Signal) { - methods << QString::fromLatin1(mo->method(i).methodSignature()); - } - } - - QElapsedTimer timer; - timer.start(); - - while (!spy.size()) { - int remaining = _timeout - int(timer.elapsed()); - if (remaining <= 0) - break; - QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - QTest::qSleep(10); - } - - return spy.size(); -} - MixApplication::MixApplication(int& _argc, char* _argv[]): QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()) { - initialize(); - //m_engine->load(QUrl("qrc:/qml/main.qml")); - m_engine->load(QUrl("qrc:/test/TestMain.qml")); - - + m_engine->load(QUrl("qrc:/qml/Application.qml")); if (!m_engine->rootObjects().empty()) { QWindow *window = qobject_cast(m_engine->rootObjects().at(0)); @@ -100,6 +62,17 @@ MixApplication::MixApplication(int& _argc, char* _argv[]): void MixApplication::initialize() { +#if __linux + //work around ubuntu appmenu-qt5 bug + //https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1323853 + putenv((char*)"QT_QPA_PLATFORMTHEME="); + putenv((char*)"QSG_RENDER_LOOP=threaded"); +#endif +#if (defined(_WIN32) || defined(_WIN64)) + if (!getenv("OPENSSL_CONF")) + putenv((char*)"OPENSSL_CONF=c:\\"); +#endif + setOrganizationName(tr("Ethereum")); setOrganizationDomain(tr("ethereum.org")); setApplicationName(tr("Mix")); diff --git a/mix/MixApplication.h b/mix/MixApplication.h index ce3544392..9756f1b89 100644 --- a/mix/MixApplication.h +++ b/mix/MixApplication.h @@ -37,12 +37,16 @@ class ApplicationService: public QObject { Q_OBJECT Q_PROPERTY(int systemPointSize READ systemPointSize CONSTANT) + Q_PROPERTY(bool haveWebEngine READ haveWebEngine CONSTANT) public: ApplicationService(); int systemPointSize() const { return m_systemPointSize; } -public slots: - bool waitForSignal(QObject* _item, QString _signalName, int _timeout); +#ifdef ETH_HAVE_WEBENGINE + bool haveWebEngine() const { return true; } +#else + bool haveWebEngine() const { return false; } +#endif private: int m_systemPointSize = 0; diff --git a/mix/QBasicNodeDefinition.cpp b/mix/QBasicNodeDefinition.cpp new file mode 100644 index 000000000..8905caef6 --- /dev/null +++ b/mix/QBasicNodeDefinition.cpp @@ -0,0 +1,41 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file QBasicNodeDefinition.cpp + * @author Yann yann@ethdev.com + * @date 2014 + */ + +#include "QBasicNodeDefinition.h" +#include + +namespace dev +{ +namespace mix +{ + +QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d): + QObject(_parent), m_name(QString::fromStdString(_d->getName())) +{ +} + +QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, std::string const& _name): + QObject(_parent), m_name(QString::fromStdString(_name)) +{ +} + +} +} diff --git a/mix/QBasicNodeDefinition.h b/mix/QBasicNodeDefinition.h index 42f8ac15d..eb81d4f85 100644 --- a/mix/QBasicNodeDefinition.h +++ b/mix/QBasicNodeDefinition.h @@ -21,11 +21,17 @@ #pragma once +#include #include -#include namespace dev { + +namespace solidity +{ +class Declaration; +} + namespace mix { @@ -37,8 +43,8 @@ class QBasicNodeDefinition: public QObject public: QBasicNodeDefinition(QObject* _parent = nullptr): QObject(_parent) {} ~QBasicNodeDefinition() {} - QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d): QObject(_parent), m_name(QString::fromStdString(_d->getName())) {} - QBasicNodeDefinition(QObject* _parent, std::string const& _name): QObject(_parent), m_name(QString::fromStdString(_name)) {} + QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d); + QBasicNodeDefinition(QObject* _parent, std::string const& _name); /// Get the name of the node. QString name() const { return m_name; } diff --git a/mix/QVariableDeclaration.cpp b/mix/QVariableDeclaration.cpp index 391d26c04..3bbb0d523 100644 --- a/mix/QVariableDeclaration.cpp +++ b/mix/QVariableDeclaration.cpp @@ -21,6 +21,7 @@ */ #include "QVariableDeclaration.h" +#include #include "CodeModel.h" namespace dev diff --git a/mix/QVariableDeclaration.h b/mix/QVariableDeclaration.h index cf9345061..4309550b2 100644 --- a/mix/QVariableDeclaration.h +++ b/mix/QVariableDeclaration.h @@ -26,14 +26,15 @@ #pragma once +namespace dev +{ + namespace solidity { class Type; class VariableDeclaration; } -namespace dev -{ namespace mix { diff --git a/mix/QVariableDefinition.cpp b/mix/QVariableDefinition.cpp new file mode 100644 index 000000000..4df31ea92 --- /dev/null +++ b/mix/QVariableDefinition.cpp @@ -0,0 +1,36 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file QVariableDefinition.cpp + * @author Yann yann@ethdev.com + * @date 2014 + */ + +#include "QVariableDefinition.h" +#include + +namespace dev +{ +namespace mix +{ +QString QVariableDefinition::encodeValueAsString() +{ + return QString::fromStdString(dev::toHex(encodeValue())); +} + +} +} + diff --git a/mix/QVariableDefinition.h b/mix/QVariableDefinition.h index 70a663ee3..e6c38f971 100644 --- a/mix/QVariableDefinition.h +++ b/mix/QVariableDefinition.h @@ -21,14 +21,14 @@ #pragma once -#include -#include "QBigInt.h" -#include "QVariableDeclaration.h" +#include +#include namespace dev { namespace mix { +class QVariableDeclaration; class QVariableDefinition: public QObject { @@ -54,7 +54,7 @@ public: /// Decode the return value @a _rawValue. virtual void decodeValue(dev::bytes const& _rawValue) = 0; /// returns String representation of the encoded value. - Q_INVOKABLE QString encodeValueAsString() { return QString::fromStdString(dev::toHex(encodeValue())); } + Q_INVOKABLE QString encodeValueAsString(); protected: QString m_value; diff --git a/mix/main.cpp b/mix/main.cpp index 908606bd9..78b5261ac 100644 --- a/mix/main.cpp +++ b/mix/main.cpp @@ -22,40 +22,19 @@ #include #include +#include +#include #include "MixApplication.h" -#include "Exceptions.h" - -#include - using namespace dev::mix; int main(int _argc, char* _argv[]) { -#ifdef ETH_HAVE_WEBENGINE - Q_INIT_RESOURCE(js); -#endif -#if __linux - //work around ubuntu appmenu-qt5 bug - //https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1323853 - putenv((char*)"QT_QPA_PLATFORMTHEME="); - putenv((char*)"QSG_RENDER_LOOP=threaded"); -#endif -#if (defined(_WIN32) || defined(_WIN64)) - if (!getenv("OPENSSL_CONF")) - putenv((char*)"OPENSSL_CONF=c:\\"); -#endif try { - - // MixApplication::initialize(); - //MixApplication app(_argc, _argv); - return quick_test_main(_argc, _argv, "mix", "/home/arkady/src/cpp-ethereum/mix/test/TestMain.qml"); - - - //MixApplication app(_argc, _argv); - //return app.exec(); + MixApplication app(_argc, _argv); + return app.exec(); } catch (boost::exception const& _e) { diff --git a/mix/qml.qrc b/mix/qml.qrc index fd437d881..0e8c37b42 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -60,6 +60,5 @@ qml/js/ProjectModel.js qml/js/QEtherHelper.js qml/js/TransactionHelper.js - qml/qmldir diff --git a/mix/qml/Application.qml b/mix/qml/Application.qml index b112cf527..b7a065cdf 100644 --- a/mix/qml/Application.qml +++ b/mix/qml/Application.qml @@ -55,6 +55,10 @@ ApplicationWindow { id: clipboard } + Style { + id: appStyle + } + Connections { target: mainApplication onClosing: diff --git a/mix/qml/CallStack.qml b/mix/qml/CallStack.qml index c9e22532d..9a938078c 100644 --- a/mix/qml/CallStack.qml +++ b/mix/qml/CallStack.qml @@ -2,7 +2,6 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 -import "." DebugInfoList { @@ -37,7 +36,7 @@ DebugInfoList anchors.leftMargin: 5 color: "#4a4a4a" text: styleData.row; - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize width: parent.width - 5 elide: Text.ElideRight } @@ -58,7 +57,7 @@ DebugInfoList color: "#4a4a4a" text: styleData.value; elide: Text.ElideRight - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } } diff --git a/mix/qml/CodeEditor.qml b/mix/qml/CodeEditor.qml index 9d3df69e5..25f76b701 100644 --- a/mix/qml/CodeEditor.qml +++ b/mix/qml/CodeEditor.qml @@ -24,31 +24,36 @@ Item { id: contentView width: parent.width height: parent.height * 0.7 - Rectangle { - id: lineColumn - property int rowHeight: codeEditor.font.pixelSize + 3 - color: "#202020" - width: 50 - height: parent.height - Column { - y: -codeEditor.flickableItem.contentY + 4 - width: parent.width - Repeater { - model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) - delegate: Text { - id: text - color: codeEditor.textColor - font: codeEditor.font - width: lineColumn.width - 4 - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - height: lineColumn.rowHeight - renderType: Text.NativeRendering - text: index + 1 - } + + CodeEditorStyle { + id: style + } + + Rectangle { + id: lineColumn + property int rowHeight: codeEditor.font.pixelSize + 3 + color: "#202020" + width: 50 + height: parent.height + Column { + y: -codeEditor.flickableItem.contentY + 4 + width: parent.width + Repeater { + model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) + delegate: Text { + id: text + color: codeEditor.textColor + font: codeEditor.font + width: lineColumn.width - 4 + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + height: lineColumn.rowHeight + renderType: Text.NativeRendering + text: index + 1 } } } + } TextArea { id: codeEditor @@ -66,7 +71,7 @@ Item { height: parent.height font.family: "Monospace" - font.pointSize: CodeEditorStyle.general.basicFontSize + font.pointSize: style.general.basicFontSize width: parent.width tabChangesFocus: false diff --git a/mix/qml/CodeEditorStyle.qml b/mix/qml/CodeEditorStyle.qml index c9740957c..999054d55 100644 --- a/mix/qml/CodeEditorStyle.qml +++ b/mix/qml/CodeEditorStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 4d8559200..0f7f5d864 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -10,6 +10,7 @@ Item { signal documentEdit(string documentId) signal breakpointsChanged(string documentId) signal isCleanChanged(var isClean, string documentId) + signal loadComplete function getDocumentText(documentId) { for (var i = 0; i < editorListModel.count; i++) { @@ -43,6 +44,9 @@ Item { function doLoadDocument(editor, document) { var data = fileIo.readFile(document.path); + editor.onLoadComplete.connect(function() { + loadComplete(); + }); editor.onEditorTextChanged.connect(function() { documentEdit(document.documentId); if (document.isContract) @@ -160,6 +164,11 @@ Item { } } + CodeEditorStyle + { + id: style; + } + MessageDialog { id: messageDialog @@ -177,12 +186,16 @@ Item { Repeater { id: editors model: editorListModel + onItemRemoved: { + console.log("unloaded"); + item.item.unloaded = true; + } delegate: Loader { id: loader active: false asynchronous: true anchors.fill: parent - source: "CodeEditor.qml" + source: appService.haveWebEngine ? "WebCodeEditor.qml" : "CodeEditor.qml" visible: (index >= 0 && index < editorListModel.count && currentDocumentId === editorListModel.get(index).documentId) property bool changed: false onVisibleChanged: { diff --git a/mix/qml/CommonSeparator.qml b/mix/qml/CommonSeparator.qml index c81a81f63..6644802c7 100644 --- a/mix/qml/CommonSeparator.qml +++ b/mix/qml/CommonSeparator.qml @@ -4,6 +4,6 @@ import "." Rectangle { height: 1 - color: Style.generic.layout.separatorColor + color: appStyle.generic.layout.separatorColor } diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 358750b24..aee23b129 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -65,6 +65,10 @@ Rectangle { Debugger.setBreakpoints(bp); } + DebuggerPaneStyle { + id: dbgStyle + } + Connections { target: clientModel onDebugDataReady: { @@ -422,7 +426,7 @@ Rectangle { color: "#b2b3ae" text: styleData.value.split(' ')[0] font.family: "monospace" - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize wrapMode: Text.NoWrap id: id } @@ -432,7 +436,7 @@ Rectangle { color: styleData.selected ? "white" : "black" font.family: "monospace" text: styleData.value.replace(styleData.value.split(' ')[0], '') - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } } @@ -505,7 +509,7 @@ Rectangle { font.family: "monospace" color: "#4a4a4a" text: styleData.row; - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } @@ -523,7 +527,7 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter color: "#4a4a4a" text: styleData.value - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } } diff --git a/mix/qml/DebuggerPaneStyle.qml b/mix/qml/DebuggerPaneStyle.qml index 1078df2f9..db8bbe253 100644 --- a/mix/qml/DebuggerPaneStyle.qml +++ b/mix/qml/DebuggerPaneStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/DefaultLabel.qml b/mix/qml/DefaultLabel.qml index d8ef1faff..5d00df137 100644 --- a/mix/qml/DefaultLabel.qml +++ b/mix/qml/DefaultLabel.qml @@ -1,6 +1,5 @@ import QtQuick 2.0 import QtQuick.Controls 1.1 -import "." Label { text: text diff --git a/mix/qml/DeploymentDialog.qml b/mix/qml/DeploymentDialog.qml index c194977cf..1d6771b4a 100644 --- a/mix/qml/DeploymentDialog.qml +++ b/mix/qml/DeploymentDialog.qml @@ -31,7 +31,7 @@ Window { property string currentAccount property alias gasToUse: gasToUseInput.text - color: Style.generic.layout.backgroundColor + color: appStyle.generic.layout.backgroundColor function close() { @@ -329,7 +329,7 @@ Window { anchors.verticalCenter: parent.verticalCenter; anchors.left: applicationUrlEth.right font.italic: true - font.pointSize: Style.absoluteSize(-1) + font.pointSize: appStyle.absoluteSize(-1) } } } diff --git a/mix/qml/FilesSection.qml b/mix/qml/FilesSection.qml index d9f664894..d89875583 100644 --- a/mix/qml/FilesSection.qml +++ b/mix/qml/FilesSection.qml @@ -18,21 +18,21 @@ Rectangle property string sectionName; property variant selManager; property int index; - color: index % 2 === 0 ? "transparent" : ProjectFilesStyle.title.background + color: index % 2 === 0 ? "transparent" : projectFilesStyle.title.background function hiddenHeightTopLevel() { - return section.state === "hidden" ? ProjectFilesStyle.documentsList.height : ProjectFilesStyle.documentsList.fileNameHeight * model.count + ProjectFilesStyle.documentsList.height; + return section.state === "hidden" ? projectFilesStyle.documentsList.height : projectFilesStyle.documentsList.fileNameHeight * model.count + projectFilesStyle.documentsList.height; } function hiddenHeightRepeater() { - return section.state === "hidden" ? 0 : ProjectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count; + return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count; } function hiddenHeightElement() { - return section.state === "hidden" ? 0 : ProjectFilesStyle.documentsList.fileNameHeight; + return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight; } function getDocumentIndex(documentId) @@ -68,7 +68,7 @@ Rectangle { anchors.top: parent.top id: rowCol - height: ProjectFilesStyle.documentsList.height + height: projectFilesStyle.documentsList.height Layout.fillWidth: true @@ -88,15 +88,15 @@ Rectangle id: section text: sectionName anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin - color: ProjectFilesStyle.documentsList.sectionColor + anchors.leftMargin: projectFilesStyle.general.leftMargin + color: projectFilesStyle.documentsList.sectionColor font.family: boldFont.name - font.pointSize: ProjectFilesStyle.documentsList.sectionFontSize + font.pointSize: projectFilesStyle.documentsList.sectionFontSize states: [ State { name: "hidden" PropertyChanges { target: filesList; visible: false; } - PropertyChanges { target: rowCol; Layout.minimumHeight: ProjectFilesStyle.documentsList.height; Layout.maximumHeight: ProjectFilesStyle.documentsList.height; height: ProjectFilesStyle.documentsList.height; } + PropertyChanges { target: rowCol; Layout.minimumHeight: projectFilesStyle.documentsList.height; Layout.maximumHeight: projectFilesStyle.documentsList.height; height: projectFilesStyle.documentsList.height; } PropertyChanges { target: imgArrow; source: "qrc:/qml/img/closedtriangleindicator_filesproject.png" } } ] @@ -138,7 +138,7 @@ Rectangle Layout.preferredHeight: wrapperItem.hiddenHeightElement() Layout.maximumHeight: wrapperItem.hiddenHeightElement() height: wrapperItem.hiddenHeightElement() - color: isSelected ? ProjectFilesStyle.documentsList.highlightColor : "transparent" + color: isSelected ? projectFilesStyle.documentsList.highlightColor : "transparent" property bool isSelected property bool renameMode @@ -147,15 +147,15 @@ Rectangle anchors.verticalCenter: parent.verticalCenter anchors.fill: parent anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin + 2 + anchors.leftMargin: projectFilesStyle.general.leftMargin + 2 Text { id: nameText height: parent.height visible: !renameMode - color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color + color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color text: name; font.family: fileNameFont.name - font.pointSize: ProjectFilesStyle.documentsList.fontSize + font.pointSize: projectFilesStyle.documentsList.fontSize verticalAlignment: Text.AlignVCenter Connections @@ -182,7 +182,7 @@ Rectangle DefaultLabel { id: editStatusLabel visible: false - color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color + color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color verticalAlignment: Text.AlignVCenter text: "*" width: 10 @@ -196,7 +196,7 @@ Rectangle visible: renameMode anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin + anchors.leftMargin: projectFilesStyle.general.leftMargin MouseArea { id: textMouseArea anchors.fill: parent diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index b40dfc4c7..7351667d4 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -3,7 +3,6 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 import org.ethereum.qml.SortFilterProxyModel 1.0 -import "." Rectangle { @@ -13,11 +12,16 @@ Rectangle logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level }); } + LogsPaneStyle { + id: logStyle + } + + id: root anchors.fill: parent radius: 5 - color: LogsPaneStyle.generic.layout.backgroundColor - border.color: LogsPaneStyle.generic.layout.borderColor - border.width: LogsPaneStyle.generic.layout.borderWidth + color: logStyle.generic.layout.backgroundColor + border.color: logStyle.generic.layout.borderColor + border.width: logStyle.generic.layout.borderWidth ColumnLayout { z: 2 height: parent.height @@ -26,14 +30,14 @@ Rectangle Row { id: rowAction - Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight - height: LogsPaneStyle.generic.layout.headerHeight - anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin + Layout.preferredHeight: logStyle.generic.layout.headerHeight + height: logStyle.generic.layout.headerHeight + anchors.leftMargin: logStyle.generic.layout.leftMargin anchors.left: parent.left - spacing: LogsPaneStyle.generic.layout.headerButtonSpacing + spacing: logStyle.generic.layout.headerButtonSpacing Button { - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter action: clearAction iconSource: "qrc:/qml/img/broom.png" @@ -50,7 +54,7 @@ Rectangle Button { - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter action: copytoClipBoardAction iconSource: "qrc:/qml/img/copy.png" @@ -81,7 +85,7 @@ Rectangle ToolButton { id: javascriptButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter checked: true onCheckedChanged: { @@ -93,9 +97,9 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) - color: LogsPaneStyle.generic.layout.logLabelColor + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) + color: logStyle.generic.layout.logLabelColor anchors.centerIn: parent text: qsTr("JavaScript") } @@ -106,7 +110,7 @@ Rectangle ToolButton { id: runButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter checked: true onCheckedChanged: { @@ -118,9 +122,9 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) - color: LogsPaneStyle.generic.layout.logLabelColor + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) + color: logStyle.generic.layout.logLabelColor anchors.centerIn: parent text: qsTr("Run") } @@ -131,7 +135,7 @@ Rectangle ToolButton { id: stateButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter checked: true onCheckedChanged: { @@ -143,8 +147,8 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) color: "#5391d8" anchors.centerIn: parent text: qsTr("State") @@ -156,7 +160,7 @@ Rectangle ToolButton { id: compilationButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter checked: false onCheckedChanged: { @@ -168,8 +172,8 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) color: "#5391d8" anchors.centerIn: parent text: qsTr("Compilation") @@ -181,11 +185,11 @@ Rectangle DefaultTextField { id: searchBox - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter - width: LogsPaneStyle.generic.layout.headerInputWidth - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) + width: logStyle.generic.layout.headerInputWidth + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) font.italic: true onTextChanged: { proxyModel.search(text); @@ -255,21 +259,21 @@ Rectangle { role: "date" title: qsTr("date") - width: LogsPaneStyle.generic.layout.dateWidth + width: logStyle.generic.layout.dateWidth delegate: itemDelegate } TableViewColumn { role: "type" title: qsTr("type") - width: LogsPaneStyle.generic.layout.typeWidth + width: logStyle.generic.layout.typeWidth delegate: itemDelegate } TableViewColumn { role: "content" title: qsTr("content") - width: LogsPaneStyle.generic.layout.contentWidth + width: logStyle.generic.layout.contentWidth delegate: itemDelegate } @@ -277,7 +281,7 @@ Rectangle Rectangle { width: logsTable.width - 4 height: 17 - color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor + color: styleData.alternate ? "transparent" : logStyle.generic.layout.logAlternateColor } } } @@ -286,8 +290,8 @@ Rectangle id: itemDelegate DefaultLabel { text: styleData.value; - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-1) + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-1) color: { if (proxyModel.get(styleData.row).level === "error") return "red"; diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml index 59b80653a..305e6f16f 100644 --- a/mix/qml/LogsPaneStyle.qml +++ b/mix/qml/LogsPaneStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/NewProjectDialog.qml b/mix/qml/NewProjectDialog.qml index 1ec53e1d9..3ea2a5a4b 100644 --- a/mix/qml/NewProjectDialog.qml +++ b/mix/qml/NewProjectDialog.qml @@ -15,6 +15,7 @@ Window { property alias projectTitle: titleField.text readonly property string projectPath: "file://" + pathField.text + property alias pathFieldText: pathField.text signal accepted function open() { diff --git a/mix/qml/ProjectFilesStyle.qml b/mix/qml/ProjectFilesStyle.qml index f4b83c728..ca4499196 100644 --- a/mix/qml/ProjectFilesStyle.qml +++ b/mix/qml/ProjectFilesStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml index 6ca6e6d1f..a98c2587b 100644 --- a/mix/qml/ProjectList.qml +++ b/mix/qml/ProjectList.qml @@ -8,6 +8,11 @@ import "." Item { property bool renameMode: false; + + ProjectFilesStyle { + id: projectFilesStyle + } + ColumnLayout { anchors.fill: parent id: filesCol @@ -20,8 +25,8 @@ Item { Rectangle { - color: ProjectFilesStyle.title.background - height: ProjectFilesStyle.title.height + color: projectFilesStyle.title.background + height: projectFilesStyle.title.height Layout.fillWidth: true Image { id: projectIcon @@ -37,14 +42,14 @@ Item { Text { id: projectTitle - color: ProjectFilesStyle.title.color + color: projectFilesStyle.title.color text: projectModel.projectTitle anchors.verticalCenter: parent.verticalCenter visible: !projectModel.isEmpty; anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin + anchors.leftMargin: projectFilesStyle.general.leftMargin font.family: srcSansProLight.name - font.pointSize: ProjectFilesStyle.title.fontSize + font.pointSize: projectFilesStyle.title.fontSize font.weight: Font.Light } @@ -54,7 +59,7 @@ Item { anchors.right: parent.right anchors.rightMargin: 15 font.family: srcSansProLight.name - font.pointSize: ProjectFilesStyle.title.fontSize + font.pointSize: projectFilesStyle.title.fontSize anchors.verticalCenter: parent.verticalCenter font.weight: Font.Light } @@ -64,14 +69,14 @@ Item { { Layout.fillWidth: true height: 3 - color: ProjectFilesStyle.documentsList.background + color: projectFilesStyle.documentsList.background } Rectangle { Layout.fillWidth: true Layout.fillHeight: true - color: ProjectFilesStyle.documentsList.background + color: projectFilesStyle.documentsList.background ColumnLayout { diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index 7cd1363a6..6c859a9f9 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -44,6 +44,7 @@ Item { property var stateListModel: projectStateListModel.model property CodeEditorView codeEditor: null property var unsavedFiles: [] + property alias newProjectDialog: newProjectDialog //interface function saveAll() { ProjectModelCode.saveAll(); } diff --git a/mix/qml/Splitter.qml b/mix/qml/Splitter.qml index 96e823795..012379700 100644 --- a/mix/qml/Splitter.qml +++ b/mix/qml/Splitter.qml @@ -9,6 +9,3 @@ SplitView color: "#cccccc" } } - - - diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index b229ed433..5b96c4fd4 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -17,7 +17,7 @@ Window { height: 480 title: qsTr("Edit State") visible: false - color: StateDialogStyle.generic.backgroundColor + color: stateDialogStyle.generic.backgroundColor property alias stateTitle: titleField.text property alias isDefault: defaultCheckBox.checked @@ -71,6 +71,10 @@ Window { return item; } + StateDialogStyle { + id: stateDialogStyle + } + ColumnLayout { anchors.fill: parent anchors.margins: 10 diff --git a/mix/qml/StateDialogStyle.qml b/mix/qml/StateDialogStyle.qml index 39214312a..993e6a1c3 100644 --- a/mix/qml/StateDialogStyle.qml +++ b/mix/qml/StateDialogStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 6d4b5e7e1..24e136d8e 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -50,6 +50,10 @@ Rectangle { logPane.push("error", type, text); } + StatusPaneStyle { + id: statusPaneStyle + } + Connections { target: webPreview onJavaScriptMessage: @@ -137,7 +141,7 @@ Rectangle { Text { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter - font.pointSize: StatusPaneStyle.general.statusFontSize + font.pointSize: statusPaneStyle.general.statusFontSize height: 15 font.family: "sans serif" objectName: "status" diff --git a/mix/qml/StatusPaneStyle.qml b/mix/qml/StatusPaneStyle.qml index e6a1c9910..1eb11b48e 100644 --- a/mix/qml/StatusPaneStyle.qml +++ b/mix/qml/StatusPaneStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/Style.qml b/mix/qml/Style.qml index c317177a3..422831c78 100644 --- a/mix/qml/Style.qml +++ b/mix/qml/Style.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 5d98d3dbd..f4de1c0e0 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -13,7 +13,7 @@ Window { width: 520 height: 500; visible: false - color: StateDialogStyle.generic.backgroundColor + color: transactionDialogStyle.generic.backgroundColor title: qsTr("Edit Transaction") property int transactionIndex property alias gas: gasValueEdit.gasValue; @@ -164,6 +164,10 @@ Window { return item; } + StateDialogStyle { + id: transactionDialogStyle + } + ColumnLayout { anchors.fill: parent anchors.margins: 10 diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 7e5945fc7..18a706d17 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -7,12 +7,14 @@ import QtWebEngine.experimental 1.0 import "js/ErrorLocationFormater.js" as ErrorLocationFormater Item { - signal editorTextChanged signal breakpointsChanged + signal editorTextChanged + signal loadComplete property bool isClean: true property string currentText: "" property string currentMode: "" property bool initialized: false + property bool unloaded: false property var currentBreakpoints: []; function setText(text, mode) { @@ -78,7 +80,7 @@ Item { onLoadingChanged: { - if (!loading) { + if (!loading && !unloaded && editorBrowser) { initialized = true; setText(currentText, currentMode); runJavaScript("getTextChanged()", function(result) { }); @@ -87,19 +89,19 @@ Item { if (currentMode === "solidity") { codeModel.onCompilationComplete.connect(function(){ - runJavaScript("compilationComplete()", function(result) { }); + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); }); codeModel.onCompilationError.connect(function(error){ var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); if (errorInfo.line && errorInfo.column) - runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); + editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); else - runJavaScript("compilationComplete()", function(result) { }); + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); }); } parent.changeGeneration(); - + loadComplete(); } } @@ -110,6 +112,8 @@ Item { running: false repeat: true onTriggered: { + if (unloaded) + return; editorBrowser.runJavaScript("getTextChanged()", function(result) { if (result === true) { editorBrowser.runJavaScript("getText()" , function(textValue) { diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 8507c9b46..b35bd8324 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -60,6 +60,10 @@ Item { setPreviewUrl(urlInput.text); } + WebPreviewStyle { + id: webPreviewStyle + } + Connections { target: mainApplication onLoaded: { @@ -182,7 +186,7 @@ Item { Rectangle { anchors.leftMargin: 4 - color: WebPreviewStyle.general.headerBackgroundColor + color: webPreviewStyle.general.headerBackgroundColor Layout.preferredWidth: parent.width Layout.preferredHeight: 32 Row { @@ -229,7 +233,7 @@ Item { { width: 1 height: parent.height - 10 - color: WebPreviewStyle.general.separatorColor + color: webPreviewStyle.general.separatorColor anchors.verticalCenter: parent.verticalCenter } @@ -250,7 +254,7 @@ Item { { width: 1 height: parent.height - 10 - color: WebPreviewStyle.general.separatorColor + color: webPreviewStyle.general.separatorColor anchors.verticalCenter: parent.verticalCenter } @@ -284,7 +288,7 @@ Item { { Layout.preferredHeight: 1 Layout.preferredWidth: parent.width - color: WebPreviewStyle.general.separatorColor + color: webPreviewStyle.general.separatorColor } Splitter @@ -354,9 +358,9 @@ Item { id: expressionInput width: parent.width - 15 height: 20 - font.family: WebPreviewStyle.general.fontName + font.family: webPreviewStyle.general.fontName font.italic: true - font.pointSize: Style.absoluteSize(-3) + font.pointSize: appStyle.absoluteSize(-3) anchors.verticalCenter: parent.verticalCenter property var history: [] @@ -416,8 +420,8 @@ Item { id: resultTextArea width: expressionPanel.width wrapMode: Text.Wrap - font.family: WebPreviewStyle.general.fontName - font.pointSize: Style.absoluteSize(-3) + font.family: webPreviewStyle.general.fontName + font.pointSize: appStyle.absoluteSize(-3) backgroundVisible: true style: TextAreaStyle { backgroundColor: "#f0f0f0" diff --git a/mix/qml/WebPreviewStyle.qml b/mix/qml/WebPreviewStyle.qml index 231bbe16c..1fc0c8b99 100644 --- a/mix/qml/WebPreviewStyle.qml +++ b/mix/qml/WebPreviewStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/qmldir b/mix/qml/qmldir deleted file mode 100644 index 73c117941..000000000 --- a/mix/qml/qmldir +++ /dev/null @@ -1,8 +0,0 @@ -singleton Style 1.0 Style.qml -singleton StateDialogStyle 1.0 StateDialogStyle.qml -singleton ProjectFilesStyle 1.0 ProjectFilesStyle.qml -singleton DebuggerPaneStyle 1.0 DebuggerPaneStyle.qml -singleton StateStyle 1.0 StateStyle.qml -singleton StatusPaneStyle 1.0 StatusPaneStyle.qml -singleton WebPreviewStyle 1.0 WebPreviewStyle.qml -singleton LogsPaneStyle 1.0 LogsPaneStyle.qml diff --git a/mix/test/TestMain.cpp b/mix/test/TestMain.cpp new file mode 100644 index 000000000..f21319004 --- /dev/null +++ b/mix/test/TestMain.cpp @@ -0,0 +1,50 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file main.cpp + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include "MixApplication.h" +#include "Exceptions.h" +#include "TestService.h" + +using namespace dev::mix; + +int main(int _argc, char* _argv[]) +{ + try + { + MixApplication::initialize(); + qmlRegisterType("org.ethereum.qml.TestService", 1, 0, "TestService"); + + return quick_test_main(_argc, _argv, "mix", _argv[1]); + } + catch (boost::exception const& _e) + { + std::cerr << boost::diagnostic_information(_e); + } + catch (std::exception const& _e) + { + std::cerr << _e.what(); + } +} diff --git a/mix/test/TestMain.qml b/mix/test/TestMain.qml deleted file mode 100644 index 737a2b2a4..000000000 --- a/mix/test/TestMain.qml +++ /dev/null @@ -1,51 +0,0 @@ -import QtQuick 2.2 -import QtTest 1.1 -//import Qt.test.qtestroot 1.0 -import "../qml" - - -TestCase -{ - -Item -{ - id: helper - function findChild(item, childId) { - if (item.children) { - var searchString = "button" - - for (var idx in item.children) { - var currentItem = item.children[idx] - - if (currentItem.id.match("^"+childId) === childId) - return currentItem; - - return findChild(currentItem, childId); - } - } - } -} - - -function test_t1() -{ - waitForRendering(mainApplication.mainContent, 10000); - mainApplication.projectModel.createProject(); - var projectDlg = helper.findChild(mainApplication, "newProjectDialog"); - - if (mainApplication.appService.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) - console.log("compiled"); -} - -function runTest() -{ - waitForRendering(mainApplication.mainContent, 10000); - console.log("runtest"); - mousePress(mainApplication, 10, 10); -} - -Application { - id: mainApplication -} - -} diff --git a/mix/test/TestService.cpp b/mix/test/TestService.cpp new file mode 100644 index 000000000..132fe81c0 --- /dev/null +++ b/mix/test/TestService.cpp @@ -0,0 +1,113 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file TestService.cpp + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#include "TestService.h" +#include +#include +#include +#include + +namespace dev +{ +namespace mix +{ + +bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeout) +{ + QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); + QMetaObject const* mo = _item->metaObject(); + + QStringList methods; + + for(int i = mo->methodOffset(); i < mo->methodCount(); ++i) { + if (mo->method(i).methodType() == QMetaMethod::Signal) { + methods << QString::fromLatin1(mo->method(i).methodSignature()); + } + } + + QElapsedTimer timer; + timer.start(); + + while (!spy.size()) { + int remaining = _timeout - int(timer.elapsed()); + if (remaining <= 0) + break; + QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QTest::qSleep(10); + } + + return spy.size(); +} + +bool TestService::keyPress(int _key, int _modifiers, int _delay) +{ + QWindow *window = eventWindow(); + QTest::keyPress(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyRelease(int _key, int _modifiers, int _delay) +{ + QWindow *window = eventWindow(); + QTest::keyRelease(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyClick(int _key, int _modifiers, int _delay) +{ + QWindow *window = eventWindow(); + QTest::keyClick(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyPressChar(QString const& _character, int _modifiers, int _delay) +{ + QTEST_ASSERT(_character.length() == 1); + QWindow *window = eventWindow(); + QTest::keyPress(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyReleaseChar(QString const& _character, int _modifiers, int _delay) +{ + QTEST_ASSERT(_character.length() == 1); + QWindow *window = eventWindow(); + QTest::keyRelease(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyClickChar(QString const& _character, int _modifiers, int _delay) +{ + QTEST_ASSERT(_character.length() == 1); + QWindow *window = eventWindow(); + QTest::keyClick(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +QWindow* TestService::eventWindow() +{ + return 0; +} + +} +} diff --git a/mix/test/TestService.h b/mix/test/TestService.h new file mode 100644 index 000000000..0049b2259 --- /dev/null +++ b/mix/test/TestService.h @@ -0,0 +1,52 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file TestService.h + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include + +class QWindow; + +namespace dev +{ +namespace mix +{ + +class TestService: public QObject +{ + Q_OBJECT + +public slots: + bool waitForSignal(QObject* _item, QString _signalName, int _timeout); + bool keyPress(int _key, int _modifiers, int _delay); + bool keyRelease(int _key, int _modifiers, int _delay); + bool keyClick(int _key, int _modifiers, int _delay); + bool keyPressChar(QString const& _character, int _modifiers, int _delay); + bool keyReleaseChar(QString const& _character, int _modifiers, int _delay); + bool keyClickChar(QString const& _character, int _modifiers, int _delay); + +private: + QWindow* eventWindow(); +}; + +} +} diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml new file mode 100644 index 000000000..861d875f5 --- /dev/null +++ b/mix/test/qml/TestMain.qml @@ -0,0 +1,52 @@ +import QtQuick 2.2 +import QtTest 1.1 +import org.ethereum.qml.TestService 1.0 +import "../../qml" + + + +TestCase +{ + id: tc + +TestService +{ + id: ts + function typeString(str) + { + for (var c in str) + ts.keyClickChar(c, Qt.NoModifier, 20); + } +} + + +function test_t1() +{ + waitForRendering(mainApplication.mainContent, 10000); + mainApplication.projectModel.createProject(); + var projectDlg = mainApplication.projectModel.newProjectDialog; + projectDlg.projectTitle = "TestProject"; + projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path + projectDlg.acceptAndClose(); + ts.waitForSignal(mainApplication.mainContent.codeEditor, "loadComplete()", 5000) + + ts.keyClickChar("A", Qt.ControlModifier, 20); + ts.typeString("CCC"); + if (ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + console.log("compiled"); + ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000); +} + +function runTest() +{ + waitForRendering(mainApplication.mainContent, 10000); + console.log("runtest"); + mousePress(mainApplication, 10, 10); +} + +Application +{ + id: mainApplication +} +} + diff --git a/mix/test/TestTransactionDebug.qml b/mix/test/qml/TestTransactionDebug.qml similarity index 100% rename from mix/test/TestTransactionDebug.qml rename to mix/test/qml/TestTransactionDebug.qml diff --git a/mix/web.qrc b/mix/web.qrc index aa5b38a67..18cb92b1c 100644 --- a/mix/web.qrc +++ b/mix/web.qrc @@ -8,7 +8,7 @@ qml/html/cm/codemirror.js qml/html/cm/javascript.js qml/html/cm/matchbrackets.js - qml/WebCodeEditor.qml + qml/WebCodeEditor.qml qml/html/codeeditor.js qml/html/cm/fullscreen.css qml/html/cm/fullscreen.js From b1a51ebd469d2439d3743744fa55ae0477c79978 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 30 Mar 2015 14:29:18 +0200 Subject: [PATCH 05/45] continue work on tests --- mix/qml/CodeEditorView.qml | 1 - mix/qml/MainContent.qml | 1 + mix/qml/WebCodeEditor.qml | 21 +++++--- mix/qml/WebPreview.qml | 1 + mix/test/TestService.cpp | 19 +++++++ mix/test/TestService.h | 6 +++ mix/test/qml/TestMain.qml | 107 ++++++++++++++++++++++++------------- 7 files changed, 111 insertions(+), 45 deletions(-) diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index dc8b7a757..87ad95bcd 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -203,7 +203,6 @@ Item { id: editors model: editorListModel onItemRemoved: { - console.log("unloaded"); item.item.unloaded = true; } delegate: Loader { diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 67034f108..ca08485c3 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -22,6 +22,7 @@ Rectangle { property alias rightViewVisible: rightView.visible property alias webViewVisible: webPreview.visible + property alias webView: webPreview property alias projectViewVisible: projectList.visible property alias runOnProjectLoad: mainSettings.runOnProjectLoad property alias rightPane: rightView diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 2d6bc94eb..c33984d36 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -19,10 +19,11 @@ Item { function setText(text, mode) { currentText = text; - currentMode = mode; + if(mode !== undefined) + currentMode = mode; if (initialized) { editorBrowser.runJavaScript("setTextBase64(\"" + Qt.btoa(text) + "\")"); - editorBrowser.runJavaScript("setMode(\"" + mode + "\")"); + editorBrowser.runJavaScript("setMode(\"" + currentMode + "\")"); } setFocus(); } @@ -94,15 +95,19 @@ Item { if (currentMode === "solidity") { codeModel.onCompilationComplete.connect(function(){ - editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + if (editorBrowser) + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); }); codeModel.onCompilationError.connect(function(error){ - var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); - if (errorInfo.line && errorInfo.column) - editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); - else - editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + if (editorBrowser) + { + var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); + if (errorInfo.line && errorInfo.column) + editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); + else + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + } }); } parent.changeGeneration(); diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index b35bd8324..0203ac680 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -12,6 +12,7 @@ Item { id: webPreview property string pendingPageUrl: "" property bool initialized: false + property alias urlInput: urlInput signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content) function setPreviewUrl(url) { diff --git a/mix/test/TestService.cpp b/mix/test/TestService.cpp index 132fe81c0..c923a1bce 100644 --- a/mix/test/TestService.cpp +++ b/mix/test/TestService.cpp @@ -23,6 +23,8 @@ #include "TestService.h" #include #include +#include +#include #include #include @@ -104,8 +106,25 @@ bool TestService::keyClickChar(QString const& _character, int _modifiers, int _d return true; } +void TestService::setTargetWindow(QObject* _window) +{ + QQuickWindow* window = qobject_cast(_window); + if (window) + m_targetWindow = window; + window->requestActivate(); +} + QWindow* TestService::eventWindow() { + QQuickWindow* window = qobject_cast(m_targetWindow); + if (window) + { + window->requestActivate(); + return window; + } + QQuickItem* item = qobject_cast(m_targetWindow); + if (item) + return item->window(); return 0; } diff --git a/mix/test/TestService.h b/mix/test/TestService.h index 0049b2259..d52fbe0c4 100644 --- a/mix/test/TestService.h +++ b/mix/test/TestService.h @@ -34,6 +34,11 @@ namespace mix class TestService: public QObject { Q_OBJECT + Q_PROPERTY(QObject* targetWindow READ targetWindow WRITE setTargetWindow) + +public: + QObject* targetWindow() const { return m_targetWindow; } + void setTargetWindow(QObject* _window); public slots: bool waitForSignal(QObject* _item, QString _signalName, int _timeout); @@ -46,6 +51,7 @@ public slots: private: QWindow* eventWindow(); + QObject* m_targetWindow; }; } diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml index 861d875f5..fd6619c0c 100644 --- a/mix/test/qml/TestMain.qml +++ b/mix/test/qml/TestMain.qml @@ -3,50 +3,85 @@ import QtTest 1.1 import org.ethereum.qml.TestService 1.0 import "../../qml" - - TestCase { id: tc - -TestService -{ - id: ts - function typeString(str) + TestService { - for (var c in str) - ts.keyClickChar(c, Qt.NoModifier, 20); + id: ts + targetWindow: mainApplication + function typeString(str) + { + for (var c in str) + { + ts.keyPressChar(str[c], Qt.NoModifier, 0); + ts.keyReleaseChar(str[c], Qt.NoModifier, 0); + } + } } -} + function newProject() + { + waitForRendering(mainApplication.mainContent, 10000); + mainApplication.projectModel.createProject(); + var projectDlg = mainApplication.projectModel.newProjectDialog; + //if (ts.waitForSignal(projectDlg, "visibleChanged", 1000)) + projectDlg.projectTitle = "TestProject"; + projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path + projectDlg.acceptAndClose(); + ts.waitForSignal(mainApplication.mainContent.codeEditor, "loadComplete()", 5000) + wait(300); + } -function test_t1() -{ - waitForRendering(mainApplication.mainContent, 10000); - mainApplication.projectModel.createProject(); - var projectDlg = mainApplication.projectModel.newProjectDialog; - projectDlg.projectTitle = "TestProject"; - projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path - projectDlg.acceptAndClose(); - ts.waitForSignal(mainApplication.mainContent.codeEditor, "loadComplete()", 5000) - - ts.keyClickChar("A", Qt.ControlModifier, 20); - ts.typeString("CCC"); - if (ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) - console.log("compiled"); - ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000); -} + function test_defaultTransactionSequence() + { + newProject(); + mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText( + //ts.typeString( + "contract Contract {\r" + + " function Contract() {\r" + + " uint x = 69;\r" + + " uint y = 5;\r" + + " for (uint i = 0; i < y; ++i) {\r" + + " x += 42;\r" + + " z += x;\r" + + " }\r" + + " }\r" + + " uint z;\r" + + "}\r" + ); + ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S + if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + fail("not compiled"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); + } -function runTest() -{ - waitForRendering(mainApplication.mainContent, 10000); - console.log("runtest"); - mousePress(mainApplication, 10, 10); -} + function test_ConstructorParameters() + { + newProject(); + mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText( + //ts.typeString( + "contract Contract {\r" + + " function Contract(uint256 x) {\r" + + " z = x;\r" + + " }\r" + + " }\r" + + " function getZ() returns(uint256) {\r" + + " return z;\r" + + " }\r" + + " }\r" + + " uint z;\r" + + "}\r" + ); + ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S + if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + fail("not compiled"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); + } -Application -{ - id: mainApplication -} + Application + { + id: mainApplication + } } From 725d19fa6d1d8c07f288ee4d501b0ee18d0c3cd5 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 30 Mar 2015 18:03:52 +0200 Subject: [PATCH 06/45] more tests --- mix/CMakeLists.txt | 4 ++- mix/qml/CodeEditorView.qml | 31 +++++++++++------ mix/qml/ItemDelegateDataDump.qml | 4 +-- mix/qml/ProjectModel.qml | 1 + mix/qml/StateDialog.qml | 2 ++ mix/qml/StateListModel.qml | 1 + mix/test/qml/TestMain.qml | 57 +++++++++++++++++++++++--------- 7 files changed, 72 insertions(+), 28 deletions(-) diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index 8137c8eba..72e77b523 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -29,7 +29,9 @@ else() qt5_add_resources(UI_RESOURCES noweb.qrc) endif() -add_definitions(-DQT_QML_DEBUG) +if (CMAKE_BUILD_TYPE EQUAL "DEBUG") + add_definitions(-DQT_QML_DEBUG) +endif() # eth_add_executable is defined in cmake/EthExecutableHelper.cmake eth_add_executable(${EXECUTABLE} diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 2733238b8..7bff03cc0 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -7,13 +7,14 @@ import QtQuick.Dialogs 1.1 Item { id: codeEditorView property string currentDocumentId: "" + property int openDocCount: 0 signal documentEdit(string documentId) signal breakpointsChanged(string documentId) signal isCleanChanged(var isClean, string documentId) signal loadComplete function getDocumentText(documentId) { - for (var i = 0; i < editorListModel.count; i++) { + for (var i = 0; i < openDocCount; i++) { if (editorListModel.get(i).documentId === documentId) { return editors.itemAt(i).item.getText(); } @@ -22,7 +23,7 @@ Item { } function isDocumentOpen(documentId) { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) if (editorListModel.get(i).documentId === documentId && editors.itemAt(i).item) return true; @@ -35,11 +36,20 @@ Item { } function loadDocument(document) { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) if (editorListModel.get(i).documentId === document.documentId) return; //already open - editorListModel.append(document); + if (editorListModel.count <= openDocCount) + editorListModel.append(document); + else + { + editorListModel.set(openDocCount, document); + editors.itemAt(openDocCount).visible = true; + doLoadDocument(editors.itemAt(openDocCount).item, editorListModel.get(openDocCount)) + } + openDocCount++; + } function doLoadDocument(editor, document) { @@ -63,7 +73,7 @@ Item { } function getEditor(documentId) { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) { if (editorListModel.get(i).documentId === documentId) return editors.itemAt(i).item; @@ -94,7 +104,7 @@ Item { } function editingContract() { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) if (editorListModel.get(i).documentId === currentDocumentId) return editorListModel.get(i).isContract; return false; @@ -102,7 +112,7 @@ Item { function getBreakpoints() { var bpMap = {}; - for (var i = 0; i < editorListModel.count; i++) { + for (var i = 0; i < openDocCount; i++) { var documentId = editorListModel.get(i).documentId; var editor = editors.itemAt(i).item; if (editor) { @@ -133,7 +143,7 @@ Item { } onProjectSaving: { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) { var doc = editorListModel.get(i); var editor = editors.itemAt(i).item; @@ -145,7 +155,7 @@ Item { onProjectSaved: { if (projectModel.appIsClosing || projectModel.projectIsClosing) return; - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) { var doc = editorListModel.get(i); resetEditStatus(doc.documentId); @@ -155,8 +165,9 @@ Item { onProjectClosed: { for (var i = 0; i < editorListModel.count; i++) editors.itemAt(i).visible = false; - editorListModel.clear(); + //editorListModel.clear(); currentDocumentId = ""; + openDocCount = 0; } onDocumentSaved: { diff --git a/mix/qml/ItemDelegateDataDump.qml b/mix/qml/ItemDelegateDataDump.qml index 9aa6d00db..2c1419e61 100644 --- a/mix/qml/ItemDelegateDataDump.qml +++ b/mix/qml/ItemDelegateDataDump.qml @@ -28,7 +28,7 @@ Rectangle { font.bold: true color: "#4a4a4a" text: modelData[0] - font.pointSize: DebuggerPaneStyle.general.dataDumpFontSize; + font.pointSize: dbgStyle.general.dataDumpFontSize; } } @@ -47,7 +47,7 @@ Rectangle { anchors.leftMargin: 4 color: "#4a4a4a" text: modelData[1] - font.pointSize: DebuggerPaneStyle.general.dataDumpFontSize + font.pointSize: dbgStyle.general.dataDumpFontSize } } } diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index d7a07eab2..e27bb89ab 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -43,6 +43,7 @@ Item { property string deploymentDir property var listModel: projectListModel property var stateListModel: projectStateListModel.model + property alias stateDialog: projectStateListModel.stateDialog property CodeEditorView codeEditor: null property var unsavedFiles: [] property alias newProjectDialog: newProjectDialog diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index 5b96c4fd4..f4cb9eb99 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -21,6 +21,8 @@ Window { property alias stateTitle: titleField.text property alias isDefault: defaultCheckBox.checked + property alias model: transactionsModel + property alias transactionDialog: transactionDialog property int stateIndex property var stateTransactions: [] property var stateAccounts: [] diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 682c4b52c..c90692572 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -12,6 +12,7 @@ Item { property alias model: stateListModel property var stateList: [] + property alias stateDialog: stateDialog property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project function fromPlainStateItem(s) { diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml index fd6619c0c..20f5e4c1f 100644 --- a/mix/test/qml/TestMain.qml +++ b/mix/test/qml/TestMain.qml @@ -25,7 +25,6 @@ TestCase waitForRendering(mainApplication.mainContent, 10000); mainApplication.projectModel.createProject(); var projectDlg = mainApplication.projectModel.newProjectDialog; - //if (ts.waitForSignal(projectDlg, "visibleChanged", 1000)) projectDlg.projectTitle = "TestProject"; projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path projectDlg.acceptAndClose(); @@ -33,11 +32,23 @@ TestCase wait(300); } + function editContract(c) + { + mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText(c); + ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S + if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + fail("not compiled"); + } + + function clickElemet(el) + { + mouseClick(el, 0, 0, Qt.LeftButton, Qt.NoModifier, 10) + } + function test_defaultTransactionSequence() { newProject(); - mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText( - //ts.typeString( + editContract( "contract Contract {\r" + " function Contract() {\r" + " uint x = 69;\r" + @@ -50,33 +61,49 @@ TestCase " uint z;\r" + "}\r" ); - ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S - if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) - fail("not compiled"); tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); } - function test_ConstructorParameters() + function test_transactionWithParameter() { newProject(); - mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText( - //ts.typeString( + editContract( "contract Contract {\r" + " function Contract(uint256 x) {\r" + " z = x;\r" + - " }\r" + " }\r" + " function getZ() returns(uint256) {\r" + " return z;\r" + - " }\r" + " }\r" + " uint z;\r" + "}\r" ); - ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S - if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) - fail("not compiled"); - tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); + mainApplication.projectModel.stateListModel.editState(0); + mainApplication.projectModel.stateDialog.model.editTransaction(2); + //TODO: edit transaction params and check results + } + + function test_constructorParameters() + { + newProject(); + editContract( + "contract Contract {\r" + + " function setZ(uint256 x) {\r" + + " z = x;\r" + + " }\r" + + " function getZ() returns(uint256) {\r" + + " return z;\r" + + " }\r" + + " uint z;\r" + + "}\r" + ); + mainApplication.projectModel.stateListModel.editState(0); + mainApplication.projectModel.stateDialog.model.addTransaction(); + var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; + transactionDialog.functionId = "setZ"; + + + //TODO: edit transaction params and check results } Application From 5c31f4cfc40662ad1a0b14792e585191c508411f Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 30 Mar 2015 21:00:16 +0200 Subject: [PATCH 07/45] more test improvements --- mix/qml/StateDialog.qml | 8 ++- mix/qml/TransactionDialog.qml | 39 ++++++++------ mix/qml/WebCodeEditor.qml | 6 +++ mix/test/TestService.cpp | 98 ++++++++++++++++++++++++++++------- mix/test/TestService.h | 15 +++--- mix/test/qml/TestMain.qml | 62 ++++++++++++++++------ 6 files changed, 169 insertions(+), 59 deletions(-) diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index f4cb9eb99..9dc1549c9 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -58,6 +58,11 @@ Window { forceActiveFocus(); } + function acceptAndClose() { + close(); + accepted(); + } + function close() { visible = false; } @@ -298,8 +303,7 @@ Window { Button { text: qsTr("OK"); onClicked: { - close(); - accepted(); + acceptAndClose(); } } Button { diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 820f8d631..62a799b26 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -59,16 +59,7 @@ Window { contractComboBox.currentIndex = contractIndex; loadFunctions(contractComboBox.currentValue()); - - var functionIndex = -1; - for (var f = 0; f < functionsModel.count; f++) - if (functionsModel.get(f).text === item.functionId) - functionIndex = f; - - if (functionIndex == -1 && functionsModel.count > 0) - functionIndex = 0; //@todo suggest unused function - - functionComboBox.currentIndex = functionIndex; + selectFunction(functionId); paramsModel = []; if (functionId !== contractComboBox.currentValue()) @@ -87,9 +78,6 @@ Window { visible = true; valueField.focus = true; - modalTransactionDialog.height = (paramsModel.length > 0 ? 500 : 300); - paramLabel.visible = paramsModel.length > 0; - paramScroll.visible = paramsModel.length > 0; } function loadFunctions(contractId) @@ -107,6 +95,19 @@ Window { } + function selectFunction(functionId) + { + var functionIndex = -1; + for (var f = 0; f < functionsModel.count; f++) + if (functionsModel.get(f).text === functionId) + functionIndex = f; + + if (functionIndex == -1 && functionsModel.count > 0) + functionIndex = 0; //@todo suggest unused function + + functionComboBox.currentIndex = functionIndex; + } + function loadParameter(parameter) { var type = parameter.type; @@ -136,6 +137,15 @@ Window { typeLoader.members = [] typeLoader.value = paramValues; typeLoader.members = paramsModel; + paramLabel.visible = paramsModel.length > 0; + paramScroll.visible = paramsModel.length > 0; + modalTransactionDialog.height = (paramsModel.length > 0 ? 500 : 300); + } + + function acceptAndClose() + { + close(); + accepted(); } function close() @@ -369,8 +379,7 @@ Window { Button { text: qsTr("OK"); onClicked: { - close(); - accepted(); + acceptAndClose(); } } Button { diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index a5f68ad66..6ab0b398a 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.1 import QtWebEngine 1.0 import QtWebEngine.experimental 1.0 +import org.ethereum.qml.Clipboard 1.0 import "js/ErrorLocationFormater.js" as ErrorLocationFormater Item { @@ -68,6 +69,11 @@ Item { editorBrowser.runJavaScript("changeGeneration()", function(result) {}); } + Clipboard + { + id: clipboard + } + Connections { target: clipboard onClipboardChanged: syncClipboard() diff --git a/mix/test/TestService.cpp b/mix/test/TestService.cpp index c923a1bce..373630bf7 100644 --- a/mix/test/TestService.cpp +++ b/mix/test/TestService.cpp @@ -33,6 +33,56 @@ namespace dev namespace mix { +enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDoubleClick, MouseMove }; + +static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt::MouseButton _button, Qt::KeyboardModifiers _stateKey, QPointF _pos, int _delay=-1) +{ + if (_delay == -1 || _delay < 30) + _delay = 30; + if (_delay > 0) + QTest::qWait(_delay); + + if (_action == MouseClick) { + mouseEvent(MousePress, _window, _item, _button, _stateKey, _pos); + mouseEvent(MouseRelease, _window, _item, _button, _stateKey, _pos); + return; + } + + QPoint pos = _pos.toPoint(); + QQuickItem *sgitem = qobject_cast(_item); + if (sgitem) + pos = sgitem->mapToScene(_pos).toPoint(); + + _stateKey &= static_cast(Qt::KeyboardModifierMask); + + QMouseEvent me(QEvent::User, QPoint(), Qt::LeftButton, _button, _stateKey); + switch (_action) + { + case MousePress: + me = QMouseEvent(QEvent::MouseButtonPress, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); + break; + case MouseRelease: + me = QMouseEvent(QEvent::MouseButtonRelease, pos, _window->mapToGlobal(pos), _button, 0, _stateKey); + break; + case MouseDoubleClick: + me = QMouseEvent(QEvent::MouseButtonDblClick, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); + break; + case MouseMove: + // with move event the _button is NoButton, but 'buttons' holds the currently pressed buttons + me = QMouseEvent(QEvent::MouseMove, pos, _window->mapToGlobal(pos), Qt::NoButton, _button, _stateKey); + break; + default: + break; + } + QSpontaneKeyEvent::setSpontaneous(&me); + if (!qApp->notify(_window, &me)) { + static const char *mouseActionNames[] = + { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove" }; + QString warning = QString::fromLatin1("Mouse event \"%1\" not accepted by receiving window"); + QWARN(warning.arg(QString::fromLatin1(mouseActionNames[static_cast(_action)])).toLatin1().data()); + } +} + bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeout) { QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); @@ -61,51 +111,57 @@ bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeou return spy.size(); } -bool TestService::keyPress(int _key, int _modifiers, int _delay) +bool TestService::keyPress(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyPress(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } -bool TestService::keyRelease(int _key, int _modifiers, int _delay) +bool TestService::keyRelease(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyRelease(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } -bool TestService::keyClick(int _key, int _modifiers, int _delay) +bool TestService::keyClick(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyClick(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } -bool TestService::keyPressChar(QString const& _character, int _modifiers, int _delay) +bool TestService::keyPressChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QTEST_ASSERT(_character.length() == 1); - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyPress(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } -bool TestService::keyReleaseChar(QString const& _character, int _modifiers, int _delay) +bool TestService::keyReleaseChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QTEST_ASSERT(_character.length() == 1); - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyRelease(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } -bool TestService::keyClickChar(QString const& _character, int _modifiers, int _delay) +bool TestService::keyClickChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QTEST_ASSERT(_character.length() == 1); - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyClick(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } +bool TestService::mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay) +{ + QWindow* window = qobject_cast(_item); + if (!window) + window = eventWindow(_item); + mouseEvent(MouseClick, window, _item, Qt::MouseButton(_button), Qt::KeyboardModifiers(_modifiers), QPointF(_x, _y), _delay); + return true; +} + void TestService::setTargetWindow(QObject* _window) { QQuickWindow* window = qobject_cast(_window); @@ -114,15 +170,21 @@ void TestService::setTargetWindow(QObject* _window) window->requestActivate(); } -QWindow* TestService::eventWindow() +QWindow* TestService::eventWindow(QObject* _item) { - QQuickWindow* window = qobject_cast(m_targetWindow); + QQuickItem* item = qobject_cast(_item); + if (item && item->window()) + return item->window(); + + QQuickWindow* window = qobject_cast(_item); + if (!window) + window = qobject_cast(m_targetWindow); if (window) { window->requestActivate(); return window; } - QQuickItem* item = qobject_cast(m_targetWindow); + item = qobject_cast(m_targetWindow); if (item) return item->window(); return 0; diff --git a/mix/test/TestService.h b/mix/test/TestService.h index d52fbe0c4..fbf6533e6 100644 --- a/mix/test/TestService.h +++ b/mix/test/TestService.h @@ -42,15 +42,16 @@ public: public slots: bool waitForSignal(QObject* _item, QString _signalName, int _timeout); - bool keyPress(int _key, int _modifiers, int _delay); - bool keyRelease(int _key, int _modifiers, int _delay); - bool keyClick(int _key, int _modifiers, int _delay); - bool keyPressChar(QString const& _character, int _modifiers, int _delay); - bool keyReleaseChar(QString const& _character, int _modifiers, int _delay); - bool keyClickChar(QString const& _character, int _modifiers, int _delay); + bool keyPress(QObject* _item, int _key, int _modifiers, int _delay); + bool keyRelease(QObject* _item, int _key, int _modifiers, int _delay); + bool keyClick(QObject* _item, int _key, int _modifiers, int _delay); + bool keyPressChar(QObject* _item, QString const& _character, int _modifiers, int _delay); + bool keyReleaseChar(QObject* _item, QString const& _character, int _modifiers, int _delay); + bool keyClickChar(QObject* _item, QString const& _character, int _modifiers, int _delay); + bool mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay); private: - QWindow* eventWindow(); + QWindow* eventWindow(QObject* _item); QObject* m_targetWindow; }; diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml index 20f5e4c1f..65b5a5076 100644 --- a/mix/test/qml/TestMain.qml +++ b/mix/test/qml/TestMain.qml @@ -10,12 +10,15 @@ TestCase { id: ts targetWindow: mainApplication - function typeString(str) + function typeString(str, el) { + if (el === undefined) + el = mainApplication; + for (var c in str) { - ts.keyPressChar(str[c], Qt.NoModifier, 0); - ts.keyReleaseChar(str[c], Qt.NoModifier, 0); + ts.keyPressChar(el, str[c], Qt.NoModifier, 0); + ts.keyReleaseChar(el, str[c], Qt.NoModifier, 0); } } } @@ -25,24 +28,24 @@ TestCase waitForRendering(mainApplication.mainContent, 10000); mainApplication.projectModel.createProject(); var projectDlg = mainApplication.projectModel.newProjectDialog; + wait(30); projectDlg.projectTitle = "TestProject"; projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path projectDlg.acceptAndClose(); - ts.waitForSignal(mainApplication.mainContent.codeEditor, "loadComplete()", 5000) - wait(300); + wait(30); } function editContract(c) { mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText(c); - ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S + ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) fail("not compiled"); } - function clickElemet(el) + function clickElement(el, x, y) { - mouseClick(el, 0, 0, Qt.LeftButton, Qt.NoModifier, 10) + ts.mouseClick(el, x, y, Qt.LeftButton, Qt.NoModifier, 10) } function test_defaultTransactionSequence() @@ -61,6 +64,8 @@ TestCase " uint z;\r" + "}\r" ); + if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000)) + fail("not run"); tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); } @@ -69,7 +74,7 @@ TestCase newProject(); editContract( "contract Contract {\r" + - " function Contract(uint256 x) {\r" + + " function setZ(uint256 x) {\r" + " z = x;\r" + " }\r" + " function getZ() returns(uint256) {\r" + @@ -79,8 +84,22 @@ TestCase "}\r" ); mainApplication.projectModel.stateListModel.editState(0); - mainApplication.projectModel.stateDialog.model.editTransaction(2); - //TODO: edit transaction params and check results + mainApplication.projectModel.stateDialog.model.addTransaction(); + var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; + transactionDialog.selectFunction("setZ"); + clickElement(transactionDialog, 140, 300); + ts.typeString("442", transactionDialog); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.model.addTransaction(); + transactionDialog.selectFunction("getZ"); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.acceptAndClose(); + mainApplication.mainContent.startQuickDebugging(); + wait(1); + if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000)) + fail("not run"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 5); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(442)"); } function test_constructorParameters() @@ -88,7 +107,7 @@ TestCase newProject(); editContract( "contract Contract {\r" + - " function setZ(uint256 x) {\r" + + " function Contract(uint256 x) {\r" + " z = x;\r" + " }\r" + " function getZ() returns(uint256) {\r" + @@ -98,12 +117,21 @@ TestCase "}\r" ); mainApplication.projectModel.stateListModel.editState(0); - mainApplication.projectModel.stateDialog.model.addTransaction(); + mainApplication.projectModel.stateDialog.model.editTransaction(2); var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; - transactionDialog.functionId = "setZ"; - - - //TODO: edit transaction params and check results + clickElement(transactionDialog, 140, 300); + ts.typeString("442", transactionDialog); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.model.addTransaction(); + transactionDialog.selectFunction("getZ"); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.acceptAndClose(); + wait(1); + mainApplication.mainContent.startQuickDebugging(); + if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000)) + fail("not run"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 4); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "returned", "(442)"); } Application From 34e0f03e57e520e41e12098910e6eced7ac3414f Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 31 Mar 2015 11:46:51 +0200 Subject: [PATCH 08/45] styled with astyle --- mix/QVariableDefinition.cpp | 1 + mix/test/TestService.cpp | 68 +++++++++++++++--------------- mix/web.qrc | 82 ++++++++++++++++++------------------- 3 files changed, 76 insertions(+), 75 deletions(-) diff --git a/mix/QVariableDefinition.cpp b/mix/QVariableDefinition.cpp index 4df31ea92..4553ffad6 100644 --- a/mix/QVariableDefinition.cpp +++ b/mix/QVariableDefinition.cpp @@ -26,6 +26,7 @@ namespace dev { namespace mix { + QString QVariableDefinition::encodeValueAsString() { return QString::fromStdString(dev::toHex(encodeValue())); diff --git a/mix/test/TestService.cpp b/mix/test/TestService.cpp index 373630bf7..7ad2bc13d 100644 --- a/mix/test/TestService.cpp +++ b/mix/test/TestService.cpp @@ -35,21 +35,22 @@ namespace mix enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDoubleClick, MouseMove }; -static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt::MouseButton _button, Qt::KeyboardModifiers _stateKey, QPointF _pos, int _delay=-1) +static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt::MouseButton _button, Qt::KeyboardModifiers _stateKey, QPointF _pos, int _delay = -1) { if (_delay == -1 || _delay < 30) _delay = 30; if (_delay > 0) QTest::qWait(_delay); - if (_action == MouseClick) { + if (_action == MouseClick) + { mouseEvent(MousePress, _window, _item, _button, _stateKey, _pos); mouseEvent(MouseRelease, _window, _item, _button, _stateKey, _pos); return; } QPoint pos = _pos.toPoint(); - QQuickItem *sgitem = qobject_cast(_item); + QQuickItem* sgitem = qobject_cast(_item); if (sgitem) pos = sgitem->mapToScene(_pos).toPoint(); @@ -58,26 +59,26 @@ static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt QMouseEvent me(QEvent::User, QPoint(), Qt::LeftButton, _button, _stateKey); switch (_action) { - case MousePress: - me = QMouseEvent(QEvent::MouseButtonPress, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); - break; - case MouseRelease: - me = QMouseEvent(QEvent::MouseButtonRelease, pos, _window->mapToGlobal(pos), _button, 0, _stateKey); - break; - case MouseDoubleClick: - me = QMouseEvent(QEvent::MouseButtonDblClick, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); - break; - case MouseMove: - // with move event the _button is NoButton, but 'buttons' holds the currently pressed buttons - me = QMouseEvent(QEvent::MouseMove, pos, _window->mapToGlobal(pos), Qt::NoButton, _button, _stateKey); - break; - default: - break; + case MousePress: + me = QMouseEvent(QEvent::MouseButtonPress, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); + break; + case MouseRelease: + me = QMouseEvent(QEvent::MouseButtonRelease, pos, _window->mapToGlobal(pos), _button, 0, _stateKey); + break; + case MouseDoubleClick: + me = QMouseEvent(QEvent::MouseButtonDblClick, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); + break; + case MouseMove: + // with move event the _button is NoButton, but 'buttons' holds the currently pressed buttons + me = QMouseEvent(QEvent::MouseMove, pos, _window->mapToGlobal(pos), Qt::NoButton, _button, _stateKey); + break; + default: + break; } QSpontaneKeyEvent::setSpontaneous(&me); - if (!qApp->notify(_window, &me)) { - static const char *mouseActionNames[] = - { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove" }; + if (!qApp->notify(_window, &me)) + { + static const char* mouseActionNames[] = { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove" }; QString warning = QString::fromLatin1("Mouse event \"%1\" not accepted by receiving window"); QWARN(warning.arg(QString::fromLatin1(mouseActionNames[static_cast(_action)])).toLatin1().data()); } @@ -85,21 +86,20 @@ static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeout) { - QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); + QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); QMetaObject const* mo = _item->metaObject(); QStringList methods; - for(int i = mo->methodOffset(); i < mo->methodCount(); ++i) { - if (mo->method(i).methodType() == QMetaMethod::Signal) { + for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) + if (mo->method(i).methodType() == QMetaMethod::Signal) methods << QString::fromLatin1(mo->method(i).methodSignature()); - } - } QElapsedTimer timer; timer.start(); - while (!spy.size()) { + while (!spy.size()) + { int remaining = _timeout - int(timer.elapsed()); if (remaining <= 0) break; @@ -113,42 +113,42 @@ bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeou bool TestService::keyPress(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyPress(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } bool TestService::keyRelease(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyRelease(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } bool TestService::keyClick(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyClick(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } bool TestService::keyPressChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyPress(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } bool TestService::keyReleaseChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyRelease(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } bool TestService::keyClickChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyClick(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } @@ -172,7 +172,7 @@ void TestService::setTargetWindow(QObject* _window) QWindow* TestService::eventWindow(QObject* _item) { - QQuickItem* item = qobject_cast(_item); + QQuickItem* item = qobject_cast(_item); if (item && item->window()) return item->window(); diff --git a/mix/web.qrc b/mix/web.qrc index 3844f7c72..6870411c5 100644 --- a/mix/web.qrc +++ b/mix/web.qrc @@ -1,44 +1,44 @@ - - qml/WebPreview.qml - qml/html/WebContainer.html - qml/html/cm/active-line.js - qml/html/codeeditor.html - qml/html/cm/codemirror.css - qml/html/cm/codemirror.js - qml/html/cm/javascript.js - qml/html/cm/matchbrackets.js + qml/WebCodeEditor.qml - qml/html/codeeditor.js - qml/html/cm/fullscreen.css - qml/html/cm/fullscreen.js - qml/html/cm/solarized.css - qml/html/cm/xml.js - qml/html/cm/htmlmixed.js - qml/html/cm/css.js - qml/html/cm/solidity.js - qml/html/cm/dialog.css - qml/html/cm/dialog.js - qml/html/cm/search.js - qml/html/cm/searchcursor.js - qml/html/cm/anyword-hint.js - qml/html/cm/show-hint.js - qml/html/cm/show-hint.css - qml/html/cm/closebrackets.js - qml/html/cm/solidityToken.js - qml/html/cm/javascript-hint.js - qml/html/cm/errorannotation.js - qml/html/cm/tern.js - qml/html/cm/ecma5spec.js - qml/html/cm/comment.js - qml/html/cm/def.js - qml/html/cm/doc_comment.js - qml/html/cm/infer.js - qml/html/cm/signal.js - qml/html/cm/ternserver.js - qml/html/cm/acorn.js - qml/html/cm/acorn_loose.js - qml/html/cm/walk.js - qml/html/cm/mark-selection.js - + qml/WebPreview.qml + qml/html/WebContainer.html + qml/html/cm/acorn.js + qml/html/cm/acorn_loose.js + qml/html/cm/active-line.js + qml/html/cm/anyword-hint.js + qml/html/cm/closebrackets.js + qml/html/cm/codemirror.css + qml/html/cm/codemirror.js + qml/html/cm/comment.js + qml/html/cm/css.js + qml/html/cm/def.js + qml/html/cm/dialog.css + qml/html/cm/dialog.js + qml/html/cm/doc_comment.js + qml/html/cm/ecma5spec.js + qml/html/cm/errorannotation.js + qml/html/cm/fullscreen.css + qml/html/cm/fullscreen.js + qml/html/cm/htmlmixed.js + qml/html/cm/infer.js + qml/html/cm/javascript-hint.js + qml/html/cm/javascript.js + qml/html/cm/mark-selection.js + qml/html/cm/matchbrackets.js + qml/html/cm/search.js + qml/html/cm/searchcursor.js + qml/html/cm/show-hint.css + qml/html/cm/show-hint.js + qml/html/cm/signal.js + qml/html/cm/solarized.css + qml/html/cm/solidity.js + qml/html/cm/solidityToken.js + qml/html/cm/tern.js + qml/html/cm/ternserver.js + qml/html/cm/walk.js + qml/html/cm/xml.js + qml/html/codeeditor.html + qml/html/codeeditor.js + From dda4ccff3ef98ebcc556ff707b5f29cdf295f12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 31 Mar 2015 13:49:39 +0200 Subject: [PATCH 09/45] Control stack size limit --- evmjit/libevmjit/BasicBlock.cpp | 1 + evmjit/libevmjit/BasicBlock.h | 4 +++ evmjit/libevmjit/Compiler.cpp | 3 ++ evmjit/libevmjit/RuntimeManager.cpp | 44 ++++++++++++++++++++++++++++- evmjit/libevmjit/RuntimeManager.h | 5 ++++ 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index 93d73b991..a41743d0b 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -49,6 +49,7 @@ void BasicBlock::LocalStack::push(llvm::Value* _value) assert(_value->getType() == Type::Word); m_bblock.m_currentStack.push_back(_value); m_bblock.m_tosOffset += 1; + m_maxSize = std::max(m_maxSize, m_bblock.m_currentStack.size()); } llvm::Value* BasicBlock::LocalStack::pop() diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 7469b7b69..5e19235a7 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -33,6 +33,9 @@ public: /// @param _index Index of value to be swaped. Must be > 0. void swap(size_t _index); + size_t getMaxSize() const { return m_maxSize; } + int getDiff() const { return m_bblock.m_tosOffset; } + private: LocalStack(BasicBlock& _owner); LocalStack(LocalStack const&) = delete; @@ -49,6 +52,7 @@ public: private: BasicBlock& m_bblock; + size_t m_maxSize = 0; ///< Max size reached by the stack. }; explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index be3670874..1e1f8fe93 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -838,6 +838,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti // Block may have no terminator if the next instruction is a jump destination. if (!_basicBlock.llvm()->getTerminator()) m_builder.CreateBr(_nextBasicBlock); + + m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); + _runtimeManager.checkStackLimit(_basicBlock.localStack().getMaxSize(), _basicBlock.localStack().getDiff()); } diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index d1ccaea8a..f4db4d052 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -78,7 +78,7 @@ llvm::Twine getName(RuntimeData::Index _index) case RuntimeData::CodeSize: return "code"; case RuntimeData::CallDataSize: return "callDataSize"; case RuntimeData::Gas: return "gas"; - case RuntimeData::Number: return "number"; + case RuntimeData::Number: return "number"; case RuntimeData::Timestamp: return "timestamp"; } } @@ -101,6 +101,48 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB assert(m_memPtr->getType() == Array::getType()->getPointerTo()); m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env"); assert(m_envPtr->getType() == Type::EnvPtr); + + m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize"); + m_builder.CreateStore(m_builder.getInt64(0), m_stackSize); + + llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr}; + m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule()); + m_checkStackLimit->setDoesNotThrow(); + m_checkStackLimit->setDoesNotCapture(1); + + auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_checkStackLimit); + auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_checkStackLimit); + auto outOfStackBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfStack", m_checkStackLimit); + + auto currSizePtr = &m_checkStackLimit->getArgumentList().front(); + currSizePtr->setName("currSize"); + auto max = currSizePtr->getNextNode(); + max->setName("max"); + auto diff = max->getNextNode(); + diff->setName("diff"); + auto jmpBuf = diff->getNextNode(); + jmpBuf->setName("jmpBuf"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(checkBB); + auto currSize = m_builder.CreateLoad(currSizePtr, "cur"); + auto maxSize = m_builder.CreateNUWAdd(currSize, max, "maxSize"); + auto ok = m_builder.CreateICmpULT(maxSize, m_builder.getInt64(1024), "ok"); + m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); + + m_builder.SetInsertPoint(updateBB); + auto newSize = m_builder.CreateNSWAdd(currSize, diff); + m_builder.CreateStore(newSize, currSizePtr); + m_builder.CreateRetVoid(); + + m_builder.SetInsertPoint(outOfStackBB); + abort(jmpBuf); + m_builder.CreateUnreachable(); +} + +void RuntimeManager::checkStackLimit(size_t _max, int _diff) +{ + createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()}); } llvm::Value* RuntimeManager::getRuntimePtr() diff --git a/evmjit/libevmjit/RuntimeManager.h b/evmjit/libevmjit/RuntimeManager.h index eb8dadf6f..c430a1098 100644 --- a/evmjit/libevmjit/RuntimeManager.h +++ b/evmjit/libevmjit/RuntimeManager.h @@ -48,6 +48,8 @@ public: static llvm::StructType* getRuntimeType(); static llvm::StructType* getRuntimeDataType(); + void checkStackLimit(size_t _max, int _diff); + private: llvm::Value* getPtr(RuntimeData::Index _index); void set(RuntimeData::Index _index, llvm::Value* _value); @@ -59,6 +61,9 @@ private: llvm::Value* m_memPtr = nullptr; llvm::Value* m_envPtr = nullptr; + llvm::Value* m_stackSize = nullptr; + llvm::Function* m_checkStackLimit = nullptr; + code_iterator m_codeBegin = {}; code_iterator m_codeEnd = {}; From 6083f49ba059c6acf40888c8b4ad633cb8d4a627 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 31 Mar 2015 15:14:49 +0200 Subject: [PATCH 10/45] eth_protocolVersion --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 5 +++++ libweb3jsonrpc/WebThreeStubServerBase.h | 1 + libweb3jsonrpc/abstractwebthreestubserver.h | 7 +++++++ libweb3jsonrpc/spec.json | 1 + test/webthreestubclient.h | 10 ++++++++++ 5 files changed, 24 insertions(+) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index cab986364..ba43a7b7e 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -284,6 +284,11 @@ bool WebThreeStubServerBase::net_listening() return network()->isNetworkStarted(); } +string WebThreeStubServerBase::eth_protocolVersion() +{ + return toJS(eth::c_protocolVersion); +} + string WebThreeStubServerBase::eth_coinbase() { return toJS(client()->address()); diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 40265ac10..b57a54c87 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -77,6 +77,7 @@ public: virtual std::string net_peerCount(); virtual bool net_listening(); + virtual std::string eth_protocolVersion(); virtual std::string eth_coinbase(); virtual bool eth_mining(); virtual std::string eth_gasPrice(); diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 516817d5c..6cc5de3e6 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -17,6 +17,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("net_version", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::net_versionI); this->bindAndAddMethod(jsonrpc::Procedure("net_peerCount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::net_peerCountI); this->bindAndAddMethod(jsonrpc::Procedure("net_listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::net_listeningI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_protocolVersion", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_protocolVersionI); this->bindAndAddMethod(jsonrpc::Procedure("eth_coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_coinbaseI); this->bindAndAddMethod(jsonrpc::Procedure("eth_mining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_miningI); this->bindAndAddMethod(jsonrpc::Procedure("eth_gasPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_gasPriceI); @@ -92,6 +93,11 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServernet_listening(); } + inline virtual void eth_protocolVersionI(const Json::Value &request, Json::Value &response) + { + (void)request; + response = this->eth_protocolVersion(); + } inline virtual void eth_coinbaseI(const Json::Value &request, Json::Value &response) { (void)request; @@ -302,6 +308,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerCallMethod("eth_protocolVersion",p); + if (result.isString()) + return result.asString(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } std::string eth_coinbase() throw (jsonrpc::JsonRpcException) { Json::Value p; From 50f78c6eb047e8889c8c22ca2336cb6630ca93a7 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 3 Apr 2015 12:17:35 +0200 Subject: [PATCH 11/45] fix #1366 --- libwhisper/WhisperHost.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 633eef5b5..97e80d7a5 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -82,10 +82,10 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p) // TODO p2p: capability-based rating for (auto i: peerSessions()) { - auto w = i.first->cap().get(); - if (w == _p) + auto w = i.first->cap(); + if (!!w && w.get() == _p) w->addRating(1); - else + else if(!!w) w->noteNewMessage(h, _m); } } @@ -163,7 +163,9 @@ void WhisperHost::uninstallWatch(unsigned _i) void WhisperHost::doWork() { for (auto& i: peerSessions()) - i.first->cap().get()->sendMessages(); + if (auto w = i.first->cap()) + if (!!w) + i.first->cap()->sendMessages(); cleanup(); } From 047d7d33aec775bdb17a026143867ca5c1dc976b Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 3 Apr 2015 15:04:49 +0200 Subject: [PATCH 12/45] Revert safeguards. Change peerSessions() use to same as EthereumHost. for(auto) instead of for(auto&). --- libwhisper/WhisperHost.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 97e80d7a5..3c6b5326b 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -83,9 +83,9 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p) for (auto i: peerSessions()) { auto w = i.first->cap(); - if (!!w && w.get() == _p) + if (w.get() == _p) w->addRating(1); - else if(!!w) + else w->noteNewMessage(h, _m); } } @@ -162,10 +162,8 @@ void WhisperHost::uninstallWatch(unsigned _i) void WhisperHost::doWork() { - for (auto& i: peerSessions()) - if (auto w = i.first->cap()) - if (!!w) - i.first->cap()->sendMessages(); + for (auto i: peerSessions()) + i.first->cap()->sendMessages(); cleanup(); } From b7a2bb35536330997e61706c18fa253e2403488d Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 3 Apr 2015 16:06:41 +0200 Subject: [PATCH 13/45] have not seen crash here. revert unrelated change. --- libwhisper/WhisperHost.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 3c6b5326b..673448051 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -82,8 +82,8 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p) // TODO p2p: capability-based rating for (auto i: peerSessions()) { - auto w = i.first->cap(); - if (w.get() == _p) + auto w = i.first->cap().get(); + if (w == _p) w->addRating(1); else w->noteNewMessage(h, _m); From 4cbc52afcf02455ec36dc7f8a4451a03ebe06247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 3 Apr 2015 16:16:29 +0200 Subject: [PATCH 14/45] Fix stack limit check --- evmjit/libevmjit/RuntimeManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index f4db4d052..dc7bc24a3 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -127,7 +127,7 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB m_builder.SetInsertPoint(checkBB); auto currSize = m_builder.CreateLoad(currSizePtr, "cur"); auto maxSize = m_builder.CreateNUWAdd(currSize, max, "maxSize"); - auto ok = m_builder.CreateICmpULT(maxSize, m_builder.getInt64(1024), "ok"); + auto ok = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "ok"); m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); m_builder.SetInsertPoint(updateBB); From c5f80a9dde7bb65dce649a3c78d23a471e373557 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 4 Apr 2015 11:00:08 +0200 Subject: [PATCH 15/45] Fix various aspects of chain-re-import. --- libethereum/BlockChain.cpp | 103 ++++++++++++++++++++++++-------- libethereum/BlockChain.h | 4 +- libethereum/BlockQueue.cpp | 22 ++++++- libethereum/BlockQueue.h | 4 +- libethereum/CanonBlockChain.cpp | 2 +- libethereum/CanonBlockChain.h | 4 +- libethereum/Client.cpp | 4 +- libethereum/State.cpp | 35 ++++++++++- 8 files changed, 141 insertions(+), 37 deletions(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 32f36f99d..69332d620 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -19,10 +19,10 @@ * @date 2014 */ -#include - #include "BlockChain.h" +#include +#include #include #include #include @@ -43,6 +43,7 @@ using namespace dev::eth; namespace js = json_spirit; #define ETH_CATCH 1 +#define ETH_TIMED_IMPORTS 0 std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) { @@ -174,6 +175,8 @@ void BlockChain::close() m_blocks.clear(); } +#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} + void BlockChain::rebuild(std::string const& _path, std::function const& _progress) { unsigned originalNumber = number(); @@ -181,6 +184,7 @@ void BlockChain::rebuild(std::string const& _path, std::function(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value), db); + bytes b = block(queryExtras(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value); + + BlockInfo bi(b); + if (bi.parentHash != lastHash) + { + cwarn << "DISJOINT CHAIN DETECTED; " << bi.hash.abridged() << "#" << d << " -> parent is" << bi.parentHash.abridged() << "; expected" << lastHash.abridged() << "#" << (d - 1); + return; + } + lastHash = bi.hash; + import(b, s.db(), true); } catch (...) { @@ -258,7 +272,7 @@ h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max _bq.tick(*this); vector blocks; - _bq.drain(blocks); + _bq.drain(blocks, _max); h256s ret; for (auto const& block: blocks) @@ -266,10 +280,7 @@ h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max try { for (auto h: import(block, _stateDB)) - if (!_max--) - break; - else - ret.push_back(h); + ret.push_back(h); } catch (UnknownParent) { @@ -288,11 +299,11 @@ h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max return ret; } -h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept +h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _force) noexcept { try { - return import(_block, _stateDB); + return import(_block, _stateDB, _force); } catch (...) { @@ -301,8 +312,18 @@ h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB) } } -h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) +h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) { +#if ETH_TIMED_IMPORTS + boost::timer total; + double preliminaryChecks; + double enactment; + double collation; + double writing; + double checkBest; + boost::timer t; +#endif + // VERIFY: populates from the block and checks the block is internally coherent. BlockInfo bi; @@ -329,7 +350,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) auto newHash = BlockInfo::headerHash(_block); // Check block doesn't already exist first! - if (isKnown(newHash)) + if (isKnown(newHash) && !_force) { clog(BlockChainNote) << newHash << ": Not new."; BOOST_THROW_EXCEPTION(AlreadyHaveBlock()); @@ -362,6 +383,11 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) clog(BlockChainNote) << "Attempting import of " << newHash.abridged() << "..."; +#if ETH_TIMED_IMPORTS + preliminaryChecks = t.elapsed(); + t.restart(); +#endif + u256 td; #if ETH_CATCH try @@ -371,6 +397,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) // Get total difficulty increase and update state, checking it. State s(bi.coinbaseAddress, _db); auto tdIncrease = s.enactOn(&_block, bi, *this); + BlockLogBlooms blb; BlockReceipts br; for (unsigned i = 0; i < s.pending().size(); ++i) @@ -381,6 +408,11 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) s.cleanup(true); td = pd.totalDifficulty + tdIncrease; +#if ETH_TIMED_IMPORTS + enactment = t.elapsed(); + t.restart(); +#endif + #if ETH_PARANOIA checkConsistency(); #endif @@ -397,10 +429,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}); m_details[bi.parentHash].children.push_back(newHash); } - { - WriteGuard l(x_blockHashes); - m_blockHashes[h256(bi.number)].value = newHash; - } h256s alteredBlooms; { WriteGuard l(x_blocksBlooms); @@ -415,6 +443,16 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) m_blocksBlooms[alteredBlooms.back()].blooms[o] |= blockBloom; } } + { + WriteGuard l(x_logBlooms); + m_logBlooms[newHash] = blb; + } + { + WriteGuard l(x_receipts); + m_receipts[newHash] = br; + } + + // TODO: FIX: URGENT!!!!! only put these into database when the block is on the main chain. // Collate transaction hashes and remember who they were. h256s newTransactionAddresses; { @@ -429,14 +467,15 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) } } { - WriteGuard l(x_logBlooms); - m_logBlooms[newHash] = blb; - } - { - WriteGuard l(x_receipts); - m_receipts[newHash] = br; + WriteGuard l(x_blockHashes); + m_blockHashes[h256(bi.number)].value = newHash; } +#if ETH_TIMED_IMPORTS + collation = t.elapsed(); + t.restart(); +#endif + { ReadGuard l1(x_blocksBlooms); ReadGuard l2(x_details); @@ -456,6 +495,11 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraBlocksBlooms), (ldb::Slice)dev::ref(m_blocksBlooms[h].rlp())); } +#if ETH_TIMED_IMPORTS + writing = t.elapsed(); + t.restart(); +#endif + #if ETH_PARANOIA checkConsistency(); #endif @@ -514,6 +558,17 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) { clog(BlockChainNote) << " Imported but not best (oTD:" << details(last).totalDifficulty << " > TD:" << td << ")"; } + +#if ETH_TIMED_IMPORTS + checkBest = t.elapsed(); + cnote << "Import took:" << total.elapsed(); + cnote << "preliminaryChecks:" << preliminaryChecks; + cnote << "enactment:" << enactment; + cnote << "collation:" << collation; + cnote << "writing:" << writing; + cnote << "checkBest:" << checkBest; +#endif + return ret; } diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 7705e0f12..a039e7d83 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -103,11 +103,11 @@ public: /// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB. /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. - h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept; + h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _force = false) noexcept; /// Import block into disk-backed DB /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. - h256s import(bytes const& _block, OverlayDB const& _stateDB); + h256s import(bytes const& _block, OverlayDB const& _stateDB, bool _force = false); /// Returns true if the given block is known (though not necessarily a part of the canon chain). bool isKnown(h256 const& _hash) const; diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index 29c9a4c71..69e387923 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -114,13 +114,29 @@ void BlockQueue::tick(BlockChain const& _bc) m_future.erase(m_future.begin(), m_future.upper_bound(t)); } -void BlockQueue::drain(std::vector& o_out) +template T advanced(T _t, unsigned _n) +{ + std::advance(_t, _n); + return _t; +} + +void BlockQueue::drain(std::vector& o_out, unsigned _max) { WriteGuard l(m_lock); if (m_drainingSet.empty()) { - swap(o_out, m_ready); - swap(m_drainingSet, m_readySet); + o_out.resize(min(_max, m_ready.size())); + for (unsigned i = 0; i < o_out.size(); ++i) + swap(o_out[i], m_ready[i]); + m_ready.erase(m_ready.begin(), advanced(m_ready.begin(), o_out.size())); + for (auto const& bs: o_out) + { + auto h = sha3(bs); + m_drainingSet.insert(h); + m_readySet.erase(h); + } +// swap(o_out, m_ready); +// swap(m_drainingSet, m_readySet); } } diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index 5eefa9d8e..9c473d766 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -61,9 +61,9 @@ public: /// Notes that time has moved on and some blocks that used to be "in the future" may no be valid. void tick(BlockChain const& _bc); - /// Grabs the blocks that are ready, giving them in the correct order for insertion into the chain. + /// Grabs at most @a _max of the blocks that are ready, giving them in the correct order for insertion into the chain. /// Don't forget to call doneDrain() once you're done importing. - void drain(std::vector& o_out); + void drain(std::vector& o_out, unsigned _max); /// Must be called after a drain() call. Notes that the drained blocks have been imported into the blockchain, so we can forget about them. void doneDrain() { WriteGuard l(m_lock); m_drainingSet.clear(); } diff --git a/libethereum/CanonBlockChain.cpp b/libethereum/CanonBlockChain.cpp index 2a7b97525..b5c47b653 100644 --- a/libethereum/CanonBlockChain.cpp +++ b/libethereum/CanonBlockChain.cpp @@ -92,6 +92,6 @@ bytes CanonBlockChain::createGenesisBlock() return block.out(); } -CanonBlockChain::CanonBlockChain(std::string const& _path, WithExisting _we): BlockChain(CanonBlockChain::createGenesisBlock(), _path, _we) +CanonBlockChain::CanonBlockChain(std::string const& _path, WithExisting _we, ProgressCallback const& _pc): BlockChain(CanonBlockChain::createGenesisBlock(), _path, _we, _pc) { } diff --git a/libethereum/CanonBlockChain.h b/libethereum/CanonBlockChain.h index be1c03998..619af87eb 100644 --- a/libethereum/CanonBlockChain.h +++ b/libethereum/CanonBlockChain.h @@ -55,8 +55,8 @@ std::map const& genesisState(); class CanonBlockChain: public BlockChain { public: - CanonBlockChain(WithExisting _we = WithExisting::Trust): CanonBlockChain(std::string(), _we) {} - CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust); + CanonBlockChain(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()): CanonBlockChain(std::string(), _we, _pc) {} + CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()); ~CanonBlockChain() {} /// @returns the genesis block header. diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 01ee9696a..d73299877 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -120,7 +120,7 @@ void BasicGasPricer::update(BlockChain const& _bc) Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners): Worker("eth"), m_vc(_dbPath), - m_bc(_dbPath, max(m_vc.action(), _forceAction)), + m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "..." << endl; }), m_gp(new TrivialGasPricer), m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))), m_preMine(Address(), m_stateDB), @@ -145,7 +145,7 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners): Worker("eth"), m_vc(_dbPath), - m_bc(_dbPath, max(m_vc.action(), _forceAction)), + m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "..." << endl; }), m_gp(_gp), m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))), m_preMine(Address(), m_stateDB), diff --git a/libethereum/State.cpp b/libethereum/State.cpp index cd46805d8..74d200381 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -43,6 +43,7 @@ using namespace dev; using namespace dev::eth; #define ctrace clog(StateTrace) +#define ETH_TIMED_ENACTMENTS 0 static const u256 c_blockReward = 1500 * finney; @@ -353,16 +354,48 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi) u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const& _bc) { +#if ETH_TIMED_ENACTMENTS + boost::timer t; + double populateVerify; + double populateGrand; + double syncReset; + double enactment; +#endif + // Check family: BlockInfo biParent(_bc.block(_bi.parentHash)); _bi.verifyParent(biParent); + +#if ETH_TIMED_ENACTMENTS + populateVerify = t.elapsed(); + t.restart(); +#endif + BlockInfo biGrandParent; if (biParent.number) biGrandParent.populate(_bc.block(biParent.parentHash)); + +#if ETH_TIMED_ENACTMENTS + populateGrand = t.elapsed(); + t.restart(); +#endif + sync(_bc, _bi.parentHash); resetCurrent(); + +#if ETH_TIMED_ENACTMENTS + syncReset = t.elapsed(); + t.restart(); +#endif + m_previousBlock = biParent; - return enact(_block, _bc); + auto ret = enact(_block, _bc); + +#if ETH_TIMED_ENACTMENTS + enactment = t.elapsed(); + cnote << "popVer/popGrand/syncReset/enactment = " << populateVerify << "/" << populateGrand << "/" << syncReset << "/" << enactment; +#endif + return ret; } map State::addresses() const From c8b5f9d2f686627ec854f04299768558954d78f8 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 4 Apr 2015 12:07:00 +0200 Subject: [PATCH 16/45] Major fix: only put in database active fork lookups. --- libethereum/BlockChain.cpp | 114 ++++++++++++++++++++----------------- libethereum/BlockChain.h | 26 ++++++--- 2 files changed, 81 insertions(+), 59 deletions(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 69332d620..9353a7e55 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -429,20 +429,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}); m_details[bi.parentHash].children.push_back(newHash); } - h256s alteredBlooms; - { - WriteGuard l(x_blocksBlooms); - LogBloom blockBloom = bi.logBloom; - blockBloom.shiftBloom<3>(sha3(bi.coinbaseAddress.ref())); - unsigned index = (unsigned)bi.number; - for (unsigned level = 0; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize) - { - unsigned i = index / c_bloomIndexSize; - unsigned o = index % c_bloomIndexSize; - alteredBlooms.push_back(chunkId(level, i)); - m_blocksBlooms[alteredBlooms.back()].blooms[o] |= blockBloom; - } - } { WriteGuard l(x_logBlooms); m_logBlooms[newHash] = blb; @@ -453,46 +439,21 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) } // TODO: FIX: URGENT!!!!! only put these into database when the block is on the main chain. - // Collate transaction hashes and remember who they were. - h256s newTransactionAddresses; - { - RLP blockRLP(_block); - TransactionAddress ta; - ta.blockHash = newHash; - WriteGuard l(x_transactionAddresses); - for (ta.index = 0; ta.index < blockRLP[1].itemCount(); ++ta.index) - { - newTransactionAddresses.push_back(sha3(blockRLP[1][ta.index].data())); - m_transactionAddresses[newTransactionAddresses.back()] = ta; - } - } - { - WriteGuard l(x_blockHashes); - m_blockHashes[h256(bi.number)].value = newHash; - } - #if ETH_TIMED_IMPORTS collation = t.elapsed(); t.restart(); #endif { - ReadGuard l1(x_blocksBlooms); ReadGuard l2(x_details); - ReadGuard l3(x_blockHashes); ReadGuard l4(x_receipts); ReadGuard l5(x_logBlooms); - ReadGuard l6(x_transactionAddresses); m_blocksDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block)); m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[newHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(h256(bi.number), ExtraBlockHash), (ldb::Slice)dev::ref(m_blockHashes[h256(bi.number)].rlp())); - for (auto const& h: newTransactionAddresses) - m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp())); - for (auto const& h: alteredBlooms) - m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraBlocksBlooms), (ldb::Slice)dev::ref(m_blocksBlooms[h].rlp())); + } #if ETH_TIMED_IMPORTS @@ -537,16 +498,68 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) h256 last = currentHash(); if (td > details(last).totalDifficulty) { - ret = treeRoute(last, newHash); + h256 common; + unsigned commonIndex; + tie(ret, common, commonIndex) = treeRoute(last, newHash); { WriteGuard l(x_lastBlockHash); m_lastBlockHash = newHash; } - noteCanonChanged(); - m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32)); + + // TODO: go through ret backwards until hash != last.parent and update m_transactionAddresses, m_blockHashes + // THEN: update database with them. + for (auto i = ret.rbegin(); i != ret.rend() && *i != common; ++i) + { + auto b = block(*i); + BlockInfo bi(b); + // Collate logs into blooms. + h256s alteredBlooms; + { + WriteGuard l(x_blocksBlooms); + LogBloom blockBloom = bi.logBloom; + blockBloom.shiftBloom<3>(sha3(bi.coinbaseAddress.ref())); + unsigned index = (unsigned)bi.number; + for (unsigned level = 0; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize) + { + unsigned i = index / c_bloomIndexSize; + unsigned o = index % c_bloomIndexSize; + alteredBlooms.push_back(chunkId(level, i)); + m_blocksBlooms[alteredBlooms.back()].blooms[o] |= blockBloom; + } + } + // Collate transaction hashes and remember who they were. + h256s newTransactionAddresses; + { + RLP blockRLP(b); + TransactionAddress ta; + ta.blockHash = bi.hash; + WriteGuard l(x_transactionAddresses); + for (ta.index = 0; ta.index < blockRLP[1].itemCount(); ++ta.index) + { + newTransactionAddresses.push_back(sha3(blockRLP[1][ta.index].data())); + m_transactionAddresses[newTransactionAddresses.back()] = ta; + } + } + { + WriteGuard l(x_blockHashes); + m_blockHashes[h256(bi.number)].value = bi.hash; + } + + ReadGuard l1(x_blocksBlooms); + ReadGuard l3(x_blockHashes); + ReadGuard l6(x_transactionAddresses); + for (auto const& h: alteredBlooms) + m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraBlocksBlooms), (ldb::Slice)dev::ref(m_blocksBlooms[h].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(h256(bi.number), ExtraBlockHash), (ldb::Slice)dev::ref(m_blockHashes[h256(bi.number)].rlp())); + for (auto const& h: newTransactionAddresses) + m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp())); + } + clog(BlockChainNote) << " Imported and best" << td << ". Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(ret); + noteCanonChanged(); + StructuredLogger::chainNewHead( bi.headerHash(WithoutNonce).abridged(), bi.nonce.abridged(), @@ -572,11 +585,11 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) return ret; } -h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const +tuple BlockChain::treeRoute(h256 const& _from, h256 const& _to, bool _common, bool _pre, bool _post) const { // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); if (!_from || !_to) - return h256s(); + return make_tuple(h256s(), h256(), 0); h256s ret; h256s back; unsigned fn = details(_from).number; @@ -608,20 +621,19 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, assert(to); from = details(from).parent; to = details(to).parent; - if (_pre) + if (_pre && (from != to || _common)) ret.push_back(from); - if (_post) + if (_post && (from != to || (!_pre && _common))) back.push_back(to); fn--; tn--; // cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); } - if (o_common) - *o_common = from; ret.reserve(ret.size() + back.size()); - for (auto it = back.cbegin(); it != back.cend(); ++it) + unsigned i = ret.size() - (int)(_common && !ret.empty() && !back.empty()); + for (auto it = back.rbegin(); it != back.rend(); ++it) ret.push_back(*it); - return ret; + return make_tuple(ret, from, i); } void BlockChain::noteUsed(h256 const& _h, unsigned _extra) const diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index a039e7d83..caf79bb1f 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -194,21 +194,31 @@ public: /// Will call _progress with the progress in this operation first param done, second total. void rebuild(std::string const& _path, ProgressCallback const& _progress = std::function()); - /** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of - * blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent. + /** @returns a tuple of: + * - an vector of hashes of all blocks between @a _from and @a _to, all blocks are ordered first by a number of + * blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent; + * - the block hash of the latest common ancestor of both blocks; + * - the index where the latest common ancestor of both blocks would either be found or inserted, depending + * on whether it is included. * - * If non-null, the h256 at @a o_common is set to the latest common ancestor of both blocks. + * @param _common if true, include the common ancestor in the returned vector. + * @param _pre if true, include all block hashes running from @a _from until the common ancestor in the returned vector. + * @param _post if true, include all block hashes running from the common ancestor until @a _to in the returned vector. * * e.g. if the block tree is 3a -> 2a -> 1a -> g and 2b -> 1b -> g (g is genesis, *a, *b are competing chains), * then: * @code - * treeRoute(3a, 2b) == { 3a, 2a, 1a, 1b, 2b }; // *o_common == g - * treeRoute(2a, 1a) == { 2a, 1a }; // *o_common == 1a - * treeRoute(1a, 2a) == { 1a, 2a }; // *o_common == 1a - * treeRoute(1b, 2a) == { 1b, 1a, 2a }; // *o_common == g + * treeRoute(3a, 2b, false) == make_tuple({ 3a, 2a, 1a, 1b, 2b }, g, 3); + * treeRoute(2a, 1a, false) == make_tuple({ 2a, 1a }, 1a, 1) + * treeRoute(1a, 2a, false) == make_tuple({ 1a, 2a }, 1a, 0) + * treeRoute(1b, 2a, false) == make_tuple({ 1b, 1a, 2a }, g, 1) + * treeRoute(3a, 2b, true) == make_tuple({ 3a, 2a, 1a, g, 1b, 2b }, g, 3); + * treeRoute(2a, 1a, true) == make_tuple({ 2a, 1a }, 1a, 1) + * treeRoute(1a, 2a, true) == make_tuple({ 1a, 2a }, 1a, 0) + * treeRoute(1b, 2a, true) == make_tuple({ 1b, g, 1a, 2a }, g, 1) * @endcode */ - h256s treeRoute(h256 const& _from, h256 const& _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; + std::tuple treeRoute(h256 const& _from, h256 const& _to, bool _common = true, bool _pre = true, bool _post = true) const; struct Statistics { From 0210c26a269dc4fd7ed69b9d5b13d25edf5baf25 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 4 Apr 2015 14:38:56 +0200 Subject: [PATCH 17/45] Clear block blooms. --- libethereum/BlockChain.cpp | 57 +++++++++++++++++++++++++++++++++++--- libethereum/BlockChain.h | 1 + 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 9353a7e55..03c5c6525 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -314,6 +314,8 @@ h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) { + //@tidy This is a behemoth of a method - could do to be split into a few smaller ones. + #if ETH_TIMED_IMPORTS boost::timer total; double preliminaryChecks; @@ -438,7 +440,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) m_receipts[newHash] = br; } - // TODO: FIX: URGENT!!!!! only put these into database when the block is on the main chain. #if ETH_TIMED_IMPORTS collation = t.elapsed(); t.restart(); @@ -453,7 +454,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp())); - } #if ETH_TIMED_IMPORTS @@ -508,8 +508,13 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32)); - // TODO: go through ret backwards until hash != last.parent and update m_transactionAddresses, m_blockHashes - // THEN: update database with them. + // Most of the time these two will be equal - only when we're doing a chain revert will they not be + if (common != last) + // If we are reverting previous blocks, we need to clear their blooms (in particular, to + // rebuild any higher level blooms that they contributed to). + clearBlockBlooms(number(common) + 1, number(last) + 1); + + // Go through ret backwards until hash != last.parent and update m_transactionAddresses, m_blockHashes for (auto i = ret.rbegin(); i != ret.rend() && *i != common; ++i) { auto b = block(*i); @@ -526,6 +531,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) unsigned i = index / c_bloomIndexSize; unsigned o = index % c_bloomIndexSize; alteredBlooms.push_back(chunkId(level, i)); + blocksBlooms(alteredBlooms.back()); m_blocksBlooms[alteredBlooms.back()].blooms[o] |= blockBloom; } } @@ -547,6 +553,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) m_blockHashes[h256(bi.number)].value = bi.hash; } + // Update database with them. ReadGuard l1(x_blocksBlooms); ReadGuard l3(x_blockHashes); ReadGuard l6(x_transactionAddresses); @@ -585,6 +592,48 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) return ret; } +void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end) +{ + // ... c c c c c c c c c c C o o o o o o + // ... /=15 /=21 + // L0...| ' | ' | ' | ' | ' | ' | ' | 'b|x'x|x'x|x'e| /=11 + // L1...| ' | ' | ' | ' b | x ' x | x ' e | /=6 + // L2...| ' | ' b | x ' x | e /=3 + // L3...| ' b | x ' e + // model: c_bloomIndexLevels = 4, c_bloomIndexSize = 2 + + // ... /=15 /=21 + // L0...| ' ' ' | ' ' ' | ' ' ' | ' ' 'b|x'x'x'x|x'e' ' | + // L1...| ' ' ' b | x ' x ' e ' | + // L2...| b ' x ' e ' | + // model: c_bloomIndexLevels = 2, c_bloomIndexSize = 4 + + // algorithm doesn't have the best memoisation coherence, but eh well... + + unsigned beginDirty = _begin; + unsigned endDirty = _end; + for (unsigned level = 0; level < c_bloomIndexLevels; level++, beginDirty /= c_bloomIndexSize, endDirty = (endDirty - 1) / c_bloomIndexSize + 1) + { + // compute earliest & latest index for each level, rebuild from previous levels. + for (unsigned item = beginDirty; item != endDirty; ++item) + { + unsigned bunch = item / c_bloomIndexSize; + unsigned offset = item % c_bloomIndexSize; + auto id = chunkId(level, bunch); + LogBloom acc; + if (!!level) + { + // rebuild the bloom from the previous (lower) level (if there is one). + auto lowerChunkId = chunkId(level - 1, item); + for (auto const& bloom: blocksBlooms(lowerChunkId).blooms) + acc |= bloom; + } + blocksBlooms(id); // make sure it has been memoized. + m_blocksBlooms[id].blooms[offset] = acc; + } + } +} + tuple BlockChain::treeRoute(h256 const& _from, h256 const& _to, bool _common, bool _pre, bool _post) const { // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index caf79bb1f..a45be0ab0 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -163,6 +163,7 @@ public: */ BlocksBlooms blocksBlooms(unsigned _level, unsigned _index) const { return blocksBlooms(chunkId(_level, _index)); } BlocksBlooms blocksBlooms(h256 const& _chunkId) const { return queryExtras(_chunkId, m_blocksBlooms, x_blocksBlooms, NullBlocksBlooms); } + void clearBlockBlooms(unsigned _begin, unsigned _end); LogBloom blockBloom(unsigned _number) const { return blocksBlooms(chunkId(0, _number / c_bloomIndexSize)).blooms[_number % c_bloomIndexSize]; } std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const; std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const; From 1e8c241f2b33813e8ad68f1239608b320977d4a4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 4 Apr 2015 14:44:44 +0200 Subject: [PATCH 18/45] Lock bug fix. --- libethereum/BlockChain.cpp | 11 +++++++---- libethereum/Client.cpp | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 03c5c6525..832d9465f 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -522,16 +522,19 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) // Collate logs into blooms. h256s alteredBlooms; { - WriteGuard l(x_blocksBlooms); LogBloom blockBloom = bi.logBloom; blockBloom.shiftBloom<3>(sha3(bi.coinbaseAddress.ref())); - unsigned index = (unsigned)bi.number; - for (unsigned level = 0; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize) + + // Pre-memoize everything we need before locking x_blocksBlooms + for (unsigned level = 0, index = (unsigned)bi.number; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize) + blocksBlooms(chunkId(level, index / c_bloomIndexSize)); + + WriteGuard l(x_blocksBlooms); + for (unsigned level = 0, index = (unsigned)bi.number; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize) { unsigned i = index / c_bloomIndexSize; unsigned o = index % c_bloomIndexSize; alteredBlooms.push_back(chunkId(level, i)); - blocksBlooms(alteredBlooms.back()); m_blocksBlooms[alteredBlooms.back()].blooms[o] |= blockBloom; } } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index d73299877..3d9f17335 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -145,7 +145,7 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners): Worker("eth"), m_vc(_dbPath), - m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "..." << endl; }), + m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), m_gp(_gp), m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))), m_preMine(Address(), m_stateDB), From d41101c307a88d7142bec0221044561e6c0fb09c Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 4 Apr 2015 17:22:54 +0200 Subject: [PATCH 19/45] log nullptr from cap() --- libwhisper/WhisperHost.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 673448051..b8cd39d19 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -163,7 +163,13 @@ void WhisperHost::uninstallWatch(unsigned _i) void WhisperHost::doWork() { for (auto i: peerSessions()) - i.first->cap()->sendMessages(); + { + auto w = i.first->cap(); + if (w) + w->sendMessages(); + else + cwarn << "cap() returned nullptr"; + } cleanup(); } From a0c0991c080458b81acf3c31326208701e7bee29 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 4 Apr 2015 17:33:15 +0200 Subject: [PATCH 20/45] Version bump. Fix version string in JSONRPC. --- libdevcore/Common.cpp | 2 +- libweb3jsonrpc/WebThreeStubServer.cpp | 5 +++++ libweb3jsonrpc/WebThreeStubServer.h | 4 +++- libwebthree/WebThree.h | 2 ++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 251774b68..c8a6a286d 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.9.2"; +char const* Version = "0.9.3"; } diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 8b24edf38..30a634b3d 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -44,6 +44,11 @@ WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, ldb::DB::Open(o, path, &m_db); } +std::string WebThreeStubServer::web3_clientVersion() +{ + return m_web3.clientVersion(); +} + dev::eth::Interface* WebThreeStubServer::client() { return m_web3.ethereum(); diff --git a/libweb3jsonrpc/WebThreeStubServer.h b/libweb3jsonrpc/WebThreeStubServer.h index 106842981..08991d2f5 100644 --- a/libweb3jsonrpc/WebThreeStubServer.h +++ b/libweb3jsonrpc/WebThreeStubServer.h @@ -42,7 +42,9 @@ class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThr { public: WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector const& _accounts); - + + virtual std::string web3_clientVersion(); + private: virtual dev::eth::Interface* client() override; virtual std::shared_ptr face() override; diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index de3cc9dcc..92feb8d40 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -127,6 +127,8 @@ public: // Misc stuff: + std::string const& clientVersion() const { return m_clientVersion; } + void setClientVersion(std::string const& _name) { m_clientVersion = _name; } // Network stuff: From c3760df77d01bd344304a0d3572140426ed4fc60 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 4 Apr 2015 20:30:22 +0200 Subject: [PATCH 21/45] Fix update problems in AZ. Fixes #1486 Connect requires peer by default. --- alethzero/Connect.cpp | 5 +++-- alethzero/Connect.ui | 3 +++ alethzero/MainWin.cpp | 2 +- libethereum/ClientBase.cpp | 8 +++++--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/alethzero/Connect.cpp b/alethzero/Connect.cpp index 32fa74f38..69cc43f93 100644 --- a/alethzero/Connect.cpp +++ b/alethzero/Connect.cpp @@ -38,13 +38,14 @@ Connect::~Connect() void Connect::setEnvironment(QStringList const& _nodes) { - ui->host->addItems(_nodes); + if (ui->host->count() == 0) + ui->host->addItems(_nodes); } void Connect::reset() { ui->nodeId->clear(); - ui->required->setChecked(false); + ui->required->setChecked(true); } QString Connect::host() diff --git a/alethzero/Connect.ui b/alethzero/Connect.ui index 9a0522e5f..1ace94c8d 100644 --- a/alethzero/Connect.ui +++ b/alethzero/Connect.ui @@ -59,6 +59,9 @@ Required (Always Connect to this Peer) + + true + false diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index b17d47cdf..af6e1a731 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -285,7 +285,7 @@ void Main::onKeysChanged() unsigned Main::installWatch(LogFilter const& _tf, WatchHandler const& _f) { - auto ret = ethereum()->installWatch(_tf); + auto ret = ethereum()->installWatch(_tf, Reaping::Manual); m_handlers[ret] = _f; _f(LocalisedLogEntries()); return ret; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index b45b9cf27..f949391fd 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -265,7 +265,8 @@ LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const // cwatch << "peekWatch" << _watchId; auto& w = m_watches.at(_watchId); // cwatch << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); - w.lastPoll = chrono::system_clock::now(); + if (w.lastPoll != chrono::system_clock::time_point::max()) + w.lastPoll = chrono::system_clock::now(); return w.changes; } @@ -278,8 +279,9 @@ LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId) auto& w = m_watches.at(_watchId); // cwatch << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); std::swap(ret, w.changes); - w.lastPoll = chrono::system_clock::now(); - + if (w.lastPoll != chrono::system_clock::time_point::max()) + w.lastPoll = chrono::system_clock::now(); + return ret; } From 1744a32547ba21c31477aa8f32fc3843f7feaa3b Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 4 Apr 2015 20:38:24 +0200 Subject: [PATCH 22/45] less tolerant connection errors --- libp2p/Host.cpp | 12 +++++------- libp2p/Session.cpp | 15 ++++++--------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 45ebd6db1..357fc4a02 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -182,10 +182,8 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io } else p = m_peers[_id]; - p->m_lastDisconnect = NoDisconnect; if (p->isOffline()) p->m_lastConnected = std::chrono::system_clock::now(); - p->m_failedAttempts = 0; p->endpoint.tcp.address(_endpoint.address()); auto protocolVersion = _rlp[0].toInt(); @@ -484,12 +482,14 @@ void Host::connect(std::shared_ptr const& _p) auto socket = make_shared(new bi::tcp::socket(m_ioService)); socket->ref().async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec) { + _p->m_lastAttempted = std::chrono::system_clock::now(); + _p->m_failedAttempts++; + if (ec) { clog(NetConnect) << "Connection refused to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "(" << ec.message() << ")"; + // Manually set error (session not present) _p->m_lastDisconnect = TCPError; - _p->m_lastAttempted = std::chrono::system_clock::now(); - _p->m_failedAttempts++; } else { @@ -499,9 +499,7 @@ void Host::connect(std::shared_ptr const& _p) Guard l(x_connecting); m_connecting.push_back(handshake); } - - // preempt setting failedAttempts; this value is cleared upon success - _p->m_failedAttempts++; + handshake->start(); } diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index dac588149..9a82a38a0 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -358,16 +358,13 @@ void Session::drop(DisconnectReason _reason) } catch (...) {} - if (m_peer) + if (m_peer->m_lastDisconnect == NoDisconnect && (_reason == ClientQuit || _reason == DisconnectRequested)) + m_peer->m_failedAttempts = 0; + m_peer->m_lastDisconnect = _reason; + if (_reason == BadProtocol) { - if (_reason != m_peer->m_lastDisconnect || _reason == NoDisconnect || _reason == ClientQuit || _reason == DisconnectRequested) - m_peer->m_failedAttempts = 0; - m_peer->m_lastDisconnect = _reason; - if (_reason == BadProtocol) - { - m_peer->m_rating /= 2; - m_peer->m_score /= 2; - } + m_peer->m_rating /= 2; + m_peer->m_score /= 2; } m_dropped = true; } From ca0ce068c6f26d1b6024c04bf26d9d1b1c0d1cd1 Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 4 Apr 2015 21:45:47 +0200 Subject: [PATCH 23/45] implicit fallback update --- libp2p/Peer.cpp | 1 + libp2p/Peer.h | 3 +++ libp2p/Session.cpp | 2 -- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libp2p/Peer.cpp b/libp2p/Peer.cpp index 4be0fd799..e37953e12 100644 --- a/libp2p/Peer.cpp +++ b/libp2p/Peer.cpp @@ -44,6 +44,7 @@ unsigned Peer::fallbackSeconds() const return 30 * (m_failedAttempts + 1); case UselessPeer: case TooManyPeers: + return 25 * (m_failedAttempts + 1); case ClientQuit: return 15 * (m_failedAttempts + 1); case NoDisconnect: diff --git a/libp2p/Peer.h b/libp2p/Peer.h index bfa2eaeb6..cb9155bbc 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -75,6 +75,9 @@ public: /// Reason peer was previously disconnected. DisconnectReason lastDisconnect() const { return m_lastDisconnect; } + /// Peer session is noted as useful. + void noteSessionGood() { m_failedAttempts = 0; } + protected: /// Returns number of seconds to wait until attempting connection, based on attempted connection history. unsigned fallbackSeconds() const; diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 9a82a38a0..632068502 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -358,8 +358,6 @@ void Session::drop(DisconnectReason _reason) } catch (...) {} - if (m_peer->m_lastDisconnect == NoDisconnect && (_reason == ClientQuit || _reason == DisconnectRequested)) - m_peer->m_failedAttempts = 0; m_peer->m_lastDisconnect = _reason; if (_reason == BadProtocol) { From 591a3ceb21a4990a04de7d5f56e9c7fa0ff27984 Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 4 Apr 2015 22:00:28 +0200 Subject: [PATCH 24/45] Reset lastDisconnect when session is started. --- libp2p/Session.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 632068502..133d8a30b 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -46,6 +46,7 @@ Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr const& _n, Pe m_info(_info), m_ping(chrono::steady_clock::time_point::max()) { + m_peer->m_lastDisconnect = NoDisconnect; m_lastReceived = m_connect = chrono::steady_clock::now(); } From cb2a9c7b24c977197d09c5322107e71dab25b68d Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 4 Apr 2015 22:04:50 +0200 Subject: [PATCH 25/45] Thread-safe initialization of capabilities. --- libp2p/Host.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 45ebd6db1..563e92da4 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -221,7 +221,6 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io } m_sessions[_id] = ps; } - ps->start(); unsigned o = (unsigned)UserPacket; for (auto const& i: caps) if (haveCapability(i)) @@ -229,6 +228,7 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io ps->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(ps.get(), o)); o += m_capabilities[i]->messageCount(); } + ps->start(); clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount()); } From d203bf6d58af4f96dfa9f1681de4a931636356a9 Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 4 Apr 2015 22:13:10 +0200 Subject: [PATCH 26/45] Thread-safe cap initiliazation. --- libp2p/Host.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 563e92da4..e4538a75e 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -219,16 +219,19 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io ps->disconnect(DuplicatePeer); return; } + + // todo: mutex Session::m_capabilities and move for(:caps) out of mutex. + unsigned o = (unsigned)UserPacket; + for (auto const& i: caps) + if (haveCapability(i)) + { + ps->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(ps.get(), o)); + o += m_capabilities[i]->messageCount(); + } + ps->start(); m_sessions[_id] = ps; } - unsigned o = (unsigned)UserPacket; - for (auto const& i: caps) - if (haveCapability(i)) - { - ps->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(ps.get(), o)); - o += m_capabilities[i]->messageCount(); - } - ps->start(); + clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount()); } From d16737c7070a31b74be0f1507e1737d6064abb2b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 5 Apr 2015 14:47:39 +0200 Subject: [PATCH 27/45] New DAG file scheme. --- libethcore/Ethasher.cpp | 39 ++++++++++++++++++++++++--------------- libethcore/Ethasher.h | 4 ++-- test/dagger.cpp | 2 +- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 64db69187..58a0aa8d7 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -43,21 +43,21 @@ Ethasher* dev::eth::Ethasher::s_this = nullptr; Ethasher::~Ethasher() { - while (!m_caches.empty()) - killCache(m_caches.begin()->first); + while (!m_lights.empty()) + killCache(m_lights.begin()->first); } void Ethasher::killCache(h256 const& _s) { RecursiveGuard l(x_this); - if (m_caches.count(_s)) + if (m_lights.count(_s)) { - ethash_delete_light(m_caches.at(_s)); - m_caches.erase(_s); + ethash_delete_light(m_lights.at(_s)); + m_lights.erase(_s); } } -void const* Ethasher::cache(BlockInfo const& _header) +void const* Ethasher::light(BlockInfo const& _header) { RecursiveGuard l(x_this); if (_header.number > c_ethashEpochLength * 2048) @@ -67,14 +67,16 @@ void const* Ethasher::cache(BlockInfo const& _header) throw std::invalid_argument( error.str() ); } - if (!m_caches.count(_header.seedHash())) + if (!m_lights.count(_header.seedHash())) { ethash_params p = params((unsigned)_header.number); - m_caches[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data()); + m_lights[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data()); } - return m_caches[_header.seedHash()]; + return m_lights[_header.seedHash()]; } +#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} + bytesConstRef Ethasher::full(BlockInfo const& _header) { RecursiveGuard l(x_this); @@ -89,19 +91,26 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) boost::filesystem::create_directories(getDataDir("ethash")); } catch (...) {} - std::string memoFile = getDataDir("ethash") + "/full"; auto info = rlpList(c_ethashRevision, _header.seedHash()); - if (boost::filesystem::exists(memoFile) && contents(memoFile + ".info") != info) - boost::filesystem::remove(memoFile); + std::string oldMemoFile = getDataDir("ethash") + "/full"; + std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8)); + if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) + { + // memofile valid - rename. + boost::filesystem::rename(oldMemoFile, memoFile); + } + + IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile)); + IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info")); + m_fulls[_header.seedHash()] = contentsNew(memoFile); if (!m_fulls[_header.seedHash()]) { ethash_params p = params((unsigned)_header.number); m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size); - auto c = cache(_header); + auto c = light(_header); ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c); writeFile(memoFile, m_fulls[_header.seedHash()]); - writeFile(memoFile + ".info", info); } } return m_fulls[_header.seedHash()]; @@ -162,7 +171,7 @@ Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) { auto p = Ethasher::params(_header); ethash_return_value r; - ethash_compute_light(&r, Ethasher::get()->cache(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + ethash_compute_light(&r, Ethasher::get()->light(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); // cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h index a10c206d1..8d1a1e84c 100644 --- a/libethcore/Ethasher.h +++ b/libethcore/Ethasher.h @@ -51,7 +51,7 @@ public: using LightType = void const*; using FullType = void const*; - LightType cache(BlockInfo const& _header); + LightType light(BlockInfo const& _header); bytesConstRef full(BlockInfo const& _header); static ethash_params params(BlockInfo const& _header); static ethash_params params(unsigned _n); @@ -99,7 +99,7 @@ private: static Ethasher* s_this; RecursiveMutex x_this; - std::map m_caches; + std::map m_lights; std::map m_fulls; }; diff --git a/test/dagger.cpp b/test/dagger.cpp index 4dda9c4fc..4abba5090 100644 --- a/test/dagger.cpp +++ b/test/dagger.cpp @@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(basic_test) unsigned cacheSize(o["cache_size"].get_int()); h256 cacheHash(o["cache_hash"].get_str()); BOOST_REQUIRE_EQUAL(Ethasher::get()->params(header).cache_size, cacheSize); - BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)Ethasher::get()->cache(header), cacheSize)), cacheHash); + BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)Ethasher::get()->light(header), cacheSize)), cacheHash); #if TEST_FULL unsigned fullSize(o["full_size"].get_int()); From 3053282582721d99ea6a190e43e9685a420c5bf2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 5 Apr 2015 15:17:39 +0200 Subject: [PATCH 28/45] Avoid killing old DAGs for now. Presents a memory leak to the tune of 1GB/epoch (!!!) but this will all be fixed when @LefterisJP introduces ethash-side memoisation. --- libethcore/Ethasher.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 58a0aa8d7..e8e4d0b0e 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -82,11 +82,13 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) RecursiveGuard l(x_this); if (!m_fulls.count(_header.seedHash())) { - if (!m_fulls.empty()) + // @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr. +/* if (!m_fulls.empty()) { delete [] m_fulls.begin()->second.data(); m_fulls.erase(m_fulls.begin()); - } + }*/ + try { boost::filesystem::create_directories(getDataDir("ethash")); } catch (...) {} From 39fe38395b8509373f7c9074a12edf844e628e29 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 5 Apr 2015 15:31:33 +0200 Subject: [PATCH 29/45] revert debug code --- libwhisper/WhisperHost.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index b8cd39d19..97a24e112 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -163,13 +163,7 @@ void WhisperHost::uninstallWatch(unsigned _i) void WhisperHost::doWork() { for (auto i: peerSessions()) - { - auto w = i.first->cap(); - if (w) - w->sendMessages(); - else - cwarn << "cap() returned nullptr"; - } + i.first->cap().get()->sendMessages(); cleanup(); } From 105be32bb4bfaf1f4e49f5993ffb3b863228b5eb Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 5 Apr 2015 16:33:51 +0200 Subject: [PATCH 30/45] Decent transaction import result provision. Give network a hint about what's going on for peer backoffs. Avoid sleeping in main loop when there's still work on. --- eth/main.cpp | 8 ++++---- libethcore/Common.h | 10 ++++++++++ libethereum/BlockChain.cpp | 6 +++--- libethereum/BlockChain.h | 2 +- libethereum/BlockQueue.h | 14 +++----------- libethereum/Client.cpp | 15 +++++++++++---- libethereum/ClientBase.cpp | 4 ++-- libethereum/EthereumPeer.cpp | 20 ++++++++++++++++++-- libethereum/TransactionQueue.cpp | 12 +++++++----- libethereum/TransactionQueue.h | 8 ++++---- libp2p/Capability.cpp | 2 +- libp2p/Capability.h | 2 +- libp2p/Session.cpp | 4 +++- libp2p/Session.h | 2 +- test/blockchain.cpp | 2 +- 15 files changed, 70 insertions(+), 41 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 3cdcba472..9ec6dec98 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -120,7 +120,7 @@ void help() << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl #endif - << " -K,--kill-blockchain First kill the blockchain." << endl + << " -K,--kill First kill the blockchain." << endl << " --listen-ip Listen on the given port for incoming connections (default: 30303)." << endl << " -l,--listen Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl << " -u,--public-ip Force public ip to given (default: auto)." << endl @@ -129,7 +129,7 @@ void help() << " -o,--mode Start a full node or a peer node (Default: full)." << endl << " -p,--port Connect to remote port (default: 30303)." << endl << " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl - << " -R,--rebuild-blockchain First rebuild the blockchain from the existing database." << endl + << " -R,--rebuild First rebuild the blockchain from the existing database." << endl << " -r,--remote Connect to remote host (default: none)." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl << " -t,--miners Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl @@ -274,9 +274,9 @@ int main(int argc, char** argv) return -1; } } - else if (arg == "-K" || arg == "--kill-blockchain") + else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill") killChain = WithExisting::Kill; - else if (arg == "-B" || arg == "--rebuild-blockchain") + else if (arg == "-B" || arg == "--rebuild") killChain = WithExisting::Verify; else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) clientName = argv[++i]; diff --git a/libethcore/Common.h b/libethcore/Common.h index aabe663cd..56f1b704a 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -85,5 +85,15 @@ enum class RelativeBlock: BlockNumber Pending = PendingBlock }; +enum class ImportResult +{ + Success = 0, + UnknownParent, + FutureTime, + AlreadyInChain, + AlreadyKnown, + Malformed +}; + } } diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 832d9465f..ae319cc14 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -267,7 +267,7 @@ LastHashes BlockChain::lastHashes(unsigned _n) const return m_lastLastHashes; } -h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max) +pair BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max) { _bq.tick(*this); @@ -295,8 +295,8 @@ h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max catch (...) {} } - _bq.doneDrain(); - return ret; + bool yetMore = _bq.doneDrain(); + return make_pair(ret, yetMore); } h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _force) noexcept diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index a45be0ab0..add4b43cd 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -99,7 +99,7 @@ public: void process(); /// Sync the chain with any incoming blocks. All blocks should, if processed in order - h256s sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max); + std::pair sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max); /// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB. /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index 9c473d766..f34a53812 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -26,6 +26,7 @@ #include #include #include +#include namespace dev { @@ -37,16 +38,6 @@ class BlockChain; struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; }; #define cblockq dev::LogOutputStream() -enum class ImportResult -{ - Success = 0, - UnknownParent, - FutureTime, - AlreadyInChain, - AlreadyKnown, - Malformed -}; - /** * @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). @@ -66,7 +57,8 @@ public: void drain(std::vector& o_out, unsigned _max); /// Must be called after a drain() call. Notes that the drained blocks have been imported into the blockchain, so we can forget about them. - void doneDrain() { WriteGuard l(m_lock); m_drainingSet.clear(); } + /// @returns true iff there are additional blocks ready to be processed. + bool doneDrain() { WriteGuard l(m_lock); m_drainingSet.clear(); return !m_readySet.empty(); } /// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain). void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 3d9f17335..2f65e5f4b 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -120,7 +120,7 @@ void BasicGasPricer::update(BlockChain const& _bc) Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners): Worker("eth"), m_vc(_dbPath), - m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "..." << endl; }), + m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), m_gp(new TrivialGasPricer), m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))), m_preMine(Address(), m_stateDB), @@ -441,6 +441,8 @@ void Client::doWork() { // TODO: Use condition variable rather than polling. + bool stillGotWork = false; + cworkin << "WORK"; h256Set changeds; @@ -496,7 +498,10 @@ void Client::doWork() cwork << "BQ ==> CHAIN ==> STATE"; OverlayDB db = m_stateDB; x_stateDB.unlock(); - h256s newBlocks = m_bc.sync(m_bq, db, 100); // TODO: remove transactions from m_tq nicely rather than relying on out of date nonce later on. + h256s newBlocks; + bool sgw; + tie(newBlocks, sgw) = m_bc.sync(m_bq, db, 100); // TODO: remove transactions from m_tq nicely rather than relying on out of date nonce later on. + stillGotWork = stillGotWork | sgw; if (newBlocks.size()) { for (auto i: newBlocks) @@ -544,7 +549,9 @@ void Client::doWork() noteChanged(changeds); cworkout << "WORK"; - this_thread::sleep_for(chrono::milliseconds(100)); + if (!stillGotWork) + this_thread::sleep_for(chrono::milliseconds(100)); + if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5)) { // watches garbage collection @@ -601,7 +608,7 @@ void Client::inject(bytesConstRef _rlp) { startWorking(); - m_tq.attemptImport(_rlp); + m_tq.import(_rlp); } void Client::flushTransactions() diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index f949391fd..fae534ff8 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -44,7 +44,7 @@ void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, b u256 n = postMine().transactionsFrom(toAddress(_secret)); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - m_tq.attemptImport(t.rlp()); + m_tq.import(t.rlp()); StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); cnote << "New transaction " << t; @@ -56,7 +56,7 @@ Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes con u256 n = postMine().transactionsFrom(toAddress(_secret)); Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); - m_tq.attemptImport(t.rlp()); + m_tq.import(t.rlp()); StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); cnote << "New transaction " << t; diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index f318a1757..15ce5c3f6 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -326,15 +326,27 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) case TransactionsPacket: { clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)"; - addRating(_r.itemCount()); Guard l(x_knownTransactions); for (unsigned i = 0; i < _r.itemCount(); ++i) { auto h = sha3(_r[i].data()); m_knownTransactions.insert(h); - if (!host()->m_tq.import(_r[i].data())) + ImportResult ir = host()->m_tq.import(_r[i].data()); + switch (ir) + { + case ImportResult::Malformed: + addRating(-100); + break; + case ImportResult::AlreadyKnown: // if we already had the transaction, then don't bother sending it on. + addRating(0); + break; + case ImportResult::Success: + addRating(100); host()->m_transactionsSent.insert(h); + break; + default:; + } } break; } @@ -352,6 +364,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) for (unsigned i = 0; i < c && p; ++i, p = host()->m_chain.details(p).parent) s << p; sealAndSend(s); + addRating(0); break; } case BlockHashesPacket: @@ -370,6 +383,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } for (unsigned i = 0; i < _r.itemCount(); ++i) { + addRating(1); auto h = _r[i].toHash(); if (host()->m_chain.isKnown(h)) { @@ -398,6 +412,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) ++n; } } + addRating(0); RLPStream s; prep(s, BlocksPacket, n).appendRaw(rlp, n); sealAndSend(s); @@ -497,6 +512,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) setNeedsSyncing(h, _r[1].toInt()); break; } + Guard l(x_knownBlocks); m_knownBlocks.insert(h); } diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 803d320ee..26962ed90 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -28,14 +28,16 @@ using namespace std; using namespace dev; using namespace dev::eth; -bool TransactionQueue::import(bytesConstRef _transactionRLP) +ImportResult TransactionQueue::import(bytesConstRef _transactionRLP) { // Check if we already know this transaction. h256 h = sha3(_transactionRLP); UpgradableGuard l(m_lock); + // TODO: keep old transactions around and check in State for nonce validity + if (m_known.count(h)) - return false; + return ImportResult::AlreadyKnown; try { @@ -52,15 +54,15 @@ bool TransactionQueue::import(bytesConstRef _transactionRLP) catch (Exception const& _e) { cwarn << "Ignoring invalid transaction: " << diagnostic_information(_e); - return false; + return ImportResult::Malformed; } catch (std::exception const& _e) { cwarn << "Ignoring invalid transaction: " << _e.what(); - return false; + return ImportResult::Malformed; } - return true; + return ImportResult::Success; } void TransactionQueue::setFuture(std::pair const& _t) diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index b104b98ca..cf40e1209 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -23,8 +23,8 @@ #include #include -#include "libethcore/Common.h" #include +#include "libethcore/Common.h" #include "Transaction.h" namespace dev @@ -34,6 +34,7 @@ namespace eth class BlockChain; + /** * @brief A queue of Transactions, each stored as RLP. * @threadsafe @@ -41,9 +42,8 @@ class BlockChain; class TransactionQueue { public: - bool attemptImport(bytesConstRef _tx) { try { import(_tx); return true; } catch (...) { return false; } } - bool attemptImport(bytes const& _tx) { return attemptImport(&_tx); } - bool import(bytesConstRef _tx); + ImportResult import(bytes const& _tx) { return import(&_tx); } + ImportResult import(bytesConstRef _tx); void drop(h256 _txHash); diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp index dccc130cd..f59fd8cdd 100644 --- a/libp2p/Capability.cpp +++ b/libp2p/Capability.cpp @@ -53,7 +53,7 @@ void Capability::sealAndSend(RLPStream& _s) m_session->sealAndSend(_s); } -void Capability::addRating(unsigned _r) +void Capability::addRating(int _r) { m_session->addRating(_r); } diff --git a/libp2p/Capability.h b/libp2p/Capability.h index ad8127bb5..d09391655 100644 --- a/libp2p/Capability.h +++ b/libp2p/Capability.h @@ -52,7 +52,7 @@ protected: RLPStream& prep(RLPStream& _s, unsigned _id, unsigned _args = 0); void sealAndSend(RLPStream& _s); - void addRating(unsigned _r); + void addRating(int _r); private: Session* m_session; diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 133d8a30b..0bf07bbe1 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -77,12 +77,14 @@ NodeId Session::id() const return m_peer ? m_peer->id : NodeId(); } -void Session::addRating(unsigned _r) +void Session::addRating(int _r) { if (m_peer) { m_peer->m_rating += _r; m_peer->m_score += _r; + if (_r >= 0) + m_peer->noteSessionGood(); } } diff --git a/libp2p/Session.h b/libp2p/Session.h index 51db5adc3..95053d2a9 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -74,7 +74,7 @@ public: void sealAndSend(RLPStream& _s); int rating() const; - void addRating(unsigned _r); + void addRating(int _r); void addNote(std::string const& _k, std::string const& _v) { m_info.notes[_k] = _v; } diff --git a/test/blockchain.cpp b/test/blockchain.cpp index ffb55da30..21345abfd 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -98,7 +98,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) { mObject tx = txObj.get_obj(); importer.importTransaction(tx); - if (!txs.attemptImport(importer.m_transaction.rlp())) + if (txs.import(importer.m_transaction.rlp()) != ImportResult::Success) cnote << "failed importing transaction\n"; } From 80df4e11fce9ce66810f5b3aa03ee69c1887b3a9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 5 Apr 2015 16:59:51 +0200 Subject: [PATCH 31/45] Nicer cmake read out. Add profiling support. --- CMakeLists.txt | 77 ++++++++++++++++++++++++++++++++- cmake/EthCompilerSettings.cmake | 6 ++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 80bad7929..1fbb83ff8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ function(createDefaultCacheConfig) set(SOLIDITY ON CACHE BOOL "Build the Solidity language components (requried unless HEADLESS)") set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed") set(ETHASHCL OFF CACHE BOOL "Build in support for GPU mining via OpenCL") + set(PROFILING OFF CACHE BOOL "Build in support for profiling") endfunction() @@ -56,6 +57,7 @@ function(configureProject) if (HEADLESS OR JUSTTESTS) add_definitions(-DETH_HEADLESS) endif() + endfunction() set(CPPETHEREUM 1) @@ -133,8 +135,79 @@ configureProject() # Force chromium. set (ETH_HAVE_WEBENGINE 1) -message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}") -message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}; FATDB: ${FATDB}; CHROMIUM: ${ETH_HAVE_WEBENGINE}; USENPM: ${USENPM}; ETHASHCL: ${ETHASHCL}") +# Normalise build options +# TODO: Abstract into something sensible and move into a function. +if (PARANOIA) + set(PARANOIA ON) +else () + set (PARANOIA OFF) +endif () +if (VMTRACE) + set(VMTRACE ON) +else () + set (VMTRACE OFF) +endif () +if (ETHASHCL) + set(ETHASHCL ON) +else () + set (ETHASHCL OFF) +endif() +if (EVMJIT) + set(EVMJIT ON) +else () + set (EVMJIT OFF) +endif() +if (FATDB) + set(FATDB ON) +else () + set (FATDB OFF) +endif() +if (SOLIDITY) + set(SOLIDITY ON) +else () + set (SOLIDITY OFF) +endif() +if (HEADLESS) + set(HEADLESS ON) +else () + set (HEADLESS OFF) +endif () +if (JUSTTESTS) + set(JUSTTESTS ON) +else () + set (JUSTTESTS OFF) +endif () +if (JSONRPC) + set(JSONRPC ON) +else () + set (JSONRPC OFF) +endif () +if (USENPM) + set(USENPM ON) +else () + set (USENPM OFF) +endif () +if (PROFILING) + set(PROFILING ON) +else () + set (PROFILING OFF) +endif () + +message(STATUS "CMake Version: ${CMAKE_VERSION}") +message("-- VM execution tracing VMTRACE ${VMTRACE}") +message("-- Profiling support PROFILING ${PROFILING}") +message("-- Additional (SLOW) database checking PARANOIA ${PARANOIA}") +message("-- Full database exploring FATDB ${FATDB}") +message("-- Build Javascript components from source USENPM ${USENPM}") +message("-- Build only headless components HEADLESS ${HEADLESS}") +message("-- Build only tests JUSTTESTS ${JUSTTESTS}") +message("-- Build Solidity language components SOLIDITY ${SOLIDITY}") +message("-- Build OpenCL components ETHASHCL ${ETHASHCL}") +message("-- Build LLVM-based JIT EVM EVMJIT ${EVMJIT}") +message("-- Build with support for JSON-RPC JSONRPC ${JSONRPC}") +message("-- Build with support for Chromium ${ETH_HAVE_WEBENGINE}") +message("------------------------------------------------------------------------") +message("") # Default TARGET_PLATFORM to "linux". diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index dd14b0650..731b258c7 100755 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -7,9 +7,13 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG -DETH_RELEASE") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DETH_RELEASE") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DETH_DEBUG") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DETH_RELEASE") set(ETH_SHARED 1) + if (PROFILING) + set(CMAKE_CXX_FLAGS "-pg ${CMAKE_CXX_FLAGS}") + endif () + execute_process( COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if (NOT (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)) From 8111a3e6b327f59a5f05f418d814f2f361220a11 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 5 Apr 2015 17:28:52 +0200 Subject: [PATCH 32/45] Ability to disable serpent building. --- CMakeLists.txt | 66 ++++++++++++++++++++------------- cmake/EthCompilerSettings.cmake | 2 +- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fbb83ff8..b174f9a28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,11 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # user defined, defaults # Normally, set(...CACHE...) creates cache variables, but does not modify them. function(createDefaultCacheConfig) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(SERPENT_DEFAULT OFF) + else () + set(SERPENT_DEFAULT ON) + endif () set(HEADLESS OFF CACHE BOOL "Do not compile GUI (AlethZero)") set(VMTRACE OFF CACHE BOOL "VM tracing and run-time checks (useful for cross-implementation VM debugging)") set(PARANOIA OFF CACHE BOOL "Additional run-time checks") @@ -21,7 +26,8 @@ function(createDefaultCacheConfig) set(EVMJIT OFF CACHE BOOL "Build a just-in-time compiler for EVM code (requires LLVM)") set(FATDB OFF CACHE BOOL "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents.") set(JUSTTESTS OFF CACHE BOOL "Build only for tests.") - set(SOLIDITY ON CACHE BOOL "Build the Solidity language components (requried unless HEADLESS)") + set(SOLIDITY ON CACHE BOOL "Build the Solidity language components (required unless HEADLESS)") + set(SERPENT ${SERPENT_DEFAULT} CACHE BOOL "Build the Serpent language components (required unless HEADLESS)") set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed") set(ETHASHCL OFF CACHE BOOL "Build in support for GPU mining via OpenCL") set(PROFILING OFF CACHE BOOL "Build in support for profiling") @@ -140,72 +146,82 @@ set (ETH_HAVE_WEBENGINE 1) if (PARANOIA) set(PARANOIA ON) else () - set (PARANOIA OFF) + set(PARANOIA OFF) endif () if (VMTRACE) set(VMTRACE ON) else () - set (VMTRACE OFF) + set(VMTRACE OFF) endif () if (ETHASHCL) set(ETHASHCL ON) else () - set (ETHASHCL OFF) + set(ETHASHCL OFF) endif() if (EVMJIT) set(EVMJIT ON) else () - set (EVMJIT OFF) + set(EVMJIT OFF) endif() if (FATDB) set(FATDB ON) else () - set (FATDB OFF) + set(FATDB OFF) endif() if (SOLIDITY) set(SOLIDITY ON) else () - set (SOLIDITY OFF) + set(SOLIDITY OFF) +endif() +if (SERPENT) + set(SERPENT ON) +else () + set(SERPENT OFF) endif() if (HEADLESS) set(HEADLESS ON) else () - set (HEADLESS OFF) + set(HEADLESS OFF) endif () if (JUSTTESTS) set(JUSTTESTS ON) else () - set (JUSTTESTS OFF) + set(JUSTTESTS OFF) endif () if (JSONRPC) set(JSONRPC ON) else () - set (JSONRPC OFF) + set(JSONRPC OFF) endif () if (USENPM) set(USENPM ON) else () - set (USENPM OFF) + set(USENPM OFF) endif () if (PROFILING) set(PROFILING ON) else () - set (PROFILING OFF) + set(PROFILING OFF) +endif () +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") endif () message(STATUS "CMake Version: ${CMAKE_VERSION}") -message("-- VM execution tracing VMTRACE ${VMTRACE}") -message("-- Profiling support PROFILING ${PROFILING}") -message("-- Additional (SLOW) database checking PARANOIA ${PARANOIA}") -message("-- Full database exploring FATDB ${FATDB}") -message("-- Build Javascript components from source USENPM ${USENPM}") -message("-- Build only headless components HEADLESS ${HEADLESS}") -message("-- Build only tests JUSTTESTS ${JUSTTESTS}") -message("-- Build Solidity language components SOLIDITY ${SOLIDITY}") -message("-- Build OpenCL components ETHASHCL ${ETHASHCL}") -message("-- Build LLVM-based JIT EVM EVMJIT ${EVMJIT}") -message("-- Build with support for JSON-RPC JSONRPC ${JSONRPC}") -message("-- Build with support for Chromium ${ETH_HAVE_WEBENGINE}") +message("-- Build type CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}") +message("-- VM execution tracing VMTRACE ${VMTRACE}") +message("-- Profiling support PROFILING ${PROFILING}") +message("-- Additional (SLOW) database checking PARANOIA ${PARANOIA}") +message("-- Full database exploring FATDB ${FATDB}") +message("-- Build Javascript components from source USENPM ${USENPM}") +message("-- Build only headless components HEADLESS ${HEADLESS}") +message("-- Build only tests JUSTTESTS ${JUSTTESTS}") +message("-- Build Solidity language components SOLIDITY ${SOLIDITY}") +message("-- Build Serpent language components SERPENT ${SERPENT}") +message("-- Build OpenCL components ETHASHCL ${ETHASHCL}") +message("-- Build LLVM-based JIT EVM EVMJIT ${EVMJIT}") +message("-- Build with support for JSON-RPC JSONRPC ${JSONRPC}") +message("-- Build with support for Chromium ${ETH_HAVE_WEBENGINE}") message("------------------------------------------------------------------------") message("") @@ -239,7 +255,7 @@ add_subdirectory(libdevcore) add_subdirectory(libevmcore) add_subdirectory(liblll) -if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) +if (SERPENT) add_subdirectory(libserpent) add_subdirectory(sc) endif () diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 731b258c7..b88c3adcb 100755 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -11,7 +11,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") set(ETH_SHARED 1) if (PROFILING) - set(CMAKE_CXX_FLAGS "-pg ${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "-pg -g ${CMAKE_CXX_FLAGS}") endif () execute_process( From 9fb30b141e57cd8b87cf066efe5fb0be438f6f7b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 5 Apr 2015 17:46:12 +0200 Subject: [PATCH 33/45] Allow Solidity build to be disabled. --- CMakeLists.txt | 29 +++++++++++++++++--------- libethereum/BlockChain.cpp | 2 ++ test/Assembly.cpp | 3 +++ test/CMakeLists.txt | 4 +++- test/SolidityABIJSON.cpp | 3 +++ test/SolidityCompiler.cpp | 3 +++ test/SolidityEndToEndTest.cpp | 4 +++- test/SolidityExpressionCompiler.cpp | 4 +++- test/SolidityInterface.cpp | 4 ++++ test/SolidityNameAndTypeResolution.cpp | 3 +++ test/SolidityNatspecJSON.cpp | 4 ++++ test/SolidityOptimizer.cpp | 5 ++++- test/SolidityParser.cpp | 3 +++ test/SolidityScanner.cpp | 4 ++++ test/SolidityTypes.cpp | 4 ++++ test/solidityExecutionFramework.h | 1 - 16 files changed, 65 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b174f9a28..8c02948a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,12 +203,23 @@ if (PROFILING) else () set(PROFILING OFF) endif () -if (NOT CMAKE_BUILD_TYPE) + +# Default CMAKE_BUILD_TYPE to "Release". +set(CMAKE_BUILD_TYPE CACHE STRING "Relase") +if ("x${CMAKE_BUILD_TYPE}" STREQUAL "x") set(CMAKE_BUILD_TYPE "Release") endif () -message(STATUS "CMake Version: ${CMAKE_VERSION}") +# Default TARGET_PLATFORM to "linux". +set(TARGET_PLATFORM CACHE STRING "linux") +if ("x${TARGET_PLATFORM}" STREQUAL "x") + set(TARGET_PLATFORM "linux") +endif () + +message("------------------------------------------------------------------------") +message("-- CMake Version ${CMAKE_VERSION}") message("-- Build type CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}") +message("-- Target platform TARGET_PLATFORM ${TARGET_PLATFORM}") message("-- VM execution tracing VMTRACE ${VMTRACE}") message("-- Profiling support PROFILING ${PROFILING}") message("-- Additional (SLOW) database checking PARANOIA ${PARANOIA}") @@ -226,12 +237,6 @@ message("----------------------------------------------------------------------- message("") -# Default TARGET_PLATFORM to "linux". -set(TARGET_PLATFORM CACHE STRING "linux") -if ("x${TARGET_PLATFORM}" STREQUAL "x") - set(TARGET_PLATFORM "linux") -endif () - if ("${TARGET_PLATFORM}" STREQUAL "linux") set(CMAKE_THREAD_LIBS_INIT pthread) endif () @@ -260,11 +265,15 @@ if (SERPENT) add_subdirectory(sc) endif () -add_subdirectory(libsolidity) +if (SOLIDITY) + add_subdirectory(libsolidity) +endif () if (NOT JUSTTESTS) add_subdirectory(lllc) - add_subdirectory(solc) + if (SOLIDITY) + add_subdirectory(solc) + endif () endif() if (JSONRPC) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index ae319cc14..488937c41 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -208,6 +208,8 @@ void BlockChain::rebuild(std::string const& _path, std::function 1000) + exit(0); try { bytes b = block(queryExtras(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value); diff --git a/test/Assembly.cpp b/test/Assembly.cpp index fbc8e47b5..fab260a9d 100644 --- a/test/Assembly.cpp +++ b/test/Assembly.cpp @@ -20,6 +20,8 @@ * Unit tests for Assembly Items from evmcore/Assembly.h */ +#if ETH_SOLIDITY + #include #include #include @@ -119,3 +121,4 @@ BOOST_AUTO_TEST_SUITE_END() } } // end namespaces +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 01681dbef..5bd44091d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -44,7 +44,9 @@ target_link_libraries(testeth ${CURL_LIBRARIES}) target_link_libraries(testeth ethereum) target_link_libraries(testeth ethcore) target_link_libraries(testeth secp256k1) -target_link_libraries(testeth solidity) +if (SOLIDITY) + target_link_libraries(testeth solidity) +endif () target_link_libraries(testeth testutils) if (NOT HEADLESS AND NOT JUSTTESTS) target_link_libraries(testeth webthree) diff --git a/test/SolidityABIJSON.cpp b/test/SolidityABIJSON.cpp index f7f968ea8..bbe5fd8c4 100644 --- a/test/SolidityABIJSON.cpp +++ b/test/SolidityABIJSON.cpp @@ -19,6 +19,7 @@ * @date 2014 * Unit tests for the solidity compiler JSON Interface output. */ +#if ETH_SOLIDITY #include "TestHelper.h" #include @@ -500,3 +501,5 @@ BOOST_AUTO_TEST_SUITE_END() } } } + +#endif diff --git a/test/SolidityCompiler.cpp b/test/SolidityCompiler.cpp index 1369b038f..bb16c88cd 100644 --- a/test/SolidityCompiler.cpp +++ b/test/SolidityCompiler.cpp @@ -20,6 +20,8 @@ * Unit tests for the solidity compiler. */ +#if ETH_SOLIDITY + #include #include #include @@ -192,3 +194,4 @@ BOOST_AUTO_TEST_SUITE_END() } } // end namespaces +#endif diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index ea6ada603..b4da07892 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -1,4 +1,3 @@ - /* This file is part of cpp-ethereum. @@ -22,6 +21,8 @@ * Unit tests for the solidity expression compiler, testing the behaviour of the code. */ +#if ETH_SOLIDITY + #include #include #include @@ -3627,3 +3628,4 @@ BOOST_AUTO_TEST_SUITE_END() } } // end namespaces +#endif diff --git a/test/SolidityExpressionCompiler.cpp b/test/SolidityExpressionCompiler.cpp index 7034085ef..b748d887d 100644 --- a/test/SolidityExpressionCompiler.cpp +++ b/test/SolidityExpressionCompiler.cpp @@ -1,4 +1,3 @@ - /* This file is part of cpp-ethereum. @@ -21,6 +20,8 @@ * Unit tests for the solidity expression compiler. */ +#if ETH_SOLIDITY + #include #include @@ -491,3 +492,4 @@ BOOST_AUTO_TEST_SUITE_END() } } // end namespaces +#endif diff --git a/test/SolidityInterface.cpp b/test/SolidityInterface.cpp index 48e2fd7aa..c836f0fa7 100644 --- a/test/SolidityInterface.cpp +++ b/test/SolidityInterface.cpp @@ -20,6 +20,8 @@ * Unit tests for generating source interfaces for Solidity contracts. */ +#if ETH_SOLIDITY + #include "TestHelper.h" #include #include @@ -147,3 +149,5 @@ BOOST_AUTO_TEST_SUITE_END() } } } + +#endif diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 531f3bc13..74a488883 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -20,6 +20,8 @@ * Unit tests for the name and type resolution of the solidity parser. */ +#if ETH_SOLIDITY + #include #include @@ -1627,3 +1629,4 @@ BOOST_AUTO_TEST_SUITE_END() } } // end namespaces +#endif diff --git a/test/SolidityNatspecJSON.cpp b/test/SolidityNatspecJSON.cpp index aeaad1966..28d657357 100644 --- a/test/SolidityNatspecJSON.cpp +++ b/test/SolidityNatspecJSON.cpp @@ -20,6 +20,8 @@ * Unit tests for the solidity compiler JSON Interface output. */ +#if ETH_SOLIDITY + #include "TestHelper.h" #include #include @@ -537,3 +539,5 @@ BOOST_AUTO_TEST_SUITE_END() } } } + +#endif diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index e69d5120e..4fedd642d 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -1,4 +1,3 @@ - /* This file is part of cpp-ethereum. @@ -21,6 +20,8 @@ * Tests for the Solidity optimizer. */ +#if ETH_SOLIDITY + #include #include #include @@ -573,3 +574,5 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces + +#endif diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 7640f91ad..b76f00656 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -20,6 +20,8 @@ * Unit tests for the solidity parser. */ +#if ETH_SOLIDITY + #include #include #include @@ -845,3 +847,4 @@ BOOST_AUTO_TEST_SUITE_END() } } // end namespaces +#endif diff --git a/test/SolidityScanner.cpp b/test/SolidityScanner.cpp index 8d3e53929..20b946ee0 100644 --- a/test/SolidityScanner.cpp +++ b/test/SolidityScanner.cpp @@ -20,6 +20,8 @@ * Unit tests for the solidity scanner. */ +#if ETH_SOLIDITY + #include #include @@ -286,3 +288,5 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces + +#endif diff --git a/test/SolidityTypes.cpp b/test/SolidityTypes.cpp index 6b6306479..da8b48303 100644 --- a/test/SolidityTypes.cpp +++ b/test/SolidityTypes.cpp @@ -20,6 +20,8 @@ * Unit tests for the type system of Solidity. */ +#if ETH_SOLIDITY + #include #include @@ -91,3 +93,5 @@ BOOST_AUTO_TEST_SUITE_END() } } } + +#endif diff --git a/test/solidityExecutionFramework.h b/test/solidityExecutionFramework.h index 2451aa381..2134d424d 100644 --- a/test/solidityExecutionFramework.h +++ b/test/solidityExecutionFramework.h @@ -1,4 +1,3 @@ - /* This file is part of cpp-ethereum. From a33de145e71f17fa38f53351bc589cdbf33ddac0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 5 Apr 2015 19:54:37 +0200 Subject: [PATCH 34/45] Don't check nonce by default. --- cmake/EthCompilerSettings.cmake | 6 +++++- libethcore/BlockInfo.cpp | 4 ++-- libethcore/BlockInfo.h | 14 +++++++------- libethereum/BlockChain.cpp | 19 +++++++++++++++++-- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index b88c3adcb..d781ad2b3 100755 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -11,7 +11,11 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") set(ETH_SHARED 1) if (PROFILING) - set(CMAKE_CXX_FLAGS "-pg -g ${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}") + add_definitions(-DETH_PROFILING_GPERF) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lprofiler") +# set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -lprofiler") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lprofiler") endif () execute_process( diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index bbd78224f..f4b6f3b4e 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -69,10 +69,10 @@ h256 const& BlockInfo::seedHash() const return m_seedHash; } -BlockInfo BlockInfo::fromHeader(bytesConstRef _block, Strictness _s) +BlockInfo BlockInfo::fromHeader(bytesConstRef _header, Strictness _s) { BlockInfo ret; - ret.populateFromHeader(RLP(_block), _s); + ret.populateFromHeader(RLP(_header), _s); return ret; } diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index d262afca7..2ef196934 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -86,14 +86,14 @@ public: Nonce nonce; BlockInfo(); - explicit BlockInfo(bytes const& _block, Strictness _s = CheckEverything): BlockInfo(&_block, _s) {} - explicit BlockInfo(bytesConstRef _block, Strictness _s = CheckEverything); + explicit BlockInfo(bytes const& _block, Strictness _s = IgnoreNonce): BlockInfo(&_block, _s) {} + explicit BlockInfo(bytesConstRef _block, Strictness _s = IgnoreNonce); static h256 headerHash(bytes const& _block) { return headerHash(&_block); } static h256 headerHash(bytesConstRef _block); - static BlockInfo fromHeader(bytes const& _block, Strictness _s = CheckEverything) { return fromHeader(bytesConstRef(&_block), _s); } - static BlockInfo fromHeader(bytesConstRef _block, Strictness _s = CheckEverything); + static BlockInfo fromHeader(bytes const& _header, Strictness _s = IgnoreNonce) { return fromHeader(bytesConstRef(&_header), _s); } + static BlockInfo fromHeader(bytesConstRef _header, Strictness _s = IgnoreNonce); explicit operator bool() const { return timestamp != Invalid256; } @@ -119,9 +119,9 @@ public: void setEmpty(); - void populateFromHeader(RLP const& _header, Strictness _s = CheckEverything); - void populate(bytesConstRef _block, Strictness _s = CheckEverything); - void populate(bytes const& _block, Strictness _s = CheckEverything) { populate(&_block, _s); } + void populateFromHeader(RLP const& _header, Strictness _s = IgnoreNonce); + void populate(bytesConstRef _block, Strictness _s = IgnoreNonce); + void populate(bytes const& _block, Strictness _s = IgnoreNonce) { populate(&_block, _s); } void verifyInternals(bytesConstRef _block) const; void verifyParent(BlockInfo const& _parent) const; void populateFromParent(BlockInfo const& parent); diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 488937c41..0aea83de7 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -155,7 +155,9 @@ void BlockChain::open(std::string const& _path, WithExisting _we) m_extrasDB->Put(m_writeOptions, toSlice(m_genesisHash, ExtraDetails), (ldb::Slice)dev::ref(r)); } +#if ETH_PARANOIA checkConsistency(); +#endif // TODO: Implement ability to rebuild details map from DB. std::string l; @@ -175,10 +177,15 @@ void BlockChain::close() m_blocks.clear(); } +#include #define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} void BlockChain::rebuild(std::string const& _path, std::function const& _progress) { +#if ETH_PROFILING_GPERF + ProfilerStart("BlockChain_rebuild.log"); +#endif + unsigned originalNumber = number(); // Keep extras DB around, but under a temp name @@ -206,10 +213,14 @@ void BlockChain::rebuild(std::string const& _path, std::function 1000) - exit(0); + if (!(d % 1000)) + { + cerr << "\n1000 blocks in " << t.elapsed() << "s = " << (1000.0 / t.elapsed()) << "b/s" << endl; + t.restart(); + } try { bytes b = block(queryExtras(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value); @@ -233,6 +244,10 @@ void BlockChain::rebuild(std::string const& _path, std::function Date: Sun, 5 Apr 2015 21:08:22 +0200 Subject: [PATCH 35/45] Move hash to a hidden function, and provide a datapath to it so avoid unneeded SHA3s. --- alethzero/MainWin.cpp | 4 +-- libethcore/BlockInfo.cpp | 33 ++++++++++++++--------- libethcore/BlockInfo.h | 21 ++++++++------- libethcore/Ethasher.cpp | 5 +++- libethereum/BlockChain.cpp | 18 ++++++++----- libethereum/BlockChain.h | 4 +-- libethereum/Client.cpp | 16 +++++------ libethereum/ClientBase.cpp | 2 +- libethereum/State.cpp | 31 ++++++++++----------- libethereum/State.h | 19 ++++++++++--- libtestutils/StateLoader.cpp | 2 +- libweb3jsonrpc/WebThreeStubServerBase.cpp | 2 +- mix/ClientModel.cpp | 2 +- mix/MixClient.cpp | 2 +- test/ClientBase.cpp | 18 +++++++++---- test/TestHelper.cpp | 7 +++-- test/TestHelper.h | 2 +- test/blockchain.cpp | 6 ++--- test/stateOriginal.cpp | 2 +- test/vm.cpp | 4 +-- 20 files changed, 122 insertions(+), 78 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index af6e1a731..48b47c3a5 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -148,7 +148,7 @@ Main::Main(QWidget *parent) : cerr << "State root: " << CanonBlockChain::genesis().stateRoot << endl; auto block = CanonBlockChain::createGenesisBlock(); - cerr << "Block Hash: " << CanonBlockChain::genesis().hash << endl; + cerr << "Block Hash: " << CanonBlockChain::genesis().hash() << endl; cerr << "Block RLP: " << RLP(block) << endl; cerr << "Block Hex: " << toHex(block) << endl; cerr << "eth Network protocol version: " << eth::c_protocolVersion << endl; @@ -1491,7 +1491,7 @@ void Main::on_blocks_currentItemChanged() { BlockInfo uncle = BlockInfo::fromHeader(u.data()); char const* line = "
 "; - s << line << "Hash: " << uncle.hash << "" << "
"; + s << line << "Hash: " << uncle.hash() << "" << ""; s << line << "Parent: " << uncle.parentHash << "" << ""; s << line << "Number: " << uncle.number << "" << ""; s << line << "Coinbase: " << pretty(uncle.coinbaseAddress).toHtmlEscaped().toStdString() << " " << uncle.coinbaseAddress << "" << ""; diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index f4b6f3b4e..66f08d2bf 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -35,9 +35,9 @@ BlockInfo::BlockInfo(): timestamp(Invalid256) { } -BlockInfo::BlockInfo(bytesConstRef _block, Strictness _s) +BlockInfo::BlockInfo(bytesConstRef _block, Strictness _s, h256 const& _h) { - populate(_block, _s); + populate(_block, _s, _h); } void BlockInfo::setEmpty() @@ -57,8 +57,7 @@ void BlockInfo::setEmpty() extraData.clear(); mixHash = h256(); nonce = Nonce(); - m_seedHash = h256(); - hash = headerHash(WithNonce); + m_hash = m_seedHash = h256(); } h256 const& BlockInfo::seedHash() const @@ -69,10 +68,17 @@ h256 const& BlockInfo::seedHash() const return m_seedHash; } -BlockInfo BlockInfo::fromHeader(bytesConstRef _header, Strictness _s) +h256 const& BlockInfo::hash() const +{ + if (!m_hash) + m_hash = headerHash(WithNonce); + return m_hash; +} + +BlockInfo BlockInfo::fromHeader(bytesConstRef _header, Strictness _s, h256 const& _h) { BlockInfo ret; - ret.populateFromHeader(RLP(_header), _s); + ret.populateFromHeader(RLP(_header), _s, _h); return ret; } @@ -97,9 +103,11 @@ h256 BlockInfo::headerHash(bytesConstRef _block) return sha3(RLP(_block)[0].data()); } -void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s) +void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const& _h) { - hash = dev::sha3(_header.data()); +// m_hash = dev::sha3(_header.data()); + m_hash = _h; + m_seedHash = h256(); int field = 0; try @@ -149,14 +157,14 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s) } } -void BlockInfo::populate(bytesConstRef _block, Strictness _s) +void BlockInfo::populate(bytesConstRef _block, Strictness _s, h256 const& _h) { RLP root(_block); RLP header = root[0]; if (!header.isList()) BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block header needs to be a list") << BadFieldError(0, header.data().toString())); - populateFromHeader(header, _s); + populateFromHeader(header, _s, _h); if (!root[1].isList()) BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block transactions need to be a list") << BadFieldError(1, root[1].data().toString())); @@ -191,8 +199,9 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const void BlockInfo::populateFromParent(BlockInfo const& _parent) { + m_hash = m_seedHash = h256(); stateRoot = _parent.stateRoot; - parentHash = _parent.hash; + parentHash = _parent.hash(); number = _parent.number + 1; gasLimit = selectGasLimit(_parent); gasUsed = 0; @@ -230,7 +239,7 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const // Check timestamp is after previous timestamp. if (parentHash) { - if (parentHash != _parent.hash) + if (parentHash != _parent.hash()) BOOST_THROW_EXCEPTION(InvalidParentHash()); if (timestamp <= _parent.timestamp) diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index 2ef196934..6a3ca68b2 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -68,7 +68,6 @@ struct BlockInfo { public: // TODO: make them all private! - h256 hash; ///< SHA3 hash of the block header! Not serialised (the only member not contained in a block header). h256 parentHash; h256 sha3Uncles; Address coinbaseAddress; @@ -86,14 +85,14 @@ public: Nonce nonce; BlockInfo(); - explicit BlockInfo(bytes const& _block, Strictness _s = IgnoreNonce): BlockInfo(&_block, _s) {} - explicit BlockInfo(bytesConstRef _block, Strictness _s = IgnoreNonce); + explicit BlockInfo(bytes const& _block, Strictness _s = IgnoreNonce, h256 const& _h = h256()): BlockInfo(&_block, _s, _h) {} + explicit BlockInfo(bytesConstRef _block, Strictness _s = IgnoreNonce, h256 const& _h = h256()); static h256 headerHash(bytes const& _block) { return headerHash(&_block); } static h256 headerHash(bytesConstRef _block); - static BlockInfo fromHeader(bytes const& _header, Strictness _s = IgnoreNonce) { return fromHeader(bytesConstRef(&_header), _s); } - static BlockInfo fromHeader(bytesConstRef _header, Strictness _s = IgnoreNonce); + static BlockInfo fromHeader(bytes const& _header, Strictness _s = IgnoreNonce, h256 const& _h = h256()) { return fromHeader(bytesConstRef(&_header), _s, _h); } + static BlockInfo fromHeader(bytesConstRef _header, Strictness _s = IgnoreNonce, h256 const& _h = h256()); explicit operator bool() const { return timestamp != Invalid256; } @@ -119,9 +118,11 @@ public: void setEmpty(); - void populateFromHeader(RLP const& _header, Strictness _s = IgnoreNonce); - void populate(bytesConstRef _block, Strictness _s = IgnoreNonce); - void populate(bytes const& _block, Strictness _s = IgnoreNonce) { populate(&_block, _s); } + void noteDirty() const { m_hash = m_seedHash= h256(); } + + void populateFromHeader(RLP const& _header, Strictness _s = IgnoreNonce, h256 const& _h = h256()); + void populate(bytesConstRef _block, Strictness _s = IgnoreNonce, h256 const& _h = h256()); + void populate(bytes const& _block, Strictness _s = IgnoreNonce, h256 const& _h = h256()) { populate(&_block, _s, _h); } void verifyInternals(bytesConstRef _block) const; void verifyParent(BlockInfo const& _parent) const; void populateFromParent(BlockInfo const& parent); @@ -129,6 +130,7 @@ public: u256 calculateDifficulty(BlockInfo const& _parent) const; u256 selectGasLimit(BlockInfo const& _parent) const; h256 const& seedHash() const; + h256 const& hash() const; /// sha3 of the header only. h256 headerHash(IncludeNonce _n) const; @@ -136,11 +138,12 @@ public: private: mutable h256 m_seedHash; + mutable h256 m_hash; ///< SHA3 hash of the block header! Not serialised. }; inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi) { - _out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " << + _out << _bi.hash() << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " << _bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.gasLimit << " " << _bi.gasUsed << " " << _bi.timestamp << " " << _bi.mixHash << " " << _bi.nonce << " (" << _bi.seedHash() << ")"; return _out; diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index e8e4d0b0e..75f0bcd5a 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -173,7 +173,10 @@ Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) { auto p = Ethasher::params(_header); ethash_return_value r; - ethash_compute_light(&r, Ethasher::get()->light(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + if (Ethasher::get()->m_fulls.count(_header.seedHash())) + ethash_compute_full(&r, Ethasher::get()->full(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + else + ethash_compute_light(&r, Ethasher::get()->light(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); // cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 0aea83de7..cad23171a 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "GenesisInfo.h" #include "State.h" @@ -200,7 +201,7 @@ void BlockChain::rebuild(std::string const& _path, std::function(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value); BlockInfo bi(b); + if (bi.number % c_ethashEpochLength == 1) + Ethasher::get()->full(bi); + if (bi.parentHash != lastHash) { - cwarn << "DISJOINT CHAIN DETECTED; " << bi.hash.abridged() << "#" << d << " -> parent is" << bi.parentHash.abridged() << "; expected" << lastHash.abridged() << "#" << (d - 1); + cwarn << "DISJOINT CHAIN DETECTED; " << bi.hash().abridged() << "#" << d << " -> parent is" << bi.parentHash.abridged() << "; expected" << lastHash.abridged() << "#" << (d - 1); return; } - lastHash = bi.hash; + lastHash = bi.hash(); import(b, s.db(), true); } catch (...) @@ -414,7 +418,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) { // Check transactions are valid and that they result in a state equivalent to our state_root. // Get total difficulty increase and update state, checking it. - State s(bi.coinbaseAddress, _db); + State s(_db); //, bi.coinbaseAddress auto tdIncrease = s.enactOn(&_block, bi, *this); BlockLogBlooms blb; @@ -493,7 +497,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) { clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(_e); _e << errinfo_comment("Malformed block "); - clog(BlockChainWarn) << "Block: " << bi.hash; + clog(BlockChainWarn) << "Block: " << bi.hash(); clog(BlockChainWarn) << bi; clog(BlockChainWarn) << "Block parent: " << bi.parentHash; clog(BlockChainWarn) << BlockInfo(block(bi.parentHash)); @@ -560,7 +564,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) { RLP blockRLP(b); TransactionAddress ta; - ta.blockHash = bi.hash; + ta.blockHash = bi.hash(); WriteGuard l(x_transactionAddresses); for (ta.index = 0; ta.index < blockRLP[1].itemCount(); ++ta.index) { @@ -570,7 +574,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force) } { WriteGuard l(x_blockHashes); - m_blockHashes[h256(bi.number)].value = bi.hash; + m_blockHashes[h256(bi.number)].value = bi.hash(); } // Update database with them. diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index add4b43cd..2e2d8cf34 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -113,8 +113,8 @@ public: bool isKnown(h256 const& _hash) const; /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. - BlockInfo info(h256 const& _hash) const { return BlockInfo(block(_hash)); } - BlockInfo info() const { return BlockInfo(block()); } + BlockInfo info(h256 const& _hash) const { return BlockInfo(block(_hash), IgnoreNonce, _hash); } + BlockInfo info() const { return info(currentHash()); } /// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe. bytes block(h256 const& _hash) const; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 2f65e5f4b..95fd2364f 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -90,7 +90,7 @@ void BasicGasPricer::update(BlockChain const& _bc) { auto bb = _bc.block(p); RLP r(bb); - BlockReceipts brs(_bc.receipts(bi.hash)); + BlockReceipts brs(_bc.receipts(bi.hash())); for (unsigned i = 0; i < r[1].size(); ++i) { auto gu = brs.receipts[i].gasUsed(); @@ -123,8 +123,8 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), m_gp(new TrivialGasPricer), m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))), - m_preMine(Address(), m_stateDB), - m_postMine(Address(), m_stateDB) + m_preMine(m_stateDB), + m_postMine(m_stateDB) { m_gp->update(m_bc); @@ -148,8 +148,8 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string c m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), m_gp(_gp), m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))), - m_preMine(Address(), m_stateDB), - m_postMine(Address(), m_stateDB) + m_preMine(m_stateDB), + m_postMine(m_stateDB) { m_gp->update(m_bc); @@ -221,8 +221,8 @@ void Client::killChain() } m_bc.reopen(Defaults::dbPath(), WithExisting::Kill); - m_preMine = State(Address(), m_stateDB); - m_postMine = State(Address(), m_stateDB); + m_preMine = State(m_stateDB); + m_postMine = State(m_stateDB); if (auto h = m_host.lock()) h->reset(); @@ -313,7 +313,7 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed) auto m = i.second.filter.matches(tr); if (m.size()) { - auto transactionHash = transaction(d.hash, j).sha3(); + auto transactionHash = transaction(d.hash(), j).sha3(); // filter catches them for (LogEntry const& l: m) i.second.changes.push_back(LocalisedLogEntry(l, (unsigned)d.number, transactionHash)); diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index fae534ff8..b9c2fa878 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -182,7 +182,7 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const if (_f.matches(receipt.bloom())) { auto info = bc().info(h); - auto th = transaction(info.hash, i).sha3(); + auto th = transaction(info.hash(), i).sha3(); LogEntries le = _f.matches(receipt); if (le.size()) { diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 74d200381..a58ba9d0f 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -78,23 +78,24 @@ OverlayDB State::openDB(std::string _path, WithExisting _we) return OverlayDB(db); } -State::State(Address _coinbaseAddress, OverlayDB const& _db, BaseState _bs): +State::State(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress): m_db(_db), m_state(&m_db), m_ourAddress(_coinbaseAddress), m_blockReward(c_blockReward) { - // Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly. - m_state.init(); + if (_bs != BaseState::PreExisting) + // Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly. + m_state.init(); - paranoia("beginning of normal construction.", true); + paranoia("beginning of Genesis construction.", true); if (_bs == BaseState::CanonGenesis) { dev::eth::commit(genesisState(), m_db, m_state); m_db.commit(); - paranoia("after DB commit of normal construction.", true); + paranoia("after DB commit of Genesis construction.", true); m_previousBlock = CanonBlockChain::genesis(); } else @@ -321,7 +322,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi) std::vector chain; while (bi.number != 0 && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block... { - chain.push_back(bi.hash); // push back for later replay. + chain.push_back(bi.hash()); // push back for later replay. bi.populate(_bc.block(bi.parentHash)); // move to parent. } @@ -537,18 +538,18 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) { // m_currentBlock is assumed to be prepopulated and reset. -#if !ETH_RELEASE BlockInfo bi(_block, _checkNonce ? CheckEverything : IgnoreNonce); - assert(m_previousBlock.hash == bi.parentHash); +#if !ETH_RELEASE + assert(m_previousBlock.hash() == bi.parentHash); assert(m_currentBlock.parentHash == bi.parentHash); assert(rootHash() == m_previousBlock.stateRoot); #endif - if (m_currentBlock.parentHash != m_previousBlock.hash) + if (m_currentBlock.parentHash != m_previousBlock.hash()) BOOST_THROW_EXCEPTION(InvalidParentHash()); // Populate m_currentBlock with the correct values. - m_currentBlock.populate(_block, _checkNonce ? CheckEverything : IgnoreNonce); + m_currentBlock = bi; m_currentBlock.verifyInternals(_block); // cnote << "playback begins:" << m_state.root(); @@ -598,7 +599,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) cwarn << TransactionReceipt(&b); } cwarn << "Recorded: " << m_currentBlock.receiptsRoot; - auto rs = _bc.receipts(m_currentBlock.hash); + auto rs = _bc.receipts(m_currentBlock.hash()); for (unsigned j = 0; j < rs.receipts.size(); ++j) { auto b = rs.receipts[j].rlp(); @@ -840,7 +841,7 @@ void State::commitToMine(BlockChain const& _bc) m_currentBlock.gasUsed = gasUsed(); m_currentBlock.stateRoot = m_state.root(); - m_currentBlock.parentHash = m_previousBlock.hash; + m_currentBlock.parentHash = m_previousBlock.hash(); } MineInfo State::mine(unsigned _msTimeout, bool _turbo) @@ -890,10 +891,10 @@ void State::completeMine() ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentUncles); ret.swapOut(m_currentBytes); - m_currentBlock.hash = sha3(RLP(m_currentBytes)[0].data()); - cnote << "Mined " << m_currentBlock.hash.abridged() << "(parent: " << m_currentBlock.parentHash.abridged() << ")"; + m_currentBlock.noteDirty(); + cnote << "Mined " << m_currentBlock.hash().abridged() << "(parent: " << m_currentBlock.parentHash.abridged() << ")"; StructuredLogger::minedNewBlock( - m_currentBlock.hash.abridged(), + m_currentBlock.hash().abridged(), m_currentBlock.nonce.abridged(), "", //TODO: chain head hash here ?? m_currentBlock.parentHash.abridged() diff --git a/libethereum/State.h b/libethereum/State.h index e64a6f0e3..662426e17 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -54,7 +54,12 @@ struct StateTrace: public LogChannel { static const char* name() { return "=S="; struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; }; struct StateSafeExceptions: public LogChannel { static const char* name() { return "(S)"; } static const int verbosity = 21; }; -enum class BaseState { Empty, CanonGenesis }; +enum class BaseState +{ + PreExisting, + Empty, + CanonGenesis +}; enum class TransactionPriority { @@ -103,8 +108,15 @@ class State friend class Executive; public: - /// Construct state object. - State(Address _coinbaseAddress = Address(), OverlayDB const& _db = OverlayDB(), BaseState _bs = BaseState::CanonGenesis); + /// Default constructor; creates with a blank database prepopulated with the genesis block. + State(): State(OverlayDB(), BaseState::Empty) {} + + /// Basic state object from database. + /// Use the default when you already have a database and you just want to make a State object + /// which uses it. If you have no preexisting database then set BaseState to something other + /// than BaseState::PreExisting in order to prepopulate the Trie. + /// You can also set the coinbase address. + explicit State(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address()); /// Construct state object from arbitrary point in blockchain. State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash); @@ -335,6 +347,7 @@ private: /// Debugging only. Good for checking the Trie is in shape. void paranoia(std::string const& _when, bool _enforceRefs = false) const; + OverlayDB m_db; ///< Our overlay for the state tree. SecureTrieDB m_state; ///< Our state tree, as an OverlayDB DB. Transactions m_transactions; ///< The current list of transactions that we've included in the state. diff --git a/libtestutils/StateLoader.cpp b/libtestutils/StateLoader.cpp index b56475b5a..464df0ec5 100644 --- a/libtestutils/StateLoader.cpp +++ b/libtestutils/StateLoader.cpp @@ -26,7 +26,7 @@ using namespace dev; using namespace dev::eth; using namespace dev::test; -StateLoader::StateLoader(Json::Value const& _json) : m_state(Address(), OverlayDB(), BaseState::Empty) +StateLoader::StateLoader(Json::Value const& _json) { for (string const& name: _json.getMemberNames()) { diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 6f95bd529..fa273a0da 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -59,7 +59,7 @@ static Json::Value toJson(dev::eth::BlockInfo const& _bi) Json::Value res; if (_bi) { - res["hash"] = toJS(_bi.hash); + res["hash"] = toJS(_bi.hash()); res["parentHash"] = toJS(_bi.parentHash); res["sha3Uncles"] = toJS(_bi.sha3Uncles); res["miner"] = toJS(_bi.coinbaseAddress); diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 767c2ac6f..08ef83e5e 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -469,7 +469,7 @@ RecordLogEntry* ClientModel::lastBlock() const strGas << blockInfo.gasUsed; std::stringstream strNumber; strNumber << blockInfo.number; - RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash.ref()))), tr("Gas Used: ") + QString::fromStdString(strGas.str()), QString(), QString(), false, RecordLogEntry::RecordType::Block); + RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash().ref()))), tr("Gas Used: ") + QString::fromStdString(strGas.str()), QString(), QString(), false, RecordLogEntry::RecordType::Block); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); return record; } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 7729c0ffe..cc200e62c 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -93,7 +93,7 @@ void MixClient::resetState(std::map _accounts) h256 stateRoot = accountState.root(); m_bc.reset(); m_bc.reset(new MixBlockChain(m_dbPath, stateRoot)); - m_state = eth::State(genesisState.begin()->first , m_stateDB, BaseState::Empty); + m_state = eth::State(m_stateDB, BaseState::PreExisting, genesisState.begin()->first); m_state.sync(bc()); m_startState = m_state; WriteGuard lx(x_executions); diff --git a/test/ClientBase.cpp b/test/ClientBase.cpp index 304182cfc..7597b6612 100644 --- a/test/ClientBase.cpp +++ b/test/ClientBase.cpp @@ -120,11 +120,15 @@ BOOST_AUTO_TEST_CASE(blocks) ETH_CHECK_EQUAL(expectedBlockInfoBloom, _blockInfo.logBloom); ETH_CHECK_EQUAL(expectedBlockInfoCoinbase, _blockInfo.coinbaseAddress); ETH_CHECK_EQUAL(expectedBlockInfoDifficulty, _blockInfo.difficulty); - ETH_CHECK_EQUAL_COLLECTIONS(expectedBlockInfoExtraData.begin(), expectedBlockInfoExtraData.end(), - _blockInfo.extraData.begin(), _blockInfo.extraData.end()); + ETH_CHECK_EQUAL_COLLECTIONS( + expectedBlockInfoExtraData.begin(), + expectedBlockInfoExtraData.end(), + _blockInfo.extraData.begin(), + _blockInfo.extraData.end() + ); ETH_CHECK_EQUAL(expectedBlockInfoGasLimit, _blockInfo.gasLimit); ETH_CHECK_EQUAL(expectedBlockInfoGasUsed, _blockInfo.gasUsed); - ETH_CHECK_EQUAL(expectedBlockInfoHash, _blockInfo.hash); + ETH_CHECK_EQUAL(expectedBlockInfoHash, _blockInfo.hash()); ETH_CHECK_EQUAL(expectedBlockInfoMixHash, _blockInfo.mixHash); ETH_CHECK_EQUAL(expectedBlockInfoNonce, _blockInfo.nonce); ETH_CHECK_EQUAL(expectedBlockInfoNumber, _blockInfo.number); @@ -155,8 +159,12 @@ BOOST_AUTO_TEST_CASE(blocks) u256 expectedTransactionSignatureS = h256(fromHex(_t["s"].asString())); // unsigned expectedTransactionSignatureV = jsToInt(t["v"].asString()); - ETH_CHECK_EQUAL_COLLECTIONS(expectedTransactionData.begin(), expectedTransactionData.end(), - _transaction.data().begin(), _transaction.data().end()); + ETH_CHECK_EQUAL_COLLECTIONS( + expectedTransactionData.begin(), + expectedTransactionData.end(), + _transaction.data().begin(), + _transaction.data().end() + ); ETH_CHECK_EQUAL(expectedTransactionGasLimit, _transaction.gas()); ETH_CHECK_EQUAL(expectedTransactionGasPrice, _transaction.gasPrice()); ETH_CHECK_EQUAL(expectedTransactionNonce, _transaction.nonce()); diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 295b759f3..140efdb99 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -70,7 +70,10 @@ namespace test struct ValueTooLarge: virtual Exception {}; bigint const c_max256plus1 = bigint(1) << 256; -ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller) : m_statePre(Address(_o["env"].get_obj()["currentCoinbase"].get_str()), OverlayDB(), eth::BaseState::Empty), m_statePost(Address(_o["env"].get_obj()["currentCoinbase"].get_str()), OverlayDB(), eth::BaseState::Empty), m_TestObject(_o) +ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller): + m_statePre(OverlayDB(), eth::BaseState::Empty, Address(_o["env"].get_obj()["currentCoinbase"].get_str())), + m_statePost(OverlayDB(), eth::BaseState::Empty, Address(_o["env"].get_obj()["currentCoinbase"].get_str())), + m_TestObject(_o) { importEnv(_o["env"].get_obj()); importState(_o["pre"].get_obj(), m_statePre); @@ -92,7 +95,7 @@ void ImportTest::importEnv(json_spirit::mObject& _o) assert(_o.count("currentCoinbase") > 0); assert(_o.count("currentNumber") > 0); - m_environment.previousBlock.hash = h256(_o["previousHash"].get_str()); + m_environment.currentBlock.parentHash = h256(_o["parentHash"].get_str()); m_environment.currentBlock.number = toInt(_o["currentNumber"]); m_environment.currentBlock.gasLimit = toInt(_o["currentGasLimit"]); m_environment.currentBlock.difficulty = toInt(_o["currentDifficulty"]); diff --git a/test/TestHelper.h b/test/TestHelper.h index e5f96f51d..7f6d73365 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -101,7 +101,7 @@ namespace test class ImportTest { public: - ImportTest(json_spirit::mObject& _o) : m_statePre(Address(), OverlayDB(), eth::BaseState::Empty), m_statePost(Address(), OverlayDB(), eth::BaseState::Empty), m_TestObject(_o) {} + ImportTest(json_spirit::mObject& _o): m_TestObject(_o) {} ImportTest(json_spirit::mObject& _o, bool isFiller); // imports void importEnv(json_spirit::mObject& _o); diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 21345abfd..50c17bdee 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -52,7 +52,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) BOOST_REQUIRE(o.count("pre")); ImportTest importer(o["pre"].get_obj()); - State state(biGenesisBlock.coinbaseAddress, OverlayDB(), BaseState::Empty); + State state(OverlayDB(), BaseState::Empty, biGenesisBlock.coinbaseAddress); importer.importState(o["pre"].get_obj(), state); o["pre"] = fillJsonWithState(state); state.commit(); @@ -599,7 +599,7 @@ void updatePoW(BlockInfo& _bi) ret = pow.mine(_bi, 10000, true, true); Ethash::assignResult(ret.second, _bi); } - _bi.hash = _bi.headerHash(WithNonce); + _bi.noteDirty(); } void writeBlockHeaderToJson(mObject& _o, BlockInfo const& _bi) @@ -619,7 +619,7 @@ void writeBlockHeaderToJson(mObject& _o, BlockInfo const& _bi) _o["extraData"] ="0x" + toHex(_bi.extraData); _o["mixHash"] = toString(_bi.mixHash); _o["nonce"] = toString(_bi.nonce); - _o["hash"] = toString(_bi.hash); + _o["hash"] = toString(_bi.hash()); } RLPStream createFullBlockFromHeader(BlockInfo const& _bi, bytes const& _txs, bytes const& _uncles) diff --git a/test/stateOriginal.cpp b/test/stateOriginal.cpp index 384d85344..572e84dcf 100644 --- a/test/stateOriginal.cpp +++ b/test/stateOriginal.cpp @@ -58,7 +58,7 @@ BOOST_AUTO_TEST_CASE(Complex) CanonBlockChain bc; cout << bc; - State s(myMiner.address(), stateDB); + State s(stateDB, BaseState::Empty, myMiner.address()); cout << s; // Sync up - this won't do much until we use the last state. diff --git a/test/vm.cpp b/test/vm.cpp index cffbaa64d..ff8903523 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -96,7 +96,7 @@ void FakeExtVM::push(mArray& a, u256 _v) mObject FakeExtVM::exportEnv() { mObject ret; - ret["previousHash"] = toString(previousBlock.hash); + ret["previousHash"] = toString(currentBlock.parentHash); push(ret, "currentDifficulty", currentBlock.difficulty); push(ret, "currentTimestamp", currentBlock.timestamp); ret["currentCoinbase"] = toString(currentBlock.coinbaseAddress); @@ -115,7 +115,7 @@ void FakeExtVM::importEnv(mObject& _o) assert(_o.count("currentCoinbase") > 0); assert(_o.count("currentNumber") > 0); - previousBlock.hash = h256(_o["previousHash"].get_str()); + currentBlock.parentHash = h256(_o["previousHash"].get_str()); currentBlock.number = toInt(_o["currentNumber"]); lastHashes = test::lastHashes(currentBlock.number); currentBlock.gasLimit = toInt(_o["currentGasLimit"]); From 55df43ff4ba3f13c10c342b48ed07b22ecc70ff3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 5 Apr 2015 22:09:02 +0200 Subject: [PATCH 36/45] Fast databases. --- libethcore/Common.cpp | 2 +- libethereum/BlockChain.cpp | 54 ++++++++++++++++++++++++++++++++++-- libethereum/BlockChain.h | 26 +++++++++++++++++ libethereum/Client.cpp | 2 +- libethereum/EthereumPeer.cpp | 7 ++++- 5 files changed, 85 insertions(+), 6 deletions(-) diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index 02e806905..572ade3e2 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -36,7 +36,7 @@ namespace eth const unsigned c_ethashVersion = c_ethashRevision; const unsigned c_protocolVersion = 60; const unsigned c_minorProtocolVersion = 0; -const unsigned c_databaseBaseVersion = 8; +const unsigned c_databaseBaseVersion = 9; #if ETH_FATDB const unsigned c_databaseVersionModifier = 1; #else diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index cad23171a..74aeb4fad 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -21,6 +21,9 @@ #include "BlockChain.h" +#if ETH_PROFILING_GPERF +#include +#endif #include #include #include @@ -65,7 +68,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) return _out; } -ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub) +ldb::Slice dev::eth::oldToSlice(h256 const& _h, unsigned _sub) { #if ALL_COMPILERS_ARE_CPP11_COMPLIANT static thread_local h256 h = _h ^ sha3(h256(u256(_sub))); @@ -79,6 +82,21 @@ ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub) #endif } +ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub) +{ +#if ALL_COMPILERS_ARE_CPP11_COMPLIANT + static thread_local h256 h = _h ^ sha3(h256(u256(_sub))); + return ldb::Slice((char const*)&h, 32); +#else + static boost::thread_specific_ptr> t_h; + if (!t_h.get()) + t_h.reset(new FixedHash<33>); + *t_h = FixedHash<33>(_h); + (*t_h)[32] = (uint8_t)_sub; + return (ldb::Slice)t_h->ref();//(char const*)t_h.get(), 32); +#endif +} + #if ETH_DEBUG static const chrono::system_clock::duration c_collectionDuration = chrono::seconds(15); static const unsigned c_collectionQueueSize = 2; @@ -148,7 +166,7 @@ void BlockChain::open(std::string const& _path, WithExisting _we) } } - if (!details(m_genesisHash)) + if (_we != WithExisting::Verify && !details(m_genesisHash)) { // Insert details of genesis block. m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {}); @@ -178,7 +196,6 @@ void BlockChain::close() m_blocks.clear(); } -#include #define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} void BlockChain::rebuild(std::string const& _path, std::function const& _progress) @@ -187,6 +204,7 @@ void BlockChain::rebuild(std::string const& _path, std::functionsecond; + } + + string d; + m_blocksDB->Get(m_readOptions, oldToSlice(_hash), &d); + + if (!d.size()) + { + cwarn << "Couldn't find requested block:" << _hash.abridged(); + return bytes(); + } + + WriteGuard l(x_blocks); + m_blocks[_hash].resize(d.size()); + memcpy(m_blocks[_hash].data(), d.data(), d.size()); + + noteUsed(_hash); + + return m_blocks[_hash]; +} diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 2e2d8cf34..83b1926e8 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -64,6 +64,7 @@ struct BlockChainWarn: public LogChannel { static const char* name() { return "= std::map const& genesisState(); ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); +ldb::Slice oldToSlice(h256 const& _h, unsigned _sub = 0); using BlocksHash = std::map; using TransactionHashes = h256s; @@ -119,6 +120,7 @@ public: /// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe. bytes block(h256 const& _hash) const; bytes block() const { return block(currentHash()); } + bytes oldBlock(h256 const& _hash) const; /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. BlockDetails details(h256 const& _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); } @@ -268,6 +270,30 @@ private: return ret.first->second; } + template T oldQueryExtras(h256 const& _h, std::map& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const + { + { + ReadGuard l(_x); + auto it = _m.find(_h); + if (it != _m.end()) + return it->second; + } + + std::string s; + (_extrasDB ? _extrasDB : m_extrasDB)->Get(m_readOptions, oldToSlice(_h, N), &s); + if (s.empty()) + { +// cout << "Not found in DB: " << _h << endl; + return _n; + } + + noteUsed(_h, N); + + WriteGuard l(_x); + auto ret = _m.insert(std::make_pair(_h, T(RLP(s)))); + return ret.first->second; + } + void checkConsistency(); /// The caches of the disk DB and their locks. diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 95fd2364f..71a9a4dbe 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -123,7 +123,7 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), m_gp(new TrivialGasPricer), m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))), - m_preMine(m_stateDB), + m_preMine(m_stateDB, BaseState::CanonGenesis), m_postMine(m_stateDB) { m_gp->update(m_bc); diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 15ce5c3f6..ca0195efe 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -478,7 +478,12 @@ 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."; if (m_asking == Asking::Blocks) - transition(Asking::Blocks); + { + if (!got) + transition(Asking::Blocks); + else + transition(Asking::Nothing); + } break; } case NewBlockPacket: From 747927553c331beaf5a63ef857395bd624a0d0f6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 5 Apr 2015 22:13:24 +0200 Subject: [PATCH 37/45] Typo "fix" fixed. --- test/TestHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 140efdb99..dd7c09eab 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -95,7 +95,7 @@ void ImportTest::importEnv(json_spirit::mObject& _o) assert(_o.count("currentCoinbase") > 0); assert(_o.count("currentNumber") > 0); - m_environment.currentBlock.parentHash = h256(_o["parentHash"].get_str()); + m_environment.currentBlock.parentHash = h256(_o["previousHash"].get_str()); m_environment.currentBlock.number = toInt(_o["currentNumber"]); m_environment.currentBlock.gasLimit = toInt(_o["currentGasLimit"]); m_environment.currentBlock.difficulty = toInt(_o["currentDifficulty"]); From 96d764e0fd6381e6ea3a2fa0cac3a570f09d7403 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Apr 2015 01:55:52 +0200 Subject: [PATCH 38/45] Linker fix. --- CMakeLists.txt | 21 +++++++++++++-------- libweb3jsonrpc/CMakeLists.txt | 14 ++++++++------ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c02948a7..17d5de3ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ function(createDefaultCacheConfig) set(JUSTTESTS OFF CACHE BOOL "Build only for tests.") set(SOLIDITY ON CACHE BOOL "Build the Solidity language components (required unless HEADLESS)") set(SERPENT ${SERPENT_DEFAULT} CACHE BOOL "Build the Serpent language components (required unless HEADLESS)") + set(SDK ON CACHE BOOL "Build the SDK components") set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed") set(ETHASHCL OFF CACHE BOOL "Build in support for GPU mining via OpenCL") set(PROFILING OFF CACHE BOOL "Build in support for profiling") @@ -63,7 +64,6 @@ function(configureProject) if (HEADLESS OR JUSTTESTS) add_definitions(-DETH_HEADLESS) endif() - endfunction() set(CPPETHEREUM 1) @@ -229,6 +229,7 @@ message("-- Build only headless components HEADLESS ${HEADLESS message("-- Build only tests JUSTTESTS ${JUSTTESTS}") message("-- Build Solidity language components SOLIDITY ${SOLIDITY}") message("-- Build Serpent language components SERPENT ${SERPENT}") +message("-- Build SDK components SDK ${SDK}") message("-- Build OpenCL components ETHASHCL ${ETHASHCL}") message("-- Build LLVM-based JIT EVM EVMJIT ${EVMJIT}") message("-- Build with support for JSON-RPC JSONRPC ${JSONRPC}") @@ -260,13 +261,15 @@ add_subdirectory(libdevcore) add_subdirectory(libevmcore) add_subdirectory(liblll) -if (SERPENT) - add_subdirectory(libserpent) - add_subdirectory(sc) -endif () +if (SDK) + if (SERPENT) + add_subdirectory(libserpent) + add_subdirectory(sc) + endif () -if (SOLIDITY) - add_subdirectory(libsolidity) + if (SOLIDITY) + add_subdirectory(libsolidity) + endif () endif () if (NOT JUSTTESTS) @@ -324,7 +327,9 @@ if (NOT JUSTTESTS) # add_subdirectory(third) // reenable once not qtwebkit. endif() - add_subdirectory(mix) + if (SDK) + add_subdirectory(mix) + endif () endif() diff --git a/libweb3jsonrpc/CMakeLists.txt b/libweb3jsonrpc/CMakeLists.txt index d3f4b70b6..7b47a3f47 100644 --- a/libweb3jsonrpc/CMakeLists.txt +++ b/libweb3jsonrpc/CMakeLists.txt @@ -22,9 +22,9 @@ file(GLOB HEADERS "*.h") if (ETH_STATIC) add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) -else() +else () add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS}) -endif() +endif () target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) @@ -33,11 +33,13 @@ target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES}) target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} secp256k1) -target_link_libraries(${EXECUTABLE} solidity) -if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) +if (SOLIDITY) + target_link_libraries(${EXECUTABLE} solidity) +endif () +if (SERPENT) target_link_libraries(${EXECUTABLE} serpent) -endif() +endif () if (ETH_JSON_RPC_STUB) add_custom_target(jsonrpcstub) @@ -51,7 +53,7 @@ if (ETH_JSON_RPC_STUB) -P "${ETH_SCRIPTS_DIR}/jsonrpcstub.cmake" ) add_dependencies(${EXECUTABLE} jsonrpcstub) -endif() +endif () install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) From e67f7dcf95c3c03b74fe305702d91f3a9de80474 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Apr 2015 02:00:36 +0200 Subject: [PATCH 39/45] Language-disable fix. --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index fa273a0da..94c5cbd03 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -26,9 +26,11 @@ #include #include +#if ETH_SOLIDITY #include #include #include +#endif #include #include #include @@ -36,7 +38,7 @@ #include #include #include -#ifndef _MSC_VER +#if ETH_SERPENT #include #endif #include "WebThreeStubServerBase.h" @@ -620,8 +622,10 @@ Json::Value WebThreeStubServerBase::eth_getCompilers() { Json::Value ret(Json::arrayValue); ret.append("lll"); +#if SOLIDITY ret.append("solidity"); -#ifndef _MSC_VER +#endif +#if SERPENT ret.append("serpent"); #endif return ret; @@ -642,7 +646,7 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _code) { // TODO throw here jsonrpc errors string res; -#ifndef _MSC_VER +#if SERPENT try { res = toJS(dev::asBytes(::compile(_code))); @@ -663,6 +667,7 @@ string WebThreeStubServerBase::eth_compileSolidity(string const& _code) { // TOOD throw here jsonrpc errors string res; +#if SOLIDITY dev::solidity::CompilerStack compiler; try { @@ -678,6 +683,7 @@ string WebThreeStubServerBase::eth_compileSolidity(string const& _code) { cwarn << "Uncought solidity compilation exception"; } +#endif return res; } From f3a98d00de41a6fbf5d232ed6400eb16f5cd583c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Apr 2015 02:01:20 +0200 Subject: [PATCH 40/45] Warning fix. --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 94c5cbd03..da0108b3e 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -646,6 +646,7 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _code) { // TODO throw here jsonrpc errors string res; + (void)_code; #if SERPENT try { @@ -666,6 +667,7 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _code) string WebThreeStubServerBase::eth_compileSolidity(string const& _code) { // TOOD throw here jsonrpc errors + (void)_code; string res; #if SOLIDITY dev::solidity::CompilerStack compiler; From 4120e83bb6b8c89dc9ca1e3592ecab9bdaf7c367 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Apr 2015 02:30:50 +0200 Subject: [PATCH 41/45] Nicer build options. --- CMakeLists.txt | 187 ++++++++++++++++++++++++++++---------------- test/CMakeLists.txt | 2 +- test/natspec.cpp | 2 +- 3 files changed, 120 insertions(+), 71 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17d5de3ad..d317b77bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,23 +15,26 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # Normally, set(...CACHE...) creates cache variables, but does not modify them. function(createDefaultCacheConfig) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(SERPENT_DEFAULT OFF) + set(DECENT_PLATFORM OFF) else () - set(SERPENT_DEFAULT ON) + set(DECENT_PLATFORM ON) endif () - set(HEADLESS OFF CACHE BOOL "Do not compile GUI (AlethZero)") set(VMTRACE OFF CACHE BOOL "VM tracing and run-time checks (useful for cross-implementation VM debugging)") set(PARANOIA OFF CACHE BOOL "Additional run-time checks") set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on") - set(EVMJIT OFF CACHE BOOL "Build a just-in-time compiler for EVM code (requires LLVM)") set(FATDB OFF CACHE BOOL "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents.") - set(JUSTTESTS OFF CACHE BOOL "Build only for tests.") - set(SOLIDITY ON CACHE BOOL "Build the Solidity language components (required unless HEADLESS)") - set(SERPENT ${SERPENT_DEFAULT} CACHE BOOL "Build the Serpent language components (required unless HEADLESS)") - set(SDK ON CACHE BOOL "Build the SDK components") set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed") - set(ETHASHCL OFF CACHE BOOL "Build in support for GPU mining via OpenCL") set(PROFILING OFF CACHE BOOL "Build in support for profiling") + + set(BUNDLE "full" CACHE STRING "Predefined bundle of software to build.") + set(SOLIDITY ON CACHE BOOL "Build the Solidity language components") + set(SERPENT ${DECENT_PLATFORM} CACHE BOOL "Build the Serpent language components") + set(TOOLS ON CACHE BOOL "Build the tools components") + set(NCURSES ${DECENT_PLATFORM} CACHE BOOL "Build the NCurses components") + set(GUI ON CACHE BOOL "Build GUI components (AlethZero, Mix)") + set(TESTS ON CACHE BOOL "Build the tests.") + set(EVMJIT OFF CACHE BOOL "Build just-in-time compiler for EVM code (requires LLVM)") + set(ETHASHCL OFF CACHE BOOL "Build in support for GPU mining via OpenCL") endfunction() @@ -61,8 +64,8 @@ function(configureProject) add_definitions(-DETH_SOLIDITY) endif() - if (HEADLESS OR JUSTTESTS) - add_definitions(-DETH_HEADLESS) + if (GUI) + add_definitions(-DETH_GUI) endif() endfunction() @@ -153,11 +156,6 @@ if (VMTRACE) else () set(VMTRACE OFF) endif () -if (ETHASHCL) - set(ETHASHCL ON) -else () - set(ETHASHCL OFF) -endif() if (EVMJIT) set(EVMJIT ON) else () @@ -168,6 +166,23 @@ if (FATDB) else () set(FATDB OFF) endif() +if (JSONRPC) + set(JSONRPC ON) +else () + set(JSONRPC OFF) +endif () +if (USENPM) + set(USENPM ON) +else () + set(USENPM OFF) +endif () +if (PROFILING) + set(PROFILING ON) +else () + set(PROFILING OFF) +endif () + + if (SOLIDITY) set(SOLIDITY ON) else () @@ -178,34 +193,66 @@ if (SERPENT) else () set(SERPENT OFF) endif() -if (HEADLESS) - set(HEADLESS ON) +if (GUI) + set(GUI ON) else () - set(HEADLESS OFF) + set(GUI OFF) endif () -if (JUSTTESTS) - set(JUSTTESTS ON) +if (TESTS) + set(TESTS ON) else () - set(JUSTTESTS OFF) + set(TESTS OFF) endif () -if (JSONRPC) - set(JSONRPC ON) +if (TOOLS) + set(TOOLS ON) else () - set(JSONRPC OFF) + set(TOOLS OFF) endif () -if (USENPM) - set(USENPM ON) +if (ETHASHCL) + set(ETHASHCL ON) else () - set(USENPM OFF) -endif () -if (PROFILING) - set(PROFILING ON) + set(ETHASHCL OFF) +endif() +if (NCURSES) + set(NCURSES ON) else () - set(PROFILING OFF) + set(NCURSES OFF) +endif () + +if (BUNDLE STREQUAL "minimal") + set(SERPENT OFF) + set(SOLIDITY OFF) + set(USENPM OFF) + set(GUI OFF) + set(TESTS OFF) + set(TOOLS ON) +elseif (BUNDLE STREQUAL "full") + set(SERPENT ON) + set(SOLIDITY ON) + set(USENPM ON) + set(GUI ON) + set(TESTS ON) + set(TOOLS ON) + set(FATDB ON) +elseif (BUNDLE STREQUAL "tests") + set(SERPENT ON) + set(SOLIDITY ON) + set(USENPM OFF) + set(GUI OFF) + set(TESTS ON) + set(TOOLS OFF) + set(FATDB ON) +elseif (BUNDLE STREQUAL "user") + set(SERPENT OFF) + set(SOLIDITY OFF) + set(USENPM OFF) + set(GUI ON) + set(TESTS OFF) + set(TOOLS ON) endif () # Default CMAKE_BUILD_TYPE to "Release". -set(CMAKE_BUILD_TYPE CACHE STRING "Relase") +set(CMAKE_BUILD_TYPE CACHE STRING "Release") if ("x${CMAKE_BUILD_TYPE}" STREQUAL "x") set(CMAKE_BUILD_TYPE "Release") endif () @@ -220,20 +267,23 @@ message("----------------------------------------------------------------------- message("-- CMake Version ${CMAKE_VERSION}") message("-- Build type CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}") message("-- Target platform TARGET_PLATFORM ${TARGET_PLATFORM}") +message("-- Build bundle BUNDLE ${BUNDLE}") +message("------------------------------------------------------------------------ features") +message("-- Chromium support [auto-detected] ${ETH_HAVE_WEBENGINE}") message("-- VM execution tracing VMTRACE ${VMTRACE}") message("-- Profiling support PROFILING ${PROFILING}") message("-- Additional (SLOW) database checking PARANOIA ${PARANOIA}") message("-- Full database exploring FATDB ${FATDB}") -message("-- Build Javascript components from source USENPM ${USENPM}") -message("-- Build only headless components HEADLESS ${HEADLESS}") -message("-- Build only tests JUSTTESTS ${JUSTTESTS}") +message("-- JSON-RPC support JSONRPC ${JSONRPC}") +message("-- Javascript source building USENPM ${USENPM}") +message("------------------------------------------------------------------------ components") +message("-- Build GUI components GUI ${GUI}") +message("-- Build NCurses components NCURSES ${NCURSES}") +message("-- Build tests TESTS ${TESTS}") message("-- Build Solidity language components SOLIDITY ${SOLIDITY}") message("-- Build Serpent language components SERPENT ${SERPENT}") -message("-- Build SDK components SDK ${SDK}") -message("-- Build OpenCL components ETHASHCL ${ETHASHCL}") -message("-- Build LLVM-based JIT EVM EVMJIT ${EVMJIT}") -message("-- Build with support for JSON-RPC JSONRPC ${JSONRPC}") -message("-- Build with support for Chromium ${ETH_HAVE_WEBENGINE}") +message("-- Build OpenCL components (experimental!) ETHASHCL ${ETHASHCL}") +message("-- Build LLVM-based JIT EVM (experimental!) EVMJIT ${EVMJIT}") message("------------------------------------------------------------------------") message("") @@ -261,18 +311,16 @@ add_subdirectory(libdevcore) add_subdirectory(libevmcore) add_subdirectory(liblll) -if (SDK) - if (SERPENT) - add_subdirectory(libserpent) - add_subdirectory(sc) - endif () +if (SERPENT) + add_subdirectory(libserpent) + add_subdirectory(sc) +endif () - if (SOLIDITY) - add_subdirectory(libsolidity) - endif () +if (SOLIDITY) + add_subdirectory(libsolidity) endif () -if (NOT JUSTTESTS) +if (TOOLS) add_subdirectory(lllc) if (SOLIDITY) add_subdirectory(solc) @@ -297,12 +345,14 @@ endif () add_subdirectory(libethcore) add_subdirectory(libevm) add_subdirectory(libethereum) - add_subdirectory(libwebthree) -add_subdirectory(libtestutils) -add_subdirectory(test) -if (NOT JUSTTESTS) +if (TESTS) + add_subdirectory(libtestutils) + add_subdirectory(test) +endif () + +if (TOOLS) add_subdirectory(rlp) add_subdirectory(abi) @@ -312,27 +362,26 @@ if (NOT JUSTTESTS) add_subdirectory(exp) endif () - # TODO check msvc - if(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) - add_subdirectory(neth) - endif () - - if (NOT HEADLESS) +endif() - add_subdirectory(libnatspec) - add_subdirectory(libjsqrc) +if (NCURSES) + add_subdirectory(neth) +endif () - if (ETH_HAVE_WEBENGINE) - add_subdirectory(alethzero) -# add_subdirectory(third) // reenable once not qtwebkit. - endif() +if (GUI) - if (SDK) - add_subdirectory(mix) - endif () + add_subdirectory(libnatspec) + add_subdirectory(libjsqrc) + if (ETH_HAVE_WEBENGINE) + add_subdirectory(alethzero) +# add_subdirectory(third) // reenable once not qtwebkit. endif() + if (SOLIDITY) + add_subdirectory(mix) + endif () + endif() #unset(TARGET_PLATFORM CACHE) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5bd44091d..90af5122e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -48,7 +48,7 @@ if (SOLIDITY) target_link_libraries(testeth solidity) endif () target_link_libraries(testeth testutils) -if (NOT HEADLESS AND NOT JUSTTESTS) +if (GUI AND NOT JUSTTESTS) target_link_libraries(testeth webthree) target_link_libraries(testeth natspec) endif() diff --git a/test/natspec.cpp b/test/natspec.cpp index cdcedca46..56478f93e 100644 --- a/test/natspec.cpp +++ b/test/natspec.cpp @@ -19,7 +19,7 @@ * @date 2015 */ -#if !ETH_HEADLESS +#if ETH_GUI #include #include From e97072c24f3a200e3907b80574c900df07e0f122 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Apr 2015 02:43:39 +0200 Subject: [PATCH 42/45] Much nicer build presets. --- CMakeLists.txt | 74 +++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d317b77bf..17c8a620a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,11 +14,6 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # user defined, defaults # Normally, set(...CACHE...) creates cache variables, but does not modify them. function(createDefaultCacheConfig) - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(DECENT_PLATFORM OFF) - else () - set(DECENT_PLATFORM ON) - endif () set(VMTRACE OFF CACHE BOOL "VM tracing and run-time checks (useful for cross-implementation VM debugging)") set(PARANOIA OFF CACHE BOOL "Additional run-time checks") set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on") @@ -26,11 +21,11 @@ function(createDefaultCacheConfig) set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed") set(PROFILING OFF CACHE BOOL "Build in support for profiling") - set(BUNDLE "full" CACHE STRING "Predefined bundle of software to build.") + set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).") set(SOLIDITY ON CACHE BOOL "Build the Solidity language components") - set(SERPENT ${DECENT_PLATFORM} CACHE BOOL "Build the Serpent language components") + set(SERPENT ON CACHE BOOL "Build the Serpent language components") set(TOOLS ON CACHE BOOL "Build the tools components") - set(NCURSES ${DECENT_PLATFORM} CACHE BOOL "Build the NCurses components") + set(NCURSES ON CACHE BOOL "Build the NCurses components") set(GUI ON CACHE BOOL "Build GUI components (AlethZero, Mix)") set(TESTS ON CACHE BOOL "Build the tests.") set(EVMJIT OFF CACHE BOOL "Build just-in-time compiler for EVM code (requires LLVM)") @@ -146,6 +141,12 @@ set (ETH_HAVE_WEBENGINE 1) # Normalise build options # TODO: Abstract into something sensible and move into a function. +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(DECENT_PLATFORM OFF) +else () + set(DECENT_PLATFORM ON) +endif () + if (PARANOIA) set(PARANOIA ON) else () @@ -224,31 +225,35 @@ if (BUNDLE STREQUAL "minimal") set(SOLIDITY OFF) set(USENPM OFF) set(GUI OFF) - set(TESTS OFF) + set(NCURSES OFF) set(TOOLS ON) + set(TESTS OFF) elseif (BUNDLE STREQUAL "full") set(SERPENT ON) set(SOLIDITY ON) set(USENPM ON) set(GUI ON) - set(TESTS ON) + set(NCURSES ${DECENT_PLATFORM}) set(TOOLS ON) + set(TESTS ON) set(FATDB ON) elseif (BUNDLE STREQUAL "tests") set(SERPENT ON) set(SOLIDITY ON) set(USENPM OFF) set(GUI OFF) - set(TESTS ON) + set(NCURSES OFF) set(TOOLS OFF) + set(TESTS ON) set(FATDB ON) elseif (BUNDLE STREQUAL "user") set(SERPENT OFF) set(SOLIDITY OFF) set(USENPM OFF) set(GUI ON) - set(TESTS OFF) + set(NCURSES ${DECENT_PLATFORM}) set(TOOLS ON) + set(TESTS OFF) endif () # Default CMAKE_BUILD_TYPE to "Release". @@ -263,28 +268,29 @@ if ("x${TARGET_PLATFORM}" STREQUAL "x") set(TARGET_PLATFORM "linux") endif () -message("------------------------------------------------------------------------") -message("-- CMake Version ${CMAKE_VERSION}") -message("-- Build type CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}") -message("-- Target platform TARGET_PLATFORM ${TARGET_PLATFORM}") -message("-- Build bundle BUNDLE ${BUNDLE}") -message("------------------------------------------------------------------------ features") -message("-- Chromium support [auto-detected] ${ETH_HAVE_WEBENGINE}") -message("-- VM execution tracing VMTRACE ${VMTRACE}") -message("-- Profiling support PROFILING ${PROFILING}") -message("-- Additional (SLOW) database checking PARANOIA ${PARANOIA}") -message("-- Full database exploring FATDB ${FATDB}") -message("-- JSON-RPC support JSONRPC ${JSONRPC}") -message("-- Javascript source building USENPM ${USENPM}") -message("------------------------------------------------------------------------ components") -message("-- Build GUI components GUI ${GUI}") -message("-- Build NCurses components NCURSES ${NCURSES}") -message("-- Build tests TESTS ${TESTS}") -message("-- Build Solidity language components SOLIDITY ${SOLIDITY}") -message("-- Build Serpent language components SERPENT ${SERPENT}") -message("-- Build OpenCL components (experimental!) ETHASHCL ${ETHASHCL}") -message("-- Build LLVM-based JIT EVM (experimental!) EVMJIT ${EVMJIT}") -message("------------------------------------------------------------------------") +message("-----------------------------------------------------------------------------") +message("-- CMake Version ${CMAKE_VERSION}") +message("-- CMAKE_BUILD_TYPE Build type ${CMAKE_BUILD_TYPE}") +message("-- TARGET_PLATFORM Target platform ${TARGET_PLATFORM}") +message("-- BUNDLE Build bundle ${BUNDLE}") +message("-------------------------------------------------------------------- features") +message("-- Chromium support ${ETH_HAVE_WEBENGINE}") +message("-- VMTRACE VM execution tracing ${VMTRACE}") +message("-- PROFILING Profiling support ${PROFILING}") +message("-- PARANOIA Additional (SLOW) database checking ${PARANOIA}") +message("-- FATDB Full database exploring ${FATDB}") +message("-- JSONRPC JSON-RPC support ${JSONRPC}") +message("-- USENPM Javascript source building ${USENPM}") +message("------------------------------------------------------------------ components") +message("-- TOOLS Build basic tools ${TOOLS}") +message("-- SOLIDITY Build Solidity language components ${SOLIDITY}") +message("-- SERPENT Build Serpent language components ${SERPENT}") +message("-- GUI Build GUI components ${GUI}") +message("-- NCURSES Build NCurses components ${NCURSES}") +message("-- TESTS Build tests ${TESTS}") +message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}") +message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}") +message("-----------------------------------------------------------------------------") message("") From e3a3b5a07b304eb236f0e720db1a62be59016483 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Apr 2015 02:45:11 +0200 Subject: [PATCH 43/45] Formatting fix. --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17c8a620a..ec7e1c091 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,12 +268,12 @@ if ("x${TARGET_PLATFORM}" STREQUAL "x") set(TARGET_PLATFORM "linux") endif () -message("-----------------------------------------------------------------------------") +message("------------------------------------------------------------------------") message("-- CMake Version ${CMAKE_VERSION}") message("-- CMAKE_BUILD_TYPE Build type ${CMAKE_BUILD_TYPE}") message("-- TARGET_PLATFORM Target platform ${TARGET_PLATFORM}") message("-- BUNDLE Build bundle ${BUNDLE}") -message("-------------------------------------------------------------------- features") +message("--------------------------------------------------------------- features") message("-- Chromium support ${ETH_HAVE_WEBENGINE}") message("-- VMTRACE VM execution tracing ${VMTRACE}") message("-- PROFILING Profiling support ${PROFILING}") @@ -281,7 +281,7 @@ message("-- PARANOIA Additional (SLOW) database checking ${PARANOIA message("-- FATDB Full database exploring ${FATDB}") message("-- JSONRPC JSON-RPC support ${JSONRPC}") message("-- USENPM Javascript source building ${USENPM}") -message("------------------------------------------------------------------ components") +message("------------------------------------------------------------- components") message("-- TOOLS Build basic tools ${TOOLS}") message("-- SOLIDITY Build Solidity language components ${SOLIDITY}") message("-- SERPENT Build Serpent language components ${SERPENT}") @@ -290,7 +290,7 @@ message("-- NCURSES Build NCurses components ${NCURSES} message("-- TESTS Build tests ${TESTS}") message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}") message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}") -message("-----------------------------------------------------------------------------") +message("------------------------------------------------------------------------") message("") From d30f62f9e57aaab1afb47b5b715e306d182a2a00 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Apr 2015 02:45:46 +0200 Subject: [PATCH 44/45] Version bump. --- libdevcore/Common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index c8a6a286d..883914ea6 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.9.3"; +char const* Version = "0.9.4"; } From 5007d27579f1e7ad46fe517fcd8e3e52be5d87ff Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Apr 2015 02:49:19 +0200 Subject: [PATCH 45/45] rpc test build fix. --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec7e1c091..8937c29d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -335,7 +335,6 @@ endif() if (JSONRPC) add_subdirectory(libweb3jsonrpc) - add_subdirectory(ethrpctest) endif() add_subdirectory(secp256k1) @@ -356,6 +355,9 @@ add_subdirectory(libwebthree) if (TESTS) add_subdirectory(libtestutils) add_subdirectory(test) + if (JSONRPC) + add_subdirectory(ethrpctest) + endif () endif () if (TOOLS)