Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

Conflicts:
	libsolidity/Types.h
cl-refactor
Gav Wood 10 years ago
parent
commit
bfabf82142
  1. 16
      CodingStandards.txt
  2. 149
      alethzero/MainWin.cpp
  3. 13
      alethzero/MainWin.h
  4. 103
      alethzero/NatspecHandler.cpp
  5. 58
      alethzero/NatspecHandler.h
  6. 50
      alethzero/OurWebThreeStubServer.cpp
  7. 8
      alethzero/OurWebThreeStubServer.h
  8. 15
      evmjit/libevmjit/BasicBlock.cpp
  9. 3
      libdevcore/CommonData.h
  10. 20
      libdevcore/CommonIO.cpp
  11. 3
      libdevcore/CommonIO.h
  12. 2
      libethereum/Client.h
  13. 77
      libjsqrc/ethereumjs/example/natspec_contract.html
  14. 53
      libsolidity/AST.cpp
  15. 12
      libsolidity/AST.h
  16. 6
      libsolidity/AST_accept.h
  17. 5
      libsolidity/Compiler.cpp
  18. 2
      libsolidity/Compiler.h
  19. 5
      libsolidity/CompilerContext.cpp
  20. 3
      libsolidity/CompilerContext.h
  21. 13
      libsolidity/CompilerStack.cpp
  22. 8
      libsolidity/CompilerStack.h
  23. 206
      libsolidity/ExpressionCompiler.cpp
  24. 18
      libsolidity/ExpressionCompiler.h
  25. 4
      libsolidity/InterfaceHandler.cpp
  26. 24
      libsolidity/Parser.cpp
  27. 4
      libsolidity/Token.h
  28. 51
      libsolidity/Types.cpp
  29. 29
      libsolidity/Types.h
  30. 2
      libsolidity/grammar.txt
  31. 7
      mix/AssemblyDebuggerControl.cpp
  32. 6
      mix/AssemblyDebuggerControl.h
  33. 2
      mix/ClientModel.cpp
  34. 21
      mix/CodeEditorExtensionManager.cpp
  35. 14
      mix/CodeEditorExtensionManager.h
  36. 24
      mix/CodeModel.cpp
  37. 24
      mix/CodeModel.h
  38. 94
      mix/ConstantCompilationControl.cpp
  39. 118
      mix/DebuggingStateWrapper.cpp
  40. 30
      mix/DebuggingStateWrapper.h
  41. 4
      mix/Extension.h
  42. 24
      mix/StateListView.cpp
  43. 66
      mix/StatusPane.cpp
  44. 13
      mix/StatusPane.h
  45. 21
      mix/qml.qrc
  46. 35
      mix/qml/DebugBasicInfo.qml
  47. 83
      mix/qml/DebugInfoList.qml
  48. 689
      mix/qml/Debugger.qml
  49. 71
      mix/qml/ItemDelegateDataDump.qml
  50. 196
      mix/qml/MainContent.qml
  51. 7
      mix/qml/StateList.qml
  52. 116
      mix/qml/StatusPane.qml
  53. 39
      mix/qml/StepActionImage.qml
  54. BIN
      mix/qml/img/bugiconactive.png
  55. BIN
      mix/qml/img/bugiconinactive.png
  56. BIN
      mix/qml/img/closedtriangleindicator.png
  57. BIN
      mix/qml/img/jumpintoback.png
  58. BIN
      mix/qml/img/jumpintobackdisabled.png
  59. BIN
      mix/qml/img/jumpintoforward.png
  60. BIN
      mix/qml/img/jumpintoforwarddisabled.png
  61. BIN
      mix/qml/img/jumpoutback.png
  62. BIN
      mix/qml/img/jumpoutbackdisabled.png
  63. BIN
      mix/qml/img/jumpoutforward.png
  64. BIN
      mix/qml/img/jumpoutforwarddisabled.png
  65. BIN
      mix/qml/img/jumpoverback.png
  66. BIN
      mix/qml/img/jumpoverbackdisabled.png
  67. BIN
      mix/qml/img/jumpoverforward.png
  68. BIN
      mix/qml/img/opentriangleindicator.png
  69. 147
      mix/qml/js/Debugger.js
  70. 27
      mix/qml/js/ErrorLocationFormater.js
  71. 19
      mix/qml/main.qml
  72. 6
      test/CMakeLists.txt
  73. 124
      test/SolidityEndToEndTest.cpp
  74. 12
      test/SolidityNameAndTypeResolution.cpp
  75. 42
      test/SolidityNatspecJSON.cpp
  76. 13
      test/TestHelper.cpp
  77. 2
      test/TestHelper.h
  78. 297
      test/checkRandomTest.cpp
  79. 24
      test/createRandomTest.cpp
  80. 32
      test/vm.cpp
  81. 66
      test/vmSystemOperationsTestFiller.json

16
CodingStandards.txt

@ -162,3 +162,19 @@ d. In general expressions should be roughly as important/semantically meaningful
a. Comments should be doxygen-compilable, using @notation rather than \notation. a. Comments should be doxygen-compilable, using @notation rather than \notation.
12. Include Headers
a. Includes should go in order of lower level (STL -> boost -> libdevcore -> libdevcrypto -> libethcore -> libethereum) to higher level. Lower levels are basically dependencies to the higher levels. For example:
#include <string>
#include <boost/filesystem.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/Log.h>
#include <libdevcrypto/SHA3.h>
#include <libethereum/Defaults.h>
b. The only exception to the above rule is the top of a .cpp file where its corresponding header should be located.

149
alethzero/MainWin.cpp

@ -37,7 +37,7 @@
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include <liblll/CodeFragment.h> #include <liblll/CodeFragment.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/AST.h>
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
@ -48,10 +48,10 @@
#include <libethereum/EthereumHost.h> #include <libethereum/EthereumHost.h>
#include <libethereum/DownloadMan.h> #include <libethereum/DownloadMan.h>
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
#include "MainWin.h"
#include "DownloadView.h" #include "DownloadView.h"
#include "MiningView.h" #include "MiningView.h"
#include "BuildInfo.h" #include "BuildInfo.h"
#include "MainWin.h"
#include "OurWebThreeStubServer.h" #include "OurWebThreeStubServer.h"
#include "ui_Main.h" #include "ui_Main.h"
using namespace std; using namespace std;
@ -66,11 +66,11 @@ static void initUnits(QComboBox* _b)
_b->addItem(QString::fromStdString(units()[n].second), n); _b->addItem(QString::fromStdString(units()[n].second), n);
} }
QString Main::fromRaw(dev::h256 _n, unsigned* _inc) QString Main::fromRaw(h256 _n, unsigned* _inc)
{ {
if (_n) if (_n)
{ {
std::string s((char const*)_n.data(), 32); string s((char const*)_n.data(), 32);
auto l = s.find_first_of('\0'); auto l = s.find_first_of('\0');
if (!l) if (!l)
return QString(); return QString();
@ -91,13 +91,13 @@ QString Main::fromRaw(dev::h256 _n, unsigned* _inc)
return QString(); return QString();
} }
static std::vector<dev::KeyPair> keysAsVector(QList<dev::KeyPair> const& keys) static vector<KeyPair> keysAsVector(QList<KeyPair> const& keys)
{ {
auto list = keys.toStdList(); auto list = keys.toStdList();
return {begin(list), end(list)}; return {begin(list), end(list)};
} }
static QString contentsOfQResource(std::string const& res) static QString contentsOfQResource(string const& res)
{ {
QFile file(QString::fromStdString(res)); QFile file(QString::fromStdString(res));
if (!file.open(QFile::ReadOnly)) if (!file.open(QFile::ReadOnly))
@ -116,7 +116,7 @@ Main::Main(QWidget *parent) :
{ {
setWindowFlags(Qt::Window); setWindowFlags(Qt::Window);
ui->setupUi(this); ui->setupUi(this);
g_logPost = [=](std::string const& s, char const* c) g_logPost = [=](string const& s, char const* c)
{ {
simpleDebugOut(s, c); simpleDebugOut(s, c);
m_logLock.lock(); m_logLock.lock();
@ -136,8 +136,8 @@ Main::Main(QWidget *parent) :
cerr << "Block Hash: " << BlockChain::genesis().hash << endl; cerr << "Block Hash: " << BlockChain::genesis().hash << endl;
cerr << "Block RLP: " << RLP(block) << endl; cerr << "Block RLP: " << RLP(block) << endl;
cerr << "Block Hex: " << toHex(block) << endl; cerr << "Block Hex: " << toHex(block) << endl;
cerr << "Network protocol version: " << dev::eth::c_protocolVersion << endl; cerr << "Network protocol version: " << c_protocolVersion << endl;
cerr << "Client database version: " << dev::eth::c_databaseVersion << endl; cerr << "Client database version: " << c_databaseVersion << endl;
ui->configDock->close(); ui->configDock->close();
on_verbosity_valueChanged(); on_verbosity_valueChanged();
@ -158,7 +158,7 @@ Main::Main(QWidget *parent) :
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"})); 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"}));
m_qwebConnector.reset(new QWebThreeConnector()); m_qwebConnector.reset(new QWebThreeConnector());
m_server.reset(new OurWebThreeStubServer(*m_qwebConnector, *web3(), keysAsVector(m_myKeys))); m_server.reset(new OurWebThreeStubServer(*m_qwebConnector, *web3(), keysAsVector(m_myKeys), this));
connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString)));
m_server->setIdentities(keysAsVector(owned())); m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening(); m_server->StartListening();
@ -234,7 +234,7 @@ void Main::addNewId(QString _ids)
refreshWhisper(); refreshWhisper();
} }
dev::p2p::NetworkPreferences Main::netPrefs() const NetworkPreferences Main::netPrefs() const
{ {
return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked()); return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked());
} }
@ -244,7 +244,7 @@ void Main::onKeysChanged()
installBalancesWatch(); installBalancesWatch();
} }
unsigned Main::installWatch(dev::eth::LogFilter const& _tf, WatchHandler const& _f) unsigned Main::installWatch(LogFilter const& _tf, WatchHandler const& _f)
{ {
auto ret = ethereum()->installWatch(_tf); auto ret = ethereum()->installWatch(_tf);
m_handlers[ret] = _f; m_handlers[ret] = _f;
@ -266,10 +266,10 @@ void Main::uninstallWatch(unsigned _w)
void Main::installWatches() void Main::installWatches()
{ {
installWatch(dev::eth::LogFilter().address(c_newConfig), [=](LocalisedLogEntries const&) { installNameRegWatch(); }); installWatch(LogFilter().address(c_newConfig), [=](LocalisedLogEntries const&) { installNameRegWatch(); });
installWatch(dev::eth::LogFilter().address(c_newConfig), [=](LocalisedLogEntries const&) { installCurrenciesWatch(); }); installWatch(LogFilter().address(c_newConfig), [=](LocalisedLogEntries const&) { installCurrenciesWatch(); });
installWatch(dev::eth::PendingChangedFilter, [=](LocalisedLogEntries const&){ onNewPending(); }); installWatch(PendingChangedFilter, [=](LocalisedLogEntries const&){ onNewPending(); });
installWatch(dev::eth::ChainChangedFilter, [=](LocalisedLogEntries const&){ onNewBlock(); }); installWatch(ChainChangedFilter, [=](LocalisedLogEntries const&){ onNewBlock(); });
} }
Address Main::getNameReg() const Address Main::getNameReg() const
@ -285,18 +285,18 @@ Address Main::getCurrencies() const
void Main::installNameRegWatch() void Main::installNameRegWatch()
{ {
uninstallWatch(m_nameRegFilter); uninstallWatch(m_nameRegFilter);
m_nameRegFilter = installWatch(dev::eth::LogFilter().address((u160)getNameReg()), [=](LocalisedLogEntries const&){ onNameRegChange(); }); m_nameRegFilter = installWatch(LogFilter().address((u160)getNameReg()), [=](LocalisedLogEntries const&){ onNameRegChange(); });
} }
void Main::installCurrenciesWatch() void Main::installCurrenciesWatch()
{ {
uninstallWatch(m_currenciesFilter); uninstallWatch(m_currenciesFilter);
m_currenciesFilter = installWatch(dev::eth::LogFilter().address((u160)getCurrencies()), [=](LocalisedLogEntries const&){ onCurrenciesChange(); }); m_currenciesFilter = installWatch(LogFilter().address((u160)getCurrencies()), [=](LocalisedLogEntries const&){ onCurrenciesChange(); });
} }
void Main::installBalancesWatch() void Main::installBalancesWatch()
{ {
dev::eth::LogFilter tf; LogFilter tf;
vector<Address> altCoins; vector<Address> altCoins;
Address coinsAddr = getCurrencies(); Address coinsAddr = getCurrencies();
@ -467,7 +467,7 @@ static Public stringToPublic(QString const& _a)
QString Main::pretty(dev::Address _a) const QString Main::pretty(dev::Address _a) const
{ {
/* static std::map<Address, QString> s_memos; /* static map<Address, QString> s_memos;
if (!s_memos.count(_a)) if (!s_memos.count(_a))
{*/ {*/
@ -505,7 +505,7 @@ QString Main::render(dev::Address _a) const
return QString::fromStdString(_a.abridged()); return QString::fromStdString(_a.abridged());
} }
string32 fromString(std::string const& _s) string32 fromString(string const& _s)
{ {
string32 ret; string32 ret;
for (unsigned i = 0; i < 32 && i <= _s.size(); ++i) for (unsigned i = 0; i < 32 && i <= _s.size(); ++i)
@ -518,7 +518,7 @@ Address Main::fromString(QString const& _n) const
if (_n == "(Create Contract)") if (_n == "(Create Contract)")
return Address(); return Address();
/* static std::map<QString, Address> s_memos; /* static map<QString, Address> s_memos;
if (!s_memos.count(_n)) if (!s_memos.count(_n))
{*/ {*/
@ -860,18 +860,18 @@ void Main::on_preview_triggered()
void Main::refreshMining() void Main::refreshMining()
{ {
dev::eth::MineProgress p = ethereum()->miningProgress(); MineProgress p = ethereum()->miningProgress();
ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining");
if (!ui->miningView->isVisible()) if (!ui->miningView->isVisible())
return; return;
list<dev::eth::MineInfo> l = ethereum()->miningHistory(); list<MineInfo> l = ethereum()->miningHistory();
static unsigned lh = 0; static unsigned lh = 0;
if (p.hashes < lh) if (p.hashes < lh)
ui->miningView->resetStats(); ui->miningView->resetStats();
lh = p.hashes; lh = p.hashes;
ui->miningView->appendStats(l, p); ui->miningView->appendStats(l, p);
/* if (p.ms) /* if (p.ms)
for (dev::eth::MineInfo const& i: l) for (MineInfo const& i: l)
cnote << i.hashes * 10 << "h/sec, need:" << i.requirement << " best:" << i.best << " best-so-far:" << p.best << " avg-speed:" << (p.hashes * 1000 / p.ms) << "h/sec"; cnote << i.hashes * 10 << "h/sec, need:" << i.requirement << " best:" << i.best << " best-so-far:" << p.best << " avg-speed:" << (p.hashes * 1000 / p.ms) << "h/sec";
*/ */
} }
@ -1026,10 +1026,10 @@ void Main::refreshBlockCount()
cwatch << "refreshBlockCount()"; cwatch << "refreshBlockCount()";
auto d = ethereum()->blockChain().details(); auto d = ethereum()->blockChain().details();
auto diff = BlockInfo(ethereum()->blockChain().block()).difficulty; auto diff = BlockInfo(ethereum()->blockChain().block()).difficulty;
ui->blockCount->setText(QString("%6 #%1 @%3 T%2 PV%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(dev::eth::c_protocolVersion).arg(dev::eth::c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet")); ui->blockCount->setText(QString("%6 #%1 @%3 T%2 PV%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(c_protocolVersion).arg(c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet"));
} }
static bool blockMatch(string const& _f, dev::eth::BlockDetails const& _b, h256 _h, BlockChain const& _bc) static bool blockMatch(string const& _f, BlockDetails const& _b, h256 _h, BlockChain const& _bc)
{ {
try try
{ {
@ -1178,7 +1178,7 @@ void Main::timerEvent(QTimerEvent*)
} }
} }
string Main::renderDiff(dev::eth::StateDiff const& _d) const string Main::renderDiff(StateDiff const& _d) const
{ {
stringstream s; stringstream s;
@ -1187,24 +1187,24 @@ string Main::renderDiff(dev::eth::StateDiff const& _d) const
{ {
s << "<hr/>"; s << "<hr/>";
dev::eth::AccountDiff const& ad = i.second; AccountDiff const& ad = i.second;
s << "<code style=\"white-space: pre; font-weight: bold\">" << lead(ad.changeType()) << " </code>" << " <b>" << render(i.first).toStdString() << "</b>"; s << "<code style=\"white-space: pre; font-weight: bold\">" << lead(ad.changeType()) << " </code>" << " <b>" << render(i.first).toStdString() << "</b>";
if (!ad.exist.to()) if (!ad.exist.to())
continue; continue;
if (ad.balance) if (ad.balance)
{ {
s << "<br/>" << indent << "Balance " << std::dec << formatBalance(ad.balance.to()); s << "<br/>" << indent << "Balance " << dec << formatBalance(ad.balance.to());
s << " <b>" << std::showpos << (((dev::bigint)ad.balance.to()) - ((dev::bigint)ad.balance.from())) << std::noshowpos << "</b>"; s << " <b>" << showpos << (((dev::bigint)ad.balance.to()) - ((dev::bigint)ad.balance.from())) << noshowpos << "</b>";
} }
if (ad.nonce) if (ad.nonce)
{ {
s << "<br/>" << indent << "Count #" << std::dec << ad.nonce.to(); s << "<br/>" << indent << "Count #" << dec << ad.nonce.to();
s << " <b>" << std::showpos << (((dev::bigint)ad.nonce.to()) - ((dev::bigint)ad.nonce.from())) << std::noshowpos << "</b>"; s << " <b>" << showpos << (((dev::bigint)ad.nonce.to()) - ((dev::bigint)ad.nonce.from())) << noshowpos << "</b>";
} }
if (ad.code) if (ad.code)
{ {
s << "<br/>" << indent << "Code " << std::hex << ad.code.to().size() << " bytes"; s << "<br/>" << indent << "Code " << hex << ad.code.to().size() << " bytes";
if (ad.code.from().size()) if (ad.code.from().size())
s << " (" << ad.code.from().size() << " bytes)"; s << " (" << ad.code.from().size() << " bytes)";
} }
@ -1226,7 +1226,7 @@ string Main::renderDiff(dev::eth::StateDiff const& _d) const
else if (i.first > u160(1) << 150) else if (i.first > u160(1) << 150)
s << (h160)(u160)i.first; s << (h160)(u160)i.first;
else else
s << std::hex << i.first; s << hex << i.first;
*/ */
if (!i.second.from()) if (!i.second.from())
s << ": " << prettyU256(i.second.to()).toStdString(); s << ": " << prettyU256(i.second.to()).toStdString();
@ -1479,10 +1479,10 @@ void Main::populateDebugger(dev::bytesConstRef _r)
bytesConstRef lastData; bytesConstRef lastData;
h256 lastHash; h256 lastHash;
h256 lastDataHash; h256 lastDataHash;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, dev::eth::VM* voidVM, dev::eth::ExtVMFace const* voidExt) auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
{ {
dev::eth::VM& vm = *voidVM; VM& vm = *voidVM;
dev::eth::ExtVM const& ext = *static_cast<dev::eth::ExtVM const*>(voidExt); ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
if (ext.code != lastExtCode) if (ext.code != lastExtCode)
{ {
lastExtCode = ext.code; lastExtCode = ext.code;
@ -1605,11 +1605,11 @@ static shh::Topic topicFromText(QString _s)
{ {
u256 v(d.cap(2).toStdString()); u256 v(d.cap(2).toStdString());
if (d.cap(6) == "szabo") if (d.cap(6) == "szabo")
v *= dev::eth::szabo; v *= szabo;
else if (d.cap(5) == "finney") else if (d.cap(5) == "finney")
v *= dev::eth::finney; v *= finney;
else if (d.cap(4) == "ether") else if (d.cap(4) == "ether")
v *= dev::eth::ether; v *= ether;
bytes bs = dev::toCompactBigEndian(v); bytes bs = dev::toCompactBigEndian(v);
if (d.cap(1) != "$") if (d.cap(1) != "$")
for (auto i = bs.size(); i < 32; ++i) for (auto i = bs.size(); i < 32; ++i)
@ -1635,6 +1635,29 @@ static shh::Topic topicFromText(QString _s)
return ret; return ret;
} }
bool Main::sourceIsSolidity(string const& _source)
{
// TODO: Improve this heuristic
return (_source.substr(0, 8) == "contract" || _source.substr(0, 5) == "//sol");
}
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->getName() + "\n";
}
return ret;
}
void Main::on_data_textChanged() void Main::on_data_textChanged()
{ {
m_pcWarp.clear(); m_pcWarp.clear();
@ -1648,7 +1671,7 @@ void Main::on_data_textChanged()
{ {
m_data = fromHex(src); m_data = fromHex(src);
} }
else if (src.substr(0, 8) == "contract" || src.substr(0, 5) == "//sol") // improve this heuristic else if (sourceIsSolidity(src))
{ {
dev::solidity::CompilerStack compiler; dev::solidity::CompilerStack compiler;
try try
@ -1657,6 +1680,7 @@ void Main::on_data_textChanged()
solidity = "<h4>Solidity</h4>"; solidity = "<h4>Solidity</h4>";
solidity += "<pre>" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + "</pre>"; solidity += "<pre>" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + "</pre>";
solidity += "<pre>" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "</pre>"; solidity += "<pre>" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "</pre>";
solidity += "<pre>" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "</pre>";
} }
catch (dev::Exception const& exception) catch (dev::Exception const& exception)
{ {
@ -1671,7 +1695,7 @@ void Main::on_data_textChanged()
} }
else else
{ {
m_data = dev::eth::compileLLL(src, m_enableOptimizer, &errors); m_data = compileLLL(src, m_enableOptimizer, &errors);
if (errors.size()) if (errors.size())
{ {
try try
@ -1687,11 +1711,11 @@ void Main::on_data_textChanged()
} }
else else
{ {
auto asmcode = dev::eth::compileLLLToAsm(src, false); auto asmcode = compileLLLToAsm(src, false);
lll = "<h4>Pre</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>"; lll = "<h4>Pre</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>";
if (m_enableOptimizer) if (m_enableOptimizer)
{ {
asmcode = dev::eth::compileLLLToAsm(src, true); asmcode = compileLLLToAsm(src, true);
lll = "<h4>Opt</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>" + lll; lll = "<h4>Opt</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>" + lll;
} }
} }
@ -1872,7 +1896,28 @@ void Main::on_send_clicked()
debugFinished(); debugFinished();
Secret s = i.secret(); Secret s = i.secret();
if (isCreation()) 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()); 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::NATSPEC_USER));
}
}
catch (...)
{
statusBar()->showMessage("Couldn't compile Solidity Contract.");
}
}
else else
ethereum()->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); ethereum()->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice());
return; return;
@ -2008,7 +2053,7 @@ void Main::on_dumpTracePretty_triggered()
f << " STORAGE" << endl; f << " STORAGE" << endl;
for (auto const& i: ws.storage) for (auto const& i: ws.storage)
f << showbase << hex << i.first << ": " << i.second << endl; f << showbase << hex << i.first << ": " << i.second << endl;
f << dec << ws.levels.size() << " | " << ws.cur << " | #" << ws.steps << " | " << hex << setw(4) << setfill('0') << ws.curPC << " : " << dev::eth::instructionInfo(ws.inst).name << " | " << dec << ws.gas << " | -" << dec << ws.gasCost << " | " << ws.newMemSize << "x32"; 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";
} }
} }
@ -2235,7 +2280,7 @@ void Main::updateDebugger()
cwarn << "PC (" << (unsigned)ws.curPC << ") is after code range (" << m_codes[ws.code].size() << ")"; cwarn << "PC (" << (unsigned)ws.curPC << ") is after code range (" << m_codes[ws.code].size() << ")";
ostringstream ss; ostringstream ss;
ss << dec << "STEP: " << ws.steps << " | PC: 0x" << hex << ws.curPC << " : " << dev::eth::instructionInfo(ws.inst).name << " | ADDMEM: " << dec << ws.newMemSize << " words | COST: " << dec << ws.gasCost << " | GAS: " << dec << ws.gas; 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())); ui->debugStateInfo->setText(QString::fromStdString(ss.str()));
stringstream s; stringstream s;
for (auto const& i: ws.storage) for (auto const& i: ws.storage)
@ -2257,6 +2302,16 @@ void Main::on_post_clicked()
whisper()->inject(m.seal(from, topicFromText(ui->shhTopic->toPlainText()), ui->shhTtl->value(), ui->shhWork->value())); 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)
{
return m_natspecDB.getUserNotice(_contractHash, _transactionData);
}
void Main::refreshWhispers() void Main::refreshWhispers()
{ {
ui->whispers->clear(); ui->whispers->clear();

13
alethzero/MainWin.h

@ -36,6 +36,9 @@
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include <libqwebthree/QWebThree.h> #include <libqwebthree/QWebThree.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libsolidity/CompilerStack.h>
#include "NatspecHandler.h"
namespace Ui { namespace Ui {
class Main; class Main;
@ -80,7 +83,8 @@ public:
dev::eth::Client* ethereum() const { return m_webThree->ethereum(); } dev::eth::Client* ethereum() const { return m_webThree->ethereum(); }
std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); } std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); }
std::string lookupNatSpec(dev::h256 const& _contractCode) const { (void)_contractCode; return ""; } // TODO: actually implement with leveldb & a UI. std::string lookupNatSpec(dev::h256 const& _contractHash) const;
std::string lookupNatSpecUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionData);
QList<dev::KeyPair> owned() const { return m_myIdentities + m_myKeys; } QList<dev::KeyPair> owned() const { return m_myIdentities + m_myKeys; }
@ -227,6 +231,12 @@ private:
void refreshBlockCount(); void refreshBlockCount();
void refreshBalances(); 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::Main> ui; std::unique_ptr<Ui::Main> ui;
std::unique_ptr<dev::WebThreeDirect> m_webThree; std::unique_ptr<dev::WebThreeDirect> m_webThree;
@ -269,4 +279,5 @@ private:
QWebThree* m_qweb = nullptr; QWebThree* m_qweb = nullptr;
static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr); static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr);
NatspecHandler m_natspecDB;
}; };

103
alethzero/NatspecHandler.cpp

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @file NatspecHandler.cpp
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "NatspecHandler.h"
#include <string>
#include <boost/filesystem.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/Log.h>
#include <libdevcrypto/SHA3.h>
#include <libethereum/Defaults.h>
using namespace dev;
using namespace dev::eth;
using namespace std;
NatspecHandler::NatspecHandler()
{
string path = Defaults::dbPath();
boost::filesystem::create_directories(path);
ldb::Options o;
o.create_if_missing = true;
ldb::DB::Open(o, path + "/natspec", &m_db);
}
NatspecHandler::~NatspecHandler()
{
delete m_db;
}
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()));
}
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);
return ret;
}
string NatspecHandler::getUserNotice(string const& json, dev::bytes const& _transactionData)
{
Json::Value natspec;
Json::Value userNotice;
string retStr;
m_reader.parse(json, natspec);
bytes transactionFunctionPart(_transactionData.begin(), _transactionData.begin() + 4);
FixedHash<4> transactionFunctionHash(transactionFunctionPart);
Json::Value methods = natspec["methods"];
for (Json::ValueIterator it = methods.begin(); it != methods.end(); ++it)
{
Json::Value keyValue = it.key();
if (!keyValue.isString())
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Illegal Natspec JSON detected"));
string functionSig = keyValue.asString();
FixedHash<4> functionHash(dev::sha3(functionSig));
if (functionHash == transactionFunctionHash)
{
Json::Value val = (*it)["notice"];
if (!val.isString())
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Illegal Natspec JSON detected"));
return val.asString();
}
}
// not found
return string();
}
string NatspecHandler::getUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionData)
{
return getUserNotice(retrieve(_contractHash), _transactionData);
}

58
alethzero/NatspecHandler.h

@ -0,0 +1,58 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file NatspecHandler.h
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#pragma once
#pragma warning(push)
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <jsoncpp/json/json.h>
#include <libdevcore/FixedHash.h>
namespace ldb = leveldb;
class NatspecHandler
{
public:
NatspecHandler();
~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;
/// Given a json natspec string and the transaction data return the user notice
std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData);
/// Given a contract code hash and the transaction's data retrieve the natspec documention's
/// user notice for that transaction.
/// @returns The user notice or an empty string if no natspec for the contract exists
/// or if the existing natspec does not document the @c _methodName
std::string getUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionDacta);
private:
ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions;
ldb::DB* m_db;
Json::Reader m_reader;
};

50
alethzero/OurWebThreeStubServer.cpp

@ -22,14 +22,17 @@
#include "OurWebThreeStubServer.h" #include "OurWebThreeStubServer.h"
#include <QMessageBox> #include <QMessageBox>
#include <QAbstractButton>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include "MainWin.h" #include "MainWin.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts): OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3,
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3) std::vector<dev::KeyPair> const& _accounts, Main* main):
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(main)
{} {}
std::string OurWebThreeStubServer::shh_newIdentity() std::string OurWebThreeStubServer::shh_newIdentity()
@ -39,36 +42,31 @@ std::string OurWebThreeStubServer::shh_newIdentity()
return toJS(kp.pub()); return toJS(kp.pub());
} }
bool OurWebThreeStubServer::authenticate(dev::TransactionSkeleton const& _t) const bool OurWebThreeStubServer::showAuthenticationPopup(std::string const& _title, std::string const& _text) const
{ {
return true; QMessageBox userInput;
userInput.setText(QString::fromStdString(_title));
// To get the balance of the sender userInput.setInformativeText(QString::fromStdString(_text + "\n Do you wish to allow this?"));
cnote << "Sender has ETH: " << m_web3->ethereum()->postState().balance(_t.from); userInput.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
userInput.button(QMessageBox::Ok)->setText("Allow");
Main* main; // don't know this yet, should be a member and set at construction time by Main, who will construct us. userInput.button(QMessageBox::Cancel)->setText("Reject");
userInput.setDefaultButton(QMessageBox::Cancel);
return userInput.exec() == QMessageBox::Ok;
}
bool OurWebThreeStubServer::authenticate(dev::TransactionSkeleton const& _t) const
{
h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to); h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to);
if (contractCodeHash == EmptySHA3) if (contractCodeHash == EmptySHA3)
{
// recipient has no code - nothing special about this transaction. // recipient has no code - nothing special about this transaction.
// TODO: show basic message for value transfer. // TODO: show basic message for value transfer.
return true; // or whatever. return true;
}
std::string natspecJson = main->lookupNatSpec(contractCodeHash);
if (natspecJson.empty())
{
// TODO: HUGE warning - we don't know what this will do!
return false; // or whatever.
}
// otherwise it's a transaction to contract for which we have the natspec:
// determine the actual message (embellish with real data) and ask user.
// QMessageBox::question(); std::string userNotice = m_main->lookupNatSpecUserNotice(contractCodeHash, _t.data);
if (userNotice.empty())
return showAuthenticationPopup("Unverified Pending Transaction",
"An undocumented transaction is about to be executed.");
return true; // otherwise it's a transaction to a contract for which we have the natspec
return showAuthenticationPopup("Pending Transaction", userNotice);
} }

8
alethzero/OurWebThreeStubServer.h

@ -24,12 +24,15 @@
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
class Main;
class OurWebThreeStubServer: public QObject, public WebThreeStubServer class OurWebThreeStubServer: public QObject, public WebThreeStubServer
{ {
Q_OBJECT Q_OBJECT
public: public:
OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts); OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3,
std::vector<dev::KeyPair> const& _accounts, Main* main);
virtual std::string shh_newIdentity() override; virtual std::string shh_newIdentity() override;
virtual bool authenticate(dev::TransactionSkeleton const& _t) const; virtual bool authenticate(dev::TransactionSkeleton const& _t) const;
@ -38,5 +41,8 @@ signals:
void onNewId(QString _s); void onNewId(QString _s);
private: private:
bool showAuthenticationPopup(std::string const& _title, std::string const& _text) const;
dev::WebThreeDirect* m_web3; dev::WebThreeDirect* m_web3;
Main* m_main;
}; };

15
evmjit/libevmjit/BasicBlock.cpp

@ -168,16 +168,13 @@ void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
if (val == nullptr) if (val == nullptr)
continue; continue;
assert(llvm::isa<llvm::PHINode>(val));
llvm::PHINode* phi = llvm::cast<llvm::PHINode>(val); llvm::PHINode* phi = llvm::cast<llvm::PHINode>(val);
if (! phi->use_empty()) // Insert call to get() just before the PHI node and replace
{ // the uses of PHI with the uses of this new instruction.
// Insert call to get() just before the PHI node and replace m_builder.SetInsertPoint(phi);
// the uses of PHI with the uses of this new instruction. auto newVal = _evmStack.get(idx); // OPT: Value may be never user but we need to check stack heigth
m_builder.SetInsertPoint(phi); // It is probably a good idea to keep heigth as a local variable accesible by LLVM directly
auto newVal = _evmStack.get(idx); phi->replaceAllUsesWith(newVal);
phi->replaceAllUsesWith(newVal);
}
phi->eraseFromParent(); phi->eraseFromParent();
} }

3
libdevcore/CommonData.h

@ -110,6 +110,9 @@ inline std::string toBigEndianString(u160 _val) { std::string ret(20, '\0'); toB
inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; }
inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); return ret; } inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); return ret; }
/// Convenience function for conversion of a u256 to hex
inline std::string toHex(u256 val) { return toHex(toBigEndian(val)); }
/// Convenience function for toBigEndian. /// Convenience function for toBigEndian.
/// @returns a byte array just big enough to represent @a _val. /// @returns a byte array just big enough to represent @a _val.
template <class _T> template <class _T>

20
libdevcore/CommonIO.cpp

@ -26,30 +26,30 @@
using namespace std; using namespace std;
using namespace dev; using namespace dev;
string dev::memDump(bytes const& _b, unsigned _w, bool _html) string dev::memDump(bytes const& _bytes, unsigned _width, bool _html)
{ {
stringstream ret; stringstream ret;
if (_html) if (_html)
ret << "<pre style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">"; ret << "<pre style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">";
for (unsigned i = 0; i < _b.size(); i += _w) for (unsigned i = 0; i < _bytes.size(); i += _width)
{ {
ret << hex << setw(4) << setfill('0') << i << " "; ret << hex << setw(4) << setfill('0') << i << " ";
for (unsigned j = i; j < i + _w; ++j) for (unsigned j = i; j < i + _width; ++j)
if (j < _b.size()) if (j < _bytes.size())
if (_b[j] >= 32 && _b[j] < 127) if (_bytes[j] >= 32 && _bytes[j] < 127)
if ((char)_b[j] == '<' && _html) if ((char)_bytes[j] == '<' && _html)
ret << "&lt;"; ret << "&lt;";
else if ((char)_b[j] == '&' && _html) else if ((char)_bytes[j] == '&' && _html)
ret << "&amp;"; ret << "&amp;";
else else
ret << (char)_b[j]; ret << (char)_bytes[j];
else else
ret << '?'; ret << '?';
else else
ret << ' '; ret << ' ';
ret << " "; ret << " ";
for (unsigned j = i; j < i + _w && j < _b.size(); ++j) for (unsigned j = i; j < i + _width && j < _bytes.size(); ++j)
ret << setfill('0') << setw(2) << hex << (unsigned)_b[j] << " "; ret << setfill('0') << setw(2) << hex << (unsigned)_bytes[j] << " ";
ret << "\n"; ret << "\n";
} }
if (_html) if (_html)

3
libdevcore/CommonIO.h

@ -47,7 +47,8 @@ bytes contents(std::string const& _file);
void writeFile(std::string const& _file, bytes const& _data); void writeFile(std::string const& _file, bytes const& _data);
/// Nicely renders the given bytes to a string, optionally as HTML. /// Nicely renders the given bytes to a string, optionally as HTML.
std::string memDump(bytes const& _b, unsigned _w = 8, bool _html = false); /// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line.
std::string memDump(bytes const& _bytes, unsigned _width = 8, bool _html = false);
// Stream I/O functions. // Stream I/O functions.
// Provides templated stream I/O for all STL collections so they can be shifted on to any iostream-like interface. // Provides templated stream I/O for all STL collections so they can be shifted on to any iostream-like interface.

2
libethereum/Client.h

@ -93,7 +93,7 @@ struct ClientWatch
explicit ClientWatch(h256 _id): id(_id) {} explicit ClientWatch(h256 _id): id(_id) {}
h256 id; h256 id;
LocalisedLogEntries changes = { InitialChange }; LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange };
}; };
struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; };

77
libjsqrc/ethereumjs/example/natspec_contract.html

@ -0,0 +1,77 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript">
var web3 = require('web3');
web3.setProvider(new web3.providers.AutoProvider());
// solidity source code
var source = "" +
"contract test {\n" +
" /// @notice Will multiplty `a` by 7. \n" +
" function multiply(uint a) returns(uint d) {\n" +
" return a * 7;\n" +
" }\n" +
"}\n";
// contract description, this will be autogenerated somehow
var desc = [{
"name": "multiply",
"inputs": [
{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
var contract;
function createExampleContract() {
// hide create button
document.getElementById('create').style.visibility = 'hidden';
document.getElementById('source').innerText = source;
// create contract
web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) {
contract = web3.contract(address, desc);
document.getElementById('call').style.visibility = 'visible';
});
}
function callExampleContract() {
// this should be generated by ethereum
var param = parseInt(document.getElementById('value').value);
// call the contract
contract.multiply(param).transact().then(function(res) {
document.getElementById('result').innerText = res[0];
});
}
</script>
</head>
<body>
<h1>contract</h1>
<div id="source"></div>
<div id='create'>
<button type="button" onClick="createExampleContract();">create example contract</button>
</div>
<div id='call' style='visibility: hidden;'>
<input type="number" id="value"></input>
<button type="button" onClick="callExampleContract()">Call Contract</button>
</div>
<div id="result"></div>
</body>
</html>

53
libsolidity/AST.cpp

@ -50,18 +50,27 @@ void ContractDefinition::checkTypeRequirements()
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions()) for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements(); function->checkTypeRequirements();
// check for hash collisions in function signatures
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
set<FixedHash<4>> hashes;
for (auto const& hashAndFunction: getInterfaceFunctionList())
{
FixedHash<4> const& hash = hashAndFunction.first;
if (hashes.count(hash))
BOOST_THROW_EXCEPTION(createTypeError("Function signature hash collision for " +
hashAndFunction.second->getCanonicalSignature()));
hashes.insert(hash);
}
} }
map<FixedHash<4>, FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const map<FixedHash<4>, FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
{ {
map<FixedHash<4>, FunctionDefinition const*> exportedFunctions; vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions) map<FixedHash<4>, FunctionDefinition const*> exportedFunctions(exportedFunctionList.begin(),
if (f->isPublic() && f->getName() != getName()) exportedFunctionList.end());
{ solAssert(exportedFunctionList.size() == exportedFunctions.size(),
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); "Hash collision at Function Definition Hash calculation");
auto res = exportedFunctions.insert(std::make_pair(hash,f.get()));
solAssert(res.second, "Hash collision at Function Definition Hash calculation");
}
return exportedFunctions; return exportedFunctions;
} }
@ -74,6 +83,19 @@ FunctionDefinition const* ContractDefinition::getConstructor() const
return nullptr; return nullptr;
} }
vector<pair<FixedHash<4>, FunctionDefinition const*>> ContractDefinition::getInterfaceFunctionList() const
{
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctions;
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName())
{
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
exportedFunctions.push_back(make_pair(hash, f.get()));
}
return exportedFunctions;
}
void StructDefinition::checkMemberTypes() const void StructDefinition::checkMemberTypes() const
{ {
for (ASTPointer<VariableDeclaration> const& member: getMembers()) for (ASTPointer<VariableDeclaration> const& member: getMembers())
@ -309,20 +331,13 @@ bool FunctionCall::isTypeConversion() const
void NewExpression::checkTypeRequirements() void NewExpression::checkTypeRequirements()
{ {
m_contractName->checkTypeRequirements(); m_contractName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration()); m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
if (!m_contract) if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
shared_ptr<ContractType const> type = make_shared<ContractType>(*m_contract); shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
m_type = type; TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
TypePointers const& parameterTypes = type->getConstructorType()->getParameterTypes(); m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
if (parameterTypes.size() != m_arguments.size()) FunctionType::Location::CREATION);
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructor call."));
} }
void MemberAccess::checkTypeRequirements() void MemberAccess::checkTypeRequirements()

12
libsolidity/AST.h

@ -191,6 +191,8 @@ public:
FunctionDefinition const* getConstructor() const; FunctionDefinition const* getConstructor() const;
private: private:
std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> getInterfaceFunctionList() const;
std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
@ -790,26 +792,22 @@ private:
}; };
/** /**
* Expression that creates a new contract, e.g. "new SomeContract(1, 2)". * Expression that creates a new contract, e.g. the "new SomeContract" part in "new SomeContract(1, 2)".
*/ */
class NewExpression: public Expression class NewExpression: public Expression
{ {
public: public:
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName, NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName):
std::vector<ASTPointer<Expression>> const& _arguments): Expression(_location), m_contractName(_contractName) {}
Expression(_location), m_contractName(_contractName), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
/// Returns the referenced contract. Can only be called after type checking. /// Returns the referenced contract. Can only be called after type checking.
ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; } ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; }
private: private:
ASTPointer<Identifier> m_contractName; ASTPointer<Identifier> m_contractName;
std::vector<ASTPointer<Expression>> m_arguments;
ContractDefinition const* m_contract = nullptr; ContractDefinition const* m_contract = nullptr;
}; };

6
libsolidity/AST_accept.h

@ -452,20 +452,14 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const
void NewExpression::accept(ASTVisitor& _visitor) void NewExpression::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{
m_contractName->accept(_visitor); m_contractName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void NewExpression::accept(ASTConstVisitor& _visitor) const void NewExpression::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{
m_contractName->accept(_visitor); m_contractName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }

5
libsolidity/Compiler.cpp

@ -50,10 +50,9 @@ void Compiler::compileContract(ContractDefinition const& _contract, vector<Magic
function->accept(*this); function->accept(*this);
// Swap the runtime context with the creation-time context // Swap the runtime context with the creation-time context
CompilerContext runtimeContext; swap(m_context, m_runtimeContext);
swap(m_context, runtimeContext);
initializeContext(_contract, _magicGlobals, _contracts); initializeContext(_contract, _magicGlobals, _contracts);
packIntoContractCreator(_contract, runtimeContext); packIntoContractCreator(_contract, m_runtimeContext);
} }
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals, void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,

2
libsolidity/Compiler.h

@ -35,6 +35,7 @@ public:
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals, void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
std::map<ContractDefinition const*, bytes const*> const& _contracts); std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private: private:
@ -70,6 +71,7 @@ private:
bool const m_optimize; bool const m_optimize;
CompilerContext m_context; CompilerContext m_context;
CompilerContext m_runtimeContext;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement

5
libsolidity/CompilerContext.cpp

@ -95,6 +95,11 @@ unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
return _baseOffset + m_asm.deposit(); return _baseOffset + m_asm.deposit();
} }
unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
{
return -baseToCurrentStackOffset(-_offset);
}
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
{ {
auto it = m_stateVariables.find(&_declaration); auto it = m_stateVariables.find(&_declaration);

3
libsolidity/CompilerContext.h

@ -62,6 +62,9 @@ public:
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns /// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
/// the distance of that variable from the current top of the stack. /// the distance of that variable from the current top of the stack.
unsigned baseToCurrentStackOffset(unsigned _baseOffset) const; unsigned baseToCurrentStackOffset(unsigned _baseOffset) const;
/// Converts an offset relative to the current stack height to a value that can be used later
/// with baseToCurrentStackOffset to point to the same stack element.
unsigned currentToBaseStackOffset(unsigned _offset) const;
u256 getStorageLocationOfVariable(Declaration const& _declaration) const; u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
/// Appends a JUMPI instruction to a new tag and @returns the tag /// Appends a JUMPI instruction to a new tag and @returns the tag

13
libsolidity/CompilerStack.cpp

@ -30,6 +30,8 @@
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/InterfaceHandler.h> #include <libsolidity/InterfaceHandler.h>
#include <libdevcrypto/SHA3.h>
using namespace std; using namespace std;
namespace dev namespace dev
@ -117,6 +119,7 @@ void CompilerStack::compile(bool _optimize)
contractBytecode); contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()]; Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler); compiledContract.compiler = move(compiler);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode; contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
} }
@ -134,6 +137,16 @@ bytes const& CompilerStack::getBytecode(string const& _contractName) const
return getContract(_contractName).bytecode; return getContract(_contractName).bytecode;
} }
bytes const& CompilerStack::getRuntimeBytecode(string const& _contractName) const
{
return getContract(_contractName).runtimeBytecode;
}
dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const
{
return dev::sha3(getRuntimeBytecode(_contractName));
}
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const
{ {
getContract(_contractName).compiler->streamAssembly(_outStream); getContract(_contractName).compiler->streamAssembly(_outStream);

8
libsolidity/CompilerStack.h

@ -28,6 +28,7 @@
#include <memory> #include <memory>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
namespace dev { namespace dev {
namespace solidity { namespace solidity {
@ -75,7 +76,13 @@ public:
/// @returns the compiled bytecode /// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false); bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
/// @returns the assembled bytecode for a contract.
bytes const& getBytecode(std::string const& _contractName = "") const; bytes const& getBytecode(std::string const& _contractName = "") const;
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
/// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
dev::h256 getContractCodeHash(std::string const& _contractName = "") const;
/// Streams a verbose version of the assembly to @a _outStream. /// Streams a verbose version of the assembly to @a _outStream.
/// Prerequisite: Successful compilation. /// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const; void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
@ -121,6 +128,7 @@ private:
ContractDefinition const* contract = nullptr; ContractDefinition const* contract = nullptr;
std::shared_ptr<Compiler> compiler; std::shared_ptr<Compiler> compiler;
bytes bytecode; bytes bytecode;
bytes runtimeBytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler; std::shared_ptr<InterfaceHandler> interfaceHandler;
mutable std::unique_ptr<std::string const> interface; mutable std::unique_ptr<std::string const> interface;
mutable std::unique_ptr<std::string const> solidityInterface; mutable std::unique_ptr<std::string const> solidityInterface;

206
libsolidity/ExpressionCompiler.cpp

@ -232,27 +232,71 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
} }
case Location::EXTERNAL: case Location::EXTERNAL:
case Location::BARE: case Location::BARE:
_functionCall.getExpression().accept(*this);
appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE);
break;
case Location::CREATION:
{ {
FunctionCallOptions options; _functionCall.getExpression().accept(*this);
options.bare = function.getLocation() == Location::BARE; solAssert(!function.gasSet(), "Gas limit set for contract creation.");
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); }; solAssert(function.getReturnParameterTypes().size() == 1, "");
appendExternalFunctionCall(function, arguments, options); ContractDefinition const& contract = dynamic_cast<ContractType const&>(
*function.getReturnParameterTypes().front()).getContractDefinition();
// copy the contract's code into memory
bytes const& bytecode = m_context.getCompiledContract(contract);
m_context << u256(bytecode.size());
//@todo could be done by actually appending the Assembly, but then we probably need to compile
// multiple times. Will revisit once external fuctions are inlined.
m_context.appendData(bytecode);
//@todo copy to memory position 0, shift as soon as we use memory
m_context << u256(0) << eth::Instruction::CODECOPY;
unsigned length = bytecode.size();
length += appendArgumentCopyToMemory(function.getParameterTypes(), arguments, length);
// size, offset, endowment
m_context << u256(length) << u256(0);
if (function.valueSet())
m_context << eth::dupInstruction(3);
else
m_context << u256(0);
m_context << eth::Instruction::CREATE;
if (function.valueSet())
m_context << eth::swapInstruction(1) << eth::Instruction::POP;
break; break;
} }
case Location::SEND: case Location::SET_GAS:
{ {
FunctionCallOptions options; // stack layout: contract_address function_id [gas] [value]
options.bare = true; _functionCall.getExpression().accept(*this);
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); }; arguments.front()->accept(*this);
options.obtainValue = [&]() appendTypeConversion(*arguments.front()->getType(), IntegerType(256), true);
{ // Note that function is not the original function, but the ".gas" function.
arguments.front()->accept(*this); // Its values of gasSet and valueSet is equal to the original function's though.
appendTypeConversion(*arguments.front()->getType(), unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
*function.getParameterTypes().front(), true); if (stackDepth > 0)
}; m_context << eth::swapInstruction(stackDepth);
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL), {}, options); if (function.gasSet())
m_context << eth::Instruction::POP;
break; break;
} }
case Location::SET_VALUE:
// stack layout: contract_address function_id [gas] [value]
_functionCall.getExpression().accept(*this);
// Note that function is not the original function, but the ".value" function.
// Its values of gasSet and valueSet is equal to the original function's though.
if (function.valueSet())
m_context << eth::Instruction::POP;
arguments.front()->accept(*this);
break;
case Location::SEND:
_functionCall.getExpression().accept(*this);
m_context << u256(0); // 0 gas, we do not want to execute code
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(),
*function.getParameterTypes().front(), true);
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{},
Location::EXTERNAL, true, true), {}, true);
break;
case Location::SUICIDE: case Location::SUICIDE:
arguments.front()->accept(*this); arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
@ -296,11 +340,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1}, static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
{Location::SHA256, 2}, {Location::SHA256, 2},
{Location::RIPEMD160, 3}}; {Location::RIPEMD160, 3}};
u256 contractAddress = contractAddresses.find(function.getLocation())->second; m_context << contractAddresses.find(function.getLocation())->second;
FunctionCallOptions options; appendExternalFunctionCall(function, arguments, true);
options.bare = true;
options.obtainAddress = [&]() { m_context << contractAddress; };
appendExternalFunctionCall(function, arguments, options);
break; break;
} }
default: default:
@ -310,40 +351,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
return false; return false;
} }
bool ExpressionCompiler::visit(NewExpression const& _newExpression) bool ExpressionCompiler::visit(NewExpression const&)
{ {
ContractType const* type = dynamic_cast<ContractType const*>(_newExpression.getType().get()); // code is created for the function call (CREATION) only
solAssert(type, "");
TypePointers const& types = type->getConstructorType()->getParameterTypes();
vector<ASTPointer<Expression const>> arguments = _newExpression.getArguments();
solAssert(arguments.size() == types.size(), "");
// copy the contracts code into memory
bytes const& bytecode = m_context.getCompiledContract(*_newExpression.getContract());
m_context << u256(bytecode.size());
//@todo could be done by actually appending the Assembly, but then we probably need to compile
// multiple times. Will revisit once external fuctions are inlined.
m_context.appendData(bytecode);
//@todo copy to memory position 0, shift as soon as we use memory
m_context << u256(0) << eth::Instruction::CODECOPY;
unsigned dataOffset = bytecode.size();
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *types[i], true);
unsigned const c_numBytes = types[i]->getCalldataEncodedSize();
if (c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(arguments[i]->getLocation())
<< errinfo_comment("Type " + types[i]->toString() + " not yet supported."));
bool const c_leftAligned = types[i]->getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, c_numBytes,
c_leftAligned, c_padToWords);
}
// size, offset, endowment
m_context << u256(dataOffset) << u256(0) << u256(0) << eth::Instruction::CREATE;
return false; return false;
} }
@ -377,6 +387,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break; break;
case Type::Category::FUNCTION:
solAssert(!!_memberAccess.getExpression().getType()->getMemberType(member),
"Invalid member access to function.");
break;
case Type::Category::MAGIC: case Type::Category::MAGIC:
// we can ignore the kind of magic and only look at the name of the member // we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase") if (member == "coinbase")
@ -653,46 +667,60 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType, void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType,
vector<ASTPointer<Expression const>> const& _arguments, vector<ASTPointer<Expression const>> const& _arguments,
FunctionCallOptions const& _options) bool bare)
{ {
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), ""); solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
_options.obtainAddress(); // Assumed stack content here:
if (!_options.bare) // <stack top>
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset); // value [if _functionType.valueSet()]
// gas [if _functionType.gasSet()]
// function identifier [unless bare]
// contract address
unsigned dataOffset = _options.bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
for (unsigned i = 0; i < _arguments.size(); ++i)
unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + (bare ? 0 : 1));
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
if (!bare)
{ {
_arguments[i]->accept(*this); // copy function identifier
Type const& type = *_functionType.getParameterTypes()[i]; m_context << eth::dupInstruction(gasValueSize + 1);
appendTypeConversion(*_arguments[i]->getType(), type, true); CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
unsigned const c_numBytes = type.getCalldataEncodedSize();
if (c_numBytes == 0 || c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(_arguments[i]->getLocation())
<< errinfo_comment("Type " + type.toString() + " not yet supported."));
bool const c_leftAligned = type.getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, c_numBytes,
c_leftAligned, c_padToWords);
} }
// reserve space for the function identifier
unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset;
dataOffset += appendArgumentCopyToMemory(_functionType.getParameterTypes(), _arguments, dataOffset);
//@todo only return the first return value for now //@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get(); _functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0; unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top) // CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0); m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
if (_options.obtainValue) if (_functionType.valueSet())
_options.obtainValue(); m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else else
m_context << u256(0); m_context << u256(0);
m_context << eth::dupInstruction(6); //copy contract address m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB if (_functionType.gasSet())
<< eth::Instruction::CALL m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
<< eth::Instruction::POP // @todo do not ignore failure indicator else
<< eth::Instruction::POP; // pop contract address // send all gas except for the 21 needed to execute "SUB" and "CALL"
m_context << u256(21) << eth::Instruction::GAS << eth::Instruction::SUB;
m_context << eth::Instruction::CALL
<< eth::Instruction::POP; // @todo do not ignore failure indicator
if (_functionType.valueSet())
m_context << eth::Instruction::POP;
if (_functionType.gasSet())
m_context << eth::Instruction::POP;
if (!bare)
m_context << eth::Instruction::POP;
m_context << eth::Instruction::POP; // pop contract address
if (retSize > 0) if (retSize > 0)
{ {
@ -701,6 +729,28 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
} }
} }
unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _types,
vector<ASTPointer<Expression const>> const& _arguments,
unsigned _memoryOffset)
{
unsigned length = 0;
for (unsigned i = 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
appendTypeConversion(*_arguments[i]->getType(), *_types[i], true);
unsigned const c_numBytes = _types[i]->getCalldataEncodedSize();
if (c_numBytes == 0 || c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(_arguments[i]->getLocation())
<< errinfo_comment("Type " + _types[i]->toString() + " not yet supported."));
bool const c_leftAligned = _types[i]->getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
length += CompilerUtils(m_context).storeInMemory(_memoryOffset + length, c_numBytes,
c_leftAligned, c_padToWords);
}
return length;
}
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset): unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset), m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),

18
libsolidity/ExpressionCompiler.h

@ -87,21 +87,13 @@ private:
//// Appends code that cleans higher-order bits for integer types. //// Appends code that cleans higher-order bits for integer types.
void appendHighBitsCleanup(IntegerType const& _typeOnStack); void appendHighBitsCleanup(IntegerType const& _typeOnStack);
/// Additional options used in appendExternalFunctionCall.
struct FunctionCallOptions
{
FunctionCallOptions() {}
/// Invoked to copy the address to the stack
std::function<void()> obtainAddress;
/// Invoked to copy the ethe value to the stack (if not specified, value is 0).
std::function<void()> obtainValue;
/// If true, do not prepend function index to call data
bool bare = false;
};
/// Appends code to call a function of the given type with the given arguments. /// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments, void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
FunctionCallOptions const& _options = FunctionCallOptions()); bool bare = false);
/// Appends code that copies the given arguments to memory (with optional offset).
/// @returns the number of bytes copied to memory
unsigned appendArgumentCopyToMemory(TypePointers const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
unsigned _memoryOffset = 0);
/** /**
* Helper class to store and retrieve lvalues to and from various locations. * Helper class to store and retrieve lvalues to and from various locations.

4
libsolidity/InterfaceHandler.cpp

@ -106,7 +106,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (!m_notice.empty()) if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear {// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice); user["notice"] = Json::Value(m_notice);
methods[it.second->getName()] = user; methods[it.second->getCanonicalSignature()] = user;
} }
} }
} }
@ -162,7 +162,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["return"] = m_return; method["return"] = m_return;
if (!method.empty()) // add the function, only if we have any documentation to add if (!method.empty()) // add the function, only if we have any documentation to add
methods[it.second->getName()] = method; methods[it.second->getCanonicalSignature()] = method;
} }
} }
doc["methods"] = methods; doc["methods"] = methods;

24
libsolidity/Parser.cpp

@ -466,17 +466,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (token == Token::NEW) if (Token::isUnaryOp(token) || Token::isCountOp(token))
{
expectToken(Token::NEW);
ASTPointer<Identifier> contractName = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
expectToken(Token::LPAREN);
vector<ASTPointer<Expression>> arguments(parseFunctionCallArguments());
expectToken(Token::RPAREN);
nodeFactory.markEndPosition();
return nodeFactory.createNode<NewExpression>(contractName, arguments);
}
else if (Token::isUnaryOp(token) || Token::isCountOp(token))
{ {
// prefix expression // prefix expression
m_scanner->next(); m_scanner->next();
@ -500,7 +490,17 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
ASTPointer<Expression> Parser::parseLeftHandSideExpression() ASTPointer<Expression> Parser::parseLeftHandSideExpression()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression = parsePrimaryExpression(); ASTPointer<Expression> expression;
if (m_scanner->getCurrentToken() == Token::NEW)
{
expectToken(Token::NEW);
ASTPointer<Identifier> contractName = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<NewExpression>(contractName);
}
else
expression = parsePrimaryExpression();
while (true) while (true)
{ {
switch (m_scanner->getCurrentToken()) switch (m_scanner->getCurrentToken())

4
libsolidity/Token.h

@ -47,6 +47,10 @@
#include <libsolidity/Utils.h> #include <libsolidity/Utils.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#if defined(DELETE)
#error The macro "DELETE" from windows.h conflicts with this file. Please change the order of includes.
#endif
namespace dev namespace dev
{ {
namespace solidity namespace solidity

51
libsolidity/Types.cpp

@ -546,7 +546,8 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
} }
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal) FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL)
{ {
TypePointers params; TypePointers params;
TypePointers retParams; TypePointers retParams;
@ -558,7 +559,6 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
retParams.push_back(var->getType()); retParams.push_back(var->getType());
swap(params, m_parameterTypes); swap(params, m_parameterTypes);
swap(retParams, m_returnParameterTypes); swap(retParams, m_returnParameterTypes);
m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
} }
bool FunctionType::operator==(Type const& _other) const bool FunctionType::operator==(Type const& _other) const
@ -580,6 +580,9 @@ bool FunctionType::operator==(Type const& _other) const
if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(),
other.m_returnParameterTypes.cbegin(), typeCompare)) other.m_returnParameterTypes.cbegin(), typeCompare))
return false; return false;
//@todo this is ugly, but cannot be prevented right now
if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
return false;
return true; return true;
} }
@ -595,17 +598,45 @@ string FunctionType::toString() const
} }
unsigned FunctionType::getSizeOnStack() const unsigned FunctionType::getSizeOnStack() const
{
unsigned size = 0;
if (m_location == Location::EXTERNAL)
size = 2;
else if (m_location == Location::INTERNAL || m_location == Location::BARE)
size = 1;
if (m_gasSet)
size++;
if (m_valueSet)
size++;
return size;
}
MemberList const& FunctionType::getMembers() const
{ {
switch (m_location) switch (m_location)
{ {
case Location::INTERNAL:
return 1;
case Location::EXTERNAL: case Location::EXTERNAL:
return 2; case Location::CREATION:
case Location::ECRECOVER:
case Location::SHA256:
case Location::RIPEMD160:
case Location::BARE: case Location::BARE:
return 1; if (!m_members)
{
map<string, TypePointer> members{
{"gas", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
TypePointers{copyAndSetGasOrValue(true, false)},
Location::SET_GAS, m_gasSet, m_valueSet)},
{"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
TypePointers{copyAndSetGasOrValue(false, true)},
Location::SET_VALUE, m_gasSet, m_valueSet)}};
if (m_location == Location::CREATION)
members.erase("gas");
m_members.reset(new MemberList(members));
}
return *m_members;
default: default:
return 0; return EmptyMemberList;
} }
} }
@ -628,6 +659,12 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
return pointers; return pointers;
} }
TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const
{
return make_shared<FunctionType>(m_parameterTypes, m_returnParameterTypes, m_location,
m_gasSet || _setGas, m_valueSet || _setValue);
}
bool MappingType::operator==(Type const& _other) const bool MappingType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())

29
libsolidity/Types.h

@ -291,6 +291,8 @@ public:
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
ContractDefinition const& getContractDefinition() const { return m_contract; }
/// Returns the function type of the constructor. Note that the location part of the function type /// Returns the function type of the constructor. Note that the location part of the function type
/// is not used, as this type cannot be the type of a variable or expression. /// is not used, as this type cannot be the type of a variable or expression.
std::shared_ptr<FunctionType const> const& getConstructorType() const; std::shared_ptr<FunctionType const> const& getConstructorType() const;
@ -345,10 +347,15 @@ class FunctionType: public Type
{ {
public: public:
/// The meaning of the value(s) on the stack referencing the function: /// The meaning of the value(s) on the stack referencing the function:
/// INTERNAL: jump tag, EXTERNAL: contract address + function index, /// INTERNAL: jump tag, EXTERNAL: contract address + function identifier,
/// BARE: contract address (non-abi contract call) /// BARE: contract address (non-abi contract call)
/// OTHERS: special virtual function, nothing on the stack /// OTHERS: special virtual function, nothing on the stack
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, LOG0, LOG1, LOG2, LOG3, LOG4, BLOCKHASH, BARE }; enum class Location { INTERNAL, EXTERNAL, CREATION, SEND,
SHA3, SUICIDE,
ECRECOVER, SHA256, RIPEMD160,
LOG0, LOG1, LOG2, LOG3, LOG4,
SET_GAS, SET_VALUE, BLOCKHASH
BARE };
virtual Category getCategory() const override { return Category::FUNCTION; } virtual Category getCategory() const override { return Category::FUNCTION; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
@ -357,9 +364,10 @@ public:
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
_location) {} _location) {}
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
Location _location = Location::INTERNAL): Location _location = Location::INTERNAL,
bool _gasSet = false, bool _valueSet = false):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
m_location(_location) {} m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {}
TypePointers const& getParameterTypes() const { return m_parameterTypes; } TypePointers const& getParameterTypes() const { return m_parameterTypes; }
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; } TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
@ -370,16 +378,27 @@ public:
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); } virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override; virtual unsigned getSizeOnStack() const override;
virtual MemberList const& getMembers() const override;
Location const& getLocation() const { return m_location; } Location const& getLocation() const { return m_location; }
std::string getCanonicalSignature() const; std::string getCanonicalSignature() const;
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
/// @returns a copy of this type, where gas or value are set manually. This will never set one
/// of the parameters to fals.
TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const;
private: private:
static TypePointers parseElementaryTypeVector(strings const& _types); static TypePointers parseElementaryTypeVector(strings const& _types);
TypePointers m_parameterTypes; TypePointers m_parameterTypes;
TypePointers m_returnParameterTypes; TypePointers m_returnParameterTypes;
Location m_location; Location const m_location;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
mutable std::unique_ptr<MemberList> m_members;
}; };
/** /**

2
libsolidity/grammar.txt

@ -33,7 +33,7 @@ Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewE
// The expression syntax is actually much more complicated // The expression syntax is actually much more complicated
Assignment = Expression (AssignmentOp Expression) Assignment = Expression (AssignmentOp Expression)
FunctionCall = Expression '(' Expression ( ',' Expression )* ')' FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
NewExpression = 'new' Identifier '(' ( Expression ( ',' Expression )* ) ')' NewExpression = 'new' Identifier
MemberAccess = Expression '.' Identifier MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expresison ']' IndexAccess = Expression '[' Expresison ']'
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'

7
mix/AssemblyDebuggerControl.cpp

@ -26,8 +26,8 @@
using namespace dev::mix; using namespace dev::mix;
AssemblyDebuggerControl::AssemblyDebuggerControl(dev::mix::AppContext* _context): AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context):
Extension(_context, ExtensionDisplayBehavior::ModalDialog) Extension(_context, ExtensionDisplayBehavior::RightView)
{ {
connect(_context->clientModel(), &ClientModel::showDebuggerWindow, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection); connect(_context->clientModel(), &ClientModel::showDebuggerWindow, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection);
} }
@ -48,5 +48,6 @@ void AssemblyDebuggerControl::start() const
void AssemblyDebuggerControl::showDebugger() void AssemblyDebuggerControl::showDebugger()
{ {
this->addContentOn(this); QObject* debugPanel = m_view->findChild<QObject*>("debugPanel", Qt::FindChildrenRecursively);
QMetaObject::invokeMethod(debugPanel, "update");
} }

6
mix/AssemblyDebuggerControl.h

@ -22,13 +22,13 @@
#include <atomic> #include <atomic>
#include "Extension.h" #include "Extension.h"
class AppContext;
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
class AppContext;
/** /**
* @brief Extension which display transaction creation or transaction call debugging. * @brief Extension which display transaction creation or transaction call debugging.
*/ */
@ -44,7 +44,7 @@ public:
QString contentUrl() const override; QString contentUrl() const override;
private slots: private slots:
/// Update UI with machine states result. Display a modal dialog. /// Update UI with machine states result. Displayed in the right side tab.
void showDebugger(); void showDebugger();
}; };

2
mix/ClientModel.cpp

@ -116,7 +116,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
{ {
bytes contractCode = compilerRes->bytes(); bytes contractCode = compilerRes->bytes();
std::vector<dev::bytes> transactonData; std::vector<dev::bytes> transactonData;
QFunctionDefinition* f; QFunctionDefinition* f = nullptr;
ContractCallDataEncoder c; ContractCallDataEncoder c;
//encode data for all transactions //encode data for all transactions
for (auto const& t: _sequence) for (auto const& t: _sequence)

21
mix/CodeEditorExtensionManager.cpp

@ -25,7 +25,7 @@
#include <QQmlEngine> #include <QQmlEngine>
#include <QQmlComponent> #include <QQmlComponent>
#include <QQuickTextDocument> #include <QQuickTextDocument>
#include "ConstantCompilationControl.h" #include "StatusPane.h"
#include "AssemblyDebuggerControl.h" #include "AssemblyDebuggerControl.h"
#include "StateListView.h" #include "StateListView.h"
#include "AppContext.h" #include "AppContext.h"
@ -56,10 +56,9 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
void CodeEditorExtensionManager::initExtensions() void CodeEditorExtensionManager::initExtensions()
{ {
std::shared_ptr<ConstantCompilationControl> output = std::make_shared<ConstantCompilationControl>(m_appContext); std::shared_ptr<StatusPane> output = std::make_shared<StatusPane>(m_appContext);
std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_appContext); std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_appContext);
std::shared_ptr<StateListView> stateList = std::make_shared<StateListView>(m_appContext); std::shared_ptr<StateListView> stateList = std::make_shared<StateListView>(m_appContext);
QObject::connect(m_appContext->clientModel(), &ClientModel::runFailed, output.get(), &ConstantCompilationControl::displayError);
QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight);
initExtension(output); initExtension(output);
@ -73,10 +72,10 @@ void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)
{ {
try try
{ {
if (_ext->getDisplayBehavior() == ExtensionDisplayBehavior::Tab) if (_ext->getDisplayBehavior() == ExtensionDisplayBehavior::RightView)
_ext->addTabOn(m_tabView); _ext->addTabOn(m_rightView);
else if (_ext->getDisplayBehavior() == ExtensionDisplayBehavior::RightTab) if (_ext->getDisplayBehavior() == ExtensionDisplayBehavior::HeaderView)
_ext->addTabOn(m_rightTabView); _ext->addTabOn(m_headerView);
} }
catch (...) catch (...)
{ {
@ -93,13 +92,13 @@ void CodeEditorExtensionManager::applyCodeHighlight()
//TODO: reimplement //TODO: reimplement
} }
void CodeEditorExtensionManager::setRightTabView(QQuickItem* _tabView) void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView)
{ {
m_rightTabView = _tabView; m_rightView = _rightView;
initExtensions(); //TODO: move this to a proper place initExtensions(); //TODO: move this to a proper place
} }
void CodeEditorExtensionManager::setTabView(QQuickItem* _tabView) void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView)
{ {
m_tabView = _tabView; m_headerView = _headerView;
} }

14
mix/CodeEditorExtensionManager.h

@ -26,7 +26,7 @@
#include <QQuickItem> #include <QQuickItem>
#include <QTextDocument> #include <QTextDocument>
#include <QVector> #include <QVector>
#include "ConstantCompilationControl.h" #include "StatusPane.h"
namespace dev namespace dev
{ {
@ -43,8 +43,8 @@ class CodeEditorExtensionManager: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QQuickItem* tabView MEMBER m_tabView WRITE setTabView) Q_PROPERTY(QQuickItem* headerView MEMBER m_headerView WRITE setHeaderView)
Q_PROPERTY(QQuickItem* rightTabView MEMBER m_rightTabView WRITE setRightTabView) Q_PROPERTY(QQuickItem* rightView MEMBER m_rightView WRITE setRightView)
public: public:
CodeEditorExtensionManager(); CodeEditorExtensionManager();
@ -54,17 +54,17 @@ public:
/// Initialize extension. /// Initialize extension.
void initExtension(std::shared_ptr<Extension>); void initExtension(std::shared_ptr<Extension>);
/// Set current tab view /// Set current tab view
void setTabView(QQuickItem*); void setHeaderView(QQuickItem*);
/// Set current right tab view. /// Set current right tab view.
void setRightTabView(QQuickItem*); void setRightView(QQuickItem*);
private slots: private slots:
void applyCodeHighlight(); void applyCodeHighlight();
private: private:
QVector<std::shared_ptr<Extension>> m_features; QVector<std::shared_ptr<Extension>> m_features;
QQuickItem* m_tabView; QQuickItem* m_headerView;
QQuickItem* m_rightTabView; QQuickItem* m_rightView;
AppContext* m_appContext; AppContext* m_appContext;
void loadEditor(QQuickItem* _editor); void loadEditor(QQuickItem* _editor);
}; };

24
mix/CodeModel.cpp

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file CodeModel.cpp /** @file CodeModel.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
@ -168,7 +168,7 @@ void CodeModel::onCompilationComplete(CompilationResult*_newResult)
bool CodeModel::hasContract() const bool CodeModel::hasContract() const
{ {
return m_result->contract()->functionsList().size() > 0; return m_result->successful();
} }
void CodeModel::updateFormatting(QTextDocument* _document) void CodeModel::updateFormatting(QTextDocument* _document)

24
mix/CodeModel.h

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file CodeModel.h /** @file CodeModel.h
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
@ -65,6 +65,8 @@ class CompilationResult: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QContractDefinition* contract READ contract) Q_PROPERTY(QContractDefinition* contract READ contract)
Q_PROPERTY(QString compilerMessage READ compilerMessage CONSTANT)
Q_PROPERTY(bool successful READ successful CONSTANT)
public: public:
/// Empty compilation result constructor /// Empty compilation result constructor

94
mix/ConstantCompilationControl.cpp

@ -1,94 +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 <http://www.gnu.org/licenses/>.
*/
/** @file ConstantCompilationControl.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QQmlContext>
#include <QQuickItem>
#include <QtCore/QFileInfo>
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtCore/QtCore>
#include <QDebug>
#include "ConstantCompilationControl.h"
#include "QContractDefinition.h"
#include "AppContext.h"
#include "CodeModel.h"
using namespace dev::mix;
ConstantCompilationControl::ConstantCompilationControl(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::Tab)
{
connect(_context->codeModel(), &CodeModel::compilationComplete, this, &ConstantCompilationControl::update);
connect(_context->codeModel(), &CodeModel::compilationComplete, this, &ConstantCompilationControl::update);
}
QString ConstantCompilationControl::contentUrl() const
{
return QStringLiteral("qrc:/qml/BasicContent.qml");
}
QString ConstantCompilationControl::title() const
{
return QApplication::tr("compiler");
}
void ConstantCompilationControl::start() const
{
}
void ConstantCompilationControl::update()
{
auto result = m_ctx->codeModel()->code();
QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
if (result->successful())
{
status->setProperty("text", "succeeded");
status->setProperty("color", "green");
content->setProperty("text", result->assemblyCode());
}
else
{
status->setProperty("text", "failure");
status->setProperty("color", "red");
content->setProperty("text", result->compilerMessage());
}
}
void ConstantCompilationControl::resetOutPut()
{
QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
status->setProperty("text", "");
content->setProperty("text", "");
}
void ConstantCompilationControl::displayError(QString const& _error)
{
QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
status->setProperty("text", "failure");
status->setProperty("color", "red");
content->setProperty("text", _error);
}

118
mix/DebuggingStateWrapper.cpp

@ -66,55 +66,134 @@ std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCod
return std::make_tuple(codeStr, QPointer<QQMLMap>(new QQMLMap(codeMapping))); return std::make_tuple(codeStr, QPointer<QQMLMap>(new QQMLMap(codeMapping)));
} }
QString DebuggingStateWrapper::gasLeft() QString DebuggingStateWrapper::gasCost()
{ {
std::ostringstream ss; std::ostringstream ss;
ss << std::dec << (m_state.gas - m_state.gasCost); ss << std::dec << m_state.gasCost;
return QString::fromStdString(ss.str()); return QString::fromStdString(ss.str());
} }
QString DebuggingStateWrapper::gasCost() QString DebuggingStateWrapper::gas()
{ {
std::ostringstream ss; std::ostringstream ss;
ss << std::dec << m_state.gasCost; ss << std::dec << m_state.gas;
return QString::fromStdString(ss.str()); return QString::fromStdString(ss.str());
} }
QString DebuggingStateWrapper::gas() QString DebuggingStateWrapper::newMemSize()
{ {
std::ostringstream ss; std::ostringstream ss;
ss << std::dec << m_state.gas; ss << std::dec << m_state.newMemSize;
return QString::fromStdString(ss.str()); return QString::fromStdString(ss.str());
} }
QString DebuggingStateWrapper::debugStack() QStringList DebuggingStateWrapper::debugStack()
{ {
QString stack; QStringList stack;
for (auto i: m_state.stack) for (auto i: m_state.stack)
stack.prepend(QString::fromStdString(prettyU256(i)) + "\n"); stack.append(QString::fromStdString(prettyU256(i)));
return stack; return fillList(stack, "");
} }
QString DebuggingStateWrapper::debugStorage() QStringList DebuggingStateWrapper::debugStorage()
{ {
std::stringstream s; QStringList storage;
for (auto const& i: m_state.storage) for (auto const& i: m_state.storage)
{
std::stringstream s;
s << "@" << prettyU256(i.first) << " " << prettyU256(i.second); s << "@" << prettyU256(i.first) << " " << prettyU256(i.second);
storage.append(QString::fromStdString(s.str()));
}
return fillList(storage, "@ -");
}
return QString::fromStdString(s.str()); QVariantList DebuggingStateWrapper::debugMemory()
{
std::vector<std::vector<std::string>> dump = memDumpToList(m_state.memory, 16);
QStringList filled;
filled.append(" ");
filled.append(" ");
filled.append(" ");
return fillList(qVariantDump(dump), QVariant(filled));
} }
QString DebuggingStateWrapper::debugMemory() QVariantList DebuggingStateWrapper::debugCallData()
{ {
return QString::fromStdString(memDump(m_state.memory, 16, false)); std::vector<std::vector<std::string>> dump = memDumpToList(m_data, 16);
QStringList filled;
filled.append(" ");
filled.append(" ");
filled.append(" ");
return fillList(qVariantDump(dump), QVariant(filled));
} }
QString DebuggingStateWrapper::debugCallData() std::vector<std::vector<std::string>> DebuggingStateWrapper::memDumpToList(bytes const& _bytes, unsigned _width)
{ {
return QString::fromStdString(memDump(m_data, 16, false)); std::vector<std::vector<std::string>> dump;
for (unsigned i = 0; i < _bytes.size(); i += _width)
{
std::stringstream ret;
std::vector<std::string> dumpLine;
ret << std::hex << std::setw(4) << std::setfill('0') << i << " ";
dumpLine.push_back(ret.str());
ret.str(std::string());
ret.clear();
for (unsigned j = i; j < i + _width; ++j)
if (j < _bytes.size())
if (_bytes[j] >= 32 && _bytes[j] < 127)
ret << (char)_bytes[j];
else
ret << '?';
else
ret << ' ';
dumpLine.push_back(ret.str());
ret.str(std::string());
ret.clear();
for (unsigned j = i; j < i + _width && j < _bytes.size(); ++j)
ret << std::setfill('0') << std::setw(2) << std::hex << (unsigned)_bytes[j] << " ";
dumpLine.push_back(ret.str());
dump.push_back(dumpLine);
}
return dump;
} }
QVariantList DebuggingStateWrapper::qVariantDump(std::vector<std::vector<std::string>> const& _dump)
{
QVariantList ret;
for (std::vector<std::string> const& line: _dump)
{
QStringList qLine;
for (std::string const& cell: line)
qLine.push_back(QString::fromStdString(cell));
ret.append(QVariant(qLine));
}
return ret;
}
QStringList DebuggingStateWrapper::fillList(QStringList& _list, QString const& _emptyValue)
{
if (_list.size() < 20)
{
for (int k = _list.size(); k < 20 - _list.size(); k++)
_list.append(_emptyValue);
}
return _list;
}
QVariantList DebuggingStateWrapper::fillList(QVariantList _list, QVariant const& _emptyValue)
{
if (_list.size() < 20)
{
for (int k = _list.size(); k < 20 - _list.size(); k++)
_list.append(_emptyValue);
}
return _list;
}
QStringList DebuggingStateWrapper::levels() QStringList DebuggingStateWrapper::levels()
{ {
QStringList levelsStr; QStringList levelsStr;
@ -136,6 +215,11 @@ QString DebuggingStateWrapper::headerInfo()
return QString::fromStdString(ss.str()); return QString::fromStdString(ss.str());
} }
QString DebuggingStateWrapper::instruction()
{
return QString::fromStdString(dev::eth::instructionInfo(m_state.inst).name);
}
QString DebuggingStateWrapper::endOfDebug() QString DebuggingStateWrapper::endOfDebug()
{ {
if (m_state.gasCost > m_state.gas) if (m_state.gasCost > m_state.gas)

30
mix/DebuggingStateWrapper.h

@ -83,13 +83,14 @@ class DebuggingStateWrapper: public QObject
Q_PROPERTY(int curPC READ curPC CONSTANT) Q_PROPERTY(int curPC READ curPC CONSTANT)
Q_PROPERTY(QString gasCost READ gasCost CONSTANT) Q_PROPERTY(QString gasCost READ gasCost CONSTANT)
Q_PROPERTY(QString gas READ gas CONSTANT) Q_PROPERTY(QString gas READ gas CONSTANT)
Q_PROPERTY(QString gasLeft READ gasLeft CONSTANT) Q_PROPERTY(QString instruction READ instruction CONSTANT)
Q_PROPERTY(QString debugStack READ debugStack CONSTANT) Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT)
Q_PROPERTY(QString debugStorage READ debugStorage CONSTANT) Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT)
Q_PROPERTY(QString debugMemory READ debugMemory CONSTANT) Q_PROPERTY(QVariantList debugMemory READ debugMemory CONSTANT)
Q_PROPERTY(QString debugCallData READ debugCallData CONSTANT) Q_PROPERTY(QVariantList debugCallData READ debugCallData CONSTANT)
Q_PROPERTY(QString headerInfo READ headerInfo CONSTANT) Q_PROPERTY(QString headerInfo READ headerInfo CONSTANT)
Q_PROPERTY(QString endOfDebug READ endOfDebug CONSTANT) Q_PROPERTY(QString endOfDebug READ endOfDebug CONSTANT)
Q_PROPERTY(QString newMemSize READ newMemSize CONSTANT)
Q_PROPERTY(QStringList levels READ levels CONSTANT) Q_PROPERTY(QStringList levels READ levels CONSTANT)
public: public:
@ -105,17 +106,21 @@ public:
/// Get gas used. /// Get gas used.
QString gas(); QString gas();
/// Get stack. /// Get stack.
QString debugStack(); QStringList debugStack();
/// Get storage. /// Get storage.
QString debugStorage(); QStringList debugStorage();
/// Get memory. /// Get memory.
QString debugMemory(); QVariantList debugMemory();
/// Get call data. /// Get call data.
QString debugCallData(); QVariantList debugCallData();
/// Get info to be displayed in the header. /// Get info to be displayed in the header.
QString headerInfo(); QString headerInfo();
/// get end of debug information. /// get end of debug information.
QString endOfDebug(); QString endOfDebug();
/// Get the new memory size.
QString newMemSize();
/// Get current instruction
QString instruction();
/// Get all previous steps. /// Get all previous steps.
QStringList levels(); QStringList levels();
/// Get the current processed machine state. /// Get the current processed machine state.
@ -129,6 +134,13 @@ private:
MachineState m_state; MachineState m_state;
bytes m_code; bytes m_code;
bytes m_data; bytes m_data;
QStringList fillList(QStringList& _list, QString const& _emptyValue);
QVariantList fillList(QVariantList _list, QVariant const& _emptyValue);
QVariantList qVariantDump(std::vector<std::vector<std::string>> const& _dump);
/// Nicely renders the given bytes to a string, store the content in an array.
/// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line.
std::vector<std::vector<std::string>> memDumpToList(bytes const& _bytes, unsigned _width);
}; };
} }

4
mix/Extension.h

@ -33,8 +33,8 @@ class AppContext;
enum ExtensionDisplayBehavior enum ExtensionDisplayBehavior
{ {
Tab, HeaderView,
RightTab, RightView,
ModalDialog ModalDialog
}; };

24
mix/StateListView.cpp

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file StateListView.cpp /** @file StateListView.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
@ -29,7 +29,7 @@
using namespace dev::mix; using namespace dev::mix;
StateListView::StateListView(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::RightTab) StateListView::StateListView(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::RightView)
{ {
} }

66
mix/StatusPane.cpp

@ -0,0 +1,66 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ConstantCompilationControl.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QQmlContext>
#include <QQuickItem>
#include <QtCore/QFileInfo>
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtCore/QtCore>
#include <QDebug>
#include "StatusPane.h"
#include "QContractDefinition.h"
#include "AppContext.h"
#include "CodeModel.h"
using namespace dev::mix;
StatusPane::StatusPane(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::HeaderView)
{
connect(_context->codeModel(), &CodeModel::compilationComplete, this, &StatusPane::update);
_context->appEngine()->rootContext()->setContextProperty("statusPane", this);
}
QString StatusPane::contentUrl() const
{
return QStringLiteral("qrc:/qml/StatusPane.qml");
}
QString StatusPane::title() const
{
return QApplication::tr("compiler");
}
void StatusPane::start() const
{
}
CompilationResult* StatusPane::result() const
{
return m_ctx->codeModel()->code();
}
void StatusPane::update()
{
QObject* ctrl = m_view->findChild<QObject*>("statusPane", Qt::FindChildrenRecursively);
QMetaObject::invokeMethod(ctrl, "updateStatus");
}

13
mix/ConstantCompilationControl.h → mix/StatusPane.h

@ -20,6 +20,7 @@
#pragma once #pragma once
#include "Extension.h" #include "Extension.h"
#include "CodeModel.h"
namespace dev namespace dev
{ {
@ -29,23 +30,21 @@ namespace mix
/** /**
* @brief Extension which display assembly code of the contract being edited. * @brief Extension which display assembly code of the contract being edited.
*/ */
class ConstantCompilationControl: public Extension class StatusPane: public Extension
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(CompilationResult* result READ result CONSTANT)
public: public:
ConstantCompilationControl(AppContext* _appContext); StatusPane(AppContext* _appContext);
~ConstantCompilationControl() {} ~StatusPane() {}
void start() const override; void start() const override;
QString title() const override; QString title() const override;
QString contentUrl() const override; QString contentUrl() const override;
CompilationResult* result() const;
private:
void resetOutPut();
public slots: public slots:
void update(); void update();
void displayError(QString const& _error);
}; };
} }

21
mix/qml.qrc

@ -10,6 +10,27 @@
<file>qml/ProjectList.qml</file> <file>qml/ProjectList.qml</file>
<file>qml/StateDialog.qml</file> <file>qml/StateDialog.qml</file>
<file>qml/StateList.qml</file> <file>qml/StateList.qml</file>
<file>qml/img/jumpintoback.png</file>
<file>qml/img/jumpintoforward.png</file>
<file>qml/img/jumpoutback.png</file>
<file>qml/img/jumpoutforward.png</file>
<file>qml/img/jumpoverback.png</file>
<file>qml/img/jumpoverforward.png</file>
<file>qml/StepActionImage.qml</file>
<file>qml/img/jumpintobackdisabled.png</file>
<file>qml/img/jumpintoforwarddisabled.png</file>
<file>qml/img/jumpoutbackdisabled.png</file>
<file>qml/img/jumpoutforwarddisabled.png</file>
<file>qml/img/jumpoverbackdisabled.png</file>
<file>qml/DebugBasicInfo.qml</file>
<file>qml/js/ErrorLocationFormater.js</file>
<file>qml/img/closedtriangleindicator.png</file>
<file>qml/img/opentriangleindicator.png</file>
<file>qml/img/bugiconactive.png</file>
<file>qml/img/bugiconinactive.png</file>
<file>qml/DebugInfoList.qml</file>
<file>qml/ItemDelegateDataDump.qml</file>
<file>qml/StatusPane.qml</file>
<file>qml/TabStyle.qml</file> <file>qml/TabStyle.qml</file>
<file>qml/TransactionDialog.qml</file> <file>qml/TransactionDialog.qml</file>
<file>qml/js/Debugger.js</file> <file>qml/js/Debugger.js</file>

35
mix/qml/DebugBasicInfo.qml

@ -0,0 +1,35 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
RowLayout {
property string titleStr
width: parent.width
height: parent.height / 4
function update(_value)
{
currentStepValue.text = _value;
}
Rectangle {
width: parent.width / 2
height: parent.height
color: "#e5e5e5"
Text
{
id: title
font.pixelSize: 12
anchors.centerIn: parent
color: "#a2a2a2"
font.family: "Sans Serif"
text: titleStr
}
}
Text
{
font.pixelSize: 13
id: currentStepValue
}
}

83
mix/qml/DebugInfoList.qml

@ -0,0 +1,83 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
ColumnLayout {
property string title
property variant listModel;
property bool collapsible;
property Component itemDelegate
spacing: 0
RowLayout {
height: 25
id: header
Image {
source: "qrc:/qml/img/opentriangleindicator.png"
width: 15
sourceSize.width: 15
id: storageImgArrow
visible: collapsible
}
Text {
anchors.left: storageImgArrow.right
color: "#8b8b8b"
text: title
id: storageListTitle
}
MouseArea
{
enabled: collapsible
anchors.fill: parent
onClicked: {
if (storageContainer.state == "collapsed")
storageContainer.state = "";
else
storageContainer.state = "collapsed";
}
}
}
RowLayout
{
height: parent.height - header.height
clip: true
Rectangle
{
height: parent.height
border.width: 3
border.color: "#deddd9"
Layout.fillWidth: true
states: [
State {
name: "collapsed"
PropertyChanges {
target: storageContainer.parent
height: 0
visible: false
}
PropertyChanges {
target: storageImgArrow
source: "qrc:/qml/img/closedtriangleindicator.png"
}
}
]
id: storageContainer
width: parent.width
ListView {
clip: true;
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 3
anchors.leftMargin: 3
width: parent.width - 3
height: parent.height - 6
id: storageList
model: listModel
delegate: itemDelegate
}
}
}
}

689
mix/qml/Debugger.qml

@ -4,266 +4,505 @@ import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import "js/Debugger.js" as Debugger import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
Rectangle { Rectangle {
id: debugPanel
objectName: "debugPanel"
anchors.fill: parent; anchors.fill: parent;
color: "lightgrey" color: "#ededed"
Component.onCompleted: Debugger.init(); clip: true
Rectangle { Keys.onPressed:
color: "transparent" {
id: headerInfo
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
height: 60
anchors.top: parent.top
Column {
width: parent.width
height: parent.height
Rectangle {
color: "transparent"
width: parent.width
height: 30
Label {
anchors.centerIn: parent
font.family: "Verdana"
font.pointSize: 9
font.italic: true
id: headerInfoLabel
}
}
Rectangle {
color: "transparent"
width: parent.width
anchors.horizontalCenter: parent.horizontalCenter
height: 30
ListView {
orientation: ListView.Horizontal
anchors.centerIn: parent;
width: parent.width
id: headerReturnList
delegate: renderDelegateReturnValues
}
Component {
id: renderDelegateReturnValues
Item {
id: wrapperItem
width: 80
Text {
anchors.centerIn: parent
text: variable.declaration.name + " = " + variable.value
font.pointSize: 9
}
}
}
}
}
}
Keys.onPressed: {
if (event.key === Qt.Key_F10) if (event.key === Qt.Key_F10)
Debugger.moveSelection(1); Debugger.moveSelection(1);
else if (event.key === Qt.Key_F9) else if (event.key === Qt.Key_F9)
Debugger.moveSelection(-1); Debugger.moveSelection(-1);
} }
Rectangle { function update()
color: "transparent" {
id: stateListContainer if (statusPane.result.successful)
focus: true {
anchors.topMargin: 10 Debugger.init();
anchors.top: headerInfo.bottom debugScrollArea.visible = true;
anchors.left: parent.left compilationErrorArea.visible = false;
height: parent.height - 70 machineStates.visible = true;
width: parent.width * 0.5 }
else
{
debugScrollArea.visible = false;
compilationErrorArea.visible = true;
machineStates.visible = false;
var errorInfo = ErrorLocationFormater.extractErrorInfo(statusPane.result.compilerMessage, false);
errorLocation.text = errorInfo.errorLocation;
errorDetail.text = errorInfo.errorDetail;
errorLine.text = errorInfo.errorLine;
}
}
Connections {
target: codeModel
onCompilationComplete: update()
}
ListView { Rectangle
{
visible: false;
id: compilationErrorArea
width: parent.width - 20
height: 500
color: "#ededed"
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 10
ColumnLayout
{
width: parent.width
anchors.top: parent.top anchors.top: parent.top
height: parent.height * 0.60 spacing: 25
width: 200 RowLayout
anchors.horizontalCenter: parent.horizontalCenter {
id: statesList height: 100
model: humanReadableExecutionCode ColumnLayout
delegate: renderDelegate {
highlight: highlightBar Text {
highlightFollowsCurrentItem: true color: "red"
} id: errorLocation
}
Text {
color: "#4a4a4a"
id: errorDetail
}
}
}
Component { Rectangle
id: highlightBar {
Rectangle { width: parent.width - 6
height: statesList.currentItem.height height: 2
width: statesList.currentItem.width color: "#d0d0d0"
border.color: "orange"
border.width: 1
Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } }
} }
}
Component { RowLayout
id: renderDelegate {
Item { Text
id: wrapperItem {
height: 20 color: "#4a4a4a"
width: parent.width id: errorLine
Text {
anchors.centerIn: parent
text: line
font.pointSize: 9
} }
} }
} }
}
Rectangle {
id: callStackPanel Flickable {
anchors.top: statesList.bottom property int firstColumnWidth: 170
height: parent.height * 0.35 property int secondColumnWidth: 250
width: parent.width id: debugScrollArea
flickableDirection: Flickable.VerticalFlick
anchors.fill: parent
contentHeight: machineStates.height + 300
contentWidth: machineStates.width
GridLayout
{
property int sideMargin: 10
id: machineStates
anchors.top: parent.top
anchors.topMargin: 15 anchors.topMargin: 15
color: "transparent" anchors.left: parent.left;
Label { anchors.leftMargin: machineStates.sideMargin
id: callStackLabel anchors.right: parent.right;
anchors.bottomMargin: 10 anchors.rightMargin: machineStates.sideMargin
horizontalAlignment: "AlignHCenter" flow: GridLayout.TopToBottom
font.family: "Verdana" rowSpacing: 15
font.pointSize: 8 RowLayout {
font.letterSpacing: 2 // step button + slider
width: parent.width spacing: machineStates.sideMargin
height: 15 height: 27
text: qsTr("callstack") width: debugPanel.width
} Rectangle
{
height: parent.height
color: "transparent"
width: debugScrollArea.firstColumnWidth
RowLayout {
anchors.horizontalCenter: parent.horizontalCenter
id: jumpButtons
spacing: 3
StepActionImage
{
id: jumpOutBackAction;
enabledStateImg: "qrc:/qml/img/jumpoutback.png"
disableStateImg: "qrc:/qml/img/jumpoutbackdisabled.png"
onClicked: Debugger.stepOutBack()
width: 25
height: 27
}
ListView { StepActionImage
height: parent.height - 15 {
width: 200 id: jumpIntoBackAction
anchors.top: callStackLabel.bottom enabledStateImg: "qrc:/qml/img/jumpintoback.png"
anchors.horizontalCenter: parent.horizontalCenter disableStateImg: "qrc:/qml/img/jumpintobackdisabled.png"
id: levelList onClicked: Debugger.stepIntoBack()
delegate: Component { width: 25
Item { height: 27
Text { }
font.family: "Verdana"
font.pointSize: 8 StepActionImage
text: modelData {
id: jumpOverBackAction
enabledStateImg: "qrc:/qml/img/jumpoverback.png"
disableStateImg: "qrc:/qml/img/jumpoverbackdisabled.png"
onClicked: Debugger.stepOverBack()
width: 25
height: 27
}
StepActionImage
{
id: jumpOverForwardAction
enabledStateImg: "qrc:/qml/img/jumpoverforward.png"
disableStateImg: "qrc:/qml/img/jumpoverforwarddisabled.png"
onClicked: Debugger.stepOverForward()
width: 25
height: 27
}
StepActionImage
{
id: jumpIntoForwardAction
enabledStateImg: "qrc:/qml/img/jumpintoforward.png"
disableStateImg: "qrc:/qml/img/jumpintoforwarddisabled.png"
onClicked: Debugger.stepIntoForward()
width: 25
height: 27
}
StepActionImage
{
id: jumpOutForwardAction
enabledStateImg: "qrc:/qml/img/jumpoutforward.png"
disableStateImg: "qrc:/qml/img/jumpoutforwarddisabled.png"
onClicked: Debugger.stepOutForward()
width: 25
height: 27
}
}
}
Rectangle {
color: "transparent"
width: debugScrollArea.secondColumnWidth
height: parent.height
Slider {
id: statesSlider
anchors.fill: parent
tickmarksEnabled: true
stepSize: 1.0
onValueChanged: Debugger.jumpTo(value);
style: SliderStyle {
groove: Rectangle {
implicitHeight: 3
color: "#7da4cd"
radius: 8
}
handle: Rectangle {
anchors.centerIn: parent
color: control.pressed ? "white" : "lightgray"
border.color: "gray"
border.width: 2
implicitWidth: 10
implicitHeight: 10
radius: 12
}
} }
} }
} }
} }
}
}
Rectangle { RowLayout {
color: "transparent" // Assembly code
anchors.topMargin: 5 width: debugPanel.width
anchors.bottomMargin: 10 height: 405
anchors.rightMargin: 10 spacing: machineStates.sideMargin
height: parent.height - 30
width: parent.width * 0.5
anchors.right: parent.right
anchors.top: headerInfo.bottom
anchors.bottom: parent.bottom
Rectangle { Rectangle
id: debugStack {
anchors.top: parent.top width: debugScrollArea.firstColumnWidth
width: parent.width height: parent.height
height: parent.height * 0.25 border.width: 3
color: "transparent" border.color: "#deddd9"
Label { color: "white"
horizontalAlignment: "AlignHCenter" anchors.top: parent.top
font.family: "Verdana" ListView {
font.pointSize: 8 anchors.fill: parent
width: parent.width anchors.leftMargin: 3
height: 15 anchors.rightMargin: 3
anchors.top : parent.top anchors.topMargin: 3
text: qsTr("debug stack") anchors.bottomMargin: 3
} clip: true
TextArea { id: statesList
anchors.bottom: parent.bottom delegate: renderDelegate
width: parent.width highlight: highlightBar
font.family: "Verdana" highlightFollowsCurrentItem: true
font.pointSize: 8 }
height: parent.height - 15
id: debugStackTxt Component {
readOnly: true; id: highlightBar
Rectangle {
radius: 4
height: statesList.currentItem.height
width: statesList.currentItem.width;
color: "#4A90E2"
Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } }
}
}
Component {
id: renderDelegate
RowLayout {
id: wrapperItem
height: 20
width: parent.width
spacing: 5
Text {
anchors.left: parent.left
anchors.leftMargin: 10
width: 15
color: "#b2b3ae"
text: line.split(' ')[0]
font.pointSize: 9
id: id
wrapMode: Text.NoWrap
}
Text {
wrapMode: Text.NoWrap
color: parent.ListView.isCurrentItem ? "white" : "black"
text: line.replace(line.split(' ')[0], '')
anchors.left: id.right
font.pointSize: 9
}
}
}
}
ColumnLayout {
width: debugScrollArea.secondColumnWidth
height: parent.height
Rectangle {
// Info
width: parent.width
id: basicInfoColumn
height: 125
color: "transparent"
ColumnLayout {
spacing: 0
width: parent.width
height: parent.height
DebugBasicInfo {
id: currentStep
titleStr: qsTr("Current step")
}
DebugBasicInfo {
id: mem
titleStr: qsTr("Adding memory")
}
DebugBasicInfo {
id: stepCost
titleStr: qsTr("Step cost")
}
DebugBasicInfo {
id: gasSpent
titleStr: qsTr("Total gas spent")
}
}
}
Rectangle {
// Stack
height: 275
width: parent.width
color: "transparent"
DebugInfoList
{
id: stack
width: parent.width
height: parent.height
collapsible: false
title : qsTr("Stack")
itemDelegate: Item {
id: renderedItem
height: 27
width: parent.width
RowLayout
{
anchors.fill: parent
Rectangle
{
id: indexColumn
color: "#f7f7f7"
Layout.fillWidth: true
Layout.minimumWidth: 30
Layout.preferredWidth: 30
Layout.maximumWidth: 30
Layout.minimumHeight: parent.height
Text {
anchors.centerIn: parent
anchors.leftMargin: 5
color: "#8b8b8b"
text: model.index;
font.pointSize: 9
}
}
Rectangle
{
anchors.left: indexColumn.right
Layout.fillWidth: true
Layout.minimumWidth: 15
Layout.preferredWidth: 15
Layout.maximumWidth: 60
Layout.minimumHeight: parent.height
Text {
anchors.left: parent.left
anchors.leftMargin: 5
anchors.verticalCenter: parent.verticalCenter
color: "#8b8b8b"
text: modelData
font.pointSize: 9
}
}
}
Rectangle {
id: separator
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom
}
}
}
}
}
} }
}
Rectangle { Rectangle {
id: debugMemory width: debugPanel.width - 2 * machineStates.sideMargin
anchors.top: debugStack.bottom height: 2;
width: parent.width color: "#e3e3e3"
height: parent.height * 0.25 radius: 3
color: "transparent"
Label {
horizontalAlignment: "AlignHCenter"
font.family: "Verdana"
font.pointSize: 8
width: parent.width
height: 15
anchors.top : parent.top
text: qsTr("debug memory")
} }
TextArea {
anchors.bottom: parent.bottom DebugInfoList
width: parent.width {
font.family: "Verdana" id: storage
font.pointSize: 8 width: debugPanel.width - 2 * machineStates.sideMargin
height: parent.height - 15 height: 223
id: debugMemoryTxt collapsible: true
readOnly: true; title : qsTr("Storage")
itemDelegate:
Item {
height: 27
width: parent.width;
RowLayout
{
id: row
width: parent.width
height: 26
Rectangle
{
color: "#f7f7f7"
Layout.fillWidth: true
Layout.minimumWidth: parent.width / 2
Layout.preferredWidth: parent.width / 2
Layout.maximumWidth: parent.width / 2
Layout.minimumHeight: parent.height
Layout.maximumHeight: parent.height
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 5
color: "#8b8b8b"
text: modelData.split(' ')[0].substring(0, 10);
font.pointSize: 9
}
}
Rectangle
{
color: "transparent"
Layout.fillWidth: true
Layout.minimumWidth: parent.width / 2
Layout.preferredWidth: parent.width / 2
Layout.maximumWidth: parent.width / 2
Layout.minimumHeight: parent.height
Layout.maximumHeight: parent.height
Text {
anchors.leftMargin: 5
width: parent.width - 5
wrapMode: Text.Wrap
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
color: "#8b8b8b"
text: modelData.split(' ')[1].substring(0, 10);
font.pointSize: 9
}
}
}
Rectangle {
anchors.top: row.bottom
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom
}
}
} }
}
Rectangle { Rectangle {
id: debugStorage width: debugPanel.width - 2 * machineStates.sideMargin
anchors.top: debugMemory.bottom height: 2;
width: parent.width color: "#e3e3e3"
height: parent.height * 0.25 radius: 3
color: "transparent"
Label {
horizontalAlignment: "AlignHCenter"
font.family: "Verdana"
font.pointSize: 8
width: parent.width
height: 15
anchors.top : parent.top
text: qsTr("debug storage")
} }
TextArea {
anchors.bottom: parent.bottom DebugInfoList {
width: parent.width id: memoryDump
font.family: "Verdana" width: debugPanel.width - 2 * machineStates.sideMargin
font.pointSize: 8 height: 223
height: parent.height - 15 collapsible: true
id: debugStorageTxt title: qsTr("Memory Dump")
readOnly: true; itemDelegate:
Item {
height: 29
width: parent.width - 3;
ItemDelegateDataDump {}
}
} }
}
Rectangle { Rectangle {
id: debugCallData width: debugPanel.width - 2 * machineStates.sideMargin
anchors.top: debugStorage.bottom height: 2;
width: parent.width color: "#e3e3e3"
height: parent.height * 0.25 radius: 3
color: "transparent"
Label {
horizontalAlignment: "AlignHCenter"
font.family: "Verdana"
font.pointSize: 8
width: parent.width
height: 15
anchors.top : parent.top
text: qsTr("debug calldata")
} }
TextArea {
anchors.bottom: parent.bottom DebugInfoList {
width: parent.width id: callDataDump
height: parent.height - 15 width: debugPanel.width - 2 * machineStates.sideMargin
font.family: "Verdana" height: 223
font.pointSize: 8 collapsible: true
font.letterSpacing: 2 title: qsTr("Call data")
id: debugCallDataTxt itemDelegate:
readOnly: true; Item {
height: 29
width: parent.width - 3;
ItemDelegateDataDump {}
}
} }
} }
} }

71
mix/qml/ItemDelegateDataDump.qml

@ -0,0 +1,71 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
Rectangle {
anchors.fill: parent
RowLayout
{
id: row;
anchors.fill: parent
spacing: 2
Rectangle
{
id: firstCol;
color: "#f7f7f7"
Layout.fillWidth: true
Layout.minimumWidth: 35
Layout.preferredWidth: 35
Layout.maximumWidth: 35
Layout.minimumHeight: parent.height
Text {
anchors.centerIn: parent
anchors.leftMargin: 5
color: "#8b8b8b"
text: modelData[0]
font.pointSize: 9;
}
}
Rectangle
{
anchors.left: firstCol.right
Layout.fillWidth: true
Layout.minimumWidth: 90
Layout.preferredWidth: 90
Layout.maximumWidth: 90
Layout.minimumHeight: parent.height
Text {
anchors.left: parent.left
anchors.leftMargin: 7
anchors.verticalCenter: parent.verticalCenter
color: "#8b8b8b"
text: modelData[1]
font.pointSize: 9
}
}
Rectangle
{
Layout.fillWidth: true
Layout.minimumWidth: 50
Layout.minimumHeight: parent.height
Text {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
color: "#ededed"
font.bold: true
text: modelData[2]
font.pointSize: 10
}
}
}
Rectangle {
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom
}
}

196
mix/qml/MainContent.qml

@ -5,6 +5,7 @@ import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0 import CodeEditorExtensionManager 1.0
Rectangle { Rectangle {
objectName: "mainContent" objectName: "mainContent"
signal keyPressed(variant event) signal keyPressed(variant event)
focus: true focus: true
@ -14,59 +15,152 @@ Rectangle {
root.keyPressed(event.key); root.keyPressed(event.key);
} }
anchors.fill: parent anchors.fill: parent
height: parent.height id: root
width: parent.width;
id:root function toggleRightView()
SplitView { {
orientation: Qt.Horizontal if (!rightView.visible)
anchors.fill: parent rightView.show();
else
ProjectList { rightView.hide();
width: parent.width * 0.2 }
height: parent.height
Layout.minimumWidth: 200 function ensureRightView()
{
if (!rightView.visible)
rightView.show();
}
function hideRightView()
{
if (rightView.visible)
rightView.hide();
}
CodeEditorExtensionManager {
headerView: headerPaneTabs;
rightView: rightPaneTabs;
}
GridLayout
{
anchors.fill: parent
rows: 2
flow: GridLayout.TopToBottom
columnSpacing: 0
rowSpacing: 0
Rectangle {
width: parent.width
height: 50
Layout.row: 0
Layout.fillWidth: true
Layout.preferredHeight: 50
id: headerView
Rectangle
{
gradient: Gradient {
GradientStop { position: 0.0; color: "#f1f1f1" }
GradientStop { position: 1.0; color: "#d9d7da" }
}
id: headerPaneContainer
anchors.fill: parent
TabView {
id: headerPaneTabs
tabsVisible: false
antialiasing: true
anchors.fill: parent
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {}
frame: Rectangle { color: "transparent" }
}
}
}
} }
SplitView { SplitView {
//anchors.fill: parent resizing: false
width: parent.width * 0.6 Layout.row: 1
orientation: Qt.Vertical orientation: Qt.Horizontal;
CodeEditorView { Layout.fillWidth: true
height: parent.height * 0.7 Layout.preferredHeight: root.height - headerView.height;
anchors.top: parent.top
width: parent.width ProjectList {
id: projectList
width: 200
height: parent.height
Layout.minimumWidth: 200
}
Rectangle {
id: contentView
width: parent.width - projectList.width
height: parent.height
CodeEditorView {
height: parent.height
anchors.top: parent.top
width: parent.width
}
} }
Rectangle { Rectangle {
anchors.bottom: parent.bottom visible: false;
id: contextualView id: rightView;
width: parent.width
Layout.minimumHeight: 20 Keys.onEscapePressed:
height: parent.height * 0.3 {
TabView { hide();
id: contextualTabs }
antialiasing: true
anchors.fill: parent function show() {
style: TabStyle {} visible = true;
} contentView.width = parent.width - projectList.width - rightView.width;
} }
}
Rectangle { function hide() {
anchors.right: parent.right visible = false;
id: rightPaneView contentView.width = parent.width - projectList.width;
width: parent.width * 0.2 }
height: parent.height
Layout.minimumWidth: 20 height: parent.height;
TabView { width: 450
id: rightPaneTabs Layout.minimumWidth: 450
antialiasing: true Rectangle {
anchors.fill: parent anchors.fill: parent;
//style: TabStyle {} id: rightPaneView
} TabView {
} id: rightPaneTabs
CodeEditorExtensionManager { tabsVisible: true
tabView: contextualTabs antialiasing: true
rightTabView: rightPaneTabs anchors.fill: parent
} style: TabViewStyle {
} frameOverlap: 1
tabBar:
Rectangle {
color: "#ededed"
id: background
}
tab: Rectangle {
color: "#ededed"
implicitWidth: 80
implicitHeight: 20
radius: 2
Text {
anchors.centerIn: parent
text: styleData.title
color: styleData.selected ? "#7da4cd" : "#202020"
}
}
frame: Rectangle {
}
}
}
}
}
}
}
} }

7
mix/qml/StateList.qml

@ -6,7 +6,7 @@ import QtQuick.Layouts 1.1
import org.ethereum.qml.ProjectModel 1.0 import org.ethereum.qml.ProjectModel 1.0
Rectangle { Rectangle {
color: "transparent" color: "#ededed"
id: stateListContainer id: stateListContainer
focus: true focus: true
anchors.topMargin: 10 anchors.topMargin: 10
@ -123,7 +123,10 @@ Rectangle {
ToolButton { ToolButton {
text: qsTr("Run"); text: qsTr("Run");
Layout.fillHeight: true Layout.fillHeight: true
onClicked: stateListModel.runState(index); onClicked:
{
stateListModel.runState(index)
}
} }
} }
} }

116
mix/qml/StatusPane.qml

@ -0,0 +1,116 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
Rectangle {
id: statusHeader
objectName: "statusPane"
function updateStatus()
{
if (statusPane.result.successful)
{
status.state = "";
status.text = qsTr("Compile without errors.");
logslink.visible = false;
}
else
{
status.state = "error";
var errorInfo = ErrorLocationFormater.extractErrorInfo(statusPane.result.compilerMessage, true);
status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail;
logslink.visible = true;
}
debugRunActionIcon.enabled = statusPane.result.successful;
}
color: "transparent"
anchors.fill: parent
Rectangle {
id: statusContainer
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
radius: 3
width: 500
height: 30
color: "#fcfbfc"
RowLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
spacing: 5
Text {
font.pointSize: 10
height: 9
font.family: "sans serif"
objectName: "status"
id: status
states:[
State {
name: "error"
PropertyChanges {
target: status
color: "red"
}
PropertyChanges {
target: statusContainer
color: "#fffcd5"
}
}
]
}
Text {
visible: false
font.pointSize: 9
height: 9
text: qsTr("See log.")
font.family: "Monospace"
objectName: "status"
id: logslink
color: "#8c8a74"
MouseArea {
anchors.fill: parent
onClicked: {
mainContent.ensureRightView();
}
}
}
}
}
Rectangle
{
color: "transparent"
width: 100
height: parent.height
anchors.top: statusHeader.top
anchors.right: statusHeader.right
RowLayout
{
anchors.fill: parent
Rectangle {
color: "transparent"
anchors.fill: parent
Button
{
anchors.right: parent.right
anchors.rightMargin: 7
anchors.verticalCenter: parent.verticalCenter
id: debugImg
iconSource: "qrc:/qml/img/bugiconinactive.png"
action: debugRunActionIcon
}
Action {
id: debugRunActionIcon
onTriggered: {
mainContent.ensureRightView();
clientModel.debugDeployment();
}
enabled: false
}
}
}
}
}

39
mix/qml/StepActionImage.qml

@ -0,0 +1,39 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
Rectangle {
id: buttonActionContainer
property string disableStateImg
property string enabledStateImg
signal clicked
function enabled(state)
{
buttonAction.enabled = state;
if (state)
debugImg.iconSource = enabledStateImg;
else
debugImg.iconSource = disableStateImg;
}
color: "transparent"
Button
{
anchors.fill: parent
id: debugImg
iconSource: enabledStateImg
action: buttonAction
width: buttonActionContainer.width - 3
height: buttonActionContainer.height
}
Action {
id: buttonAction
onTriggered: {
buttonActionContainer.clicked();
}
}
}

BIN
mix/qml/img/bugiconactive.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
mix/qml/img/bugiconinactive.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
mix/qml/img/closedtriangleindicator.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

BIN
mix/qml/img/jumpintoback.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

BIN
mix/qml/img/jumpintobackdisabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

BIN
mix/qml/img/jumpintoforward.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

BIN
mix/qml/img/jumpintoforwarddisabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

BIN
mix/qml/img/jumpoutback.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

BIN
mix/qml/img/jumpoutbackdisabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

BIN
mix/qml/img/jumpoutforward.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

BIN
mix/qml/img/jumpoutforwarddisabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

BIN
mix/qml/img/jumpoverback.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

BIN
mix/qml/img/jumpoverbackdisabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

BIN
mix/qml/img/jumpoverforward.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

BIN
mix/qml/img/opentriangleindicator.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

147
mix/qml/js/Debugger.js

@ -4,11 +4,22 @@
//statesList => ListView //statesList => ListView
var currentSelectedState = null; var currentSelectedState = null;
var jumpStartingPoint = null;
function init() function init()
{ {
if (debugStates === undefined)
return;
statesSlider.maximumValue = debugStates.length - 1;
statesSlider.value = 0;
statesList.model = humanReadableExecutionCode;
currentSelectedState = 0; currentSelectedState = 0;
select(currentSelectedState); select(currentSelectedState);
displayReturnValue();
jumpOutBackAction.enabled(false);
jumpIntoBackAction.enabled(false);
jumpIntoForwardAction.enabled(false);
jumpOutForwardAction.enabled(false);
} }
function moveSelection(incr) function moveSelection(incr)
@ -16,25 +27,34 @@ function moveSelection(incr)
if (currentSelectedState + incr >= 0) if (currentSelectedState + incr >= 0)
{ {
if (currentSelectedState + incr < debugStates.length) if (currentSelectedState + incr < debugStates.length)
{
select(currentSelectedState + incr); select(currentSelectedState + incr);
} statesSlider.value = currentSelectedState;
else
{
endOfDebug();
}
} }
} }
function select(stateIndex) function select(stateIndex)
{ {
var codeLine = codeStr(stateIndex);
var state = debugStates[stateIndex]; var state = debugStates[stateIndex];
var codeStr = bytesCodeMapping.getValue(state.curPC); highlightSelection(codeLine);
highlightSelection(codeStr);
currentSelectedState = stateIndex; currentSelectedState = stateIndex;
completeCtxInformation(state); completeCtxInformation(state);
levelList.model = state.levels;
levelList.update(); if (state.instruction === "JUMP")
jumpIntoForwardAction.enabled(true);
else
jumpIntoForwardAction.enabled(false);
if (state.instruction === "JUMPDEST")
jumpIntoBackAction.enabled(true);
else
jumpIntoBackAction.enabled(false);
}
function codeStr(stateIndex)
{
var state = debugStates[stateIndex];
return bytesCodeMapping.getValue(state.curPC);
} }
function highlightSelection(index) function highlightSelection(index)
@ -44,21 +64,15 @@ function highlightSelection(index)
function completeCtxInformation(state) function completeCtxInformation(state)
{ {
debugStackTxt.text = state.debugStack; currentStep.update(state.step);
debugStorageTxt.text = state.debugStorage; mem.update(state.newMemSize + " " + qsTr("words"));
debugMemoryTxt.text = state.debugMemory; stepCost.update(state.gasCost);
debugCallDataTxt.text = state.debugCallData; gasSpent.update(debugStates[0].gas - state.gas);
headerInfoLabel.text = state.headerInfo
}
function endOfDebug() stack.listModel = state.debugStack;
{ storage.listModel = state.debugStorage;
var state = debugStates[debugStates.length - 1]; memoryDump.listModel = state.debugMemory;
debugStorageTxt.text = ""; callDataDump.listModel = state.debugCallData;
debugCallDataTxt.text = "";
debugStackTxt.text = "";
debugMemoryTxt.text = state.endOfDebug;
headerInfoLabel.text = "EXIT | GAS: " + state.gasLeft;
} }
function displayReturnValue() function displayReturnValue()
@ -66,3 +80,86 @@ function displayReturnValue()
headerReturnList.model = contractCallReturnParameters; headerReturnList.model = contractCallReturnParameters;
headerReturnList.update(); headerReturnList.update();
} }
function stepOutBack()
{
if (jumpStartingPoint != null)
{
select(jumpStartingPoint);
jumpStartingPoint = null;
jumpOutBackAction.enabled(false);
jumpOutForwardAction.enabled(false);
}
}
function stepIntoBack()
{
moveSelection(-1);
}
function stepOverBack()
{
var state = debugStates[currentSelectedState];
if (state.instruction === "JUMPDEST")
{
for (var k = currentSelectedState; k > 0; k--)
{
var line = bytesCodeMapping.getValue(debugStates[k].curPC);
if (line === statesList.currentIndex - 2)
{
select(k);
break;
}
}
}
else
moveSelection(-1);
}
function stepOverForward()
{
var state = debugStates[currentSelectedState];
if (state.instruction === "JUMP")
{
for (var k = currentSelectedState; k < debugStates.length; k++)
{
var line = bytesCodeMapping.getValue(debugStates[k].curPC);
if (line === statesList.currentIndex + 2)
{
select(k);
break;
}
}
}
else
moveSelection(1);
}
function stepIntoForward()
{
var state = debugStates[currentSelectedState];
if (state.instruction === "JUMP")
{
jumpStartingPoint = currentSelectedState;
moveSelection(1);
jumpOutBackAction.enabled(true);
jumpOutForwardAction.enabled(true);
}
}
function stepOutForward()
{
if (jumpStartingPoint != null)
{
stepOutBack();
stepOverForward();
jumpOutBackAction.enabled(false);
jumpOutForwardAction.enabled(false);
}
}
function jumpTo(value)
{
currentSelectedState = value;
select(currentSelectedState);
}

27
mix/qml/js/ErrorLocationFormater.js

@ -0,0 +1,27 @@
function formatLocation(raw, shortMessage)
{
var splitted = raw.split(':');
if (!shortMessage)
return qsTr("Error in line ") + splitted[1] + ", " + qsTr("character ") + splitted[2];
else
return "L" + splitted[1] + "," + "C" + splitted[2];
}
function extractErrorInfo(raw, shortMessage)
{
var _return = {};
var detail = raw.split('\n')[0];
var reg = detail.match(/:\d+:\d+:/g);
if (reg !== null)
{
_return.errorLocation = ErrorLocationFormater.formatLocation(reg[0], shortMessage);
_return.errorDetail = detail.replace(reg[0], "");
}
else
{
_return.errorLocation = "";
_return.errorDetail = detail;
}
_return.errorLine = raw.split('\n')[1];
return _return;
}

19
mix/qml/main.qml

@ -38,13 +38,20 @@ ApplicationWindow {
MenuItem { action: debugRunAction } MenuItem { action: debugRunAction }
MenuItem { action: debugResetStateAction } MenuItem { action: debugResetStateAction }
} }
Menu {
title: qsTr("Windows")
MenuItem { action: showHideRightPanel }
}
} }
Component.onCompleted: { Component.onCompleted: {
setX(Screen.width / 2 - width / 2); setX(Screen.width / 2 - width / 2);
setY(Screen.height / 2 - height / 2); setY(Screen.height / 2 - height / 2);
} }
MainContent { MainContent {
id: mainContent;
anchors.fill: parent
} }
ModalDialog { ModalDialog {
@ -68,8 +75,11 @@ ApplicationWindow {
id: debugRunAction id: debugRunAction
text: "&Run" text: "&Run"
shortcut: "F5" shortcut: "F5"
onTriggered: {
mainContent.ensureRightView();
clientModel.debugDeployment();
}
enabled: codeModel.hasContract && !clientModel.running; enabled: codeModel.hasContract && !clientModel.running;
onTriggered: clientModel.debugDeployment();
} }
Action { Action {
@ -79,6 +89,13 @@ ApplicationWindow {
onTriggered: clientModel.resetState(); onTriggered: clientModel.resetState();
} }
Action {
id: showHideRightPanel
text: "Show/Hide right view"
shortcut: "F7"
onTriggered: mainContent.toggleRightView();
}
Action { Action {
id: createProjectAction id: createProjectAction
text: qsTr("&New project") text: qsTr("&New project")

6
test/CMakeLists.txt

@ -2,6 +2,7 @@ cmake_policy(SET CMP0015 NEW)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
list(REMOVE_ITEM SRC_LIST "./createRandomTest.cpp") list(REMOVE_ITEM SRC_LIST "./createRandomTest.cpp")
list(REMOVE_ITEM SRC_LIST "./checkRandomTest.cpp")
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS})
@ -12,6 +13,7 @@ include_directories(..)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
add_executable(testeth ${SRC_LIST} ${HEADERS}) add_executable(testeth ${SRC_LIST} ${HEADERS})
add_executable(createRandomTest createRandomTest.cpp vm.cpp TestHelper.cpp) add_executable(createRandomTest createRandomTest.cpp vm.cpp TestHelper.cpp)
add_executable(checkRandomTest checkRandomTest.cpp vm.cpp TestHelper.cpp)
target_link_libraries(testeth ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) target_link_libraries(testeth ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(testeth ${CURL_LIBRARIES}) target_link_libraries(testeth ${CURL_LIBRARIES})
@ -29,3 +31,7 @@ endif()
target_link_libraries(createRandomTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) target_link_libraries(createRandomTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(createRandomTest ethereum) target_link_libraries(createRandomTest ethereum)
target_link_libraries(createRandomTest ethcore) target_link_libraries(createRandomTest ethcore)
target_link_libraries(checkRandomTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(checkRandomTest ethereum)
target_link_libraries(checkRandomTest ethcore)

124
test/SolidityEndToEndTest.cpp

@ -28,10 +28,6 @@
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include <test/solidityExecutionFramework.h> #include <test/solidityExecutionFramework.h>
#ifdef _MSC_VER
#pragma warning(disable: 4307) //integral constant overflow for high_bits_cleaning
#endif
using namespace std; using namespace std;
namespace dev namespace dev
@ -386,7 +382,8 @@ BOOST_AUTO_TEST_CASE(high_bits_cleaning)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"
" function run() returns(uint256 y) {\n" " function run() returns(uint256 y) {\n"
" uint32 x = uint32(0xffffffff) + 10;\n" " uint32 t = uint32(0xffffffff);\n"
" uint32 x = t + 10;\n"
" if (x >= 0xffffffff) return 0;\n" " if (x >= 0xffffffff) return 0;\n"
" return x;" " return x;"
" }\n" " }\n"
@ -394,7 +391,8 @@ BOOST_AUTO_TEST_CASE(high_bits_cleaning)
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto high_bits_cleaning_cpp = []() -> u256 auto high_bits_cleaning_cpp = []() -> u256
{ {
uint32_t x = uint32_t(0xffffffff) + 10; uint32_t t = uint32_t(0xffffffff);
uint32_t x = t + 10;
if (x >= 0xffffffff) if (x >= 0xffffffff)
return 0; return 0;
return x; return x;
@ -426,14 +424,16 @@ BOOST_AUTO_TEST_CASE(small_unsigned_types)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"
" function run() returns(uint256 y) {\n" " function run() returns(uint256 y) {\n"
" uint32 x = uint32(0xffffff) * 0xffffff;\n" " uint32 t = uint32(0xffffff);\n"
" uint32 x = t * 0xffffff;\n"
" return x / 0x100;" " return x / 0x100;"
" }\n" " }\n"
"}\n"; "}\n";
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto small_unsigned_types_cpp = []() -> u256 auto small_unsigned_types_cpp = []() -> u256
{ {
uint32_t x = uint32_t(0xffffff) * 0xffffff; uint32_t t = uint32_t(0xffffff);
uint32_t x = t * 0xffffff;
return x / 0x100; return x / 0x100;
}; };
testSolidityAgainstCpp("run()", small_unsigned_types_cpp); testSolidityAgainstCpp("run()", small_unsigned_types_cpp);
@ -1295,7 +1295,113 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses)
} }
)"; )";
compileAndRun(sourceCode, 20); compileAndRun(sourceCode, 20);
BOOST_REQUIRE(callContractFunction("getBalance()") == toBigEndian(u256(20 - 5)) + toBigEndian(u256(5))); BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5)));
}
BOOST_AUTO_TEST_CASE(gas_and_value_basic)
{
char const* sourceCode = R"(
contract helper {
bool flag;
function getBalance() returns (uint256 myBalance) {
return this.balance;
}
function setFlag() { flag = true; }
function getFlag() returns (bool fl) { return flag; }
}
contract test {
helper h;
function test() { h = new helper(); }
function sendAmount(uint amount) returns (uint256 bal) {
return h.getBalance.value(amount)();
}
function outOfGas() returns (bool flagBefore, bool flagAfter, uint myBal) {
flagBefore = h.getFlag();
h.setFlag.gas(2)(); // should fail due to OOG, return value can be garbage
flagAfter = h.getFlag();
myBal = this.balance;
}
}
)";
compileAndRun(sourceCode, 20);
BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5));
// call to helper should not succeed but amount should be transferred anyway
BOOST_REQUIRE(callContractFunction("outOfGas()", 5) == encodeArgs(false, false, 20 - 5));
}
BOOST_AUTO_TEST_CASE(value_complex)
{
char const* sourceCode = R"(
contract helper {
function getBalance() returns (uint256 myBalance) {
return this.balance;
}
}
contract test {
helper h;
function test() { h = new helper(); }
function sendAmount(uint amount) returns (uint256 bal) {
var x1 = h.getBalance.value(amount);
uint someStackElement = 20;
var x2 = x1.gas(1000);
return x2.value(amount + 3)();// overwrite value
}
}
)";
compileAndRun(sourceCode, 20);
BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(8));
}
BOOST_AUTO_TEST_CASE(value_insane)
{
char const* sourceCode = R"(
contract helper {
function getBalance() returns (uint256 myBalance) {
return this.balance;
}
}
contract test {
helper h;
function test() { h = new helper(); }
function sendAmount(uint amount) returns (uint256 bal) {
var x1 = h.getBalance.value;
uint someStackElement = 20;
var x2 = x1(amount).gas;
var x3 = x2(1000).value;
return x3(amount + 3)();// overwrite value
}
}
)";
compileAndRun(sourceCode, 20);
BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(8));
}
BOOST_AUTO_TEST_CASE(value_for_constructor)
{
char const* sourceCode = R"(
contract Helper {
string3 name;
bool flag;
function Helper(string3 x, bool f) {
name = x;
flag = f;
}
function getName() returns (string3 ret) { return name; }
function getFlag() returns (bool ret) { return flag; }
}
contract Main {
Helper h;
function Main() {
h = new Helper.value(10)("abc", true);
}
function getFlag() returns (bool ret) { return h.getFlag(); }
function getName() returns (string3 ret) { return h.getName(); }
function getBalances() returns (uint me, uint them) { me = this.balance; them = h.balance;}
})";
compileAndRun(sourceCode, 22, "Main");
BOOST_REQUIRE(callContractFunction("getFlag()") == encodeArgs(true));
BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc"));
BOOST_REQUIRE(callContractFunction("getBalances()") == encodeArgs(12, 10));
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

12
test/SolidityNameAndTypeResolution.cpp

@ -357,6 +357,18 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases)
} }
} }
BOOST_AUTO_TEST_CASE(hash_collision_in_interface)
{
char const* text = "contract test {\n"
" function gsf() {\n"
" }\n"
" function tgeo() {\n"
" }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

42
test/SolidityNatspecJSON.cpp

@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(user_basic_test)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}" " \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}"
"}}"; "}}";
checkNatspec(sourceCode, natspec, true); checkNatspec(sourceCode, natspec, true);
@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test)
char const* devNatspec = "{" char const* devNatspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \n" " \"mul(uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7\"\n" " \"details\": \"Multiplies a number by 7\"\n"
" }\n" " }\n"
" }\n" " }\n"
@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test)
char const* userNatspec = "{" char const* userNatspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}" " \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}"
"}}"; "}}";
checkNatspec(sourceCode, devNatspec, false); checkNatspec(sourceCode, devNatspec, false);
@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(user_multiline_comment)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul_and_add\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}" " \"mul_and_add(uint256,uint256)\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}"
"}}"; "}}";
checkNatspec(sourceCode, natspec, true); checkNatspec(sourceCode, natspec, true);
@ -157,9 +157,9 @@ BOOST_AUTO_TEST_CASE(user_multiple_functions)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul_and_add\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}," " \"mul_and_add(uint256,uint256)\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"},"
" \"divide\":{ \"notice\": \"Divides `input` by `div`\"}," " \"divide(uint256,uint256)\":{ \"notice\": \"Divides `input` by `div`\"},"
" \"sub\":{ \"notice\": \"Subtracts 3 from `input`\"}" " \"sub(int256)\":{ \"notice\": \"Subtracts 3 from `input`\"}"
"}}"; "}}";
checkNatspec(sourceCode, natspec, true); checkNatspec(sourceCode, natspec, true);
@ -205,7 +205,7 @@ BOOST_AUTO_TEST_CASE(dev_desc_after_nl)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \" Multiplies a number by 7 and adds second parameter\",\n" " \"details\": \" Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n" " \"a\": \"Documentation for the first parameter\",\n"
@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n" " \"a\": \"Documentation for the first parameter\",\n"
@ -252,7 +252,7 @@ BOOST_AUTO_TEST_CASE(dev_mutiline_param_description)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
@ -289,21 +289,21 @@ BOOST_AUTO_TEST_CASE(dev_multiple_functions)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n" " \"a\": \"Documentation for the first parameter\",\n"
" \"second\": \"Documentation for the second parameter\"\n" " \"second\": \"Documentation for the second parameter\"\n"
" }\n" " }\n"
" },\n" " },\n"
" \"divide\":{ \n" " \"divide(uint256,uint256)\":{ \n"
" \"details\": \"Divides 2 numbers\",\n" " \"details\": \"Divides 2 numbers\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"input\": \"Documentation for the input parameter\",\n" " \"input\": \"Documentation for the input parameter\",\n"
" \"div\": \"Documentation for the div parameter\"\n" " \"div\": \"Documentation for the div parameter\"\n"
" }\n" " }\n"
" },\n" " },\n"
" \"sub\":{ \n" " \"sub(int256)\":{ \n"
" \"details\": \"Subtracts 3 from `input`\",\n" " \"details\": \"Subtracts 3 from `input`\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"input\": \"Documentation for the input parameter\"\n" " \"input\": \"Documentation for the input parameter\"\n"
@ -327,7 +327,7 @@ BOOST_AUTO_TEST_CASE(dev_return)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
@ -353,7 +353,7 @@ BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
@ -410,7 +410,7 @@ BOOST_AUTO_TEST_CASE(dev_multiline_comment)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
@ -432,7 +432,7 @@ BOOST_AUTO_TEST_CASE(dev_contract_no_doc)
char const* natspec = "{" char const* natspec = "{"
" \"methods\":{" " \"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Mul function\"\n" " \"details\": \"Mul function\"\n"
" }\n" " }\n"
" }\n" " }\n"
@ -454,7 +454,7 @@ BOOST_AUTO_TEST_CASE(dev_contract_doc)
" \"author\": \"Lefteris\"," " \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\"," " \"title\": \"Just a test contract\","
" \"methods\":{" " \"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Mul function\"\n" " \"details\": \"Mul function\"\n"
" }\n" " }\n"
" }\n" " }\n"
@ -477,7 +477,7 @@ BOOST_AUTO_TEST_CASE(dev_author_at_function)
" \"author\": \"Lefteris\"," " \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\"," " \"title\": \"Just a test contract\","
" \"methods\":{" " \"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Mul function\",\n" " \"details\": \"Mul function\",\n"
" \"author\": \"John Doe\",\n" " \"author\": \"John Doe\",\n"
" }\n" " }\n"
@ -501,7 +501,7 @@ BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
" \"author\": \"Lefteris\"," " \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\"," " \"title\": \"Just a test contract\","
" \"methods\":{" " \"methods\":{"
" \"mul\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Mul function\"\n" " \"details\": \"Mul function\"\n"
" }\n" " }\n"
" }\n" " }\n"

13
test/TestHelper.cpp

@ -350,6 +350,19 @@ void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs)
} }
} }
void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _expectedCallCreates)
{
BOOST_REQUIRE_EQUAL(_resultCallCreates.size(), _expectedCallCreates.size());
for (size_t i = 0; i < _resultCallCreates.size(); ++i)
{
BOOST_CHECK(_resultCallCreates[i].data() == _expectedCallCreates[i].data());
BOOST_CHECK(_resultCallCreates[i].receiveAddress() == _expectedCallCreates[i].receiveAddress());
BOOST_CHECK(_resultCallCreates[i].gas() == _expectedCallCreates[i].gas());
BOOST_CHECK(_resultCallCreates[i].value() == _expectedCallCreates[i].value());
}
}
std::string getTestPath() std::string getTestPath()
{ {
string testPath; string testPath;

2
test/TestHelper.h

@ -73,6 +73,8 @@ json_spirit::mArray exportLog(eth::LogEntries _logs);
void checkOutput(bytes const& _output, json_spirit::mObject& _o); void checkOutput(bytes const& _output, json_spirit::mObject& _o);
void checkStorage(std::map<u256, u256> _expectedStore, std::map<u256, u256> _resultStore, Address _expectedAddr); void checkStorage(std::map<u256, u256> _expectedStore, std::map<u256, u256> _resultStore, Address _expectedAddr);
void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs); void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs);
void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _expectedCallCreates);
void executeTests(const std::string& _name, const std::string& _testPathAppendix, std::function<void(json_spirit::mValue&, bool)> doTests); void executeTests(const std::string& _name, const std::string& _testPathAppendix, std::function<void(json_spirit::mValue&, bool)> doTests);
std::string getTestPath(); std::string getTestPath();
void userDefinedTest(std::string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests); void userDefinedTest(std::string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests);

297
test/checkRandomTest.cpp

@ -0,0 +1,297 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file checkRandomTest.cpp
* @author Christoph Jentzsch <jentzsch.simulationsoftware@gmail.com>
* @date 2015
* Check a random test and return 0/1 for success or failure. To be used for efficiency in the random test simulation.
*/
#include <libdevcore/Common.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/Log.h>
#include <libevm/VMFactory.h>
#include "vm.h"
#pragma GCC diagnostic ignored "-Wunused-parameter"
using namespace std;
using namespace json_spirit;
using namespace dev::test;
using namespace dev;
bool doVMTest(mValue& v);
int main(int argc, char *argv[])
{
g_logVerbosity = 0;
bool ret = false;
try
{
mValue v;
string s;
for (int i = 1; i < argc; ++i)
s += argv[i];
if (asserts(s.length() > 0))
{
cout << "Content of argument is empty\n";
return 1;
}
read_string(s, v);
ret = doVMTest(v);
}
catch (Exception const& _e)
{
cout << "Failed test with Exception: " << diagnostic_information(_e) << endl;
ret = false;
}
catch (std::exception const& _e)
{
cout << "Failed test with Exception: " << _e.what() << endl;
ret = false;
}
return ret;
}
bool doVMTest(mValue& v)
{
eth::VMFactory::setKind(eth::VMKind::JIT);
for (auto& i: v.get_obj())
{
cnote << i.first;
mObject& o = i.second.get_obj();
assert(o.count("env") > 0);
assert(o.count("pre") > 0);
assert(o.count("exec") > 0);
FakeExtVM fev;
fev.importEnv(o["env"].get_obj());
fev.importState(o["pre"].get_obj());
fev.importExec(o["exec"].get_obj());
if (fev.code.empty())
{
fev.thisTxCode = get<3>(fev.addresses.at(fev.myAddress));
fev.code = fev.thisTxCode;
}
bytes output;
u256 gas;
bool vmExceptionOccured = false;
try
{
auto vm = eth::VMFactory::create(fev.gas);
output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas();
}
catch (eth::VMException)
{
cnote << "Safe VM Exception";
vmExceptionOccured = true;
}
catch (Exception const& _e)
{
cnote << "VM did throw an exception: " << diagnostic_information(_e);
cnote << "Failed VM Test with Exception: " << _e.what();
return 1;
}
catch (std::exception const& _e)
{
cnote << "VM did throw an exception: " << _e.what();
cnote << "Failed VM Test with Exception: " << _e.what();
return 1;
}
// delete null entries in storage for the sake of comparison
for (auto &a: fev.addresses)
{
vector<u256> keystoDelete;
for (auto &s: get<2>(a.second))
{
if (s.second == 0)
keystoDelete.push_back(s.first);
}
for (auto const key: keystoDelete )
{
get<2>(a.second).erase(key);
}
}
if (o.count("post") > 0) // No exceptions expected
{
if (asserts(!vmExceptionOccured) || asserts(o.count("post") > 0) || asserts(o.count("callcreates") > 0) || asserts(o.count("out") > 0) || asserts(o.count("gas") > 0) || asserts(o.count("logs") > 0))
return 1;
dev::test::FakeExtVM test;
test.importState(o["post"].get_obj());
test.importCallCreates(o["callcreates"].get_array());
test.sub.logs = importLog(o["logs"].get_array());
//checkOutput(output, o);
int j = 0;
if (o["out"].type() == array_type)
for (auto const& d: o["out"].get_array())
{
if (asserts(output[j] == toInt(d)))
{
cout << "Output byte [" << j << "] different!";
return 1;
}
++j;
}
else if (o["out"].get_str().find("0x") == 0)
{
if (asserts(output == fromHex(o["out"].get_str().substr(2))))
return 1;
}
else
{
if (asserts(output == fromHex(o["out"].get_str())))
return 1;
}
if (asserts(toInt(o["gas"]) == gas))
return 1;
auto& expectedAddrs = test.addresses;
auto& resultAddrs = fev.addresses;
for (auto&& expectedPair : expectedAddrs)
{
auto& expectedAddr = expectedPair.first;
auto resultAddrIt = resultAddrs.find(expectedAddr);
if (resultAddrIt == resultAddrs.end())
{
cout << "Missing expected address " << expectedAddr;
return 1;
}
else
{
auto& expectedState = expectedPair.second;
auto& resultState = resultAddrIt->second;
if (asserts(std::get<0>(expectedState) == std::get<0>(resultState)))
{
cout << expectedAddr << ": incorrect balance " << std::get<0>(resultState) << ", expected " << std::get<0>(expectedState);
return 1;
}
if (asserts(std::get<1>(expectedState) == std::get<1>(resultState)))
{
cout << expectedAddr << ": incorrect txCount " << std::get<1>(resultState) << ", expected " << std::get<1>(expectedState);
return 1;
}
if (asserts(std::get<3>(expectedState) == std::get<3>(resultState)))
{
cout << expectedAddr << ": incorrect code";
return 1;
}
//checkStorage(std::get<2>(expectedState), std::get<2>(resultState), expectedAddr);
for (auto&& expectedStorePair : std::get<2>(expectedState))
{
auto& expectedStoreKey = expectedStorePair.first;
auto resultStoreIt = std::get<2>(resultState).find(expectedStoreKey);
if (resultStoreIt == std::get<2>(resultState).end())
{
cout << expectedAddr << ": missing store key " << expectedStoreKey << endl;
return 1;
}
else
{
auto& expectedStoreValue = expectedStorePair.second;
auto& resultStoreValue = resultStoreIt->second;
if (asserts(expectedStoreValue == resultStoreValue))
{
cout << expectedAddr << ": store[" << expectedStoreKey << "] = " << resultStoreValue << ", expected " << expectedStoreValue << endl;
return 1;
}
}
}
if (assertsEqual(std::get<2>(resultState).size(), std::get<2>(expectedState).size()))
return 1;
for (auto&& resultStorePair: std::get<2>(resultState))
{
if (!std::get<2>(expectedState).count(resultStorePair.first))
{
cout << expectedAddr << ": unexpected store key " << resultStorePair.first << endl;
return 1;
}
}
}
}
//checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses);
for (auto& resultPair : fev.addresses)
{
auto& resultAddr = resultPair.first;
auto expectedAddrIt = test.addresses.find(resultAddr);
if (expectedAddrIt == test.addresses.end())
{
cout << "Missing result address " << resultAddr << endl;
return 1;
}
}
if (asserts(test.addresses == fev.addresses))
return 1;
if (asserts(test.callcreates == fev.callcreates))
return 1;
//checkCallCreates(fev.callcreates, test.callcreates);
{
if (assertsEqual(test.callcreates.size(), fev.callcreates.size()))
return 1;
for (size_t i = 0; i < test.callcreates.size(); ++i)
{
if (asserts(test.callcreates[i].data() == fev.callcreates[i].data()))
return 1;
if (asserts(test.callcreates[i].receiveAddress() == fev.callcreates[i].receiveAddress()))
return 1;
if (asserts(test.callcreates[i].gas() == fev.callcreates[i].gas()))
return 1;
if (asserts(test.callcreates[i].value() == fev.callcreates[i].value()))
return 1;
}
}
//checkLog(fev.sub.logs, test.sub.logs);
{
if (assertsEqual(fev.sub.logs.size(), test.sub.logs.size()))
return 1;
for (size_t i = 0; i < fev.sub.logs.size(); ++i)
{
if (assertsEqual(fev.sub.logs[i].address, test.sub.logs[i].address))
return 1;
if (assertsEqual(fev.sub.logs[i].topics, test.sub.logs[i].topics))
return 1;
if (asserts(fev.sub.logs[i].data == test.sub.logs[i].data))
return 1;
}
}
}
else // Exception expected
{
if (asserts(vmExceptionOccured))
return 1;
}
}
// test passed
return 0;
}

24
test/createRandomTest.cpp

@ -54,18 +54,26 @@ int main(int argc, char *argv[])
gen.seed(static_cast<unsigned int>(timeSinceEpoch)); gen.seed(static_cast<unsigned int>(timeSinceEpoch));
boost::random::uniform_int_distribution<> lengthOfCodeDist(2, 16); boost::random::uniform_int_distribution<> lengthOfCodeDist(2, 16);
boost::random::uniform_int_distribution<> opcodeDist(0, 255); boost::random::uniform_int_distribution<> opcodeDist(0, 255);
boost::random::uniform_int_distribution<> BlockInfoOpcodeDist(0x40, 0x45);
boost::random::variate_generator<boost::mt19937&, boost::random::variate_generator<boost::mt19937&,
boost::random::uniform_int_distribution<> > randGen(gen, opcodeDist); boost::random::uniform_int_distribution<> > randGen(gen, opcodeDist);
boost::random::variate_generator<boost::mt19937&,
boost::random::uniform_int_distribution<> > randGenBlockInfoOpcode(gen, BlockInfoOpcodeDist);
int lengthOfCode = lengthOfCodeDist(gen); int lengthOfCode = lengthOfCodeDist(gen);
string randomCode; string randomCode;
for (int i = 0; i < lengthOfCode; ++i) for (int i = 0; i < lengthOfCode; ++i)
{ {
uint8_t opcode = randGen(); if (i < 8 && (randGen() < 192))
{
randomCode += toHex(toCompactBigEndian((uint8_t)randGenBlockInfoOpcode()));
continue;
}
// disregard all invalid commands, except of one (0x10) uint8_t opcode = randGen();
if (dev::eth::isValidInstruction(dev::eth::Instruction(opcode)) || opcode == 0x10) // disregard all invalid commands, except of one (0x0c)
if ((dev::eth::isValidInstruction(dev::eth::Instruction(opcode)) || (randGen() > 250)))
randomCode += toHex(toCompactBigEndian(opcode)); randomCode += toHex(toCompactBigEndian(opcode));
else else
i--; i--;
@ -76,10 +84,10 @@ int main(int argc, char *argv[])
\"randomVMtest\": {\n\ \"randomVMtest\": {\n\
\"env\" : {\n\ \"env\" : {\n\
\"previousHash\" : \"5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6\",\n\ \"previousHash\" : \"5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6\",\n\
\"currentNumber\" : \"0\",\n\ \"currentNumber\" : \"300\",\n\
\"currentGasLimit\" : \"1000000\",\n\ \"currentGasLimit\" : \"1000000\",\n\
\"currentDifficulty\" : \"256\",\n\ \"currentDifficulty\" : \"115792089237316195423570985008687907853269984665640564039457584007913129639935\",\n\
\"currentTimestamp\" : 1,\n\ \"currentTimestamp\" : 2,\n\
\"currentCoinbase\" : \"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba\"\n\ \"currentCoinbase\" : \"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba\"\n\
},\n\ },\n\
\"pre\" : {\n\ \"pre\" : {\n\
@ -106,7 +114,7 @@ int main(int argc, char *argv[])
read_string(s, v); read_string(s, v);
// insert new random code // insert new random code
v.get_obj().find("randomVMtest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode; v.get_obj().find("randomVMtest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode + (randGen() > 128 ? "55" : "");
// execute code in vm // execute code in vm
doMyTests(v); doMyTests(v);
@ -119,6 +127,8 @@ int main(int argc, char *argv[])
void doMyTests(json_spirit::mValue& v) void doMyTests(json_spirit::mValue& v)
{ {
eth::VMFactory::setKind(eth::VMKind::Interpreter);
for (auto& i: v.get_obj()) for (auto& i: v.get_obj())
{ {
cnote << i.first; cnote << i.first;

32
test/vm.cpp

@ -232,13 +232,13 @@ void FakeExtVM::importCallCreates(mArray& _callcreates)
for (mValue& v: _callcreates) for (mValue& v: _callcreates)
{ {
auto tx = v.get_obj(); auto tx = v.get_obj();
BOOST_REQUIRE(tx.count("data") > 0); assert(tx.count("data") > 0);
BOOST_REQUIRE(tx.count("value") > 0); assert(tx.count("value") > 0);
BOOST_REQUIRE(tx.count("destination") > 0); assert(tx.count("destination") > 0);
BOOST_REQUIRE(tx.count("gasLimit") > 0); assert(tx.count("gasLimit") > 0);
Transaction t = tx["destination"].get_str().empty() ? Transaction t = tx["destination"].get_str().empty() ?
Transaction(toInt(tx["value"]), 0, toInt(tx["gasLimit"]), data.toBytes()) : Transaction(toInt(tx["value"]), 0, toInt(tx["gasLimit"]), fromHex(tx["data"].get_str())) :
Transaction(toInt(tx["value"]), 0, toInt(tx["gasLimit"]), Address(tx["destination"].get_str()), data.toBytes()); Transaction(toInt(tx["value"]), 0, toInt(tx["gasLimit"]), Address(tx["destination"].get_str()), fromHex(tx["data"].get_str()));
callcreates.push_back(t); callcreates.push_back(t);
} }
} }
@ -448,7 +448,8 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
} }
checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses); checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses);
BOOST_CHECK(test.callcreates == fev.callcreates);
checkCallCreates(fev.callcreates, test.callcreates);
checkLog(fev.sub.logs, test.sub.logs); checkLog(fev.sub.logs, test.sub.logs);
} }
@ -507,23 +508,28 @@ BOOST_AUTO_TEST_CASE(vmLogTest)
dev::test::executeTests("vmLogTest", "/VMTests", dev::test::doVMTests); dev::test::executeTests("vmLogTest", "/VMTests", dev::test::doVMTests);
} }
BOOST_AUTO_TEST_CASE(vmPerformanceTest) BOOST_AUTO_TEST_CASE(vmSystemOperationsTest)
{
dev::test::executeTests("vmSystemOperationsTest", "/VMTests", dev::test::doVMTests);
}
BOOST_AUTO_TEST_CASE(vmInputLimitsTest1)
{ {
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
{ {
string arg = boost::unit_test::framework::master_test_suite().argv[i]; string arg = boost::unit_test::framework::master_test_suite().argv[i];
if (arg == "--performance") if (arg == "--inputlimits")
dev::test::executeTests("vmPerformanceTest", "/VMTests", dev::test::doVMTests); dev::test::executeTests("vmInputLimitsTest1", "/VMTests", dev::test::doVMTests);
} }
} }
BOOST_AUTO_TEST_CASE(vmArithPerformanceTest) BOOST_AUTO_TEST_CASE(vmInputLimitsTest2)
{ {
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
{ {
string arg = boost::unit_test::framework::master_test_suite().argv[i]; string arg = boost::unit_test::framework::master_test_suite().argv[i];
if (arg == "--performance") if (arg == "--inputlimits")
dev::test::executeTests("vmArithPerformanceTest", "/VMTests", dev::test::doVMTests); dev::test::executeTests("vmInputLimitsTest2", "/VMTests", dev::test::doVMTests);
} }
} }

66
test/vmSystemOperationsTestFiller.json

@ -12,7 +12,7 @@
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000", "balance" : "1000000000000000000",
"nonce" : 0, "nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f200600035560f6009590060203560003557) [[ 0 ]] (CREATE 23 4 28) }", "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 23 3 29) }",
"storage": {} "storage": {}
} }
}, },
@ -40,7 +40,7 @@
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "100", "balance" : "100",
"nonce" : 0, "nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f200600035560f6009590060203560003557) [[ 0 ]] (CREATE 230 4 28) }", "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 230 3 29) }",
"storage": {} "storage": {}
} }
}, },
@ -68,7 +68,7 @@
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "100", "balance" : "100",
"nonce" : 0, "nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f200600035560f6009590060203560003557) [[ 0 ]] (CREATE 23 0xfffffffffff 28) }", "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 23 0xfffffffffff 29) }",
"storage": {} "storage": {}
} }
}, },
@ -96,7 +96,7 @@
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "100", "balance" : "100",
"nonce" : 0, "nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f200600035560f6009590060203560003557) [[ 0 ]] (CREATE 23 4 0xffffffff) }", "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 23 3 0xffffffff) }",
"storage": {} "storage": {}
} }
}, },
@ -129,7 +129,7 @@
}, },
"945304eb96065b2a98b57a48a06ae28d285a71b5" : { "945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -147,6 +147,36 @@
} }
}, },
"CallToPrecompiledContract" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : "2",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"code" : "0x4243434242434243f14555",
"data" : "0x",
"gas" : "10000",
"gasPrice" : "100000000000000",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"code" : "0x4243434242434243f14555",
"nonce" : "0",
"storage" : {
}
}
}
},
"CallToReturn1": { "CallToReturn1": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
@ -310,7 +340,7 @@
}, },
"945304eb96065b2a98b57a48a06ae28d285a71b5" : { "945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -346,7 +376,7 @@
}, },
"945304eb96065b2a98b57a48a06ae28d285a71b5" : { "945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -382,7 +412,7 @@
}, },
"945304eb96065b2a98b57a48a06ae28d285a71b5" : { "945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -418,7 +448,7 @@
}, },
"945304eb96065b2a98b57a48a06ae28d285a71b5" : { "945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -455,7 +485,7 @@
}, },
"945304eb96065b2a98b57a48a06ae28d285a71b5" : { "945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -491,7 +521,7 @@
}, },
"945304eb96065b2a98b57a48a06ae28d285a71b5" : { "945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -645,7 +675,7 @@
}, },
"cd1722f3947def4cf144679da39c4c32bdc35681" : { "cd1722f3947def4cf144679da39c4c32bdc35681" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -681,7 +711,7 @@
}, },
"cd1722f3947def4cf144679da39c4c32bdc35681" : { "cd1722f3947def4cf144679da39c4c32bdc35681" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -717,7 +747,7 @@
}, },
"cd1722f3947def4cf144679da39c4c32bdc35681" : { "cd1722f3947def4cf144679da39c4c32bdc35681" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -843,7 +873,7 @@
}, },
"945304eb96065b2a98b57a48a06ae28d285a71b5" : { "945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -879,7 +909,7 @@
}, },
"945304eb96065b2a98b57a48a06ae28d285a71b5" : { "945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -915,7 +945,7 @@
}, },
"945304eb96065b2a98b57a48a06ae28d285a71b5" : { "945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23", "balance" : "23",
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
@ -946,7 +976,7 @@
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000", "balance" : "1000000000000000000",
"nonce" : 0, "nonce" : 0,
"code" : "0x600035560f600a59005d60203560003557", "code" : "0x6000355415600957005b60203560003555",
"storage": {} "storage": {}
} }
}, },

Loading…
Cancel
Save