Browse Source

Merge remote-tracking branch 'upstream/develop' into ide_m25_uidesign

cl-refactor
yann300 10 years ago
parent
commit
34b7b1701a
  1. 1
      .gitignore
  2. 12
      CMakeLists.txt
  3. 16
      CodingStandards.txt
  4. 61
      alethzero/MainWin.cpp
  5. 13
      alethzero/MainWin.h
  6. 103
      alethzero/NatspecHandler.cpp
  7. 58
      alethzero/NatspecHandler.h
  8. 50
      alethzero/OurWebThreeStubServer.cpp
  9. 8
      alethzero/OurWebThreeStubServer.h
  10. 13
      cmake/EthCompilerSettings.cmake
  11. 7
      eth/main.cpp
  12. 6
      evmjit/libevmjit/Arith256.cpp
  13. 7
      evmjit/libevmjit/BasicBlock.cpp
  14. 6
      evmjit/libevmjit/Cache.cpp
  15. 8
      evmjit/libevmjit/Ext.cpp
  16. 8
      evmjit/libevmjit/GasMeter.cpp
  17. 6
      evmjit/libevmjit/Runtime.cpp
  18. 3
      libdevcore/CommonData.h
  19. 7
      libethereum/Client.cpp
  20. 5
      libethereum/Client.h
  21. 4
      libevm/VM.cpp
  22. 26
      libjsqrc/ethereumjs/dist/ethereum.js
  23. 4
      libjsqrc/ethereumjs/dist/ethereum.js.map
  24. 2
      libjsqrc/ethereumjs/dist/ethereum.min.js
  25. 77
      libjsqrc/ethereumjs/example/natspec_contract.html
  26. 26
      libjsqrc/ethereumjs/lib/abi.js
  27. 2
      libp2p/Host.cpp
  28. 2
      libserpent/rewriteutils.cpp
  29. 2
      libserpent/util.cpp
  30. 53
      libsolidity/AST.cpp
  31. 12
      libsolidity/AST.h
  32. 6
      libsolidity/AST_accept.h
  33. 5
      libsolidity/Compiler.cpp
  34. 2
      libsolidity/Compiler.h
  35. 9
      libsolidity/CompilerContext.cpp
  36. 9
      libsolidity/CompilerContext.h
  37. 15
      libsolidity/CompilerStack.cpp
  38. 8
      libsolidity/CompilerStack.h
  39. 200
      libsolidity/ExpressionCompiler.cpp
  40. 18
      libsolidity/ExpressionCompiler.h
  41. 4
      libsolidity/InterfaceHandler.cpp
  42. 24
      libsolidity/Parser.cpp
  43. 51
      libsolidity/Types.cpp
  44. 31
      libsolidity/Types.h
  45. 6
      libsolidity/Utils.h
  46. 2
      libsolidity/grammar.txt
  47. 4
      mergeMaster.sh
  48. 4
      mix/qml/NewProjectDialog.qml
  49. 1
      mix/qml/ProjectList.qml
  50. 4
      mix/qml/js/ProjectModel.js
  51. 6
      test/CMakeLists.txt
  52. 112
      test/SolidityEndToEndTest.cpp
  53. 12
      test/SolidityNameAndTypeResolution.cpp
  54. 42
      test/SolidityNatspecJSON.cpp
  55. 13
      test/TestHelper.cpp
  56. 2
      test/TestHelper.h
  57. 2
      test/boostTest.cpp
  58. 297
      test/checkRandomTest.cpp
  59. 24
      test/createRandomTest.cpp
  60. 17
      test/vm.cpp
  61. 168
      test/vmArithmeticTestFiller.json
  62. 30
      test/vmSystemOperationsTestFiller.json

1
.gitignore

@ -68,3 +68,4 @@ project.pbxproj
evmjit evmjit
doc/html doc/html
*.autosave *.autosave
node_modules/

12
CMakeLists.txt

@ -49,7 +49,7 @@ endfunction()
function(createBuildInfo) function(createBuildInfo)
# Set build platform; to be written to BuildInfo.h # Set build platform; to be written to BuildInfo.h
set(ETH_BUILD_PLATFORM ${TARGET_PLATFORM}) set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}")
if (CMAKE_COMPILER_IS_MINGW) if (CMAKE_COMPILER_IS_MINGW)
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/mingw") set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/mingw")
elseif (CMAKE_COMPILER_IS_MSYS) elseif (CMAKE_COMPILER_IS_MSYS)
@ -64,6 +64,16 @@ function(createBuildInfo)
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/unknown") set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/unknown")
endif () endif ()
if (EVMJIT)
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/JIT")
else ()
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/int")
endif ()
if (PARANOIA)
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/PARA")
endif ()
#cmake build type may be not specified when using msvc #cmake build type may be not specified when using msvc
if (CMAKE_BUILD_TYPE) if (CMAKE_BUILD_TYPE)
set(_cmake_build_type ${CMAKE_BUILD_TYPE}) set(_cmake_build_type ${CMAKE_BUILD_TYPE})

16
CodingStandards.txt

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

61
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>
@ -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();
@ -1635,6 +1635,29 @@ static shh::Topic topicFromText(QString _s)
return ret; return ret;
} }
bool Main::sourceIsSolidity(std::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)
{ {
@ -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 (std::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;
@ -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()));
} }
std::string Main::lookupNatSpec(dev::h256 const& _contractHash) const
{
return m_natspecDB.retrieve(_contractHash);
}
std::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;
}; };

13
cmake/EthCompilerSettings.cmake

@ -28,8 +28,17 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# specify Exception Handling Model in msvc # specify Exception Handling Model in msvc
set(CMAKE_C_FLAGS "/EHsc") # disable unknown pragma warning (4068)
set(CMAKE_CXX_FLAGS "/EHsc") # disable unsafe function warning (4996)
# disable decorated name length exceeded, name was truncated (4503)
# disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests)
# declare Windows XP requirement
add_compile_options(/EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501)
# disable empty object file warning
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
# warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification
# warning LNK4099: pdb was not found with lib
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075")
# windows likes static # windows likes static
set(ETH_STATIC 1) set(ETH_STATIC 1)

7
eth/main.cpp

@ -270,10 +270,11 @@ int main(int argc, char** argv)
mining = ~(unsigned)0; mining = ~(unsigned)0;
else if (isFalse(m)) else if (isFalse(m))
mining = 0; mining = 0;
else if (int i = stoi(m))
mining = i;
else else
{ try {
mining = stoi(m);
}
catch (...) {
cerr << "Unknown -m/--mining option: " << m << endl; cerr << "Unknown -m/--mining option: " << m << endl;
return -1; return -1;
} }

6
evmjit/libevmjit/Arith256.cpp

@ -178,7 +178,10 @@ extern "C"
auto arg1 = llvm2eth(*_arg1); auto arg1 = llvm2eth(*_arg1);
auto arg2 = llvm2eth(*_arg2); auto arg2 = llvm2eth(*_arg2);
auto arg3 = llvm2eth(*_arg3); auto arg3 = llvm2eth(*_arg3);
if (arg3 != 0)
*o_result = eth2llvm(u256((bigint(arg1) * bigint(arg2)) % arg3)); *o_result = eth2llvm(u256((bigint(arg1) * bigint(arg2)) % arg3));
else
*o_result = {};
} }
EXPORT void arith_addmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result) EXPORT void arith_addmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result)
@ -186,7 +189,10 @@ extern "C"
auto arg1 = llvm2eth(*_arg1); auto arg1 = llvm2eth(*_arg1);
auto arg2 = llvm2eth(*_arg2); auto arg2 = llvm2eth(*_arg2);
auto arg3 = llvm2eth(*_arg3); auto arg3 = llvm2eth(*_arg3);
if (arg3 != 0)
*o_result = eth2llvm(u256((bigint(arg1) + bigint(arg2)) % arg3)); *o_result = eth2llvm(u256((bigint(arg1) + bigint(arg2)) % arg3));
else
*o_result = {};
} }
} }

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

6
evmjit/libevmjit/Cache.cpp

@ -45,8 +45,7 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
{ {
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(id, llvm::getGlobalContext())); auto module = std::unique_ptr<llvm::Module>(new llvm::Module(id, llvm::getGlobalContext()));
auto mainFuncType = llvm::FunctionType::get(llvm::IntegerType::get(llvm::getGlobalContext(), 32), {}, false); auto mainFuncType = llvm::FunctionType::get(llvm::IntegerType::get(llvm::getGlobalContext(), 32), {}, false);
auto func = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get());
(void)func;
} }
return nullptr; return nullptr;
} }
@ -69,9 +68,8 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory
cacheFile << _object->getBuffer(); cacheFile << _object->getBuffer();
} }
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const*)
{ {
(void)_module;
auto o = lastObject; auto o = lastObject;
lastObject = nullptr; lastObject = nullptr;
return o; return o;

8
evmjit/libevmjit/Ext.cpp

@ -22,11 +22,9 @@ namespace jit
Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
RuntimeHelper(_runtimeManager), RuntimeHelper(_runtimeManager),
m_memoryMan(_memoryMan) m_memoryMan(_memoryMan),
// TODO: fix: either initialise properly or don't specify in constructor. m_funcs({}), // The only std::array initialization that works in both Visual Studio & GCC
/*, m_argAllocas({})
m_funcs{},
m_argAllocas{}*/
{ {
m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
} }

8
evmjit/libevmjit/GasMeter.cpp

@ -173,10 +173,12 @@ void GasMeter::countSha3Data(llvm::Value* _dataLength)
assert(m_blockCost > 0); // SHA3 instruction is already counted assert(m_blockCost > 0); // SHA3 instruction is already counted
// TODO: This round ups to 32 happens in many places // TODO: This round ups to 32 happens in many places
// FIXME: Overflow possible but Memory::require() also called. Probably 64-bit arith can be used. // FIXME: 64-bit arith used, but not verified
static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter"); static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter");
auto words = m_builder.CreateUDiv(m_builder.CreateAdd(_dataLength, Constant::get(31)), Constant::get(32)); auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::lowPrecision);
auto cost = m_builder.CreateNUWMul(Constant::get(c_sha3WordGas), words); auto words64 = m_builder.CreateUDiv(m_builder.CreateAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32));
auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64);
auto cost = getBuilder().CreateZExt(cost64, Type::Word);
count(cost); count(cost);
} }

6
evmjit/libevmjit/Runtime.cpp

@ -24,8 +24,10 @@ bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy
auto offset = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataOffset])); auto offset = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataOffset]));
auto size = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataSize])); auto size = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataSize]));
assert(offset + size <= m_memory.size()); assert(offset + size <= m_memory.size() || size == 0);
// TODO: Handle invalid data access by returning empty ref if (offset + size > m_memory.size())
return {};
auto dataBeg = m_memory.begin() + offset; auto dataBeg = m_memory.begin() + offset;
return {dataBeg, dataBeg + size}; return {dataBeg, dataBeg + size};
} }

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>

7
libethereum/Client.cpp

@ -185,6 +185,8 @@ unsigned Client::installWatch(h256 _h)
cwatch << "+++" << ret << _h; cwatch << "+++" << ret << _h;
} }
auto ch = logs(ret); auto ch = logs(ret);
if (ch.empty())
ch.push_back(InitialChange);
{ {
Guard l(m_filterLock); Guard l(m_filterLock);
swap(m_watches[ret].changes, ch); swap(m_watches[ret].changes, ch);
@ -229,9 +231,10 @@ void Client::noteChanged(h256Set const& _filters)
if (_filters.count(i.second.id)) if (_filters.count(i.second.id))
{ {
// cwatch << "!!!" << i.first << i.second.id; // cwatch << "!!!" << i.first << i.second.id;
try { if (m_filters.count(i.second.id))
i.second.changes += m_filters.at(i.second.id).changes; i.second.changes += m_filters.at(i.second.id).changes;
} catch(...){} else
i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0));
} }
// clear the filters now. // clear the filters now.
for (auto& i: m_filters) for (auto& i: m_filters)

5
libethereum/Client.h

@ -84,13 +84,16 @@ struct InstalledFilter
static const h256 PendingChangedFilter = u256(0); static const h256 PendingChangedFilter = u256(0);
static const h256 ChainChangedFilter = u256(1); static const h256 ChainChangedFilter = u256(1);
static const LogEntry SpecialLogEntry = LogEntry(Address(), h256s(), bytes());
static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0);
struct ClientWatch struct ClientWatch
{ {
ClientWatch() {} ClientWatch() {}
explicit ClientWatch(h256 _id): id(_id) {} explicit ClientWatch(h256 _id): id(_id) {}
h256 id; h256 id;
LocalisedLogEntries changes; 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; };

4
libevm/VM.cpp

@ -398,12 +398,12 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.pop_back(); m_stack.pop_back();
break; break;
case Instruction::ADDMOD: case Instruction::ADDMOD:
m_stack[m_stack.size() - 3] = u256((bigint(m_stack.back()) + bigint(m_stack[m_stack.size() - 2])) % m_stack[m_stack.size() - 3]); m_stack[m_stack.size() - 3] = m_stack[m_stack.size() - 3] ? u256((bigint(m_stack.back()) + bigint(m_stack[m_stack.size() - 2])) % m_stack[m_stack.size() - 3]) : 0;
m_stack.pop_back(); m_stack.pop_back();
m_stack.pop_back(); m_stack.pop_back();
break; break;
case Instruction::MULMOD: case Instruction::MULMOD:
m_stack[m_stack.size() - 3] = u256((bigint(m_stack.back()) * bigint(m_stack[m_stack.size() - 2])) % m_stack[m_stack.size() - 3]); m_stack[m_stack.size() - 3] = m_stack[m_stack.size() - 3] ? u256((bigint(m_stack.back()) * bigint(m_stack[m_stack.size() - 2])) % m_stack[m_stack.size() - 3]) : 0;
m_stack.pop_back(); m_stack.pop_back();
m_stack.pop_back(); m_stack.pop_back();
break; break;

26
libjsqrc/ethereumjs/dist/ethereum.js

@ -85,6 +85,18 @@ var calcRealPadding = function (type, expected) {
var setupInputTypes = function () { var setupInputTypes = function () {
// convert from int, decimal-string, prefixed hex string whatever into a bare hex string.
var formatStandard = function (value) {
if (typeof value === "number")
return value.toString(16);
else if (typeof value === "string" && value.indexOf('0x') === 0)
return value.substr(2);
else if (typeof value === "string")
return web3.toHex(value);
else
return (+value).toString(16);
};
var prefixedType = function (prefix, calcPadding) { var prefixedType = function (prefix, calcPadding) {
return function (type, value) { return function (type, value) {
var expected = prefix; var expected = prefix;
@ -99,15 +111,7 @@ var setupInputTypes = function () {
if (prefix === "string") if (prefix === "string")
return web3.fromAscii(value, padding).substr(2); return web3.fromAscii(value, padding).substr(2);
if (typeof value === "number") return padLeft(formatStandard(value), padding * 2);
value = value.toString(16);
else if (typeof value === "string")
value = web3.toHex(value);
else if (value.indexOf('0x') === 0)
value = value.substr(2);
else
value = (+value).toString(16);
return padLeft(value, padding * 2);
}; };
}; };
@ -124,7 +128,7 @@ var setupInputTypes = function () {
}; };
var formatBool = function (value) { var formatBool = function (value) {
return value ? '0x1' : '0x0'; return value ? '01' : '00';
}; };
return [ return [
@ -134,7 +138,7 @@ var setupInputTypes = function () {
prefixedType('string', calcBytePadding), prefixedType('string', calcBytePadding),
prefixedType('real', calcRealPadding), prefixedType('real', calcRealPadding),
prefixedType('ureal', calcRealPadding), prefixedType('ureal', calcRealPadding),
namedType('address', 20), namedType('address', 20, formatStandard),
namedType('bool', 1, formatBool), namedType('bool', 1, formatBool),
]; ];
}; };

4
libjsqrc/ethereumjs/dist/ethereum.js.map

File diff suppressed because one or more lines are too long

2
libjsqrc/ethereumjs/dist/ethereum.min.js

File diff suppressed because one or more lines are too long

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>

26
libjsqrc/ethereumjs/lib/abi.js

@ -84,6 +84,18 @@ var calcRealPadding = function (type, expected) {
var setupInputTypes = function () { var setupInputTypes = function () {
// convert from int, decimal-string, prefixed hex string whatever into a bare hex string.
var formatStandard = function (value) {
if (typeof value === "number")
return value.toString(16);
else if (typeof value === "string" && value.indexOf('0x') === 0)
return value.substr(2);
else if (typeof value === "string")
return web3.toHex(value);
else
return (+value).toString(16);
};
var prefixedType = function (prefix, calcPadding) { var prefixedType = function (prefix, calcPadding) {
return function (type, value) { return function (type, value) {
var expected = prefix; var expected = prefix;
@ -98,15 +110,7 @@ var setupInputTypes = function () {
if (prefix === "string") if (prefix === "string")
return web3.fromAscii(value, padding).substr(2); return web3.fromAscii(value, padding).substr(2);
if (typeof value === "number") return padLeft(formatStandard(value), padding * 2);
value = value.toString(16);
else if (typeof value === "string")
value = web3.toHex(value);
else if (value.indexOf('0x') === 0)
value = value.substr(2);
else
value = (+value).toString(16);
return padLeft(value, padding * 2);
}; };
}; };
@ -123,7 +127,7 @@ var setupInputTypes = function () {
}; };
var formatBool = function (value) { var formatBool = function (value) {
return value ? '0x1' : '0x0'; return value ? '01' : '00';
}; };
return [ return [
@ -133,7 +137,7 @@ var setupInputTypes = function () {
prefixedType('string', calcBytePadding), prefixedType('string', calcBytePadding),
prefixedType('real', calcRealPadding), prefixedType('real', calcRealPadding),
prefixedType('ureal', calcRealPadding), prefixedType('ureal', calcRealPadding),
namedType('address', 20), namedType('address', 20, formatStandard),
namedType('bool', 1, formatBool), namedType('bool', 1, formatBool),
]; ];
}; };

2
libp2p/Host.cpp

@ -184,7 +184,7 @@ shared_ptr<Node> Host::noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, boo
{ {
RecursiveGuard l(x_peers); RecursiveGuard l(x_peers);
if (_a.port() < 30300 || _a.port() > 30305) if (_a.port() < 30300 || _a.port() > 30305)
cwarn << "Weird port being recorded: " << _a.port(); cwarn << "Non-standard port being recorded: " << _a.port();
if (_a.port() >= /*49152*/32768) if (_a.port() >= /*49152*/32768)
{ {

2
libserpent/rewriteutils.cpp

@ -54,7 +54,7 @@ bool isValidFunctionName(std::string f) {
vfMap[validFunctions[i][0]] = true; vfMap[validFunctions[i][0]] = true;
} }
} }
return vfMap.count(f); return vfMap.count(f) != 0;
} }
// Cool function for debug purposes (named cerrStringList to make // Cool function for debug purposes (named cerrStringList to make

2
libserpent/util.cpp

@ -260,7 +260,7 @@ std::string get_file_contents(std::string filename)
{ {
std::string contents; std::string contents;
in.seekg(0, std::ios::end); in.seekg(0, std::ios::end);
contents.resize(in.tellg()); contents.resize((unsigned)in.tellg());
in.seekg(0, std::ios::beg); in.seekg(0, std::ios::beg);
in.read(&contents[0], contents.size()); in.read(&contents[0], contents.size());
in.close(); in.close();

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

9
libsolidity/CompilerContext.cpp

@ -53,8 +53,8 @@ void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _decla
{ {
addVariable(_declaration); addVariable(_declaration);
unsigned const size = _declaration.getType()->getSizeOnStack(); int const size = _declaration.getType()->getSizeOnStack();
for (unsigned i = 0; i < size; ++i) for (int i = 0; i < size; ++i)
*this << u256(0); *this << u256(0);
m_asm.adjustDeposit(-size); m_asm.adjustDeposit(-size);
} }
@ -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);

9
libsolidity/CompilerContext.h

@ -51,10 +51,10 @@ public:
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); } bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); } bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration) != 0; }
bool isLocalVariable(Declaration const* _declaration) const; bool isLocalVariable(Declaration const* _declaration) const;
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); } bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const; eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
/// Returns the distance of the given local variable from the top of the local variable stack. /// Returns the distance of the given local variable from the top of the local variable stack.
@ -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

15
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
@ -39,7 +41,7 @@ namespace solidity
bool CompilerStack::addSource(string const& _name, string const& _content) bool CompilerStack::addSource(string const& _name, string const& _content)
{ {
bool existed = m_sources.count(_name); bool existed = m_sources.count(_name) != 0;
reset(true); reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name); m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
return existed; return existed;
@ -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;

200
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);
@ -289,11 +333,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:
@ -305,38 +346,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
bool ExpressionCompiler::visit(NewExpression const& _newExpression) bool ExpressionCompiler::visit(NewExpression const& _newExpression)
{ {
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 +380,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 +660,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 +722,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())

51
libsolidity/Types.cpp

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

31
libsolidity/Types.h

@ -178,7 +178,7 @@ public:
int getNumBits() const { return m_bits; } int getNumBits() const { return m_bits; }
bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; } bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; }
bool isAddress() const { return m_modifier == Modifier::ADDRESS; } bool isAddress() const { return m_modifier == Modifier::ADDRESS; }
int isSigned() const { return m_modifier == Modifier::SIGNED; } bool isSigned() const { return m_modifier == Modifier::SIGNED; }
static const MemberList AddressMemberList; static const MemberList AddressMemberList;
@ -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,
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;
}; };
/** /**

6
libsolidity/Utils.h

@ -45,5 +45,11 @@ inline void solAssertAux(bool _condition, std::string const& _errorDescription,
<< ::boost::throw_line(_line)); << ::boost::throw_line(_line));
} }
inline void solAssertAux(void const* _pointer, std::string const& _errorDescription, unsigned _line,
char const* _file, char const* _function)
{
solAssertAux(_pointer != nullptr, _errorDescription, _line, _file, _function);
}
} }
} }

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

4
mergeMaster.sh

@ -0,0 +1,4 @@
#!/bin/bash
git checkout master && git merge --no-ff $1+ && git push && git tag -f $1 && git push --tags -f

4
mix/qml/NewProjectDialog.qml

@ -1,8 +1,8 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.2 import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import QtQuick.Dialogs 1.2 import QtQuick.Dialogs 1.1
Window { Window {

1
mix/qml/ProjectList.qml

@ -80,7 +80,6 @@ Item {
hoverEnabled: true hoverEnabled: true
z:2 z:2
onClicked: { onClicked: {
console.log("clicked");
textInput.forceActiveFocus(); textInput.forceActiveFocus();
} }
} }

4
mix/qml/js/ProjectModel.js

@ -130,11 +130,11 @@ function doCreateProject(title, path) {
var contractsFile = "contracts.sol"; var contractsFile = "contracts.sol";
var projectData = { var projectData = {
title: title, title: title,
files: [ indexFile, contractsFile ] files: [ contractsFile, indexFile ]
}; };
//TODO: copy from template //TODO: copy from template
fileIo.writeFile(dirPath + indexFile, "<html></html>"); fileIo.writeFile(dirPath + indexFile, "<html></html>");
fileIo.writeFile(dirPath + contractsFile, "contract MyContract {\n }\n"); fileIo.writeFile(dirPath + contractsFile, "contract MyContract {\n}\n");
var json = JSON.stringify(projectData); var json = JSON.stringify(projectData);
fileIo.writeFile(projectFile, json); fileIo.writeFile(projectFile, json);
loadProject(dirPath); loadProject(dirPath);

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)

112
test/SolidityEndToEndTest.cpp

@ -28,6 +28,10 @@
#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
@ -1291,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);

2
test/boostTest.cpp

@ -23,6 +23,6 @@
#define BOOST_TEST_MODULE EthereumTests #define BOOST_TEST_MODULE EthereumTests
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"
#define BOOST_DISABLE_WIN32 //disables SEH warning
#include <boost/test/included/unit_test.hpp> #include <boost/test/included/unit_test.hpp>
#pragma warning(pop)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop

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;

17
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);
} }
} }
@ -345,7 +345,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
output = vm->go(fev, fev.simpleTrace()).toBytes(); output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas(); gas = vm->gas();
} }
catch (VMException const& _e) catch (VMException const&)
{ {
cnote << "Safe VM Exception"; cnote << "Safe VM Exception";
vmExceptionOccured = true; vmExceptionOccured = true;
@ -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);
} }

168
test/vmArithmeticTestFiller.json

@ -1346,6 +1346,90 @@
} }
}, },
"addmodDivByZero": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (ADDMOD 4 1 0) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"addmodDivByZero1": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (ADDMOD 0 1 0) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"addmodDivByZero1": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (ADDMOD 1 0 0) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"mulmod0": { "mulmod0": {
"env" : { "env" : {
@ -1543,6 +1627,90 @@
} }
}, },
"mulmoddivByZero": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (MULMOD 5 1 0) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"mulmoddivByZero1": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (MULMOD 0 1 0) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"mulmoddivByZero2": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (MULMOD 1 0 0) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"exp0": { "exp0": {
"env" : { "env" : {

30
test/vmSystemOperationsTestFiller.json

@ -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",

Loading…
Cancel
Save