Browse Source

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

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

20
CodingStandards.txt

@ -128,6 +128,10 @@ d. Use override, final and const judiciously.
e. No implementations with the class declaration, except: e. No implementations with the class declaration, except:
- template or force-inline method (though prefer implementation at bottom of header file). - template or force-inline method (though prefer implementation at bottom of header file).
- one-line implementation (in which case include it in same line as declaration). - one-line implementation (in which case include it in same line as declaration).
f. For a method 'foo'
- Member: m_foo;
- Getter: foo() [ also: for booleans, isFoo() ];
- Setter: setFoo();
@ -162,3 +166,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;
}; };

7
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 // Insert call to get() just before the PHI node and replace
// the uses of PHI with the uses of this new instruction. // the uses of PHI with the uses of this new instruction.
m_builder.SetInsertPoint(phi); m_builder.SetInsertPoint(phi);
auto newVal = _evmStack.get(idx); auto newVal = _evmStack.get(idx); // OPT: Value may be never user but we need to check stack heigth
// It is probably a good idea to keep heigth as a local variable accesible by LLVM directly
phi->replaceAllUsesWith(newVal); phi->replaceAllUsesWith(newVal);
}
phi->eraseFromParent(); phi->eraseFromParent();
} }

4
evmjit/libevmjit/Compiler.cpp

@ -644,7 +644,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::EXTCODESIZE: case Instruction::EXTCODESIZE:
{ {
auto addr = stack.pop(); auto addr = stack.pop();
auto codeRef = _ext.getExtCode(addr); auto codeRef = _ext.extcode(addr);
stack.push(codeRef.size); stack.push(codeRef.size);
break; break;
} }
@ -682,7 +682,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
auto srcIdx = stack.pop(); auto srcIdx = stack.pop();
auto reqBytes = stack.pop(); auto reqBytes = stack.pop();
auto codeRef = _ext.getExtCode(addr); auto codeRef = _ext.extcode(addr);
_memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes); _memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes);
break; break;

11
evmjit/libevmjit/Ext.cpp

@ -22,9 +22,12 @@ namespace jit
Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
RuntimeHelper(_runtimeManager), RuntimeHelper(_runtimeManager),
m_memoryMan(_memoryMan), m_memoryMan(_memoryMan)
#ifdef __MSCVER
,
m_funcs({}), // The only std::array initialization that works in both Visual Studio & GCC m_funcs({}), // The only std::array initialization that works in both Visual Studio & GCC
m_argAllocas({}) m_argAllocas({})
#endif
{ {
m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
} }
@ -48,7 +51,7 @@ std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})}, FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_getExtCode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})},
}}; }};
@ -166,10 +169,10 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
return hash; return hash;
} }
MemoryRef Ext::getExtCode(llvm::Value* _addr) MemoryRef Ext::extcode(llvm::Value* _addr)
{ {
auto addr = Endianness::toBE(m_builder, _addr); auto addr = Endianness::toBE(m_builder, _addr);
auto code = createCall(EnvFunc::getExtCode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size}); auto code = createCall(EnvFunc::extcode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size});
auto codeSize = m_builder.CreateLoad(m_size); auto codeSize = m_builder.CreateLoad(m_size);
auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word); auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word);
return {code, codeSize256}; return {code, codeSize256};

4
evmjit/libevmjit/Ext.h

@ -34,7 +34,7 @@ enum class EnvFunc
call, call,
log, log,
blockhash, blockhash,
getExtCode, extcode,
calldataload, // Helper function, not client Env interface calldataload, // Helper function, not client Env interface
_size _size
@ -55,7 +55,7 @@ public:
llvm::Value* blockhash(llvm::Value* _number); llvm::Value* blockhash(llvm::Value* _number);
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize);
MemoryRef getExtCode(llvm::Value* _addr); MemoryRef extcode(llvm::Value* _addr);
void log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics); void log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics);

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
libdevcore/CommonJS.h

@ -65,7 +65,7 @@ template <unsigned N> FixedHash<N> jsToFixed(std::string const& _s)
{ {
if (_s.substr(0, 2) == "0x") if (_s.substr(0, 2) == "0x")
// Hex // Hex
return FixedHash<N>(_s.substr(2)); return FixedHash<N>(_s.substr(2 + std::max<unsigned>(40, _s.size() - 2) - 40));
else if (_s.find_first_not_of("0123456789") == std::string::npos) else if (_s.find_first_not_of("0123456789") == std::string::npos)
// Decimal // Decimal
return (typename FixedHash<N>::Arith)(_s); return (typename FixedHash<N>::Arith)(_s);

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;

209
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;
options.bare = true;
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
options.obtainValue = [&]()
{ {
// stack layout: contract_address function_id [gas] [value]
_functionCall.getExpression().accept(*this);
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), IntegerType(256), true);
// Note that function is not the original function, but the ".gas" function.
// Its values of gasSet and valueSet is equal to the original function's though.
unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
if (stackDepth > 0)
m_context << eth::swapInstruction(stackDepth);
if (function.gasSet())
m_context << eth::Instruction::POP;
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); arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), appendTypeConversion(*arguments.front()->getType(),
*function.getParameterTypes().front(), true); *function.getParameterTypes().front(), true);
}; appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{},
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL), {}, options); Location::EXTERNAL, true, true), {}, true);
break; 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);
@ -282,6 +326,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << u256(32) << u256(0) << eth::logInstruction(logNumber); m_context << u256(32) << u256(0) << eth::logInstruction(logNumber);
break; break;
} }
case Location::BLOCKHASH:
{
arguments[0]->accept(*this);
appendTypeConversion(*arguments[0]->getType(), *function.getParameterTypes()[0], true);
m_context << eth::Instruction::BLOCKHASH;
break;
}
case Location::ECRECOVER: case Location::ECRECOVER:
case Location::SHA256: case Location::SHA256:
case Location::RIPEMD160: case Location::RIPEMD160:
@ -289,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:
@ -303,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;
} }
@ -370,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")
@ -646,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)
{ {
@ -694,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

53
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())
@ -657,7 +694,7 @@ MagicType::MagicType(MagicType::Kind _kind):
case Kind::BLOCK: case Kind::BLOCK:
m_members = MemberList({{"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)}, m_members = MemberList({{"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)},
{"timestamp", make_shared<IntegerType>(256)}, {"timestamp", make_shared<IntegerType>(256)},
{"prevhash", make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}, {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"hash"}, FunctionType::Location::BLOCKHASH)},
{"difficulty", make_shared<IntegerType>(256)}, {"difficulty", make_shared<IntegerType>(256)},
{"number", make_shared<IntegerType>(256)}, {"number", make_shared<IntegerType>(256)},
{"gaslimit", make_shared<IntegerType>(256)}}); {"gaslimit", make_shared<IntegerType>(256)}});

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, 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);
}; };

2
mix/CodeModel.cpp

@ -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)

2
mix/CodeModel.h

@ -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)
{
std::vector<std::vector<std::string>> dump;
for (unsigned i = 0; i < _bytes.size(); i += _width)
{ {
return QString::fromStdString(memDump(m_data, 16, false)); 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
}; };

2
mix/StateListView.cpp

@ -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
}
}
}
}

595
mix/qml/Debugger.qml

@ -4,85 +4,246 @@ 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 if (event.key === Qt.Key_F10)
anchors.horizontalCenter: parent.horizontalCenter Debugger.moveSelection(1);
width: parent.width else if (event.key === Qt.Key_F9)
height: 60 Debugger.moveSelection(-1);
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
} }
function update()
{
if (statusPane.result.successful)
{
Debugger.init();
debugScrollArea.visible = true;
compilationErrorArea.visible = false;
machineStates.visible = true;
}
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;
} }
Rectangle { }
color: "transparent"
width: parent.width Connections {
anchors.horizontalCenter: parent.horizontalCenter target: codeModel
height: 30 onCompilationComplete: update()
ListView { }
orientation: ListView.Horizontal
anchors.centerIn: parent; 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 width: parent.width
id: headerReturnList anchors.top: parent.top
delegate: renderDelegateReturnValues spacing: 25
RowLayout
{
height: 100
ColumnLayout
{
Text {
color: "red"
id: errorLocation
} }
Component {
id: renderDelegateReturnValues
Item {
id: wrapperItem
width: 80
Text { Text {
anchors.centerIn: parent color: "#4a4a4a"
text: variable.declaration.name + " = " + variable.value id: errorDetail
font.pointSize: 9
} }
} }
} }
Rectangle
{
width: parent.width - 6
height: 2
color: "#d0d0d0"
}
RowLayout
{
Text
{
color: "#4a4a4a"
id: errorLine
}
} }
} }
} }
Keys.onPressed: {
if (event.key === Qt.Key_F10) Flickable {
Debugger.moveSelection(1); property int firstColumnWidth: 170
else if (event.key === Qt.Key_F9) property int secondColumnWidth: 250
Debugger.moveSelection(-1); 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.left: parent.left;
anchors.leftMargin: machineStates.sideMargin
anchors.right: parent.right;
anchors.rightMargin: machineStates.sideMargin
flow: GridLayout.TopToBottom
rowSpacing: 15
RowLayout {
// step button + slider
spacing: machineStates.sideMargin
height: 27
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
}
StepActionImage
{
id: jumpIntoBackAction
enabledStateImg: "qrc:/qml/img/jumpintoback.png"
disableStateImg: "qrc:/qml/img/jumpintobackdisabled.png"
onClicked: Debugger.stepIntoBack()
width: 25
height: 27
}
StepActionImage
{
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 { Rectangle {
color: "transparent" color: "transparent"
id: stateListContainer width: debugScrollArea.secondColumnWidth
focus: true height: parent.height
anchors.topMargin: 10 Slider {
anchors.top: headerInfo.bottom id: statesSlider
anchors.left: parent.left anchors.fill: parent
height: parent.height - 70 tickmarksEnabled: true
width: parent.width * 0.5 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
}
}
}
}
}
ListView { RowLayout {
// Assembly code
width: debugPanel.width
height: 405
spacing: machineStates.sideMargin
Rectangle
{
width: debugScrollArea.firstColumnWidth
height: parent.height
border.width: 3
border.color: "#deddd9"
color: "white"
anchors.top: parent.top anchors.top: parent.top
height: parent.height * 0.60 ListView {
width: 200 anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter anchors.leftMargin: 3
anchors.rightMargin: 3
anchors.topMargin: 3
anchors.bottomMargin: 3
clip: true
id: statesList id: statesList
model: humanReadableExecutionCode
delegate: renderDelegate delegate: renderDelegate
highlight: highlightBar highlight: highlightBar
highlightFollowsCurrentItem: true highlightFollowsCurrentItem: true
@ -91,179 +252,257 @@ Rectangle {
Component { Component {
id: highlightBar id: highlightBar
Rectangle { Rectangle {
radius: 4
height: statesList.currentItem.height height: statesList.currentItem.height
width: statesList.currentItem.width width: statesList.currentItem.width;
border.color: "orange" color: "#4A90E2"
border.width: 1
Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } } Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } }
} }
} }
Component { Component {
id: renderDelegate id: renderDelegate
Item { RowLayout {
id: wrapperItem id: wrapperItem
height: 20 height: 20
width: parent.width width: parent.width
spacing: 5
Text { Text {
anchors.centerIn: parent anchors.left: parent.left
text: line 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 font.pointSize: 9
} }
} }
} }
}
ColumnLayout {
width: debugScrollArea.secondColumnWidth
height: parent.height
Rectangle { Rectangle {
id: callStackPanel // Info
anchors.top: statesList.bottom
height: parent.height * 0.35
width: parent.width width: parent.width
anchors.topMargin: 15 id: basicInfoColumn
height: 125
color: "transparent" color: "transparent"
Label { ColumnLayout {
id: callStackLabel spacing: 0
anchors.bottomMargin: 10
horizontalAlignment: "AlignHCenter"
font.family: "Verdana"
font.pointSize: 8
font.letterSpacing: 2
width: parent.width width: parent.width
height: 15 height: parent.height
text: qsTr("callstack") DebugBasicInfo {
} id: currentStep
titleStr: qsTr("Current step")
ListView {
height: parent.height - 15
width: 200
anchors.top: callStackLabel.bottom
anchors.horizontalCenter: parent.horizontalCenter
id: levelList
delegate: Component {
Item {
Text {
font.family: "Verdana"
font.pointSize: 8
text: modelData
} }
DebugBasicInfo {
id: mem
titleStr: qsTr("Adding memory")
} }
DebugBasicInfo {
id: stepCost
titleStr: qsTr("Step cost")
} }
DebugBasicInfo {
id: gasSpent
titleStr: qsTr("Total gas spent")
} }
} }
} }
Rectangle { Rectangle {
// Stack
height: 275
width: parent.width
color: "transparent" color: "transparent"
anchors.topMargin: 5
anchors.bottomMargin: 10
anchors.rightMargin: 10
height: parent.height - 30
width: parent.width * 0.5
anchors.right: parent.right
anchors.top: headerInfo.bottom
anchors.bottom: parent.bottom
Rectangle { DebugInfoList
id: debugStack {
anchors.top: parent.top id: stack
width: parent.width width: parent.width
height: parent.height * 0.25 height: parent.height
color: "transparent" collapsible: false
Label { title : qsTr("Stack")
horizontalAlignment: "AlignHCenter" itemDelegate: Item {
font.family: "Verdana" id: renderedItem
font.pointSize: 8 height: 27
width: parent.width width: parent.width
height: 15 RowLayout
anchors.top : parent.top {
text: qsTr("debug stack") 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
} }
TextArea { }
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 anchors.bottom: parent.bottom
width: parent.width }
font.family: "Verdana" }
font.pointSize: 8 }
height: parent.height - 15 }
id: debugStackTxt
readOnly: true;
} }
} }
Rectangle { Rectangle {
id: debugMemory width: debugPanel.width - 2 * machineStates.sideMargin
anchors.top: debugStack.bottom height: 2;
color: "#e3e3e3"
radius: 3
}
DebugInfoList
{
id: storage
width: debugPanel.width - 2 * machineStates.sideMargin
height: 223
collapsible: true
title : qsTr("Storage")
itemDelegate:
Item {
height: 27
width: parent.width;
RowLayout
{
id: row
width: parent.width width: parent.width
height: parent.height * 0.25 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" color: "transparent"
Label { Layout.fillWidth: true
horizontalAlignment: "AlignHCenter" Layout.minimumWidth: parent.width / 2
font.family: "Verdana" Layout.preferredWidth: parent.width / 2
font.pointSize: 8 Layout.maximumWidth: parent.width / 2
width: parent.width Layout.minimumHeight: parent.height
height: 15 Layout.maximumHeight: parent.height
anchors.top : parent.top Text {
text: qsTr("debug memory") 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
} }
TextArea { }
}
Rectangle {
anchors.top: row.bottom
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
width: parent.width }
font.family: "Verdana"
font.pointSize: 8
height: parent.height - 15
id: debugMemoryTxt
readOnly: true;
} }
} }
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" DebugInfoList {
font.family: "Verdana" id: callDataDump
font.pointSize: 8 width: debugPanel.width - 2 * machineStates.sideMargin
width: parent.width height: 223
height: 15 collapsible: true
anchors.top : parent.top title: qsTr("Call data")
text: qsTr("debug calldata") itemDelegate:
Item {
height: 29
width: parent.width - 3;
ItemDelegateDataDump {}
} }
TextArea {
anchors.bottom: parent.bottom
width: parent.width
height: parent.height - 15
font.family: "Verdana"
font.pointSize: 8
font.letterSpacing: 2
id: debugCallDataTxt
readOnly: true;
} }
} }
} }

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
}
}

150
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
width: parent.width;
id: root id: root
SplitView {
orientation: Qt.Horizontal function toggleRightView()
{
if (!rightView.visible)
rightView.show();
else
rightView.hide();
}
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 anchors.fill: parent
TabView {
id: headerPaneTabs
tabsVisible: false
antialiasing: true
anchors.fill: parent
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {}
frame: Rectangle { color: "transparent" }
}
}
}
}
SplitView {
resizing: false
Layout.row: 1
orientation: Qt.Horizontal;
Layout.fillWidth: true
Layout.preferredHeight: root.height - headerView.height;
ProjectList { ProjectList {
width: parent.width * 0.2 id: projectList
width: 200
height: parent.height height: parent.height
Layout.minimumWidth: 200 Layout.minimumWidth: 200
} }
SplitView { Rectangle {
//anchors.fill: parent id: contentView
width: parent.width * 0.6 width: parent.width - projectList.width
orientation: Qt.Vertical height: parent.height
CodeEditorView { CodeEditorView {
height: parent.height * 0.7 height: parent.height
anchors.top: parent.top anchors.top: parent.top
width: parent.width 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
style: TabStyle {}
} }
function show() {
visible = true;
contentView.width = parent.width - projectList.width - rightView.width;
} }
function hide() {
visible = false;
contentView.width = parent.width - projectList.width;
} }
height: parent.height;
width: 450
Layout.minimumWidth: 450
Rectangle { Rectangle {
anchors.right: parent.right anchors.fill: parent;
id: rightPaneView id: rightPaneView
width: parent.width * 0.2
height: parent.height
Layout.minimumWidth: 20
TabView { TabView {
id: rightPaneTabs id: rightPaneTabs
tabsVisible: true
antialiasing: true antialiasing: true
anchors.fill: parent anchors.fill: parent
//style: TabStyle {} 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 {
} }
} }
CodeEditorExtensionManager {
tabView: contextualTabs
rightTabView: rightPaneTabs
} }
} }
} }
}
}
}

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