diff --git a/CMakeLists.txt b/CMakeLists.txt index 682ba5a14..0cd7a80c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,7 +181,6 @@ if (NOT HEADLESS) add_subdirectory(libnatspec) add_subdirectory(libjsqrc) - add_subdirectory(libqwebthree) add_subdirectory(alethzero) add_subdirectory(third) add_subdirectory(mix) diff --git a/CodingStandards.txt b/CodingStandards.txt index 8acea78ae..e1a1b3ba9 100644 --- a/CodingStandards.txt +++ b/CodingStandards.txt @@ -1,8 +1,11 @@ 0. Formatting +GOLDEN RULE: Never *ever* use spaces for formatting. + a. Use tabs for indentation! - tab stops are every 4 characters. - One indentation level -> exactly one byte (i.e. a tab character) in the source file. +- Never use spaces to line up sequential lines: If you have run-on lines, indent as you would for a block. b. Line widths: - Don't worry about having lines of code > 80-char wide. - Lines of comments should be formatted according to ease of viewing, but simplicity is to be prefered over beauty. @@ -17,10 +20,22 @@ j. Braces, when used, always have their own lines and are at same indentation le (WRONG) if( a==b[ i ] ) { printf ("Hello\n"); } +foo->bar(someLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName); (RIGHT) if (a == b[i]) - printf("Hello\n"); // NOTE spaces used instead of tab here for clarify - first byte should be '\t'. + printf("Hello\n"); // NOTE spaces used instead of tab here for clarity - first byte should be '\t'. +foo->bar( + someLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName +); @@ -57,6 +72,8 @@ e. Split complex macro on multiple lines with '\'. 3. Capitalization; +GOLDEN RULE: Preprocessor: ALL_CAPS; C++: camelCase. + a. Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions. b. The following entities' first alpha is upper case: - Type names. @@ -65,6 +82,7 @@ b. The following entities' first alpha is upper case: - static const variables that form an external API. c. All preprocessor symbols (macros, macro argments) in full uppercase with underscore word separation. + All other entities' first alpha is lower case. @@ -98,7 +116,7 @@ g. Never use a macro where adequate non-preprocessor C++ can be written. h. Prefer "using NewType = OldType" to "typedef OldType NewType". i. Make use of auto whenever type is clear or unimportant: - Always avoid doubly-stating the type. -- Use to avoid vast and unimportant type declarations +- Use to avoid vast and unimportant type declarations. (WRONG) @@ -122,6 +140,7 @@ for (auto i = x.begin(); i != x.end(); ++i) {} 7. Structs & classes a. Structs to be used when all members public and no virtual functions. +- In this case, members should be named naturally and not prefixed with 'm_' b. Classes to be used in all other circumstances. @@ -131,7 +150,7 @@ b. Classes to be used in all other circumstances. a. One member per line only. b. Private, non-static, non-const fields prefixed with m_. c. Avoid public fields, except in structs. -d. Use override, final and const judiciously. +d. Use override, final and const as much as possible. e. No implementations with the class declaration, except: - template or force-inline method (though prefer implementation at bottom of header file). - one-line implementation (in which case include it in same line as declaration). @@ -154,14 +173,18 @@ c. Avoid unpronouncable names; - If you need to shorten a name favour a pronouncable slice of the original to a scatterred set of consonants. - e.g. Manager shortens to Man rather than Mgr. d. Avoid prefixes of initials (e.g. DON'T use IMyInterface, CMyImplementation) -e. A dictionary and thesaurus are your friends. +e. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments. +- A dictionary and thesaurus are your friends. - Spell correctly. -- Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments. +- Think carefully about the class's purpose. +- Imagine it as an isolated component to try to decontextualise it when considering its name. +- Don't be trapped into naming it (purely) in terms of its implementation. + 10. Type-definitions -a. Prefer using to typedef. e.g. using ints = std::vector; rather than typedef std::vector ints; +a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector; rather than typedef std::vector ints; b. Generally avoid shortening a standard form that already includes all important information: - e.g. stick to shared_ptr rather than shortening to ptr. c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently. @@ -169,9 +192,17 @@ c. Where there are exceptions to this (due to excessive use and clear meaning), d. In general expressions should be roughly as important/semantically meaningful as the space they occupy. + 11. Commenting a. Comments should be doxygen-compilable, using @notation rather than \notation. +b. Document the interface, not the implementation. +- Documentation should be able to remain completely unchanged, even if the method is reimplemented. +- Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports). +- Be careful to scrutinise documentation that extends only to intended purpose and usage. +- Reject documentation that is simply an English transaction of the implementation. + + 12. Include Headers diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index c18a76c36..9e41260fe 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -15,6 +15,8 @@ include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) qt5_wrap_ui(ui_Main.h Main.ui) +qt5_wrap_ui(ui_Debugger.h Debugger.ui) +qt5_wrap_ui(ui_Transact.h Transact.ui) file(GLOB HEADERS "*.h") @@ -27,15 +29,17 @@ endif () # eth_add_executable is defined in cmake/EthExecutableHelper.cmake eth_add_executable(${EXECUTABLE} ICON alethzero - UI_RESOURCES alethzero.icns Main.ui + UI_RESOURCES alethzero.icns Main.ui Debugger.ui Transact.ui WIN_RESOURCES alethzero.rc ) add_dependencies(${EXECUTABLE} BuildInfo.h) target_link_libraries(${EXECUTABLE} Qt5::Core) +target_link_libraries(${EXECUTABLE} Qt5::Widgets) +target_link_libraries(${EXECUTABLE} Qt5::WebKit) +target_link_libraries(${EXECUTABLE} Qt5::WebKitWidgets) target_link_libraries(${EXECUTABLE} webthree) -target_link_libraries(${EXECUTABLE} qwebthree) target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) diff --git a/alethzero/Context.cpp b/alethzero/Context.cpp new file mode 100644 index 000000000..d187e6d47 --- /dev/null +++ b/alethzero/Context.cpp @@ -0,0 +1,59 @@ +/* + 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 Debugger.h + * @author Gav Wood + * @date 2015 + */ + +#include "Context.h" +#include +#include +using namespace std; +using namespace dev; +using namespace dev::eth; + +NatSpecFace::~NatSpecFace() +{ +} + +Context::~Context() +{ +} + +void initUnits(QComboBox* _b) +{ + for (auto n = (unsigned)units().size(); n-- != 0; ) + _b->addItem(QString::fromStdString(units()[n].second), n); +} + +vector keysAsVector(QList const& keys) +{ + auto list = keys.toStdList(); + return {begin(list), end(list)}; +} + +bool sourceIsSolidity(string const& _source) +{ + // TODO: Improve this heuristic + return (_source.substr(0, 8) == "contract" || _source.substr(0, 5) == "//sol"); +} + +bool sourceIsSerpent(string const& _source) +{ + // TODO: Improve this heuristic + return (_source.substr(0, 5) == "//ser"); +} diff --git a/alethzero/Context.h b/alethzero/Context.h new file mode 100644 index 000000000..a2fb1a130 --- /dev/null +++ b/alethzero/Context.h @@ -0,0 +1,68 @@ +/* + 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 Debugger.h + * @author Gav Wood + * @date 2015 + */ + +#pragma once + +#include +#include +#include +#include +#include + +class QComboBox; + +namespace dev { namespace eth { class StateDiff; } } + +#define Small "font-size: small; " +#define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; " +#define Div(S) "
" +#define Span(S) "" + +void initUnits(QComboBox* _b); + +std::vector keysAsVector(QList const& _keys); + +bool sourceIsSolidity(std::string const& _source); +bool sourceIsSerpent(std::string const& _source); + +class NatSpecFace +{ +public: + virtual ~NatSpecFace(); + + virtual void add(dev::h256 const& _contractHash, std::string const& _doc) = 0; + virtual std::string retrieve(dev::h256 const& _contractHash) const = 0; + virtual std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData) = 0; + virtual std::string getUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionDacta) = 0; +}; + +class Context +{ +public: + virtual ~Context(); + + virtual QString pretty(dev::Address _a) const = 0; + virtual QString prettyU256(dev::u256 _n) const = 0; + virtual QString render(dev::Address _a) const = 0; + virtual dev::Address fromString(QString const& _a) const = 0; + virtual std::string renderDiff(dev::eth::StateDiff const& _d) const = 0; +}; + diff --git a/alethzero/Debugger.cpp b/alethzero/Debugger.cpp new file mode 100644 index 000000000..93d6b1f6a --- /dev/null +++ b/alethzero/Debugger.cpp @@ -0,0 +1,380 @@ +/* + 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 Debugger.cpp + * @author Gav Wood + * @date 2015 + */ + +#include "Debugger.h" + +#include +#include +#include +#include +#include +#include "ui_Debugger.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +Debugger::Debugger(Context* _c, QWidget* _parent): + QDialog(_parent), + ui(new Ui::Debugger), + m_context(_c) +{ + ui->setupUi(this); +} + +Debugger::~Debugger() +{ + delete ui; +} + +void Debugger::init() +{ + if (m_session.history.size()) + { + alterDebugStateGroup(true); + ui->debugCode->setEnabled(false); + ui->debugTimeline->setMinimum(0); + ui->debugTimeline->setMaximum(m_session.history.size()); + ui->debugTimeline->setValue(0); + } +} + +void Debugger::populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction) +{ + finished(); + if (m_session.populate(_executive, _transaction)) + init(); + update(); +} + +bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction) +{ + try { + if (_executive.setup(_transaction)) + return false; + } + catch (...) + { + // Invalid transaction + return false; + } + + vector levels; + bytes lastExtCode; + bytesConstRef lastData; + h256 lastHash; + h256 lastDataHash; + auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, VM* voidVM, ExtVMFace const* voidExt) + { + VM& vm = *voidVM; + ExtVM const& ext = *static_cast(voidExt); + if (ext.code != lastExtCode) + { + lastExtCode = ext.code; + lastHash = sha3(lastExtCode); + if (!codes.count(lastHash)) + codes[lastHash] = ext.code; + } + if (ext.data != lastData) + { + lastData = ext.data; + lastDataHash = sha3(lastData); + if (!codes.count(lastDataHash)) + codes[lastDataHash] = ext.data.toBytes(); + } + if (levels.size() < ext.depth) + levels.push_back(&history.back()); + else + levels.resize(ext.depth); + history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); + }; + _executive.go(onOp); + _executive.finalize(); + return true; +} + +void Debugger::finished() +{ + m_session = DebugSession(); + ui->callStack->clear(); + ui->debugCode->clear(); + ui->debugStack->clear(); + ui->debugMemory->setHtml(""); + ui->debugStorage->setHtml(""); + ui->debugStateInfo->setText(""); + alterDebugStateGroup(false); +} + +void Debugger::update() +{ + if (m_session.history.size()) + { + WorldState const& nws = m_session.history[min((int)m_session.history.size() - 1, ui->debugTimeline->value())]; + WorldState const& ws = ui->callStack->currentRow() > 0 ? *nws.levels[nws.levels.size() - ui->callStack->currentRow()] : nws; + + if (ui->debugTimeline->value() >= m_session.history.size()) + { + if (ws.gasCost > ws.gas) + ui->debugMemory->setHtml("

OUT-OF-GAS

"); + else if (ws.inst == Instruction::RETURN && ws.stack.size() >= 2) + { + unsigned from = (unsigned)ws.stack.back(); + unsigned size = (unsigned)ws.stack[ws.stack.size() - 2]; + unsigned o = 0; + bytes out(size, 0); + for (; o < size && from + o < ws.memory.size(); ++o) + out[o] = ws.memory[from + o]; + ui->debugMemory->setHtml("

RETURN

" + QString::fromStdString(dev::memDump(out, 16, true))); + } + else if (ws.inst == Instruction::STOP) + ui->debugMemory->setHtml("

STOP

"); + else if (ws.inst == Instruction::SUICIDE && ws.stack.size() >= 1) + ui->debugMemory->setHtml("

SUICIDE

0x" + QString::fromStdString(toString(right160(ws.stack.back())))); + else + ui->debugMemory->setHtml("

EXCEPTION

"); + + ostringstream ss; + ss << dec << "EXIT | GAS: " << dec << max(0, (dev::bigint)ws.gas - ws.gasCost); + ui->debugStateInfo->setText(QString::fromStdString(ss.str())); + ui->debugStorage->setHtml(""); + ui->debugCallData->setHtml(""); + m_session.currentData = h256(); + ui->callStack->clear(); + m_session.currentLevels.clear(); + ui->debugCode->clear(); + m_session.currentCode = h256(); + ui->debugStack->setHtml(""); + } + else + { + if (m_session.currentLevels != nws.levels || !ui->callStack->count()) + { + m_session.currentLevels = nws.levels; + ui->callStack->clear(); + for (unsigned i = 0; i <= nws.levels.size(); ++i) + { + WorldState const& s = i ? *nws.levels[nws.levels.size() - i] : nws; + ostringstream out; + out << s.cur.abridged(); + if (i) + out << " " << instructionInfo(s.inst).name << " @0x" << hex << s.curPC; + ui->callStack->addItem(QString::fromStdString(out.str())); + } + } + + if (ws.code != m_session.currentCode) + { + m_session.currentCode = ws.code; + bytes const& code = m_session.codes[ws.code]; + QListWidget* dc = ui->debugCode; + dc->clear(); + m_session.pcWarp.clear(); + for (unsigned i = 0; i <= code.size(); ++i) + { + byte b = i < code.size() ? code[i] : 0; + try + { + QString s = QString::fromStdString(instructionInfo((Instruction)b).name); + ostringstream out; + out << hex << setw(4) << setfill('0') << i; + m_session.pcWarp[i] = dc->count(); + if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32) + { + unsigned bc = b - (byte)Instruction::PUSH1 + 1; + s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&code[i + 1], bc))); + i += bc; + } + dc->addItem(QString::fromStdString(out.str()) + " " + s); + } + catch (...) + { + cerr << "Unhandled exception!" << endl << boost::current_exception_diagnostic_information(); + break; // probably hit data segment + } + } + } + + if (ws.callData != m_session.currentData) + { + m_session.currentData = ws.callData; + if (ws.callData) + { + assert(m_session.codes.count(ws.callData)); + ui->debugCallData->setHtml(QString::fromStdString(dev::memDump(m_session.codes[ws.callData], 16, true))); + } + else + ui->debugCallData->setHtml(""); + } + + QString stack; + for (auto i: ws.stack) + stack.prepend("
" + m_context->prettyU256(i) + "
"); + ui->debugStack->setHtml(stack); + ui->debugMemory->setHtml(QString::fromStdString(dev::memDump(ws.memory, 16, true))); + assert(m_session.codes.count(ws.code)); + + if (m_session.codes[ws.code].size() >= (unsigned)ws.curPC) + { + int l = m_session.pcWarp[(unsigned)ws.curPC]; + ui->debugCode->setCurrentRow(max(0, l - 5)); + ui->debugCode->setCurrentRow(min(ui->debugCode->count() - 1, l + 5)); + ui->debugCode->setCurrentRow(l); + } + else + cwarn << "PC (" << (unsigned)ws.curPC << ") is after code range (" << m_session.codes[ws.code].size() << ")"; + + ostringstream ss; + ss << dec << "STEP: " << ws.steps << " | PC: 0x" << hex << ws.curPC << " : " << instructionInfo(ws.inst).name << " | ADDMEM: " << dec << ws.newMemSize << " words | COST: " << dec << ws.gasCost << " | GAS: " << dec << ws.gas; + ui->debugStateInfo->setText(QString::fromStdString(ss.str())); + stringstream s; + for (auto const& i: ws.storage) + s << "@" << m_context->prettyU256(i.first).toStdString() << "    " << m_context->prettyU256(i.second).toStdString() << "
"; + ui->debugStorage->setHtml(QString::fromStdString(s.str())); + } + } +} + +void Debugger::on_callStack_currentItemChanged() +{ + update(); +} + +void Debugger::alterDebugStateGroup(bool _enable) const +{ + ui->stepOver->setEnabled(_enable); + ui->stepInto->setEnabled(_enable); + ui->stepOut->setEnabled(_enable); + ui->backOver->setEnabled(_enable); + ui->backInto->setEnabled(_enable); + ui->backOut->setEnabled(_enable); + ui->dump->setEnabled(_enable); + ui->dumpStorage->setEnabled(_enable); + ui->dumpPretty->setEnabled(_enable); +} + +void Debugger::on_debugTimeline_valueChanged() +{ + update(); +} + +void Debugger::on_stepOver_clicked() +{ + if (ui->debugTimeline->value() < m_session.history.size()) { + auto l = m_session.history[ui->debugTimeline->value()].levels.size(); + if ((ui->debugTimeline->value() + 1) < m_session.history.size() && m_session.history[ui->debugTimeline->value() + 1].levels.size() > l) + { + on_stepInto_clicked(); + if (m_session.history[ui->debugTimeline->value()].levels.size() > l) + on_stepOut_clicked(); + } + else + on_stepInto_clicked(); + } +} + +void Debugger::on_stepInto_clicked() +{ + ui->debugTimeline->setValue(ui->debugTimeline->value() + 1); + ui->callStack->setCurrentRow(0); +} + +void Debugger::on_stepOut_clicked() +{ + if (ui->debugTimeline->value() < m_session.history.size()) + { + auto ls = m_session.history[ui->debugTimeline->value()].levels.size(); + auto l = ui->debugTimeline->value(); + for (; l < m_session.history.size() && m_session.history[l].levels.size() >= ls; ++l) {} + ui->debugTimeline->setValue(l); + ui->callStack->setCurrentRow(0); + } +} + +void Debugger::on_backInto_clicked() +{ + ui->debugTimeline->setValue(ui->debugTimeline->value() - 1); + ui->callStack->setCurrentRow(0); +} + +void Debugger::on_backOver_clicked() +{ + auto l = m_session.history[ui->debugTimeline->value()].levels.size(); + if (ui->debugTimeline->value() > 0 && m_session.history[ui->debugTimeline->value() - 1].levels.size() > l) + { + on_backInto_clicked(); + if (m_session.history[ui->debugTimeline->value()].levels.size() > l) + on_backOut_clicked(); + } + else + on_backInto_clicked(); +} + +void Debugger::on_backOut_clicked() +{ + if (ui->debugTimeline->value() > 0 && m_session.history.size() > 0) + { + auto ls = m_session.history[min(ui->debugTimeline->value(), m_session.history.size() - 1)].levels.size(); + int l = ui->debugTimeline->value(); + for (; l > 0 && m_session.history[l].levels.size() >= ls; --l) {} + ui->debugTimeline->setValue(l); + ui->callStack->setCurrentRow(0); + } +} + +void Debugger::on_dump_clicked() +{ + QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace"); + ofstream f(fn.toStdString()); + if (f.is_open()) + for (WorldState const& ws: m_session.history) + f << ws.cur << " " << hex << toHex(dev::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl; +} + +void Debugger::on_dumpPretty_clicked() +{ + QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace"); + ofstream f(fn.toStdString()); + if (f.is_open()) + for (WorldState const& ws: m_session.history) + { + f << endl << " STACK" << endl; + for (auto i: ws.stack) + f << (h256)i << endl; + f << " MEMORY" << endl << dev::memDump(ws.memory); + f << " STORAGE" << endl; + for (auto const& i: ws.storage) + f << showbase << hex << i.first << ": " << i.second << endl; + f << dec << ws.levels.size() << " | " << ws.cur << " | #" << ws.steps << " | " << hex << setw(4) << setfill('0') << ws.curPC << " : " << instructionInfo(ws.inst).name << " | " << dec << ws.gas << " | -" << dec << ws.gasCost << " | " << ws.newMemSize << "x32"; + } +} + +void Debugger::on_dumpStorage_clicked() +{ + QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace"); + ofstream f(fn.toStdString()); + if (f.is_open()) + for (WorldState const& ws: m_session.history) + { + if (ws.inst == Instruction::STOP || ws.inst == Instruction::RETURN || ws.inst == Instruction::SUICIDE) + for (auto i: ws.storage) + f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; + f << ws.cur << " " << hex << toHex(dev::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl; + } +} diff --git a/alethzero/Debugger.h b/alethzero/Debugger.h new file mode 100644 index 000000000..370ad6e30 --- /dev/null +++ b/alethzero/Debugger.h @@ -0,0 +1,103 @@ +/* + 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 Debugger.h + * @author Gav Wood + * @date 2015 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "Context.h" + +namespace Ui { class Debugger; } + +struct WorldState +{ + uint64_t steps; + dev::Address cur; + dev::u256 curPC; + dev::eth::Instruction inst; + dev::bigint newMemSize; + dev::u256 gas; + dev::h256 code; + dev::h256 callData; + dev::u256s stack; + dev::bytes memory; + dev::bigint gasCost; + std::map storage; + std::vector levels; +}; + +struct DebugSession +{ + DebugSession() {} + + bool populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction); + + dev::h256 currentCode; + dev::h256 currentData; + std::vector currentLevels; + + QMap pcWarp; + QList history; + + std::map codes; +}; + +class Debugger: public QDialog +{ + Q_OBJECT + +public: + explicit Debugger(Context* _context, QWidget* _parent = 0); + ~Debugger(); + + void populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction); + +protected slots: + void on_callStack_currentItemChanged(); + void on_debugTimeline_valueChanged(); + void on_stepOver_clicked(); + void on_stepInto_clicked(); + void on_stepOut_clicked(); + void on_backOver_clicked(); + void on_backInto_clicked(); + void on_backOut_clicked(); + void on_dump_clicked(); + void on_dumpPretty_clicked(); + void on_dumpStorage_clicked(); + void on_close_clicked() { close(); } + +private: + void init(); + void update(); + void finished(); + + void alterDebugStateGroup(bool _enable) const; + + Ui::Debugger* ui; + + DebugSession m_session; + Context* m_context; +}; diff --git a/alethzero/Debugger.ui b/alethzero/Debugger.ui new file mode 100644 index 000000000..6751655ae --- /dev/null +++ b/alethzero/Debugger.ui @@ -0,0 +1,300 @@ + + + Debugger + + + + 0 + 0 + 989 + 690 + + + + Dialog + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Step Over + + + + + + + Step Into + + + + + + + Step Out + + + + + + + Back Over + + + + + + + Back Into + + + + + + + Back Out + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + 1 + 0 + + + + Qt::Vertical + + + + + 0 + 0 + + + + QFrame::NoFrame + + + 0 + + + + + + Ubuntu Mono + + + + QFrame::NoFrame + + + 0 + + + + + + + 1 + 0 + + + + Qt::Vertical + + + + QFrame::NoFrame + + + 0 + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + QFrame::NoFrame + + + 0 + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + QFrame::NoFrame + + + 0 + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + QFrame::NoFrame + + + true + + + + + + + + + + 0 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Dump + + + + + + + Dump Storage + + + + + + + Dump Pretty + + + + + + + Qt::Horizontal + + + + 577 + 20 + + + + + + + + Close + + + + + + + + + + + diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 328b4acba..b7c4f6c96 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -117,7 +117,7 @@ 0 0 1617 - 25 + 24 @@ -140,11 +140,12 @@ - T&ools + &Tools - + + @@ -160,18 +161,14 @@ - Deb&ug + &Special - - - - @@ -185,42 +182,28 @@ - - - D&ebugger - - - - &Dump Trace - - - - - - - - - - - - - - - &Whisper + + + &Debug + + + + + - - + + @@ -533,237 +516,11 @@ false + - - - - 0 - 0 - - - - - 510 - 386 - - - - QDockWidget::DockWidgetFeatureMask - - - Transact - - - 1 - - - - - - - - 0 - 0 - - - - &Data - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - data - - - - - - - Qt::Vertical - - - - QFrame::NoFrame - - - 0 - - - - - Qt::ClickFocus - - - QFrame::NoFrame - - - 0 - - - true - - - - - - - - &Gas - - - gas - - - - - - - - 0 - 0 - - - - &To - - - destination - - - - - - - gas - - - 1 - - - 430000000 - - - 10000 - - - - - - - - - - - - - @ - - - 1 - - - 430000000 - - - - - - - false - - - true - - - - - - - - - - - - - 430000000 - - - 0 - - - - - - - &Amount - - - value - - - - - - - - 0 - 0 - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - - (Create Contract) - - - - - - - - &Execute - - - - - - - - 0 - 0 - - - - - - - - - - - De&bug - - - - - - QDockWidget::DockWidgetFeatureMask @@ -987,171 +744,6 @@ - - - false - - - QDockWidget::DockWidgetFeatureMask - - - Debugger - - - 1 - - - - false - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - - 1 - 0 - - - - Qt::Vertical - - - - - 0 - 0 - - - - QFrame::NoFrame - - - 0 - - - - - - Ubuntu Mono - - - - QFrame::NoFrame - - - 0 - - - - - - - 1 - 0 - - - - Qt::Vertical - - - - QFrame::NoFrame - - - 0 - - - true - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - QFrame::NoFrame - - - 0 - - - true - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - QFrame::NoFrame - - - 0 - - - true - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - QFrame::NoFrame - - - true - - - - - - - - - - 0 - 0 - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - - - QDockWidget::DockWidgetFeatureMask @@ -1566,9 +1158,6 @@ font-size: 14pt Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - data - @@ -1621,9 +1210,6 @@ font-size: 14pt TTL - - destination - @@ -1637,9 +1223,6 @@ font-size: 14pt From - - destination - @@ -1653,9 +1236,6 @@ font-size: 14pt To - - destination - @@ -1686,9 +1266,6 @@ font-size: 14pt Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - data - @@ -1715,9 +1292,6 @@ font-size: 14pt Work to Prove - - destination - @@ -1776,7 +1350,7 @@ font-size: 14pt true - Use &UPnP + &Use UPnP @@ -1800,9 +1374,9 @@ font-size: 14pt &Mine - + - &New Address + &New Address... @@ -1834,7 +1408,7 @@ font-size: 14pt true - Mining &Paranoia + &Mining Paranoia @@ -1862,7 +1436,7 @@ font-size: 14pt true - Show Ancient &Blocks + &Show Ancient Blocks @@ -1870,7 +1444,7 @@ font-size: 14pt true - Show Anonymous &Accounts + Show &Anonymous Accounts @@ -1970,7 +1544,7 @@ font-size: 14pt false - Debu&g Current Transaction + &Debug Current Transaction @@ -1978,7 +1552,7 @@ font-size: 14pt false - D&ump Current Transaction State (post) + Dump &Current Transaction State (post) @@ -1986,7 +1560,7 @@ font-size: 14pt false - D&ump Current Transaction State (pre) + Dump Current &Transaction State (pre) @@ -2007,7 +1581,7 @@ font-size: 14pt true - &Use Private Chain... + Use &Private Chain... @@ -2015,7 +1589,7 @@ font-size: 14pt true - &Enable LLL &Optimizer + &Enable LLL Optimizer @@ -2023,7 +1597,7 @@ font-size: 14pt true - Reserved Debug 1 + &Reserved Debug 1 @@ -2031,7 +1605,7 @@ font-size: 14pt true - Enable Local Addresses + &Enable Local Addresses @@ -2044,7 +1618,7 @@ font-size: 14pt - Go! + &Go! @@ -2054,7 +1628,7 @@ font-size: 14pt - Clear Pe&nd&ing + &Clear Pending @@ -2073,6 +1647,11 @@ font-size: 14pt &Kill Account + + + New &Transaction... + + @@ -2095,27 +1674,30 @@ font-size: 14pt - destination - calculatedName - value - valueUnits - gas - gasPrice - gasPriceUnits - data - send - idealPeers - port - clientName + shhTo + shhFrom + shhTtl + shhTopic + shhWork + shhData + log + post verbosity + jsConsole tabWidget urlEdit webView + idealPeers + forceAddress + port + clientName + transactionQueue + pendingInfo + blockChainFilter nameReg - debugCode - debugStack - debugMemory - debugStorage + nodes + whispers + jsInput diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 397b2330c..00fb29e2d 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -50,11 +50,14 @@ #include #include #include +#include #include "MainWin.h" #include "DownloadView.h" #include "MiningView.h" #include "BuildInfo.h" #include "OurWebThreeStubServer.h" +#include "Transact.h" +#include "Debugger.h" #include "ui_Main.h" using namespace std; using namespace dev; @@ -62,17 +65,6 @@ using namespace dev::p2p; using namespace dev::eth; namespace js = json_spirit; -#define Small "font-size: small; " -#define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; " -#define Div(S) "
" -#define Span(S) "" - -static void initUnits(QComboBox* _b) -{ - for (auto n = (unsigned)units().size(); n-- != 0; ) - _b->addItem(QString::fromStdString(units()[n].second), n); -} - QString Main::fromRaw(h256 _n, unsigned* _inc) { if (_n) @@ -98,12 +90,6 @@ QString Main::fromRaw(h256 _n, unsigned* _inc) return QString(); } -static vector keysAsVector(QList const& keys) -{ - auto list = keys.toStdList(); - return {begin(list), end(list)}; -} - QString contentsOfQResource(string const& res) { QFile file(QString::fromStdString(res)); @@ -119,7 +105,8 @@ Address c_newConfig = Address("c6d9d2cd449a754c494264e1809c50e34d64562b"); Main::Main(QWidget *parent) : QMainWindow(parent), - ui(new Ui::Main) + ui(new Ui::Main), + m_transact(this, this) { setWindowFlags(Qt::Window); ui->setupUi(this); @@ -148,12 +135,6 @@ Main::Main(QWidget *parent) : ui->configDock->close(); on_verbosity_valueChanged(); - initUnits(ui->gasPriceUnits); - initUnits(ui->valueUnits); - ui->valueUnits->setCurrentIndex(6); - ui->gasPriceUnits->setCurrentIndex(4); - ui->gasPrice->setValue(10); - on_destination_currentTextChanged(); statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->peerCount); @@ -167,25 +148,26 @@ Main::Main(QWidget *parent) : bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size()); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/AlethZero", false, {"eth", "shh"}, p2p::NetworkPreferences(), network)); - m_qwebConnector.reset(new QWebThreeConnector()); - m_server.reset(new OurWebThreeStubServer(*m_qwebConnector, *web3(), keysAsVector(m_myKeys), this)); + m_httpConnector.reset(new jsonrpc::HttpServer(8080)); + m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this)); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); m_server->setIdentities(keysAsVector(owned())); m_server->StartListening(); connect(ui->webView, &QWebView::loadStarted, [this]() { - // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. - m_qweb = new QWebThree(this); - auto qweb = m_qweb; - m_qwebConnector->setQWeb(qweb); - QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); QWebFrame* f = ui->webView->page()->mainFrame(); f->disconnect(SIGNAL(javaScriptWindowObjectCleared())); - connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qweb)); - connect(m_qweb, SIGNAL(onNewId(QString)), this, SLOT(addNewId(QString))); + connect(f, &QWebFrame::javaScriptWindowObjectCleared, [f, this]() + { + f->disconnect(); + f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership); + f->evaluateJavaScript(contentsOfQResource(":/js/bignumber.min.js")); + f->evaluateJavaScript(contentsOfQResource(":/js/webthree.js")); + f->evaluateJavaScript(contentsOfQResource(":/js/setup.js")); + }); }); connect(ui->webView, &QWebView::loadFinished, [=]() @@ -216,7 +198,6 @@ Main::~Main() writeSettings(); // Must do this here since otherwise m_ethereum'll be deleted (and therefore clearWatches() called by the destructor) // *after* the client is dead. - m_qweb->clientDieing(); g_logPost = simpleDebugOut; } @@ -372,12 +353,6 @@ void Main::on_forceMining_triggered() ethereum()->setForceMining(ui->forceMining->isChecked()); } -void Main::on_enableOptimizer_triggered() -{ - m_enableOptimizer = ui->enableOptimizer->isChecked(); - on_data_textChanged(); -} - QString Main::contents(QString _s) { return QString::fromStdString(dev::asString(dev::contents(_s.toStdString()))); @@ -411,6 +386,12 @@ void Main::load(QString _s) }*/ } +void Main::on_newTransaction_triggered() +{ + m_transact.setEnvironment(m_myKeys, ethereum(), &m_natSpecDB); + m_transact.exec(); +} + void Main::on_loadJS_triggered() { QString f = QFileDialog::getOpenFileName(this, "Load Javascript", QString(), "Javascript (*.js);;All files (*)"); @@ -672,7 +653,6 @@ void Main::writeSettings() s.setValue("paranoia", ui->paranoia->isChecked()); s.setValue("showAll", ui->showAll->isChecked()); s.setValue("showAllAccounts", ui->showAllAccounts->isChecked()); - s.setValue("enableOptimizer", m_enableOptimizer); s.setValue("clientName", ui->clientName->text()); s.setValue("idealPeers", ui->idealPeers->value()); s.setValue("port", ui->port->value()); @@ -742,8 +722,6 @@ void Main::readSettings(bool _skipGeometry) ui->paranoia->setChecked(s.value("paranoia", false).toBool()); ui->showAll->setChecked(s.value("showAll", false).toBool()); ui->showAllAccounts->setChecked(s.value("showAllAccounts", false).toBool()); - m_enableOptimizer = s.value("enableOptimizer", true).toBool(); - ui->enableOptimizer->setChecked(m_enableOptimizer); ui->clientName->setText(s.value("clientName", "").toString()); if (ui->clientName->text().isEmpty()) ui->clientName->setText(QInputDialog::getText(nullptr, "Enter identity", "Enter a name that will identify you on the peer network")); @@ -990,7 +968,6 @@ void Main::refreshNetwork() void Main::refreshAll() { - refreshDestination(); refreshBlockChain(); refreshBlockCount(); refreshPending(); @@ -1041,20 +1018,6 @@ void Main::refreshAccounts() } } -void Main::refreshDestination() -{ - cwatch << "refreshDestination()"; - QString s; - for (auto i: ethereum()->addresses()) - if ((s = pretty(i)).size()) - // A namereg address - if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1) - ui->destination->addItem(s); - for (int i = 0; i < ui->destination->count(); ++i) - if (ui->destination->itemText(i) != "(Create Contract)" && !fromString(ui->destination->itemText(i))) - ui->destination->removeItem(i--); -} - void Main::refreshBlockCount() { cwatch << "refreshBlockCount()"; @@ -1477,13 +1440,12 @@ void Main::on_debugCurrent_triggered() if (!item->data(Qt::UserRole + 1).isNull()) { unsigned txi = item->data(Qt::UserRole + 1).toInt(); - m_executiveState = ethereum()->state(txi + 1, h); - m_currentExecution = unique_ptr(new Executive(m_executiveState, ethereum()->blockChain(), 0)); - Transaction t = m_executiveState.pending()[txi]; - m_executiveState = m_executiveState.fromPending(txi); - auto r = t.rlp(); - populateDebugger(&r); - m_currentExecution.reset(); + bytes t = ethereum()->blockChain().transaction(h, txi); + State s(ethereum()->state(txi, h)); + Executive e(s, ethereum()->blockChain(), 0); + Debugger dw(this, this); + dw.populate(e, Transaction(t, CheckSignature::Sender)); + dw.exec(); } } } @@ -1514,49 +1476,6 @@ void Main::on_debugDumpStatePre_triggered() on_debugDumpState_triggered(0); } -void Main::populateDebugger(dev::bytesConstRef _r) -{ - bool done = m_currentExecution->setup(_r); - if (!done) - { - debugFinished(); - vector levels; - m_codes.clear(); - bytes lastExtCode; - bytesConstRef lastData; - h256 lastHash; - h256 lastDataHash; - auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, VM* voidVM, ExtVMFace const* voidExt) - { - VM& vm = *voidVM; - ExtVM const& ext = *static_cast(voidExt); - if (ext.code != lastExtCode) - { - lastExtCode = ext.code; - lastHash = sha3(lastExtCode); - if (!m_codes.count(lastHash)) - m_codes[lastHash] = ext.code; - } - if (ext.data != lastData) - { - lastData = ext.data; - lastDataHash = sha3(lastData); - if (!m_codes.count(lastDataHash)) - m_codes[lastDataHash] = ext.data.toBytes(); - } - if (levels.size() < ext.depth) - levels.push_back(&m_history.back()); - else - levels.resize(ext.depth); - m_history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); - }; - m_currentExecution->go(onOp); - m_currentExecution->finalize(); - initDebugger(); - updateDebugger(); - } -} - void Main::on_contracts_currentItemChanged() { ui->contractInfo->clear(); @@ -1572,7 +1491,7 @@ void Main::on_contracts_currentItemChanged() auto storage = ethereum()->storageAt(address); for (auto const& i: storage) s << "@" << showbase << hex << prettyU256(i.first).toStdString() << "    " << showbase << hex << prettyU256(i.second).toStdString() << "
"; - s << "

Body Code

" << disassemble(ethereum()->codeAt(address)); + s << "

Body Code (" << sha3(ethereum()->codeAt(address)).abridged() << ")

" << disassemble(ethereum()->codeAt(address)); s << Div(Mono) << toHex(ethereum()->codeAt(address)) << "
"; ui->contractInfo->appendHtml(QString::fromStdString(s.str())); } @@ -1616,19 +1535,6 @@ void Main::on_contracts_doubleClicked() qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); } -void Main::on_destination_currentTextChanged() -{ - if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)") - if (Address a = fromString(ui->destination->currentText())) - ui->calculatedName->setText(render(a)); - else - ui->calculatedName->setText("Unknown Address"); - else - ui->calculatedName->setText("Create Contract"); - on_data_textChanged(); -// updateFee(); -} - static shh::FullTopic topicFromText(QString _s) { shh::BuildTopic ret; @@ -1683,134 +1589,6 @@ static shh::FullTopic topicFromText(QString _s) return ret; } -bool Main::sourceIsSolidity(string const& _source) -{ - // TODO: Improve this heuristic - return (_source.substr(0, 8) == "contract" || _source.substr(0, 5) == "//sol"); -} - -static bool sourceIsSerpent(string const& _source) -{ - // TODO: Improve this heuristic - return (_source.substr(0, 5) == "//ser"); -} - -string const Main::getFunctionHashes(dev::solidity::CompilerStack const &_compiler, - string const& _contractName) -{ - string ret = ""; - auto const& contract = _compiler.getContractDefinition(_contractName); - auto interfaceFunctions = contract.getInterfaceFunctions(); - - for (auto const& it: interfaceFunctions) - { - ret += it.first.abridged(); - ret += " :"; - ret += it.second->getDeclaration().getName() + "\n"; - } - return ret; -} - -void Main::on_data_textChanged() -{ - m_pcWarp.clear(); - if (isCreation()) - { - string src = ui->data->toPlainText().toStdString(); - vector errors; - QString lll; - QString solidity; - if (src.find_first_not_of("1234567890abcdefABCDEF") == string::npos && src.size() % 2 == 0) - { - m_data = fromHex(src); - } - else if (sourceIsSolidity(src)) - { - dev::solidity::CompilerStack compiler; - try - { -// compiler.addSources(dev::solidity::StandardSources); - m_data = compiler.compile(src, m_enableOptimizer); - solidity = "

Solidity

"; - solidity += "
var " + QString::fromStdString(compiler.getContractNames().front()) + " = web3.eth.contractFromAbi(" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + ");
"; - solidity += "
" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "
"; - solidity += "
" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "
"; - } - catch (dev::Exception const& exception) - { - ostringstream error; - solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); - solidity = "

Solidity

" + QString::fromStdString(error.str()).toHtmlEscaped() + "
"; - } - catch (...) - { - solidity = "

Solidity

Uncaught exception.
"; - } - } -#ifndef _MSC_VER - else if (sourceIsSerpent(src)) - { - try - { - m_data = dev::asBytes(::compile(src)); - for (auto& i: errors) - i = "(LLL " + i + ")"; - } - catch (string err) - { - errors.push_back("Serpent " + err); - } - } -#endif - else - { - m_data = compileLLL(src, m_enableOptimizer, &errors); - if (errors.empty()) - { - auto asmcode = compileLLLToAsm(src, false); - lll = "

Pre

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
"; - if (m_enableOptimizer) - { - asmcode = compileLLLToAsm(src, true); - lll = "

Opt

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
" + lll; - } - } - } - QString errs; - if (errors.size()) - { - errs = "

Errors

"; - for (auto const& i: errors) - errs.append("
" + QString::fromStdString(i).toHtmlEscaped() + "
"); - } - ui->code->setHtml(errs + lll + solidity + "

Code

" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped() + "

Hex

" Div(Mono) + QString::fromStdString(toHex(m_data)) + "
"); - ui->gas->setMinimum((qint64)Client::txGas(m_data, 0)); - if (!ui->gas->isEnabled()) - ui->gas->setValue(m_backupGas); - ui->gas->setEnabled(true); - } - else - { - m_data = parseData(ui->data->toPlainText().toStdString()); - ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true))); - if (ethereum()->codeAt(fromString(ui->destination->currentText()), 0).size()) - { - ui->gas->setMinimum((qint64)Client::txGas(m_data, 1)); - if (!ui->gas->isEnabled()) - ui->gas->setValue(m_backupGas); - ui->gas->setEnabled(true); - } - else - { - if (ui->gas->isEnabled()) - m_backupGas = ui->gas->value(); - ui->gas->setValue((qint64)Client::txGas(m_data)); - ui->gas->setEnabled(false); - } - } - updateFee(); -} - void Main::on_clearPending_triggered() { writeSettings(); @@ -1835,54 +1613,6 @@ void Main::on_killBlockchain_triggered() refreshAll(); } -bool Main::isCreation() const -{ - return ui->destination->currentText().isEmpty() || ui->destination->currentText() == "(Create Contract)"; -} - -u256 Main::fee() const -{ - return ui->gas->value() * gasPrice(); -} - -u256 Main::value() const -{ - if (ui->valueUnits->currentIndex() == -1) - return 0; - return ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first; -} - -u256 Main::gasPrice() const -{ - if (ui->gasPriceUnits->currentIndex() == -1) - return 0; - return ui->gasPrice->value() * units()[units().size() - 1 - ui->gasPriceUnits->currentIndex()].first; -} - -u256 Main::total() const -{ - return value() + fee(); -} - -void Main::updateFee() -{ - ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str())); - auto totalReq = total(); - ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str())); - - bool ok = false; - for (auto i: m_myKeys) - if (ethereum()->balanceAt(i.address()) >= totalReq) - { - ok = true; - break; - } - ui->send->setEnabled(ok); - QPalette p = ui->total->palette(); - p.setColor(QPalette::WindowText, QColor(ok ? 0x00 : 0x80, 0x00, 0x00)); - ui->total->setPalette(p); -} - void Main::on_net_triggered() { ui->port->setEnabled(!ui->net->isChecked()); @@ -1944,79 +1674,12 @@ void Main::on_mine_triggered() ethereum()->stopMining(); } -void Main::on_send_clicked() -{ - u256 totalReq = value() + fee(); - for (auto i: m_myKeys) - if (ethereum()->balanceAt(i.address(), 0) >= totalReq) - { - debugFinished(); - Secret s = i.secret(); - if (isCreation()) - { - // If execution is a contract creation, add Natspec to - // a local Natspec LEVELDB - ethereum()->transact(s, value(), m_data, ui->gas->value(), gasPrice()); - string src = ui->data->toPlainText().toStdString(); - if (sourceIsSolidity(src)) - try - { - dev::solidity::CompilerStack compiler; - m_data = compiler.compile(src, m_enableOptimizer); - for (string& s: compiler.getContractNames()) - { - h256 contractHash = compiler.getContractCodeHash(s); - m_natspecDB.add(contractHash, - compiler.getMetadata(s, dev::solidity::DocumentationType::NatspecUser)); - } - } - catch (...) - { - statusBar()->showMessage("Couldn't compile Solidity Contract."); - } - } - else - ethereum()->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); - return; - } - statusBar()->showMessage("Couldn't make transaction: no single account contains at least the required amount."); -} - void Main::keysChanged() { onBalancesChange(); m_server->setAccounts(keysAsVector(m_myKeys)); } -void Main::on_debug_clicked() -{ - debugFinished(); - try - { - u256 totalReq = value() + fee(); - for (auto i: m_myKeys) - if (ethereum()->balanceAt(i.address()) >= totalReq) - { - Secret s = i.secret(); - m_executiveState = ethereum()->postState(); - m_currentExecution = unique_ptr(new Executive(m_executiveState, ethereum()->blockChain(), 0)); - Transaction t = isCreation() ? - Transaction(value(), gasPrice(), ui->gas->value(), m_data, m_executiveState.transactionsFrom(dev::toAddress(s)), s) : - Transaction(value(), gasPrice(), ui->gas->value(), fromString(ui->destination->currentText()), m_data, m_executiveState.transactionsFrom(dev::toAddress(s)), s); - auto r = t.rlp(); - populateDebugger(&r); - m_currentExecution.reset(); - return; - } - statusBar()->showMessage("Couldn't make transaction: no single account contains at least the required amount."); - } - catch (dev::Exception const& _e) - { - statusBar()->showMessage("Error running transaction: " + QString::fromStdString(diagnostic_information(_e))); - // this output is aimed at developers, reconsider using _e.what for more user friendly output. - } -} - bool beginsWith(Address _a, bytes const& _b) { for (unsigned i = 0; i < min(20, _b.size()); ++i) @@ -2025,7 +1688,7 @@ bool beginsWith(Address _a, bytes const& _b) return true; } -void Main::on_create_triggered() +void Main::on_newAccount_triggered() { bool ok = true; enum { NoVanity = 0, FirstTwo, FirstTwoNextTwo, FirstThree, FirstFour, StringMatch }; @@ -2093,111 +1756,6 @@ void Main::on_killAccount_triggered() } } -void Main::on_debugStep_triggered() -{ - if (ui->debugTimeline->value() < m_history.size()) { - auto l = m_history[ui->debugTimeline->value()].levels.size(); - if ((ui->debugTimeline->value() + 1) < m_history.size() && m_history[ui->debugTimeline->value() + 1].levels.size() > l) - { - on_debugStepInto_triggered(); - if (m_history[ui->debugTimeline->value()].levels.size() > l) - on_debugStepOut_triggered(); - } - else - on_debugStepInto_triggered(); - } -} - -void Main::on_debugStepInto_triggered() -{ - ui->debugTimeline->setValue(ui->debugTimeline->value() + 1); - ui->callStack->setCurrentRow(0); -} - -void Main::on_debugStepOut_triggered() -{ - if (ui->debugTimeline->value() < m_history.size()) - { - auto ls = m_history[ui->debugTimeline->value()].levels.size(); - auto l = ui->debugTimeline->value(); - for (; l < m_history.size() && m_history[l].levels.size() >= ls; ++l) {} - ui->debugTimeline->setValue(l); - ui->callStack->setCurrentRow(0); - } -} - -void Main::on_debugStepBackInto_triggered() -{ - ui->debugTimeline->setValue(ui->debugTimeline->value() - 1); - ui->callStack->setCurrentRow(0); -} - -void Main::on_debugStepBack_triggered() -{ - auto l = m_history[ui->debugTimeline->value()].levels.size(); - if (ui->debugTimeline->value() > 0 && m_history[ui->debugTimeline->value() - 1].levels.size() > l) - { - on_debugStepBackInto_triggered(); - if (m_history[ui->debugTimeline->value()].levels.size() > l) - on_debugStepBackOut_triggered(); - } - else - on_debugStepBackInto_triggered(); -} - -void Main::on_debugStepBackOut_triggered() -{ - if (ui->debugTimeline->value() > 0 && m_history.size() > 0) - { - auto ls = m_history[min(ui->debugTimeline->value(), m_history.size() - 1)].levels.size(); - int l = ui->debugTimeline->value(); - for (; l > 0 && m_history[l].levels.size() >= ls; --l) {} - ui->debugTimeline->setValue(l); - ui->callStack->setCurrentRow(0); - } -} - -void Main::on_dumpTrace_triggered() -{ - QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace"); - ofstream f(fn.toStdString()); - if (f.is_open()) - for (WorldState const& ws: m_history) - f << ws.cur << " " << hex << toHex(dev::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl; -} - -void Main::on_dumpTracePretty_triggered() -{ - QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace"); - ofstream f(fn.toStdString()); - if (f.is_open()) - for (WorldState const& ws: m_history) - { - f << endl << " STACK" << endl; - for (auto i: ws.stack) - f << (h256)i << endl; - f << " MEMORY" << endl << dev::memDump(ws.memory); - f << " STORAGE" << endl; - for (auto const& i: ws.storage) - f << showbase << hex << i.first << ": " << i.second << endl; - f << dec << ws.levels.size() << " | " << ws.cur << " | #" << ws.steps << " | " << hex << setw(4) << setfill('0') << ws.curPC << " : " << instructionInfo(ws.inst).name << " | " << dec << ws.gas << " | -" << dec << ws.gasCost << " | " << ws.newMemSize << "x32"; - } -} - -void Main::on_dumpTraceStorage_triggered() -{ - QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace"); - ofstream f(fn.toStdString()); - if (f.is_open()) - for (WorldState const& ws: m_history) - { - if (ws.inst == Instruction::STOP || ws.inst == Instruction::RETURN || ws.inst == Instruction::SUICIDE) - for (auto i: ws.storage) - f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; - f << ws.cur << " " << hex << toHex(dev::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl; - } -} - void Main::on_go_triggered() { if (!ui->net->isChecked()) @@ -2208,60 +1766,6 @@ void Main::on_go_triggered() web3()->connect(Host::pocHost()); } -void Main::on_callStack_currentItemChanged() -{ - updateDebugger(); -} - -void Main::alterDebugStateGroup(bool _enable) const -{ - ui->debugStep->setEnabled(_enable); - ui->debugStepInto->setEnabled(_enable); - ui->debugStepOut->setEnabled(_enable); - ui->debugStepBackInto->setEnabled(_enable); - ui->debugStepBackOut->setEnabled(_enable); - ui->dumpTrace->setEnabled(_enable); - ui->dumpTraceStorage->setEnabled(_enable); - ui->dumpTracePretty->setEnabled(_enable); - ui->debugStepBack->setEnabled(_enable); - ui->debugPanel->setEnabled(_enable); -} - -void Main::debugFinished() -{ - m_codes.clear(); - m_pcWarp.clear(); - m_history.clear(); - m_lastLevels.clear(); - m_lastCode = h256(); - ui->callStack->clear(); - ui->debugCode->clear(); - ui->debugStack->clear(); - ui->debugMemory->setHtml(""); - ui->debugStorage->setHtml(""); - ui->debugStateInfo->setText(""); - alterDebugStateGroup(false); -// ui->send->setEnabled(true); -} - -void Main::initDebugger() -{ -// ui->send->setEnabled(false); - if (m_history.size()) - { - alterDebugStateGroup(true); - ui->debugCode->setEnabled(false); - ui->debugTimeline->setMinimum(0); - ui->debugTimeline->setMaximum(m_history.size()); - ui->debugTimeline->setValue(0); - } -} - -void Main::on_debugTimeline_valueChanged() -{ - updateDebugger(); -} - QString Main::prettyU256(dev::u256 _n) const { unsigned inc = 0; @@ -2289,136 +1793,6 @@ QString Main::prettyU256(dev::u256 _n) const return QString::fromStdString(s.str()); } -void Main::updateDebugger() -{ - if (m_history.size()) - { - WorldState const& nws = m_history[min((int)m_history.size() - 1, ui->debugTimeline->value())]; - WorldState const& ws = ui->callStack->currentRow() > 0 ? *nws.levels[nws.levels.size() - ui->callStack->currentRow()] : nws; - - if (ui->debugTimeline->value() >= m_history.size()) - { - if (ws.gasCost > ws.gas) - ui->debugMemory->setHtml("

OUT-OF-GAS

"); - else if (ws.inst == Instruction::RETURN && ws.stack.size() >= 2) - { - unsigned from = (unsigned)ws.stack.back(); - unsigned size = (unsigned)ws.stack[ws.stack.size() - 2]; - unsigned o = 0; - bytes out(size, 0); - for (; o < size && from + o < ws.memory.size(); ++o) - out[o] = ws.memory[from + o]; - ui->debugMemory->setHtml("

RETURN

" + QString::fromStdString(dev::memDump(out, 16, true))); - } - else if (ws.inst == Instruction::STOP) - ui->debugMemory->setHtml("

STOP

"); - else if (ws.inst == Instruction::SUICIDE && ws.stack.size() >= 1) - ui->debugMemory->setHtml("

SUICIDE

0x" + QString::fromStdString(toString(right160(ws.stack.back())))); - else - ui->debugMemory->setHtml("

EXCEPTION

"); - - ostringstream ss; - ss << dec << "EXIT | GAS: " << dec << max(0, (dev::bigint)ws.gas - ws.gasCost); - ui->debugStateInfo->setText(QString::fromStdString(ss.str())); - ui->debugStorage->setHtml(""); - ui->debugCallData->setHtml(""); - m_lastData = h256(); - ui->callStack->clear(); - m_lastLevels.clear(); - ui->debugCode->clear(); - m_lastCode = h256(); - ui->debugStack->setHtml(""); - } - else - { - if (m_lastLevels != nws.levels || !ui->callStack->count()) - { - m_lastLevels = nws.levels; - ui->callStack->clear(); - for (unsigned i = 0; i <= nws.levels.size(); ++i) - { - WorldState const& s = i ? *nws.levels[nws.levels.size() - i] : nws; - ostringstream out; - out << s.cur.abridged(); - if (i) - out << " " << instructionInfo(s.inst).name << " @0x" << hex << s.curPC; - ui->callStack->addItem(QString::fromStdString(out.str())); - } - } - - if (ws.code != m_lastCode) - { - bytes const& code = m_codes[ws.code]; - QListWidget* dc = ui->debugCode; - dc->clear(); - m_pcWarp.clear(); - for (unsigned i = 0; i <= code.size(); ++i) - { - byte b = i < code.size() ? code[i] : 0; - try - { - QString s = QString::fromStdString(instructionInfo((Instruction)b).name); - ostringstream out; - out << hex << setw(4) << setfill('0') << i; - m_pcWarp[i] = dc->count(); - if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32) - { - unsigned bc = b - (byte)Instruction::PUSH1 + 1; - s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&code[i + 1], bc))); - i += bc; - } - dc->addItem(QString::fromStdString(out.str()) + " " + s); - } - catch (...) - { - cerr << "Unhandled exception!" << endl << - boost::current_exception_diagnostic_information(); - break; // probably hit data segment - } - } - m_lastCode = ws.code; - } - - if (ws.callData != m_lastData) - { - m_lastData = ws.callData; - if (ws.callData) - { - assert(m_codes.count(ws.callData)); - ui->debugCallData->setHtml(QString::fromStdString(dev::memDump(m_codes[ws.callData], 16, true))); - } - else - ui->debugCallData->setHtml(""); - } - - QString stack; - for (auto i: ws.stack) - stack.prepend("
" + prettyU256(i) + "
"); - ui->debugStack->setHtml(stack); - ui->debugMemory->setHtml(QString::fromStdString(dev::memDump(ws.memory, 16, true))); - assert(m_codes.count(ws.code)); - - if (m_codes[ws.code].size() >= (unsigned)ws.curPC) - { - int l = m_pcWarp[(unsigned)ws.curPC]; - ui->debugCode->setCurrentRow(max(0, l - 5)); - ui->debugCode->setCurrentRow(min(ui->debugCode->count() - 1, l + 5)); - ui->debugCode->setCurrentRow(l); - } - else - cwarn << "PC (" << (unsigned)ws.curPC << ") is after code range (" << m_codes[ws.code].size() << ")"; - - ostringstream ss; - ss << dec << "STEP: " << ws.steps << " | PC: 0x" << hex << ws.curPC << " : " << instructionInfo(ws.inst).name << " | ADDMEM: " << dec << ws.newMemSize << " words | COST: " << dec << ws.gasCost << " | GAS: " << dec << ws.gas; - ui->debugStateInfo->setText(QString::fromStdString(ss.str())); - stringstream s; - for (auto const& i: ws.storage) - s << "@" << prettyU256(i.first).toStdString() << "    " << prettyU256(i.second).toStdString() << "
"; - ui->debugStorage->setHtml(QString::fromStdString(s.str())); - } - } -} - void Main::on_post_clicked() { shh::Message m; @@ -2431,14 +1805,16 @@ void Main::on_post_clicked() whisper()->inject(m.seal(from, topicFromText(ui->shhTopic->toPlainText()), ui->shhTtl->value(), ui->shhWork->value())); } -string Main::lookupNatSpec(dev::h256 const& _contractHash) const -{ - return m_natspecDB.retrieve(_contractHash); -} - -string Main::lookupNatSpecUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionData) +int Main::authenticate(QString _title, QString _text) { - return m_natspecDB.getUserNotice(_contractHash, _transactionData); + QMessageBox userInput(this); + userInput.setText(_title); + userInput.setInformativeText(_text); + userInput.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + userInput.button(QMessageBox::Ok)->setText("Allow"); + userInput.button(QMessageBox::Cancel)->setText("Reject"); + userInput.setDefaultButton(QMessageBox::Cancel); + return userInput.exec(); } void Main::refreshWhispers() diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 4e21d493f..6c4a97301 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -34,10 +34,10 @@ #include #include #include -#include #include #include - +#include "Context.h" +#include "Transact.h" #include "NatspecHandler.h" namespace Ui { @@ -49,31 +49,18 @@ class Client; class State; }} +namespace jsonrpc { +class HttpServer; +} + class QQuickView; class OurWebThreeStubServer; -struct WorldState -{ - uint64_t steps; - dev::Address cur; - dev::u256 curPC; - dev::eth::Instruction inst; - dev::bigint newMemSize; - dev::u256 gas; - dev::h256 code; - dev::h256 callData; - dev::u256s stack; - dev::bytes memory; - dev::bigint gasCost; - std::map storage; - std::vector levels; -}; - using WatchHandler = std::function; QString contentsOfQResource(std::string const& res); -class Main : public QMainWindow +class Main: public QMainWindow, public Context { Q_OBJECT @@ -85,13 +72,20 @@ public: dev::eth::Client* ethereum() const { return m_webThree->ethereum(); } std::shared_ptr whisper() const { return m_webThree->whisper(); } - std::string lookupNatSpec(dev::h256 const& _contractHash) const; - std::string lookupNatSpecUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionData); + NatSpecFace* natSpec() { return &m_natSpecDB; } - QList owned() const { return m_myIdentities + m_myKeys; } - QVariant evalRaw(QString const& _js); + QString pretty(dev::Address _a) const override; + QString prettyU256(dev::u256 _n) const override; + QString render(dev::Address _a) const override; + dev::Address fromString(QString const& _a) const override; + std::string renderDiff(dev::eth::StateDiff const& _d) const override; + + QList owned() const { return m_myIdentities + m_myKeys; } + + dev::u256 gasPrice() const { return 10 * dev::eth::szabo; } + public slots: void load(QString _file); void note(QString _entry); @@ -99,75 +93,80 @@ public slots: void warn(QString _entry); QString contents(QString _file); + int authenticate(QString _title, QString _text); + void onKeysChanged(); private slots: void eval(QString const& _js); + // Application + void on_about_triggered(); + void on_quit_triggered() { close(); } + + // Network + void on_go_triggered(); + void on_net_triggered(); void on_connect_triggered(); + void on_idealPeers_valueChanged(); + + // Mining void on_mine_triggered(); - void on_send_clicked(); - void on_create_triggered(); + + // View + void on_refresh_triggered(); + void on_showAll_triggered() { refreshBlockChain(); } + void on_showAllAccounts_triggered() { refreshAccounts(); } + void on_preview_triggered(); + + // Account management + void on_newAccount_triggered(); void on_killAccount_triggered(); - void on_net_triggered(); - void on_verbosity_valueChanged(); - void on_ourAccounts_doubleClicked(); + void on_importKey_triggered(); + void on_importKeyFile_triggered(); + void on_exportKey_triggered(); + + // Tools + void on_newTransaction_triggered(); + void on_loadJS_triggered(); + + // Stuff concerning the blocks/transactions/accounts panels void ourAccountsRowsMoved(); + void on_ourAccounts_doubleClicked(); void on_accounts_doubleClicked(); - void on_destination_currentTextChanged(); - void on_data_textChanged(); - void on_idealPeers_valueChanged(); - void on_value_valueChanged() { updateFee(); } - void on_gas_valueChanged() { updateFee(); } - void on_valueUnits_currentIndexChanged() { updateFee(); } - void on_gasPriceUnits_currentIndexChanged() { updateFee(); } - void on_gasPrice_valueChanged() { updateFee(); } - void on_log_doubleClicked(); - void on_blocks_currentItemChanged(); void on_contracts_doubleClicked(); void on_contracts_currentItemChanged(); void on_transactionQueue_currentItemChanged(); - void on_about_triggered(); - void on_paranoia_triggered(); - void on_nameReg_textChanged(); - void on_preview_triggered(); - void on_quit_triggered() { close(); } + void on_blockChainFilter_textChanged(); + void on_blocks_currentItemChanged(); + + // Logging + void on_log_doubleClicked(); + void on_verbosity_valueChanged(); + + // Misc void on_urlEdit_returnPressed(); - void on_debugStep_triggered(); - void on_debugStepBack_triggered(); - void on_debug_clicked(); - void on_debugTimeline_valueChanged(); void on_jsInput_returnPressed(); + void on_nameReg_textChanged(); + + // Special (debug) stuff + void on_paranoia_triggered(); void on_killBlockchain_triggered(); void on_clearPending_triggered(); - void on_importKey_triggered(); - void on_exportKey_triggered(); void on_inject_triggered(); - void on_showAll_triggered() { refreshBlockChain(); } - void on_showAllAccounts_triggered() { refreshAccounts(); } - void on_loadJS_triggered(); - void on_blockChainFilter_textChanged(); void on_forceMining_triggered(); - void on_dumpTrace_triggered(); - void on_dumpTraceStorage_triggered(); - void on_dumpTracePretty_triggered(); - void on_debugStepInto_triggered(); - void on_debugStepOut_triggered(); - void on_debugStepBackOut_triggered(); - void on_debugStepBackInto_triggered(); - void on_callStack_currentItemChanged(); + void on_usePrivate_triggered(); + void on_turboMining_triggered(); + void on_jitvm_triggered(); + + // Debugger void on_debugCurrent_triggered(); void on_debugDumpState_triggered(int _add = 1); void on_debugDumpStatePre_triggered(); - void on_refresh_triggered(); - void on_usePrivate_triggered(); - void on_enableOptimizer_triggered(); - void on_turboMining_triggered(); - void on_go_triggered(); - void on_importKeyFile_triggered(); - void on_post_clicked(); + + // Whisper void on_newIdentity_triggered(); - void on_jitvm_triggered(); + void on_post_clicked(); void refreshWhisper(); void refreshBlockChain(); @@ -179,33 +178,14 @@ signals: private: dev::p2p::NetworkPreferences netPrefs() const; - QString pretty(dev::Address _a) const; - QString prettyU256(dev::u256 _n) const; - QString lookup(QString const& _n) const; dev::Address getNameReg() const; dev::Address getCurrencies() const; - void populateDebugger(dev::bytesConstRef r); - void initDebugger(); - void updateDebugger(); - void debugFinished(); - QString render(dev::Address _a) const; - dev::Address fromString(QString const& _a) const; - std::string renderDiff(dev::eth::StateDiff const& _d) const; - - void alterDebugStateGroup(bool _enable) const; - void updateFee(); void readSettings(bool _skipGeometry = false); void writeSettings(); - bool isCreation() const; - dev::u256 fee() const; - dev::u256 total() const; - dev::u256 value() const; - dev::u256 gasPrice() const; - unsigned installWatch(dev::eth::LogFilter const& _tf, WatchHandler const& _f); unsigned installWatch(dev::h256 _tf, WatchHandler const& _f); void uninstallWatch(unsigned _w); @@ -232,16 +212,9 @@ private: void refreshAll(); void refreshPending(); void refreshAccounts(); - void refreshDestination(); void refreshBlockCount(); void refreshBalances(); - /// Attempts to infer that @c _source contains Solidity code - bool sourceIsSolidity(std::string const& _source); - /// @eturns all method hashes of a Solidity contract in a string - std::string const getFunctionHashes(dev::solidity::CompilerStack const &_compiler, - std::string const& _contractName = ""); - std::unique_ptr ui; std::unique_ptr m_webThree; @@ -256,22 +229,8 @@ private: QList m_myKeys; QList m_myIdentities; QString m_privateChain; - dev::bytes m_data; dev::Address m_nameReg; - unsigned m_backupGas; - - dev::eth::State m_executiveState; - std::unique_ptr m_currentExecution; - dev::h256 m_lastCode; - dev::h256 m_lastData; - std::vector m_lastLevels; - - QMap m_pcWarp; - QList m_history; - std::map m_codes; // and pcWarps - bool m_enableOptimizer = true; - QNetworkAccessManager m_webCtrl; QList> m_consoleHistory; @@ -279,10 +238,11 @@ private: QString m_logHistory; bool m_logChanged = true; - std::unique_ptr m_qwebConnector; + std::unique_ptr m_httpConnector; std::unique_ptr m_server; - QWebThree* m_qweb = nullptr; static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr); - NatspecHandler m_natspecDB; + NatspecHandler m_natSpecDB; + + Transact m_transact; }; diff --git a/alethzero/NatspecHandler.cpp b/alethzero/NatspecHandler.cpp index 25cc13d4a..bfdbaa178 100644 --- a/alethzero/NatspecHandler.cpp +++ b/alethzero/NatspecHandler.cpp @@ -50,16 +50,15 @@ NatspecHandler::~NatspecHandler() void NatspecHandler::add(dev::h256 const& _contractHash, string const& _doc) { - bytes k = _contractHash.asBytes(); - string v = _doc; - m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size())); + m_db->Put(m_writeOptions, _contractHash.ref(), _doc); + cdebug << "Registering NatSpec: " << _contractHash.abridged() << _doc; } string NatspecHandler::retrieve(dev::h256 const& _contractHash) const { - bytes k = _contractHash.asBytes(); string ret; - m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret); + m_db->Get(m_readOptions, _contractHash.ref(), &ret); + cdebug << "Looking up NatSpec: " << _contractHash.abridged() << ret; return ret; } @@ -69,8 +68,8 @@ string NatspecHandler::getUserNotice(string const& json, dev::bytes const& _tran Json::Value userNotice; string retStr; m_reader.parse(json, natspec); - bytes transactionFunctionPart(_transactionData.begin(), _transactionData.begin() + 4); - FixedHash<4> transactionFunctionHash(transactionFunctionPart); + + FixedHash<4> transactionFunctionHash((bytesConstRef(&_transactionData).cropped(0, 4).toBytes())); Json::Value methods = natspec["methods"]; for (Json::ValueIterator it = methods.begin(); it != methods.end(); ++it) diff --git a/alethzero/NatspecHandler.h b/alethzero/NatspecHandler.h index 98677dbc2..15ceb7b0b 100644 --- a/alethzero/NatspecHandler.h +++ b/alethzero/NatspecHandler.h @@ -28,10 +28,11 @@ #pragma warning(pop) #include #include +#include "Context.h" namespace ldb = leveldb; -class NatspecHandler +class NatspecHandler: public NatSpecFace { public: NatspecHandler(); @@ -40,7 +41,7 @@ class NatspecHandler /// Stores locally in a levelDB a key value pair of contract code hash to natspec documentation void add(dev::h256 const& _contractHash, std::string const& _doc); /// Retrieves the natspec documentation as a string given a contract code hash - std::string retrieve(dev::h256 const& _contractHash) const; + std::string retrieve(dev::h256 const& _contractHash) const override; /// Given a json natspec string and the transaction data return the user notice std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData); diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index 02d7236df..2d1dd0481 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -46,48 +46,71 @@ string OurWebThreeStubServer::shh_newIdentity() bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string const& _text) const { - QMessageBox userInput; - userInput.setText(QString::fromStdString(_title)); - userInput.setInformativeText(QString::fromStdString(_text + "\n Do you wish to allow this?")); - userInput.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - userInput.button(QMessageBox::Ok)->setText("Allow"); - userInput.button(QMessageBox::Cancel)->setText("Reject"); - userInput.setDefaultButton(QMessageBox::Cancel); - return userInput.exec() == QMessageBox::Ok; + int button; + QMetaObject::invokeMethod(m_main, "authenticate", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, button), Q_ARG(QString, QString::fromStdString(_title)), Q_ARG(QString, QString::fromStdString(_text))); + return button == QMessageBox::Ok; } -void OurWebThreeStubServer::showBasicValueTransferNotice(u256 _value) const +bool OurWebThreeStubServer::showCreationNotice(TransactionSkeleton const& _t) const { - QMessageBox notice; - notice.setText("Basic Value Transfer Transaction"); - notice.setInformativeText(QString::fromStdString("Value is " + toString(_value))); - notice.setStandardButtons(QMessageBox::Ok); - notice.exec(); + return showAuthenticationPopup("Contract Creation Transaction", "ÐApp is attemping to create a contract; to be endowed with " + formatBalance(_t.value) + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + "."); +} + +bool OurWebThreeStubServer::showSendNotice(TransactionSkeleton const& _t) const +{ + return showAuthenticationPopup("Fund Transfer Transaction", "ÐApp is attempting to send " + formatBalance(_t.value) + " to a recipient " + m_main->pretty(_t.to).toStdString() + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + "."); +} + +bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t) const +{ + return showAuthenticationPopup("DANGEROUS! Unknown Contract Transaction!", + "ÐApp is attempting to call into an unknown contract at address " + + m_main->pretty(_t.to).toStdString() + + ".\n\nCall involves sending " + + formatBalance(_t.value) + " to the recipient, with additional network fees of up to " + + formatBalance(_t.gas * _t.gasPrice) + + "However, this also does other stuff which we don't understand, and does so in your name.\n\n" + + "WARNING: This is probably going to cost you at least " + + formatBalance(_t.value + _t.gas * _t.gasPrice) + + ", however this doesn't include any side-effects, which could be of far greater importance.\n\n" + + "REJECT UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!"); } bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t) { + if (_t.creation) + { + // recipient has no code - nothing special about this transaction, show basic value transfer info + return showCreationNotice(_t); + } + h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to); if (contractCodeHash == EmptySHA3) - // contract creation - return true; - - if (false) //TODO: When is is just a value transfer? { // recipient has no code - nothing special about this transaction, show basic value transfer info - showBasicValueTransferNotice(_t.value); - return true; + return showSendNotice(_t); } - string userNotice = m_main->lookupNatSpecUserNotice(contractCodeHash, _t.data); + string userNotice = m_main->natSpec()->getUserNotice(contractCodeHash, _t.data); if (userNotice.empty()) - return showAuthenticationPopup("Unverified Pending Transaction", - "An undocumented transaction is about to be executed."); + return showUnknownCallNotice(_t); NatspecExpressionEvaluator evaluator; userNotice = evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString(); // otherwise it's a transaction to a contract for which we have the natspec - return showAuthenticationPopup("Pending Transaction", userNotice); + return showAuthenticationPopup("Contract Transaction", + "ÐApp attempting to conduct contract interaction with " + + m_main->pretty(_t.to).toStdString() + + ": " + userNotice + ".\n\n" + + (_t.value > 0 ? + "In addition, ÐApp is attempting to send " + + formatBalance(_t.value) + " to said recipient, with additional network fees of up to " + + formatBalance(_t.gas * _t.gasPrice) + " = " + + formatBalance(_t.value + _t.gas * _t.gasPrice) + "." + : + "Additional network fees are at most" + + formatBalance(_t.gas * _t.gasPrice) + ".") + ); } diff --git a/alethzero/OurWebThreeStubServer.h b/alethzero/OurWebThreeStubServer.h index 303b73111..fbdae7d03 100644 --- a/alethzero/OurWebThreeStubServer.h +++ b/alethzero/OurWebThreeStubServer.h @@ -42,7 +42,9 @@ signals: private: bool showAuthenticationPopup(std::string const& _title, std::string const& _text) const; - void showBasicValueTransferNotice(dev::u256 _value) const; + bool showCreationNotice(dev::eth::TransactionSkeleton const& _t) const; + bool showSendNotice(dev::eth::TransactionSkeleton const& _t) const; + bool showUnknownCallNotice(dev::eth::TransactionSkeleton const& _t) const; dev::WebThreeDirect* m_web3; Main* m_main; diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp new file mode 100644 index 000000000..3031232c2 --- /dev/null +++ b/alethzero/Transact.cpp @@ -0,0 +1,336 @@ +/* + 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 Transact.cpp + * @author Gav Wood + * @date 2015 + */ + +#include "Transact.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#include +#endif +#include "Debugger.h" +#include "ui_Transact.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +Transact::Transact(Context* _c, QWidget* _parent): + QDialog(_parent), + ui(new Ui::Transact), + m_context(_c) +{ + ui->setupUi(this); + + initUnits(ui->gasPriceUnits); + initUnits(ui->valueUnits); + ui->valueUnits->setCurrentIndex(6); + ui->gasPriceUnits->setCurrentIndex(4); + ui->gasPrice->setValue(10); + on_destination_currentTextChanged(); +} + +Transact::~Transact() +{ + delete ui; +} + +void Transact::setEnvironment(QList _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB) +{ + m_myKeys = _myKeys; + m_ethereum = _eth; + m_natSpecDB = _natSpecDB; +} + +bool Transact::isCreation() const +{ + return ui->destination->currentText().isEmpty() || ui->destination->currentText() == "(Create Contract)"; +} + +u256 Transact::fee() const +{ + return ui->gas->value() * gasPrice(); +} + +u256 Transact::value() const +{ + if (ui->valueUnits->currentIndex() == -1) + return 0; + return ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first; +} + +u256 Transact::gasPrice() const +{ + if (ui->gasPriceUnits->currentIndex() == -1) + return 0; + return ui->gasPrice->value() * units()[units().size() - 1 - ui->gasPriceUnits->currentIndex()].first; +} + +u256 Transact::total() const +{ + return value() + fee(); +} + +void Transact::updateDestination() +{ + cwatch << "updateDestination()"; + QString s; + for (auto i: ethereum()->addresses()) + if ((s = m_context->pretty(i)).size()) + // A namereg address + if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1) + ui->destination->addItem(s); + for (int i = 0; i < ui->destination->count(); ++i) + if (ui->destination->itemText(i) != "(Create Contract)" && !m_context->fromString(ui->destination->itemText(i))) + ui->destination->removeItem(i--); +} + +void Transact::updateFee() +{ + ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str())); + auto totalReq = total(); + ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str())); + + bool ok = false; + for (auto i: m_myKeys) + if (ethereum()->balanceAt(i.address()) >= totalReq) + { + ok = true; + break; + } + ui->send->setEnabled(ok); + QPalette p = ui->total->palette(); + p.setColor(QPalette::WindowText, QColor(ok ? 0x00 : 0x80, 0x00, 0x00)); + ui->total->setPalette(p); +} + +string Transact::getFunctionHashes(dev::solidity::CompilerStack const& _compiler, string const& _contractName) +{ + string ret = ""; + auto const& contract = _compiler.getContractDefinition(_contractName); + auto interfaceFunctions = contract.getInterfaceFunctions(); + + for (auto const& it: interfaceFunctions) + { + ret += it.first.abridged(); + ret += " :"; + ret += it.second->getDeclaration().getName() + "\n"; + } + return ret; +} + +void Transact::on_destination_currentTextChanged() +{ + if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)") + if (Address a = m_context->fromString(ui->destination->currentText())) + ui->calculatedName->setText(m_context->render(a)); + else + ui->calculatedName->setText("Unknown Address"); + else + ui->calculatedName->setText("Create Contract"); + rejigData(); +// updateFee(); +} + +void Transact::rejigData() +{ + if (isCreation()) + { + string src = ui->data->toPlainText().toStdString(); + vector errors; + QString lll; + QString solidity; + if (src.find_first_not_of("1234567890abcdefABCDEF") == string::npos && src.size() % 2 == 0) + { + m_data = fromHex(src); + } + else if (sourceIsSolidity(src)) + { + dev::solidity::CompilerStack compiler; + try + { +// compiler.addSources(dev::solidity::StandardSources); + m_data = compiler.compile(src, ui->optimize->isChecked()); + solidity = "

Solidity

"; + solidity += "
var " + QString::fromStdString(compiler.defaultContractName()) + " = web3.eth.contractFromAbi(" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + ");
"; + solidity += "
" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "
"; + solidity += "
" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "
"; + } + catch (dev::Exception const& exception) + { + ostringstream error; + solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); + solidity = "

Solidity

" + QString::fromStdString(error.str()).toHtmlEscaped() + "
"; + } + catch (...) + { + solidity = "

Solidity

Uncaught exception.
"; + } + } +#ifndef _MSC_VER + else if (sourceIsSerpent(src)) + { + try + { + m_data = dev::asBytes(::compile(src)); + for (auto& i: errors) + i = "(LLL " + i + ")"; + } + catch (string err) + { + errors.push_back("Serpent " + err); + } + } +#endif + else + { + m_data = compileLLL(src, ui->optimize->isChecked(), &errors); + if (errors.empty()) + { + auto asmcode = compileLLLToAsm(src, false); + lll = "

Pre

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
"; + if (ui->optimize->isChecked()) + { + asmcode = compileLLLToAsm(src, true); + lll = "

Opt

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
" + lll; + } + } + } + QString errs; + if (errors.size()) + { + errs = "

Errors

"; + for (auto const& i: errors) + errs.append("
" + QString::fromStdString(i).toHtmlEscaped() + "
"); + } + ui->code->setHtml(errs + lll + solidity + "

Code

" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped() + "

Hex

" Div(Mono) + QString::fromStdString(toHex(m_data)) + ""); + ui->gas->setMinimum((qint64)Interface::txGas(m_data, 0)); + if (!ui->gas->isEnabled()) + ui->gas->setValue(m_backupGas); + ui->gas->setEnabled(true); + } + else + { + m_data = parseData(ui->data->toPlainText().toStdString()); + auto to = m_context->fromString(ui->destination->currentText()); + QString natspec; + if (ethereum()->codeAt(to, 0).size()) + { + string userNotice = m_natSpecDB->getUserNotice(ethereum()->postState().codeHash(to), m_data); + if (userNotice.empty()) + natspec = "Destination contract unknown."; + else + { + NatspecExpressionEvaluator evaluator; + natspec = evaluator.evalExpression(QString::fromStdString(userNotice)); + } + ui->gas->setMinimum((qint64)Interface::txGas(m_data, 1)); + if (!ui->gas->isEnabled()) + ui->gas->setValue(m_backupGas); + ui->gas->setEnabled(true); + } + else + { + natspec += "Destination not a contract."; + if (ui->gas->isEnabled()) + m_backupGas = ui->gas->value(); + ui->gas->setValue((qint64)Interface::txGas(m_data)); + ui->gas->setEnabled(false); + } + ui->code->setHtml("

NatSpec

" + natspec + "

Dump

" + QString::fromStdString(dev::memDump(m_data, 8, true)) + "

Hex

" + Div(Mono) + QString::fromStdString(toHex(m_data)) + ""); + } + updateFee(); +} + +void Transact::on_send_clicked() +{ + u256 totalReq = value() + fee(); + for (auto const& i: m_myKeys) + if (ethereum()->balanceAt(i.address(), 0) >= totalReq) + { + Secret s = i.secret(); + if (isCreation()) + { + // If execution is a contract creation, add Natspec to + // a local Natspec LEVELDB + ethereum()->transact(s, value(), m_data, ui->gas->value(), gasPrice()); + string src = ui->data->toPlainText().toStdString(); + if (sourceIsSolidity(src)) + try + { + dev::solidity::CompilerStack compiler; + m_data = compiler.compile(src, ui->optimize->isChecked()); + for (string const& s: compiler.getContractNames()) + { + h256 contractHash = compiler.getContractCodeHash(s); + m_natSpecDB->add(contractHash, compiler.getMetadata(s, dev::solidity::DocumentationType::NatspecUser)); + } + } + catch (...) + { + } + close(); + return; + } + else + ethereum()->transact(s, value(), m_context->fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); + return; + } + QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount."); +} + +void Transact::on_debug_clicked() +{ + try + { + u256 totalReq = value() + fee(); + for (auto i: m_myKeys) + if (ethereum()->balanceAt(i.address()) >= totalReq) + { + State st(ethereum()->postState()); + Secret s = i.secret(); + Transaction t = isCreation() ? + Transaction(value(), gasPrice(), ui->gas->value(), m_data, st.transactionsFrom(dev::toAddress(s)), s) : + Transaction(value(), gasPrice(), ui->gas->value(), m_context->fromString(ui->destination->currentText()), m_data, st.transactionsFrom(dev::toAddress(s)), s); + Debugger dw(m_context, this); + Executive e(st, ethereum()->blockChain(), 0); + dw.populate(e, t); + dw.exec(); + return; + } + QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount."); + } + catch (dev::Exception const& _e) + { + QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction. Low-level error: " + QString::fromStdString(diagnostic_information(_e))); + // this output is aimed at developers, reconsider using _e.what for more user friendly output. + } +} diff --git a/alethzero/Transact.h b/alethzero/Transact.h new file mode 100644 index 000000000..afb45f62d --- /dev/null +++ b/alethzero/Transact.h @@ -0,0 +1,82 @@ +/* + 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 Transact.h + * @author Gav Wood + * @date 2015 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "Context.h" + +namespace Ui { class Transact; } +namespace dev { namespace eth { class Client; } } +namespace dev { namespace solidity { class CompilerStack; } } + +class Transact: public QDialog +{ + Q_OBJECT + +public: + explicit Transact(Context* _context, QWidget* _parent = 0); + ~Transact(); + + void setEnvironment(QList _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); + +private slots: + void on_destination_currentTextChanged(); + void on_value_valueChanged() { updateFee(); } + void on_gas_valueChanged() { updateFee(); } + void on_valueUnits_currentIndexChanged() { updateFee(); } + void on_gasPriceUnits_currentIndexChanged() { updateFee(); } + void on_gasPrice_valueChanged() { updateFee(); } + void on_data_textChanged() { rejigData(); } + void on_optimize_clicked() { rejigData(); } + void on_send_clicked(); + void on_debug_clicked(); + void on_cancel_clicked() { close(); } + +private: + dev::eth::Client* ethereum() { return m_ethereum; } + void rejigData(); + + void updateDestination(); + void updateFee(); + bool isCreation() const; + dev::u256 fee() const; + dev::u256 total() const; + dev::u256 value() const; + dev::u256 gasPrice() const; + + std::string getFunctionHashes(dev::solidity::CompilerStack const& _compiler, std::string const& _contractName = std::string()); + + Ui::Transact* ui; + + unsigned m_backupGas; + dev::bytes m_data; + + QList m_myKeys; + dev::eth::Client* m_ethereum; + Context* m_context; + NatSpecFace* m_natSpecDB; +}; diff --git a/alethzero/Transact.ui b/alethzero/Transact.ui new file mode 100644 index 000000000..c66e47aa8 --- /dev/null +++ b/alethzero/Transact.ui @@ -0,0 +1,244 @@ + + + Transact + + + + 0 + 0 + 543 + 695 + + + + Dialog + + + + + + + + + 430000000 + + + 0 + + + + + + + &Amount + + + value + + + + + + + false + + + true + + + + + + + + + + Qt::Vertical + + + + QFrame::NoFrame + + + 0 + + + + + Qt::ClickFocus + + + QFrame::NoFrame + + + 0 + + + true + + + + + + + + + + + + 0 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + &To + + + destination + + + + + + + + + + &Debug + + + + + + + &Execute + + + false + + + + + + + &Gas + + + gas + + + + + + + gas + + + 1 + + + 430000000 + + + 10000 + + + + + + + @ + + + 1 + + + 430000000 + + + + + + + &Optimise + + + true + + + + + + + + 0 + 0 + + + + D&ata + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + data + + + + + + + true + + + + (Create Contract) + + + + + + + + + 0 + 0 + + + + + + + + + + + &Cancel + + + Esc + + + + + + + + diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 12adc1d02..4d56c808b 100755 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -27,13 +27,14 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + # enable parallel compilation # specify Exception Handling Model in msvc # disable unknown pragma warning (4068) # disable unsafe function warning (4996) # disable decorated name length exceeded, name was truncated (4503) # disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests) # declare Windows XP requirement - add_compile_options(/EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501) + add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501) # disable empty object file warning set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index bcb0780c1..fb6b09bb8 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -59,6 +59,10 @@ if (JSONRPC) message (" - json-rpc-cpp lib : ${JSON_RPC_CPP_LIBRARIES}") add_definitions(-DETH_JSONRPC) + find_package(MHD) + message(" - microhttpd header: ${MHD_INCLUDE_DIRS}") + message(" - microhttpd lib : ${MHD_LIBRARIES}") + endif() #JSONRPC # TODO readline package does not yet check for correct version number diff --git a/cmake/FindMHD.cmake b/cmake/FindMHD.cmake new file mode 100755 index 000000000..84875bc47 --- /dev/null +++ b/cmake/FindMHD.cmake @@ -0,0 +1,47 @@ +# Find microhttpd +# +# Find the microhttpd includes and library +# +# if you need to add a custom library search path, do it via via CMAKE_PREFIX_PATH +# +# This module defines +# MHD_INCLUDE_DIRS, where to find header, etc. +# MHD_LIBRARIES, the libraries needed to use jsoncpp. +# MHD_FOUND, If false, do not try to use jsoncpp. + +find_path( + MHD_INCLUDE_DIR + NAMES microhttpd.h + DOC "microhttpd include dir" +) + +find_library( + MHD_LIBRARY + NAMES microhttpd microhttpd-10 libmicrohttpd libmicrohttpd-dll + DOC "microhttpd library" +) + +set(MHD_INCLUDE_DIRS ${MHD_INCLUDE_DIR}) +set(MHD_LIBRARIES ${MHD_LIBRARY}) + +# debug library on windows +# same naming convention as in QT (appending debug library with d) +# boost is using the same "hack" as us with "optimized" and "debug" +# official MHD project actually uses _d suffix +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + find_library( + MHD_LIBRARY_DEBUG + NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d + DOC "mhd debug library" + ) + + set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG}) + +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(mhd DEFAULT_MSG + MHD_INCLUDE_DIR MHD_LIBRARY) + +mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY) + diff --git a/extdep/CMakeLists.txt b/extdep/CMakeLists.txt index fdee34602..99f76800c 100644 --- a/extdep/CMakeLists.txt +++ b/extdep/CMakeLists.txt @@ -35,14 +35,18 @@ if (ETH_COMPILE) include(compile/boost.cmake) else() eth_download(jsoncpp) - eth_download(json-rpc-cpp OSX_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/json-rpc-cpp_osx.sh) + eth_download(microhttpd) + eth_download(json-rpc-cpp + VERSION 4.2 + OSX_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/json-rpc-cpp_osx.sh + ) if (APPLE) eth_download(snappy OSX_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/snappy_osx.sh) endif() eth_download(leveldb OSX_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/leveldb_osx.sh) - eth_download(qt) + eth_download(qt VERSION 5.4) eth_download(cryptopp) eth_download(boost) eth_download(curl) diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index 4b1df924d..9039c3149 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -1,7 +1,8 @@ #pragma once -#include +#include #include +#include #include #include @@ -18,7 +19,7 @@ public: vector_ref(): m_data(nullptr), m_count(0) {} vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} - vector_ref(std::string* _data): m_data((_T*)_data->data()), m_count(_data->size() / sizeof(_T)) {} + vector_ref(typename std::conditional::value, std::string const*, std::string*>::type _data): m_data((_T*)_data->data()), m_count(_data->size() / sizeof(_T)) {} vector_ref(typename std::conditional::value, std::vector::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {} vector_ref(typename std::conditional::value, std::string const&, std::string&>::type _data): m_data((_T*)_data.data()), m_count(_data.size() / sizeof(_T)) {} #ifdef STORAGE_LEVELDB_INCLUDE_DB_H_ diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index 47344a156..e07eb04ba 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -35,16 +35,6 @@ namespace eth const unsigned c_protocolVersion = 53; const unsigned c_databaseVersion = 5; -template u256 exp10() -{ - return exp10() * u256(10); -} - -template <> u256 exp10<0>() -{ - return u256(1); -} - vector> const& units() { static const vector> s_units = diff --git a/libethcore/CommonEth.h b/libethcore/CommonEth.h index 966794953..79525082f 100644 --- a/libethcore/CommonEth.h +++ b/libethcore/CommonEth.h @@ -47,26 +47,21 @@ std::vector> const& units(); /// The log bloom's size (512 bit). using LogBloom = h512; +template inline u256 exp10() +{ + return exp10() * u256(10); +} + +template <> inline u256 exp10<0>() +{ + return u256(1); +} + // The various denominations; here for ease of use where needed within code. -/*static const u256 Uether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; -static const u256 Vether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; -static const u256 Dether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; -static const u256 Nether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; -static const u256 Yether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; -static const u256 Zether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; -static const u256 Eether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000000; -static const u256 Pether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000; -static const u256 Tether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000; -static const u256 Gether = (u256(1000000000) * 1000000000) * 1000000000; -static const u256 Mether = (u256(1000000000) * 1000000000) * 1000000; -static const u256 grand = (u256(1000000000) * 1000000000) * 1000;*/ -static const u256 ether = u256(1000000000) * 1000000000; -static const u256 finney = u256(1000000000) * 1000000; -static const u256 szabo = u256(1000000000) * 1000; -/*static const u256 Gwei = u256(1000000000); -static const u256 Mwei = u256(1000000); -static const u256 Kwei = u256(1000);*/ -static const u256 wei = u256(1); +static const u256 ether = exp10<18>(); +static const u256 finney = exp10<15>(); +static const u256 szabo = exp10<12>(); +static const u256 wei = exp10<0>(); } } diff --git a/libethcore/CommonJS.cpp b/libethcore/CommonJS.cpp index 3ed4e2796..388738528 100644 --- a/libethcore/CommonJS.cpp +++ b/libethcore/CommonJS.cpp @@ -97,7 +97,7 @@ std::string fromRaw(h256 _n, unsigned* _inc) return ""; } -std::string prettyU256(u256 _n) +std::string prettyU256(u256 _n, bool _abridged) { unsigned inc = 0; std::string raw; @@ -110,11 +110,16 @@ std::string prettyU256(u256 _n) { Address a = right160(_n); - std::string n = a.abridged(); + std::string n; + if (_abridged) + n = a.abridged(); + else + n = toHex(a.ref()); + if (n.empty()) - s << "0x" << a; + s << "0"; else - s << n << "(0x" << a.abridged() << ")"; + s << _n << "(0x" << n << ")"; } else if ((raw = fromRaw((h256)_n, &inc)).size()) return "\"" + raw + "\"" + (inc ? " + " + std::to_string(inc) : ""); diff --git a/libethcore/CommonJS.h b/libethcore/CommonJS.h index ccc3b3103..abe74f0af 100644 --- a/libethcore/CommonJS.h +++ b/libethcore/CommonJS.h @@ -59,7 +59,7 @@ bytes unpadded(bytes _s); /// Remove all 0 byte on the head of @a _s. bytes unpadLeft(bytes _s); /// Convert u256 into user-readable string. Returns int/hex value of 64 bits int, hex of 160 bits FixedHash. As a fallback try to handle input as h256. -std::string prettyU256(u256 _n); +std::string prettyU256(u256 _n, bool _abridged = true); /// Convert h256 into user-readable string (by directly using std::string constructor). std::string fromRaw(h256 _n, unsigned* _inc = nullptr); /// Convert string to Address (h160), returns empty address if (_a.size != 40). @@ -144,6 +144,7 @@ inline Address jsToAddress(std::string const& _s) { return jsToFixed m_ext; ///< The VM externality object for the VM execution or null if no VM is required. diff --git a/libjsqrc/setup.js b/libjsqrc/setup.js index 2aa54ed30..fa1921877 100644 --- a/libjsqrc/setup.js +++ b/libjsqrc/setup.js @@ -20,8 +20,6 @@ * @date 2014 */ -navigator.qt = _web3; - var web3 = require('web3'); -web3.setProvider(new web3.providers.QtSyncProvider()); +web3.setProvider(new web3.providers.HttpSyncProvider()); diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 0217c700e..23ca33661 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -168,10 +168,10 @@ public: /// Returns the Node to the corresponding node id or the empty Node if that id is not found. Node node(NodeId const& _id); -#ifndef BOOST_AUTO_TEST_SUITE -private: -#else +#if defined(BOOST_AUTO_TEST_SUITE) || defined(_MSC_VER) // MSVC includes access specifier in symbol name protected: +#else +private: #endif /// Constants for Kademlia, derived from address space. diff --git a/libqwebthree/CMakeLists.txt b/libqwebthree/CMakeLists.txt deleted file mode 100644 index 4de131ead..000000000 --- a/libqwebthree/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -cmake_policy(SET CMP0015 NEW) -# let cmake autolink dependencies on windows -cmake_policy(SET CMP0020 NEW) -# this policy was introduced in cmake 3.0 -# remove if, once 3.0 will be used on unix -if (${CMAKE_MAJOR_VERSION} GREATER 2) - # old policy do not use MACOSX_RPATH - cmake_policy(SET CMP0042 OLD) - cmake_policy(SET CMP0043 OLD) -endif() - -set(CMAKE_INCLUDE_CURRENT_DIR ON) -aux_source_directory(. SRC_LIST) - -include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) -include_directories(..) - -set(EXECUTABLE qwebthree) - -file(GLOB HEADERS "*.h") - -if(ETH_STATIC) - add_library(${EXECUTABLE} STATIC ${RESOURCE_ADDED} ${SRC_LIST} ${HEADERS}) -else() - add_library(${EXECUTABLE} SHARED ${RESOURCE_ADDED} ${SRC_LIST} ${HEADERS}) -endif() - -target_link_libraries(${EXECUTABLE} Qt5::Core) -target_link_libraries(${EXECUTABLE} Qt5::Gui) -target_link_libraries(${EXECUTABLE} Qt5::WebKit) -target_link_libraries(${EXECUTABLE} Qt5::WebKitWidgets) -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} ${JSON_RPC_CPP_SERVER_LIBRARIES}) -target_link_libraries(${EXECUTABLE} ethereum) -target_link_libraries(${EXECUTABLE} secp256k1) - -install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) -install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) diff --git a/libqwebthree/QWebThree.cpp b/libqwebthree/QWebThree.cpp deleted file mode 100644 index 31f2f6b92..000000000 --- a/libqwebthree/QWebThree.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - 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 QWebThree.cpp - * @authors: - * Gav Wood - * Marek Kotewicz - * @date 2014 - */ - -#include -#include "QWebThree.h" - -using namespace std; - -QWebThree::QWebThree(QObject* _p): QObject(_p) -{ - moveToThread(_p->thread()); -} - -QWebThree::~QWebThree() -{ - clientDieing(); -} - -void QWebThree::clientDieing() -{ - this->disconnect(); -} - -QString QWebThree::callMethod(QString _json) -{ - emit processData(_json, ""); // it's synchronous - return m_response; -} - -void QWebThree::onDataProcessed(QString _json, QString) -{ - QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object(); - syncResponse(QString::fromUtf8(QJsonDocument(f).toJson())); -} - -void QWebThree::syncResponse(QString _json) -{ - m_response = _json; -} - -QWebThreeConnector::QWebThreeConnector() -{ -} - -QWebThreeConnector::~QWebThreeConnector() -{ - StopListening(); -} - -void QWebThreeConnector::setQWeb(QWebThree* _q) -{ - m_qweb = _q; - if (m_isListening) - { - StopListening(); - StartListening(); - } -} - -bool QWebThreeConnector::StartListening() -{ - m_isListening = true; - if (m_qweb) - { - connect(m_qweb, SIGNAL(processData(QString, QString)), this, SLOT(onProcessData(QString, QString))); - connect(this, SIGNAL(dataProcessed(QString, QString)), m_qweb, SLOT(onDataProcessed(QString, QString))); - } - return true; -} - -bool QWebThreeConnector::StopListening() -{ - this->disconnect(); - return true; -} - -bool QWebThreeConnector::SendResponse(std::string const& _response, void* _addInfo) -{ - emit dataProcessed(QString::fromStdString(_response), *(QString*)_addInfo); - return true; -} - -void QWebThreeConnector::onProcessData(QString const& _json, QString const& _addInfo) -{ - OnRequest(_json.toStdString(), (void*)&_addInfo); -} - diff --git a/libqwebthree/QWebThree.h b/libqwebthree/QWebThree.h deleted file mode 100644 index 72c434649..000000000 --- a/libqwebthree/QWebThree.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - 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 QWebThree.h - * @authors: - * Gav Wood - * Marek Kotewicz - * @date 2014 - */ - -#pragma once - -#include -#include -#include - -class QWebThree: public QObject -{ - Q_OBJECT - -public: - QWebThree(QObject* _p); - virtual ~QWebThree(); - void clientDieing(); - - Q_INVOKABLE QString callMethod(QString _json); - void syncResponse(QString _json); - -public slots: - void onDataProcessed(QString _json, QString _addInfo); - -signals: - void processData(QString _json, QString _addInfo); - void response(QString _json); - void onNewId(QString _id); - -private: - QString m_response; -}; - -class QWebThreeConnector: public QObject, public jsonrpc::AbstractServerConnector -{ - Q_OBJECT - -public: - QWebThreeConnector(); - virtual ~QWebThreeConnector(); - - void setQWeb(QWebThree *_q); - - virtual bool StartListening(); - virtual bool StopListening(); - virtual bool SendResponse(std::string const& _response, void* _addInfo = NULL); - -public slots: - void onProcessData(QString const& _json, QString const& _addInfo); - -signals: - void dataProcessed(QString const& _json, QString const& _addInfo); - -private: - QWebThree* m_qweb = nullptr; - bool m_isListening; -}; - -#define QETH_INSTALL_JS_NAMESPACE(_frame, _env, qweb) [_frame, _env, qweb]() \ -{ \ - _frame->disconnect(); \ - _frame->addToJavaScriptWindowObject("_web3", qweb, QWebFrame::ScriptOwnership); \ - _frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \ - _frame->evaluateJavaScript(contentsOfQResource(":/js/bignumber.min.js")); \ - _frame->evaluateJavaScript(contentsOfQResource(":/js/webthree.js")); \ - _frame->evaluateJavaScript(contentsOfQResource(":/js/setup.js")); \ -} - - diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 3c6b6007c..0dbad433f 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -206,6 +206,13 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn return *m_interfaceFunctionList; } +TypePointer EnumValue::getType(ContractDefinition const*) const +{ + EnumDefinition const* parentDef = dynamic_cast(getScope()); + solAssert(parentDef, "Enclosing Scope of EnumValue was not set"); + return make_shared(*parentDef); +} + void InheritanceSpecifier::checkTypeRequirements() { m_baseName->checkTypeRequirements(); @@ -255,6 +262,11 @@ void StructDefinition::checkRecursion() const } } +TypePointer EnumDefinition::getType(ContractDefinition const*) const +{ + return make_shared(make_shared(*this)); +} + TypePointer FunctionDefinition::getType(ContractDefinition const*) const { return make_shared(*this); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 4e79026e8..51d8031a3 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -209,6 +209,7 @@ public: ASTPointer const& _documentation, std::vector> const& _baseContracts, std::vector> const& _definedStructs, + std::vector> const& _definedEnums, std::vector> const& _stateVariables, std::vector> const& _definedFunctions, std::vector> const& _functionModifiers, @@ -216,6 +217,7 @@ public: Declaration(_location, _name), Documented(_documentation), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), + m_definedEnums(_definedEnums), m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions), m_functionModifiers(_functionModifiers), @@ -227,6 +229,7 @@ public: std::vector> const& getBaseContracts() const { return m_baseContracts; } std::vector> const& getDefinedStructs() const { return m_definedStructs; } + std::vector> const& getDefinedEnums() const { return m_definedEnums; } std::vector> const& getStateVariables() const { return m_stateVariables; } std::vector> const& getFunctionModifiers() const { return m_functionModifiers; } std::vector> const& getDefinedFunctions() const { return m_definedFunctions; } @@ -260,6 +263,7 @@ private: std::vector> m_baseContracts; std::vector> m_definedStructs; + std::vector> m_definedEnums; std::vector> m_stateVariables; std::vector> m_definedFunctions; std::vector> m_functionModifiers; @@ -315,6 +319,39 @@ private: std::vector> m_members; }; +class EnumDefinition: public Declaration +{ +public: + EnumDefinition(Location const& _location, + ASTPointer const& _name, + std::vector> const& _members): + Declaration(_location, _name), m_members(_members) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector> const& getMembers() const { return m_members; } + + virtual TypePointer getType(ContractDefinition const*) const override; + +private: + std::vector> m_members; +}; + +/** + * Declaration of an Enum Value + */ +class EnumValue: public Declaration +{ + public: + EnumValue(Location const& _location, + ASTPointer const& _name): + Declaration(_location, _name) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + TypePointer getType(ContractDefinition const* = nullptr) const; +}; + /** * Parameter list, used as function parameter list and return list. * None of the parameters is allowed to contain mappings (not even recursively @@ -679,7 +716,7 @@ public: Expression const& getCondition() const { return *m_condition; } Statement const& getTrueStatement() const { return *m_trueBody; } - /// @returns the "else" part of the if statement or nullptr if there is no "else" part. + /// @returns the "else" part of the if statement or nullptr if there is no "else" part. Statement const* getFalseStatement() const { return m_falseBody.get(); } private: diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index 22015f26b..0b6817e45 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -40,6 +40,8 @@ class Declaration; class ContractDefinition; class InheritanceSpecifier; class StructDefinition; +class EnumDefinition; +class EnumValue; class ParameterList; class FunctionDefinition; class VariableDeclaration; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 949740e89..d380b0029 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -71,6 +71,18 @@ bool ASTPrinter::visit(StructDefinition const& _node) return goDeeper(); } +bool ASTPrinter::visit(EnumDefinition const& _node) +{ + writeLine("EnumDefinition \"" + _node.getName() + "\""); + return goDeeper(); +} + +bool ASTPrinter::visit(EnumValue const& _node) +{ + writeLine("EnumValue \"" + _node.getName() + "\""); + return goDeeper(); +} + bool ASTPrinter::visit(ParameterList const& _node) { writeLine("ParameterList"); @@ -347,6 +359,16 @@ void ASTPrinter::endVisit(StructDefinition const&) m_indentation--; } +void ASTPrinter::endVisit(EnumDefinition const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(EnumValue const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(ParameterList const&) { m_indentation--; diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index ebc163e31..d9072aacc 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -46,6 +46,8 @@ public: bool visit(ContractDefinition const& _node) override; bool visit(InheritanceSpecifier const& _node) override; bool visit(StructDefinition const& _node) override; + bool visit(EnumDefinition const& _node) override; + bool visit(EnumValue const& _node) override; bool visit(ParameterList const& _node) override; bool visit(FunctionDefinition const& _node) override; bool visit(VariableDeclaration const& _node) override; @@ -85,6 +87,8 @@ public: void endVisit(ContractDefinition const&) override; void endVisit(InheritanceSpecifier const&) override; void endVisit(StructDefinition const&) override; + void endVisit(EnumDefinition const&) override; + void endVisit(EnumValue const&) override; void endVisit(ParameterList const&) override; void endVisit(FunctionDefinition const&) override; void endVisit(VariableDeclaration const&) override; diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index 294902778..a7fa6b1cf 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -47,6 +47,8 @@ public: virtual bool visit(ContractDefinition&) { return true; } virtual bool visit(InheritanceSpecifier&) { return true; } virtual bool visit(StructDefinition&) { return true; } + virtual bool visit(EnumDefinition&) { return true; } + virtual bool visit(EnumValue&) { return true; } virtual bool visit(ParameterList&) { return true; } virtual bool visit(FunctionDefinition&) { return true; } virtual bool visit(VariableDeclaration&) { return true; } @@ -88,6 +90,8 @@ public: virtual void endVisit(ContractDefinition&) { } virtual void endVisit(InheritanceSpecifier&) { } virtual void endVisit(StructDefinition&) { } + virtual void endVisit(EnumDefinition&) { } + virtual void endVisit(EnumValue&) { } virtual void endVisit(ParameterList&) { } virtual void endVisit(FunctionDefinition&) { } virtual void endVisit(VariableDeclaration&) { } @@ -133,6 +137,8 @@ public: virtual bool visit(ContractDefinition const&) { return true; } virtual bool visit(InheritanceSpecifier const&) { return true; } virtual bool visit(StructDefinition const&) { return true; } + virtual bool visit(EnumDefinition const&) { return true; } + virtual bool visit(EnumValue const&) { return true; } virtual bool visit(ParameterList const&) { return true; } virtual bool visit(FunctionDefinition const&) { return true; } virtual bool visit(VariableDeclaration const&) { return true; } @@ -174,6 +180,8 @@ public: virtual void endVisit(ContractDefinition const&) { } virtual void endVisit(InheritanceSpecifier const&) { } virtual void endVisit(StructDefinition const&) { } + virtual void endVisit(EnumDefinition const&) { } + virtual void endVisit(EnumValue const&) { } virtual void endVisit(ParameterList const&) { } virtual void endVisit(FunctionDefinition const&) { } virtual void endVisit(VariableDeclaration const&) { } diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index 38108cd72..b71e103df 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/AST_accept.h @@ -63,6 +63,7 @@ void ContractDefinition::accept(ASTVisitor& _visitor) { listAccept(m_baseContracts, _visitor); listAccept(m_definedStructs, _visitor); + listAccept(m_definedEnums, _visitor); listAccept(m_stateVariables, _visitor); listAccept(m_events, _visitor); listAccept(m_functionModifiers, _visitor); @@ -77,6 +78,7 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const { listAccept(m_baseContracts, _visitor); listAccept(m_definedStructs, _visitor); + listAccept(m_definedEnums, _visitor); listAccept(m_stateVariables, _visitor); listAccept(m_events, _visitor); listAccept(m_functionModifiers, _visitor); @@ -105,6 +107,32 @@ void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void EnumDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_members, _visitor); + _visitor.endVisit(*this); +} + +void EnumDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + listAccept(m_members, _visitor); + _visitor.endVisit(*this); +} + +void EnumValue::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void EnumValue::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + void StructDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 762edb521..ca9c75bcd 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -333,6 +333,11 @@ void CompilerStack::resolveImports() swap(m_sourceOrder, sourceOrder); } +std::string CompilerStack::defaultContractName() const +{ + return getContract("").contract->getName(); +} + CompilerStack::Contract const& CompilerStack::getContract(string const& _contractName) const { if (m_contracts.empty()) diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 439077f36..cb4770cd3 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -73,6 +73,7 @@ public: void parse(std::string const& _sourceCode); /// Returns a list of the contract names in the sources. std::vector getContractNames() const; + std::string defaultContractName() const; /// Compiles the source units that were previously added and parsed. void compile(bool _optimize = false); diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 90f860a1e..0f0e94f21 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -489,13 +489,21 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); break; } + case Type::Category::Enum: + { + EnumType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); + m_context << type.getMemberValue(_memberAccess.getMemberName()); + break; + } case Type::Category::TypeType: { TypeType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - if (type.getMembers().getMemberType(member)) + if (!type.getMembers().getMemberType(member)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to " + type.toString())); + + if (auto contractType = dynamic_cast(type.getActualType().get())) { - ContractDefinition const& contract = dynamic_cast(*type.getActualType()) - .getContractDefinition(); + ContractDefinition const& contract = contractType->getContractDefinition(); for (ASTPointer const& function: contract.getDefinedFunctions()) if (function->getName() == member) { @@ -503,7 +511,9 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) return; } } - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to " + type.toString())); + else if (auto enumType = dynamic_cast(type.getActualType().get())) + m_context << enumType->getMemberValue(_memberAccess.getMemberName()); + break; } case Type::Category::ByteArray: { @@ -562,6 +572,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) { // no-op } + else if (dynamic_cast(declaration)) + { + // no-op + } else { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); @@ -745,6 +759,9 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con } } } + else if (stackTypeCategory == Type::Category::Enum) + solAssert(targetTypeCategory == Type::Category::Integer || + targetTypeCategory == Type::Category::Enum, ""); else if (stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::Contract || stackTypeCategory == Type::Category::IntegerConstant) { @@ -758,6 +775,9 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con solAssert(typeOnStack.getNumBits() == targetStringType.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL; } + else if (targetTypeCategory == Type::Category::Enum) + // just clean + appendTypeConversion(_typeOnStack, *_typeOnStack.getRealType(), true); else { solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 7dc42bc62..dbe5693a8 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -58,6 +58,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) for (ASTPointer const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, &_contract, nullptr); + for (ASTPointer const& enumDef: _contract.getDefinedEnums()) + ReferencesResolver resolver(*enumDef, *this, &_contract, nullptr); for (ASTPointer const& variable: _contract.getStateVariables()) ReferencesResolver resolver(*variable, *this, &_contract, nullptr); for (ASTPointer const& event: _contract.getEvents()) @@ -221,6 +223,23 @@ void DeclarationRegistrationHelper::endVisit(StructDefinition&) closeCurrentScope(); } +bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum) +{ + registerDeclaration(_enum, true); + return true; +} + +void DeclarationRegistrationHelper::endVisit(EnumDefinition&) +{ + closeCurrentScope(); +} + +bool DeclarationRegistrationHelper::visit(EnumValue& _value) +{ + registerDeclaration(_value, false); + return true; +} + bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function) { registerDeclaration(_function, true); diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 4b7ce6e7d..d9ac98ce5 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -98,6 +98,9 @@ private: void endVisit(ContractDefinition& _contract) override; bool visit(StructDefinition& _struct) override; void endVisit(StructDefinition& _struct) override; + bool visit(EnumDefinition& _enum) override; + void endVisit(EnumDefinition& _enum) override; + bool visit(EnumValue& _value) override; bool visit(FunctionDefinition& _function) override; void endVisit(FunctionDefinition& _function) override; bool visit(ModifierDefinition& _modifier) override; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index f076df876..c96593f64 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -119,6 +119,7 @@ ASTPointer Parser::parseContractDefinition() ASTPointer name = expectIdentifierToken(); vector> baseContracts; vector> structs; + vector> enums; vector> stateVariables; vector> functions; vector> modifiers; @@ -140,6 +141,8 @@ ASTPointer Parser::parseContractDefinition() functions.push_back(parseFunctionDefinition(name.get())); else if (currentToken == Token::Struct) structs.push_back(parseStructDefinition()); + else if (currentToken == Token::Enum) + enums.push_back(parseEnumDefinition()); else if (currentToken == Token::Identifier || currentToken == Token::Mapping || Token::isElementaryTypeName(currentToken)) { @@ -157,7 +160,7 @@ ASTPointer Parser::parseContractDefinition() } nodeFactory.markEndPosition(); expectToken(Token::RBrace); - return nodeFactory.createNode(name, docString, baseContracts, structs, + return nodeFactory.createNode(name, docString, baseContracts, structs, enums, stateVariables, functions, modifiers, events); } @@ -263,6 +266,36 @@ ASTPointer Parser::parseStructDefinition() return nodeFactory.createNode(name, members); } +ASTPointer Parser::parseEnumValue() +{ + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); + return nodeFactory.createNode(expectIdentifierToken()); +} + +ASTPointer Parser::parseEnumDefinition() +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::Enum); + ASTPointer name = expectIdentifierToken(); + vector> members; + expectToken(Token::LBrace); + + while (m_scanner->getCurrentToken() != Token::RBrace) + { + members.push_back(parseEnumValue()); + if (m_scanner->getCurrentToken() == Token::RBrace) + break; + expectToken(Token::Comma); + if (m_scanner->getCurrentToken() != Token::Identifier) + BOOST_THROW_EXCEPTION(createParserError("Expected Identifier after ','")); + } + + nodeFactory.markEndPosition(); + expectToken(Token::RBrace); + return nodeFactory.createNode(name, members); +} + ASTPointer Parser::parseVariableDeclaration(VarDeclParserOptions const& _options) { ASTNodeFactory nodeFactory(*this); diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 5816fec40..1bb4ea977 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -61,6 +61,8 @@ private: Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); ASTPointer parseFunctionDefinition(ASTString const* _contractName); ASTPointer parseStructDefinition(); + ASTPointer parseEnumDefinition(); + ASTPointer parseEnumValue(); ASTPointer parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions()); ASTPointer parseModifierDefinition(); ASTPointer parseEventDefinition(); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 5167c1a77..3e599a6ee 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -67,31 +67,31 @@ namespace solidity #define IGNORE_TOKEN(name, string, precedence) -#define TOKEN_LIST(T, K) \ - /* End of source indicator. */ \ - T(EOS, "EOS", 0) \ +#define TOKEN_LIST(T, K) \ + /* End of source indicator. */ \ + T(EOS, "EOS", 0) \ + \ + /* Punctuators (ECMA-262, section 7.7, page 15). */ \ + T(LParen, "(", 0) \ + T(RParen, ")", 0) \ + T(LBrack, "[", 0) \ + T(RBrack, "]", 0) \ + T(LBrace, "{", 0) \ + T(RBrace, "}", 0) \ + T(Colon, ":", 0) \ + T(Semicolon, ";", 0) \ + T(Period, ".", 0) \ + T(Conditional, "?", 3) \ + T(Arrow, "=>", 0) \ \ - /* Punctuators (ECMA-262, section 7.7, page 15). */ \ - T(LParen, "(", 0) \ - T(RParen, ")", 0) \ - T(LBrack, "[", 0) \ - T(RBrack, "]", 0) \ - T(LBrace, "{", 0) \ - T(RBrace, "}", 0) \ - T(Colon, ":", 0) \ - T(Semicolon, ";", 0) \ - T(Period, ".", 0) \ - T(Conditional, "?", 3) \ - T(Arrow, "=>", 0) \ - \ - /* Assignment operators. */ \ - /* IsAssignmentOp() relies on this block of enum values being */ \ - /* contiguous and sorted in the same order!*/ \ - T(Assign, "=", 2) \ + /* Assignment operators. */ \ + /* IsAssignmentOp() relies on this block of enum values being */ \ + /* contiguous and sorted in the same order!*/ \ + T(Assign, "=", 2) \ /* The following have to be in exactly the same order as the simple binary operators*/ \ - T(AssignBitOr, "|=", 2) \ - T(AssignBitXor, "^=", 2) \ - T(AssignBitAnd, "&=", 2) \ + T(AssignBitOr, "|=", 2) \ + T(AssignBitXor, "^=", 2) \ + T(AssignBitAnd, "&=", 2) \ T(AssignShl, "<<=", 2) \ T(AssignSar, ">>=", 2) \ T(AssignShr, ">>>=", 2) \ @@ -107,9 +107,9 @@ namespace solidity T(Comma, ",", 1) \ T(Or, "||", 4) \ T(And, "&&", 5) \ - T(BitOr, "|", 8) \ - T(BitXor, "^", 9) \ - T(BitAnd, "&", 10) \ + T(BitOr, "|", 8) \ + T(BitXor, "^", 9) \ + T(BitAnd, "&", 10) \ T(SHL, "<<", 11) \ T(SAR, ">>", 11) \ T(SHR, ">>>", 11) \ @@ -123,19 +123,19 @@ namespace solidity /* Compare operators sorted by precedence. */ \ /* IsCompareOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ - T(Equal, "==", 6) \ - T(NotEqual, "!=", 6) \ - T(LessThan, "<", 7) \ - T(GreaterThan, ">", 7) \ - T(LessThanOrEqual, "<=", 7) \ - T(GreaterThanOrEqual, ">=", 7) \ + T(Equal, "==", 6) \ + T(NotEqual, "!=", 6) \ + T(LessThan, "<", 7) \ + T(GreaterThan, ">", 7) \ + T(LessThanOrEqual, "<=", 7) \ + T(GreaterThanOrEqual, ">=", 7) \ K(In, "in", 7) \ \ /* Unary operators. */ \ /* IsUnaryOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ T(Not, "!", 0) \ - T(BitNot, "~", 0) \ + T(BitNot, "~", 0) \ T(Inc, "++", 0) \ T(Dec, "--", 0) \ K(Delete, "delete", 0) \ @@ -168,7 +168,7 @@ namespace solidity K(Switch, "switch", 0) \ K(Var, "var", 0) \ K(While, "while", 0) \ - \ + K(Enum, "enum", 0) \ \ /* Ether subdenominations */ \ K(SubWei, "wei", 0) \ @@ -316,15 +316,15 @@ namespace solidity K(Text, "text", 0) \ K(Real, "real", 0) \ K(UReal, "ureal", 0) \ - T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ + T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ \ /* Literals */ \ - K(NullLiteral, "null", 0) \ - K(TrueLiteral, "true", 0) \ - K(FalseLiteral, "false", 0) \ + K(NullLiteral, "null", 0) \ + K(TrueLiteral, "true", 0) \ + K(FalseLiteral, "false", 0) \ T(Number, NULL, 0) \ - T(StringLiteral, NULL, 0) \ - T(CommentLiteral, NULL, 0) \ + T(StringLiteral, NULL, 0) \ + T(CommentLiteral, NULL, 0) \ \ /* Identifiers (not keywords or future reserved words). */ \ T(Identifier, NULL, 0) \ diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 16514b148..5d753645c 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -74,6 +74,8 @@ TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) Declaration const* declaration = _typeName.getReferencedDeclaration(); if (StructDefinition const* structDef = dynamic_cast(declaration)) return make_shared(*structDef); + else if (EnumDefinition const* enumDef = dynamic_cast(declaration)) + return make_shared(*enumDef); else if (FunctionDefinition const* function = dynamic_cast(declaration)) return make_shared(*function); else if (ContractDefinition const* contract = dynamic_cast(declaration)) @@ -154,7 +156,9 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const StaticStringType const& convertTo = dynamic_cast(_convertTo); return isHash() && (m_bits == convertTo.getNumBytes() * 8); } - return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::Contract; + return _convertTo.getCategory() == getCategory() || + _convertTo.getCategory() == Category::Contract || + _convertTo.getCategory() == Category::Enum; } TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const @@ -662,6 +666,41 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); } +TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const +{ + return _operator == Token::Delete ? make_shared() : TypePointer(); +} + +bool EnumType::operator==(Type const& _other) const +{ + if (_other.getCategory() != getCategory()) + return false; + EnumType const& other = dynamic_cast(_other); + return other.m_enum == m_enum; +} + +string EnumType::toString() const +{ + return string("enum ") + m_enum.getName(); +} + +bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::Integer; +} + +unsigned int EnumType::getMemberValue(ASTString const& _member) const +{ + unsigned int index = 0; + for (ASTPointer const& decl: m_enum.getMembers()) + { + if (decl->getName() == _member) + return index; + ++index; + } + BOOST_THROW_EXCEPTION(m_enum.createTypeError("Requested unknown enum value ." + _member)); +} + FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): m_location(_isInternal ? Location::Internal : Location::External), m_isConstant(_function.isDeclaredConst()), @@ -924,6 +963,13 @@ MemberList const& TypeType::getMembers() const if (!f->isConstructor() && !f->getName().empty()) members[f->getName()] = make_shared(*f); } + else if (m_actualType->getCategory() == Category::Enum) + { + EnumDefinition const& enumDef = dynamic_cast(*m_actualType).getEnumDefinition(); + auto enumType = make_shared(enumDef); + for (ASTPointer const& enumValue: enumDef.getMembers()) + members.insert(make_pair(enumValue->getName(), enumType)); + } m_members.reset(new MemberList(members)); } return *m_members; diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 5a0e2c429..3b4eee57f 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -76,10 +76,9 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this m_members; }; +/** + * The type of an enum instance, there is one distinct type per enum definition. + */ +class EnumType: public Type +{ +public: + virtual Category getCategory() const override { return Category::Enum; } + explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual bool operator==(Type const& _other) const override; + virtual unsigned getSizeOnStack() const override { return 1; } + virtual std::string toString() const override; + virtual bool isValueType() const override { return true; } + + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + + EnumDefinition const& getEnumDefinition() const { return m_enum; } + /// @returns the value that the string has in the Enum + unsigned int getMemberValue(ASTString const& _member) const; + +private: + EnumDefinition const& m_enum; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr m_members; +}; + /** * The type of a function, identified by its (return) parameter types. * @todo the return parameters should also have names, i.e. return parameters should be a struct diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 1785b516c..5e6e65f85 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -1,7 +1,7 @@ ContractDefinition = 'contract' Identifier ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? '{' ContractPart* '}' -ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition +ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )? StructDefinition = 'struct' Identifier '{' @@ -11,6 +11,9 @@ ModifierDefinition = 'modifier' Identifier ParameterList? Block FunctionDefinition = 'function' Identifier ParameterList ( Identifier | 'constant' | 'public' | 'protected' | 'private' )* ( 'returns' ParameterList )? Block + +EnumValue = Identifier +EnumDefinition = 'enum' '{' EnumValue (',' EnumValue)* '}' ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' // semantic restriction: mappings and structs (recursively) containing mappings // are not allowed in argument lists diff --git a/libweb3jsonrpc/CMakeLists.txt b/libweb3jsonrpc/CMakeLists.txt index 98bbaa339..34df1633a 100644 --- a/libweb3jsonrpc/CMakeLists.txt +++ b/libweb3jsonrpc/CMakeLists.txt @@ -9,9 +9,11 @@ set(CMAKE_AUTOMOC OFF) aux_source_directory(. SRC_LIST) +include_directories(..) +include_directories(${JSONCPP_INCLUDE_DIRS}) +include_directories(${MHD_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(${LEVELDB_INCLUDE_DIRS}) -include_directories(..) set(EXECUTABLE web3jsonrpc) @@ -26,6 +28,7 @@ endif() target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_SERVER_LIBRARIES}) +target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES}) target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} secp256k1) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 7ad29ed1b..27b8268da 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -264,7 +264,7 @@ Json::Value WebThreeStubServerBase::eth_blockByHash(std::string const& _hash) return toJson(client()->blockInfo(jsToFixed<32>(_hash))); } -Json::Value WebThreeStubServerBase::eth_blockByNumber(int const& _number) +Json::Value WebThreeStubServerBase::eth_blockByNumber(int _number) { return toJson(client()->blockInfo(client()->hashFromNumber(_number))); } @@ -279,6 +279,8 @@ static TransactionSkeleton toTransaction(Json::Value const& _json) ret.from = jsToAddress(_json["from"].asString()); if (_json["to"].isString()) ret.to = jsToAddress(_json["to"].asString()); + else + ret.creation = true; if (!_json["value"].empty()) { if (_json["value"].isString()) @@ -342,7 +344,7 @@ std::string WebThreeStubServerBase::eth_call(Json::Value const& _json) return ret; } -Json::Value WebThreeStubServerBase::eth_changed(int const& _id) +Json::Value WebThreeStubServerBase::eth_changed(int _id) { auto entries = client()->checkWatch(_id); if (entries.size()) @@ -381,7 +383,7 @@ std::string WebThreeStubServerBase::db_get(std::string const& _name, std::string return toJS(dev::asBytes(ret)); } -Json::Value WebThreeStubServerBase::eth_filterLogs(int const& _id) +Json::Value WebThreeStubServerBase::eth_filterLogs(int _id) { return toJson(client()->logs(_id)); } @@ -563,13 +565,13 @@ bool WebThreeStubServerBase::eth_setCoinbase(std::string const& _address) return true; } -bool WebThreeStubServerBase::eth_setDefaultBlock(int const& _block) +bool WebThreeStubServerBase::eth_setDefaultBlock(int _block) { client()->setDefault(_block); return true; } -bool WebThreeStubServerBase::eth_setListening(bool const& _listening) +bool WebThreeStubServerBase::eth_setListening(bool _listening) { if (_listening) network()->startNetwork(); @@ -578,7 +580,7 @@ bool WebThreeStubServerBase::eth_setListening(bool const& _listening) return true; } -bool WebThreeStubServerBase::eth_setMining(bool const& _mining) +bool WebThreeStubServerBase::eth_setMining(bool _mining) { if (_mining) client()->startMining(); @@ -587,7 +589,7 @@ bool WebThreeStubServerBase::eth_setMining(bool const& _mining) return true; } -Json::Value WebThreeStubServerBase::shh_changed(int const& _id) +Json::Value WebThreeStubServerBase::shh_changed(int _id) { Json::Value ret(Json::arrayValue); auto pub = m_shhWatches[_id]; @@ -619,7 +621,7 @@ int WebThreeStubServerBase::shh_newFilter(Json::Value const& _json) return ret; } -bool WebThreeStubServerBase::shh_uninstallFilter(int const& _id) +bool WebThreeStubServerBase::shh_uninstallFilter(int _id) { face()->uninstallWatch(_id); return true; @@ -671,27 +673,27 @@ bool WebThreeStubServerBase::authenticate(TransactionSkeleton const& _t) return true; } -Json::Value WebThreeStubServerBase::eth_transactionByHash(std::string const& _hash, int const& _i) +Json::Value WebThreeStubServerBase::eth_transactionByHash(std::string const& _hash, int _i) { return toJson(client()->transaction(jsToFixed<32>(_hash), _i)); } -Json::Value WebThreeStubServerBase::eth_transactionByNumber(int const& _number, int const& _i) +Json::Value WebThreeStubServerBase::eth_transactionByNumber(int _number, int _i) { return toJson(client()->transaction(client()->hashFromNumber(_number), _i)); } -Json::Value WebThreeStubServerBase::eth_uncleByHash(std::string const& _hash, int const& _i) +Json::Value WebThreeStubServerBase::eth_uncleByHash(std::string const& _hash, int _i) { return toJson(client()->uncle(jsToFixed<32>(_hash), _i)); } -Json::Value WebThreeStubServerBase::eth_uncleByNumber(int const& _number, int const& _i) +Json::Value WebThreeStubServerBase::eth_uncleByNumber(int _number, int _i) { return toJson(client()->uncle(client()->hashFromNumber(_number), _i)); } -bool WebThreeStubServerBase::eth_uninstallFilter(int const& _id) +bool WebThreeStubServerBase::eth_uninstallFilter(int _id) { client()->uninstallWatch(_id); return true; diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 1190ce407..005ac4130 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -69,16 +69,16 @@ public: virtual Json::Value eth_accounts(); virtual std::string eth_balanceAt(std::string const& _address); virtual Json::Value eth_blockByHash(std::string const& _hash); - virtual Json::Value eth_blockByNumber(int const& _number); + virtual Json::Value eth_blockByNumber(int _number); virtual std::string eth_call(Json::Value const& _json); - virtual Json::Value eth_changed(int const& _id); + virtual Json::Value eth_changed(int _id); virtual std::string eth_codeAt(std::string const& _address); virtual std::string eth_coinbase(); virtual Json::Value eth_compilers(); virtual double eth_countAt(std::string const& _address); virtual int eth_defaultBlock(); virtual std::string eth_gasPrice(); - virtual Json::Value eth_filterLogs(int const& _id); + virtual Json::Value eth_filterLogs(int _id); virtual bool eth_flush(); virtual Json::Value eth_logs(Json::Value const& _json); virtual bool eth_listening(); @@ -88,20 +88,20 @@ public: virtual int eth_number(); virtual int eth_peerCount(); virtual bool eth_setCoinbase(std::string const& _address); - virtual bool eth_setDefaultBlock(int const& _block); - virtual bool eth_setListening(bool const& _listening); + virtual bool eth_setDefaultBlock(int _block); + virtual bool eth_setListening(bool _listening); virtual std::string eth_lll(std::string const& _s); virtual std::string eth_serpent(std::string const& _s); - virtual bool eth_setMining(bool const& _mining); + virtual bool eth_setMining(bool _mining); virtual std::string eth_solidity(std::string const& _code); virtual std::string eth_stateAt(std::string const& _address, std::string const& _storage); virtual Json::Value eth_storageAt(std::string const& _address); virtual std::string eth_transact(Json::Value const& _json); - virtual Json::Value eth_transactionByHash(std::string const& _hash, int const& _i); - virtual Json::Value eth_transactionByNumber(int const& _number, int const& _i); - virtual Json::Value eth_uncleByHash(std::string const& _hash, int const& _i); - virtual Json::Value eth_uncleByNumber(int const& _number, int const& _i); - virtual bool eth_uninstallFilter(int const& _id); + virtual Json::Value eth_transactionByHash(std::string const& _hash, int _i); + virtual Json::Value eth_transactionByNumber(int _number, int _i); + virtual Json::Value eth_uncleByHash(std::string const& _hash, int _i); + virtual Json::Value eth_uncleByNumber(int _number, int _i); + virtual bool eth_uninstallFilter(int _id); virtual Json::Value eth_getWork(); virtual bool eth_submitWork(std::string const& _nonce); @@ -112,13 +112,13 @@ public: virtual bool db_putString(std::string const& _name, std::string const& _key, std::string const& _value); virtual std::string shh_addToGroup(std::string const& _group, std::string const& _who); - virtual Json::Value shh_changed(int const& _id); + virtual Json::Value shh_changed(int _id); virtual bool shh_haveIdentity(std::string const& _id); virtual int shh_newFilter(Json::Value const& _json); virtual std::string shh_newGroup(std::string const& _id, std::string const& _who); virtual std::string shh_newIdentity(); virtual bool shh_post(Json::Value const& _json); - virtual bool shh_uninstallFilter(int const& _id); + virtual bool shh_uninstallFilter(int _id); void setAccounts(std::vector const& _accounts); void setIdentities(std::vector const& _ids); diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index dda8def08..5bade41f6 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -10,59 +10,59 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer { public: - AbstractWebThreeStubServer(jsonrpc::AbstractServerConnector &conn) : jsonrpc::AbstractServer(conn) - { - this->bindAndAddMethod(new jsonrpc::Procedure("web3_sha3", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::web3_sha3I); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_coinbaseI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_setCoinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_setCoinbaseI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_listeningI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_setListening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_setListeningI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_mining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_miningI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_setMining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_setMiningI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_gasPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_gasPriceI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_accounts", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_accountsI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_peerCount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_peerCountI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_defaultBlock", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_defaultBlockI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_setDefaultBlock", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_setDefaultBlockI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_number", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_numberI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_balanceAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_balanceAtI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_stateAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_stateAtI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_storageAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_storageAtI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_countAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_REAL, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_countAtI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_codeAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_codeAtI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_transact", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_transactI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_call", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_callI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_flush", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_flushI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_blockByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_blockByHashI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_blockByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_blockByNumberI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_transactionByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_transactionByHashI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_transactionByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_transactionByNumberI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_uncleByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_uncleByHashI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_uncleByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_uncleByNumberI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_compilers", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_compilersI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_lll", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_lllI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_solidity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_solidityI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_serpent", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_serpentI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_newFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_newFilterI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_newFilterString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_newFilterStringI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_uninstallFilterI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_changedI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_filterLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_filterLogsI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_logs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_logsI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_getWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_getWorkI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI); - this->bindAndAddMethod(new jsonrpc::Procedure("db_put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putI); - this->bindAndAddMethod(new jsonrpc::Procedure("db_get", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getI); - this->bindAndAddMethod(new jsonrpc::Procedure("db_putString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putStringI); - this->bindAndAddMethod(new jsonrpc::Procedure("db_getString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getStringI); - this->bindAndAddMethod(new jsonrpc::Procedure("shh_post", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::shh_postI); - this->bindAndAddMethod(new jsonrpc::Procedure("shh_newIdentity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_newIdentityI); - this->bindAndAddMethod(new jsonrpc::Procedure("shh_haveIdentity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_haveIdentityI); - this->bindAndAddMethod(new jsonrpc::Procedure("shh_newGroup", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_newGroupI); - this->bindAndAddMethod(new jsonrpc::Procedure("shh_addToGroup", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_addToGroupI); - this->bindAndAddMethod(new jsonrpc::Procedure("shh_newFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::shh_newFilterI); - this->bindAndAddMethod(new jsonrpc::Procedure("shh_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::shh_uninstallFilterI); - this->bindAndAddMethod(new jsonrpc::Procedure("shh_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::shh_changedI); + AbstractWebThreeStubServer(jsonrpc::AbstractServerConnector &conn, jsonrpc::serverVersion_t type = jsonrpc::JSONRPC_SERVER_V2) : jsonrpc::AbstractServer(conn, type) + { + this->bindAndAddMethod(jsonrpc::Procedure("web3_sha3", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::web3_sha3I); + this->bindAndAddMethod(jsonrpc::Procedure("eth_coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_coinbaseI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_setCoinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_setCoinbaseI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_listeningI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_setListening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_setListeningI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_mining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_miningI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_setMining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_setMiningI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_gasPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_gasPriceI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_accounts", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_accountsI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_peerCount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_peerCountI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_defaultBlock", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_defaultBlockI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_setDefaultBlock", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_setDefaultBlockI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_number", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_numberI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_balanceAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_balanceAtI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_stateAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_stateAtI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_storageAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_storageAtI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_countAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_REAL, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_countAtI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_codeAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_codeAtI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_transact", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_transactI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_call", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_callI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_flush", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_flushI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_blockByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_blockByHashI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_blockByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_blockByNumberI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_transactionByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_transactionByHashI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_transactionByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_transactionByNumberI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_uncleByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_uncleByHashI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_uncleByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_uncleByNumberI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_compilers", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_compilersI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_lll", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_lllI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_solidity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_solidityI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_serpent", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_serpentI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_newFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_newFilterI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_newFilterString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_newFilterStringI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_uninstallFilterI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_changedI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_filterLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_filterLogsI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_logs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_logsI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_getWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_getWorkI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI); + this->bindAndAddMethod(jsonrpc::Procedure("db_put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putI); + this->bindAndAddMethod(jsonrpc::Procedure("db_get", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getI); + this->bindAndAddMethod(jsonrpc::Procedure("db_putString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putStringI); + this->bindAndAddMethod(jsonrpc::Procedure("db_getString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getStringI); + this->bindAndAddMethod(jsonrpc::Procedure("shh_post", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::shh_postI); + this->bindAndAddMethod(jsonrpc::Procedure("shh_newIdentity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_newIdentityI); + this->bindAndAddMethod(jsonrpc::Procedure("shh_haveIdentity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_haveIdentityI); + this->bindAndAddMethod(jsonrpc::Procedure("shh_newGroup", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_newGroupI); + this->bindAndAddMethod(jsonrpc::Procedure("shh_addToGroup", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_addToGroupI); + this->bindAndAddMethod(jsonrpc::Procedure("shh_newFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::shh_newFilterI); + this->bindAndAddMethod(jsonrpc::Procedure("shh_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::shh_uninstallFilterI); + this->bindAndAddMethod(jsonrpc::Procedure("shh_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::shh_changedI); } inline virtual void web3_sha3I(const Json::Value &request, Json::Value &response) @@ -71,6 +71,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_coinbase(); } inline virtual void eth_setCoinbaseI(const Json::Value &request, Json::Value &response) @@ -79,6 +80,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_listening(); } inline virtual void eth_setListeningI(const Json::Value &request, Json::Value &response) @@ -87,6 +89,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_mining(); } inline virtual void eth_setMiningI(const Json::Value &request, Json::Value &response) @@ -95,18 +98,22 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_gasPrice(); } inline virtual void eth_accountsI(const Json::Value &request, Json::Value &response) { + (void)request; response = this->eth_accounts(); } inline virtual void eth_peerCountI(const Json::Value &request, Json::Value &response) { + (void)request; response = this->eth_peerCount(); } inline virtual void eth_defaultBlockI(const Json::Value &request, Json::Value &response) { + (void)request; response = this->eth_defaultBlock(); } inline virtual void eth_setDefaultBlockI(const Json::Value &request, Json::Value &response) @@ -115,6 +122,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_number(); } inline virtual void eth_balanceAtI(const Json::Value &request, Json::Value &response) @@ -147,6 +155,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_flush(); } inline virtual void eth_blockByHashI(const Json::Value &request, Json::Value &response) @@ -175,6 +184,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_compilers(); } inline virtual void eth_lllI(const Json::Value &request, Json::Value &response) @@ -215,6 +225,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_getWork(); } inline virtual void eth_submitWorkI(const Json::Value &request, Json::Value &response) @@ -243,6 +254,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServershh_newIdentity(); } inline virtual void shh_haveIdentityI(const Json::Value &request, Json::Value &response) @@ -273,14 +285,14 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerqml/DefaultTextField.qml qml/CommonSeparator.qml qml/Style.qml + qml/WebPreviewStyle.qml + qml/img/available_updates.png diff --git a/test/PerformaceTester.sol b/test/PerformaceTester.sol new file mode 100644 index 000000000..3b1202cea --- /dev/null +++ b/test/PerformaceTester.sol @@ -0,0 +1,17 @@ +contract PerformanceTester { + function ackermann(uint m, uint n) returns (uint) { + if (m == 0) + return n + 1; + + if (n == 0) + return ackermann(m - 1, 1); + + return ackermann(m - 1, ackermann(m, n - 1)); + } + + function fibonacci(uint n) returns (uint) { + if (n == 0 || n == 1) + return n; + return fibonacci(n - 1) + fibonacci(n - 2); + } +} \ No newline at end of file diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 259123db6..0325c4c6a 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -2499,6 +2499,41 @@ BOOST_AUTO_TEST_CASE(struct_copy_via_local) BOOST_CHECK(callContractFunction("test()") == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(using_enums) +{ + char const* sourceCode = R"( + contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + function test() + { + choices = ActionChoices.GoStraight; + } + function getChoice() returns (uint d) + { + d = uint256(choices); + } + ActionChoices choices; + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("getChoice()") == encodeArgs(2)); +} + +BOOST_AUTO_TEST_CASE(constructing_enums_from_ints) +{ + char const* sourceCode = R"( + contract c { + enum Truth { False, True } + function test() returns (uint) + { + return uint(Truth(uint8(0x701))); + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(1)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 3ead6f1c5..d6e4ed516 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -992,6 +992,97 @@ BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big) BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); } +BOOST_AUTO_TEST_CASE(enum_member_access) +{ + char const* text = R"( + contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + function test() + { + choices = ActionChoices.GoStraight; + } + ActionChoices choices; + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNamesWithChecks(text)); +} + +BOOST_AUTO_TEST_CASE(enum_invalid_member_access) +{ + char const* text = R"( + contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + function test() + { + choices = ActionChoices.RunAroundWavingYourHands; + } + ActionChoices choices; + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(enum_explicit_conversion_is_okay) +{ + char const* text = R"( + contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + function test() + { + a = uint256(ActionChoices.GoStraight); + b = uint64(ActionChoices.Sit); + } + uint256 a; + uint64 b; + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNamesWithChecks(text)); +} + +BOOST_AUTO_TEST_CASE(int_to_enum_explicit_conversion_is_okay) +{ + char const* text = R"( + contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + function test() + { + a = 2; + b = ActionChoices(a); + } + uint256 a; + ActionChoices b; + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNamesWithChecks(text)); +} + +BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay) +{ + char const* text = R"( + contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + function test() + { + a = ActionChoices.GoStraight; + b = ActionChoices.Sit; + } + uint256 a; + uint64 b; + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(enum_duplicate_values) +{ + char const* text = R"( + contract test { + enum ActionChoices { GoLeft, GoRight, GoLeft, Sit } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 84f36170f..af82f612a 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -703,6 +703,38 @@ BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations_in_expression BOOST_CHECK_NO_THROW(parseTextExplainError(text)); } +BOOST_AUTO_TEST_CASE(enum_valid_declaration) +{ + char const* text = R"( + contract c { + enum validEnum { Value1, Value2, Value3, Value4 } + function c () + { + a = foo.Value3; + } + uint256 a; + })"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + +BOOST_AUTO_TEST_CASE(empty_enum_declaration) +{ + char const* text = R"( + contract c { + enum foo { } + })"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + +BOOST_AUTO_TEST_CASE(malformed_enum_declaration) +{ + char const* text = R"( + contract c { + enum foo { WARNING,} + })"; + BOOST_CHECK_THROW(parseText(text), ParserError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/vm.cpp b/test/vm.cpp index faab48129..66925f929 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -525,7 +525,7 @@ BOOST_AUTO_TEST_CASE(vmPerformanceTest) dev::test::executeTests("vmPerformanceTest", "/VMTests", dev::test::doVMTests); auto end = chrono::steady_clock::now(); - chrono::milliseconds duration(chrono::duration_cast(end - start)); + auto duration(chrono::duration_cast(end - start)); cnote << "test duration: " << duration.count() << " milliseconds.\n"; } } @@ -543,7 +543,7 @@ BOOST_AUTO_TEST_CASE(vmInputLimitsTest1) dev::test::executeTests("vmInputLimitsTest1", "/VMTests", dev::test::doVMTests); auto end = chrono::steady_clock::now(); - chrono::milliseconds duration(chrono::duration_cast(end - start)); + auto duration(chrono::duration_cast(end - start)); cnote << "test duration: " << duration.count() << " milliseconds.\n"; } } diff --git a/test/vmPerformanceTestFiller.json b/test/vmPerformanceTestFiller.json index 7cfe659d5..3292916ec 100644 --- a/test/vmPerformanceTestFiller.json +++ b/test/vmPerformanceTestFiller.json @@ -1,4 +1,62 @@ { + "ackermann31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "100000000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "//" : "PerformanceTester.sol", + "code" : "0x60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "//" : "ackermann(3, 1)", + "data" : "0x2839e92800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "ackermann32": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "100000000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "//" : "PerformanceTester.sol", + "code" : "0x60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "//" : "ackermann(3, 2)", + "data" : "0x2839e92800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000002", + "gasPrice" : "100000000000000", + "gas" : "100000" + } + }, "ackermann33": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -12,16 +70,8 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : "0", - "//" : "contract PerformanceTester {", - "//" : " function ackermann(uint m, uint n) returns (uint) {", - "//" : " if (m == 0)", - "//" : " return n + 1;", - "//" : " if (n == 0)", - "//" : " return ackermann(m - 1, 1);", - "//" : " return ackermann(m - 1, ackermann(m, n - 1));", - "//" : " }", - "//" : "}", - "code" : "0x60e060020a6000350480632839e92814601457005b6020600435602435602a565b8060005260206000f35b6000826000146037576041565b8160010190506076565b81600014604c57605e565b6058600184036001602a565b90506076565b607360018403606f8560018603602a565b602a565b90505b9291505056", + "//" : "PerformanceTester.sol", + "code" : "0x60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056", "storage": {} } }, @@ -30,9 +80,68 @@ "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", + "//" : "ackermann(3, 3)", "data" : "0x2839e92800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003", "gasPrice" : "100000000000000", - "gas" : "100000000000" + "gas" : "100000" + } + }, + "fibonacci10": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "100000000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "//" : "PerformanceTester.sol", + "code" : "0x60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "//" : "fibonacci(10)", + "data" : "0x61047ff4000000000000000000000000000000000000000000000000000000000000000a", + "gasPrice" : "100000000000000", + "gas" : "100000" + } + }, + "fibonacci16": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "100000000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "//" : "PerformanceTester.sol", + "code" : "0x60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "//" : "fibonacci(16)", + "data" : "0x61047ff40000000000000000000000000000000000000000000000000000000000000010", + "gasPrice" : "100000000000000", + "gas" : "100000000" } } } diff --git a/test/webthreestubclient.h b/test/webthreestubclient.h index 1836f6506..02f5b5e40 100644 --- a/test/webthreestubclient.h +++ b/test/webthreestubclient.h @@ -10,7 +10,7 @@ class WebThreeStubClient : public jsonrpc::Client { public: - WebThreeStubClient(jsonrpc::IClientConnector &conn) : jsonrpc::Client(conn) {} + WebThreeStubClient(jsonrpc::IClientConnector &conn, jsonrpc::clientVersion_t type = jsonrpc::JSONRPC_CLIENT_V2) : jsonrpc::Client(conn, type) {} std::string web3_sha3(const std::string& param1) throw (jsonrpc::JsonRpcException) { @@ -52,7 +52,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - bool eth_setListening(const bool& param1) throw (jsonrpc::JsonRpcException) + bool eth_setListening(bool param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -72,7 +72,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - bool eth_setMining(const bool& param1) throw (jsonrpc::JsonRpcException) + bool eth_setMining(bool param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -122,7 +122,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - bool eth_setDefaultBlock(const int& param1) throw (jsonrpc::JsonRpcException) + bool eth_setDefaultBlock(int param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -233,7 +233,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value eth_blockByNumber(const int& param1) throw (jsonrpc::JsonRpcException) + Json::Value eth_blockByNumber(int param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -243,7 +243,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value eth_transactionByHash(const std::string& param1, const int& param2) throw (jsonrpc::JsonRpcException) + Json::Value eth_transactionByHash(const std::string& param1, int param2) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -254,7 +254,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value eth_transactionByNumber(const int& param1, const int& param2) throw (jsonrpc::JsonRpcException) + Json::Value eth_transactionByNumber(int param1, int param2) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -265,7 +265,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value eth_uncleByHash(const std::string& param1, const int& param2) throw (jsonrpc::JsonRpcException) + Json::Value eth_uncleByHash(const std::string& param1, int param2) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -276,7 +276,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value eth_uncleByNumber(const int& param1, const int& param2) throw (jsonrpc::JsonRpcException) + Json::Value eth_uncleByNumber(int param1, int param2) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -347,7 +347,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - bool eth_uninstallFilter(const int& param1) throw (jsonrpc::JsonRpcException) + bool eth_uninstallFilter(int param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -357,7 +357,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value eth_changed(const int& param1) throw (jsonrpc::JsonRpcException) + Json::Value eth_changed(int param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -367,7 +367,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value eth_filterLogs(const int& param1) throw (jsonrpc::JsonRpcException) + Json::Value eth_filterLogs(int param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -515,7 +515,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - bool shh_uninstallFilter(const int& param1) throw (jsonrpc::JsonRpcException) + bool shh_uninstallFilter(int param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -525,7 +525,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value shh_changed(const int& param1) throw (jsonrpc::JsonRpcException) + Json::Value shh_changed(int param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -537,4 +537,4 @@ class WebThreeStubClient : public jsonrpc::Client } }; -#endif //JSONRPC_CPP_WEBTHREESTUBCLIENT_H_ +#endif //JSONRPC_CPP_STUB_WEBTHREESTUBCLIENT_H_ diff --git a/third/CMakeLists.txt b/third/CMakeLists.txt index 71f1e70f2..3f4981e29 100644 --- a/third/CMakeLists.txt +++ b/third/CMakeLists.txt @@ -32,8 +32,11 @@ eth_add_executable(${EXECUTABLE} add_dependencies(${EXECUTABLE} BuildInfo.h) target_link_libraries(${EXECUTABLE} Qt5::Core) +target_link_libraries(${EXECUTABLE} Qt5::Widgets) +target_link_libraries(${EXECUTABLE} Qt5::WebKit) +target_link_libraries(${EXECUTABLE} Qt5::WebKitWidgets) +target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} webthree) -target_link_libraries(${EXECUTABLE} qwebthree) target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) diff --git a/third/MainWin.cpp b/third/MainWin.cpp index f1b7bc826..5fb5074bc 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include "BuildInfo.h" #include "MainWin.h" #include "ui_Main.h" @@ -118,20 +119,24 @@ Main::Main(QWidget *parent) : m_web3.reset(new WebThreeDirect("Third", getDataDir() + "/Third", false, {"eth", "shh"}, NetworkPreferences(), networkConfig)); m_web3->connect(Host::pocHost()); - m_server = unique_ptr(new WebThreeStubServer(m_qwebConnector, *web3(), keysAsVector(m_myKeys))); + m_httpConnector.reset(new jsonrpc::HttpServer(8080)); + m_server.reset(new WebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys))); +// m_server = unique_ptr(new WebThreeStubServer(m_httpConnector, *web3(), keysAsVector(m_myKeys))); m_server->setIdentities(keysAsVector(owned())); m_server->StartListening(); connect(ui->webView, &QWebView::loadStarted, [this]() { - // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. - m_qweb = new QWebThree(this); - auto qweb = m_qweb; - m_qwebConnector.setQWeb(qweb); - QWebFrame* f = ui->webView->page()->mainFrame(); f->disconnect(SIGNAL(javaScriptWindowObjectCleared())); - connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qweb)); + connect(f, &QWebFrame::javaScriptWindowObjectCleared, [f, this]() + { + f->disconnect(); + f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership); + f->evaluateJavaScript(contentsOfQResource(":/js/bignumber.min.js")); + f->evaluateJavaScript(contentsOfQResource(":/js/webthree.js")); + f->evaluateJavaScript(contentsOfQResource(":/js/setup.js")); + }); }); connect(ui->webView, &QWebView::loadFinished, [=]() @@ -163,7 +168,6 @@ Main::~Main() { // Must do this here since otherwise m_ethereum'll be deleted (and therefore clearWatches() called by the destructor) // *after* the client is dead. - m_qweb->clientDieing(); writeSettings(); } diff --git a/third/MainWin.h b/third/MainWin.h index 924fc057e..513cfe96e 100644 --- a/third/MainWin.h +++ b/third/MainWin.h @@ -30,7 +30,6 @@ #include #include #include -#include namespace Ui { class Main; @@ -47,6 +46,10 @@ class WhisperHost; } } +namespace jsonrpc { +class HttpServer; +} + class QQuickView; class WebThreeStubServer; @@ -136,7 +139,6 @@ private: QNetworkAccessManager m_webCtrl; + std::unique_ptr m_httpConnector; std::unique_ptr m_server; - QWebThreeConnector m_qwebConnector; - QWebThree* m_qweb = nullptr; };