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. 13
      eth/main.cpp
  12. 10
      evmjit/libevmjit/Arith256.cpp
  13. 15
      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. 204
      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
doc/html
*.autosave
node_modules/

12
CMakeLists.txt

@ -49,7 +49,7 @@ endfunction()
function(createBuildInfo)
# 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)
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/mingw")
elseif (CMAKE_COMPILER_IS_MSYS)
@ -64,6 +64,16 @@ function(createBuildInfo)
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/unknown")
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
if (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.
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/CodeFragment.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/AST.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libevm/VM.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_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)));
m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening();
@ -1635,6 +1635,29 @@ static shh::Topic topicFromText(QString _s)
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()
{
m_pcWarp.clear();
@ -1648,7 +1671,7 @@ void Main::on_data_textChanged()
{
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;
try
@ -1657,6 +1680,7 @@ void Main::on_data_textChanged()
solidity = "<h4>Solidity</h4>";
solidity += "<pre>" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + "</pre>";
solidity += "<pre>" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "</pre>";
solidity += "<pre>" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "</pre>";
}
catch (dev::Exception const& exception)
{
@ -1872,7 +1896,28 @@ void Main::on_send_clicked()
debugFinished();
Secret s = i.secret();
if (isCreation())
{
// If execution is a contract creation, add Natspec to
// a local Natspec LEVELDB
ethereum()->transact(s, value(), m_data, ui->gas->value(), gasPrice());
string src = ui->data->toPlainText().toStdString();
if (sourceIsSolidity(src))
try
{
dev::solidity::CompilerStack compiler;
m_data = compiler.compile(src, m_enableOptimizer);
for (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
ethereum()->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice());
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()));
}
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()
{
ui->whispers->clear();

13
alethzero/MainWin.h

@ -36,6 +36,9 @@
#include <libethereum/Executive.h>
#include <libqwebthree/QWebThree.h>
#include <libwebthree/WebThree.h>
#include <libsolidity/CompilerStack.h>
#include "NatspecHandler.h"
namespace Ui {
class Main;
@ -80,7 +83,8 @@ public:
dev::eth::Client* ethereum() const { return m_webThree->ethereum(); }
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; }
@ -227,6 +231,12 @@ private:
void refreshBlockCount();
void refreshBalances();
/// Attempts to infer that @c _source contains Solidity code
bool sourceIsSolidity(std::string const& _source);
/// @eturns all method hashes of a Solidity contract in a string
std::string const getFunctionHashes(dev::solidity::CompilerStack const &_compiler,
std::string const& _contractName = "");
std::unique_ptr<Ui::Main> ui;
std::unique_ptr<dev::WebThreeDirect> m_webThree;
@ -269,4 +279,5 @@ private:
QWebThree* m_qweb = 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 <QMessageBox>
#include <QAbstractButton>
#include <libwebthree/WebThree.h>
#include "MainWin.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts):
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3)
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3,
std::vector<dev::KeyPair> const& _accounts, Main* main):
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(main)
{}
std::string OurWebThreeStubServer::shh_newIdentity()
@ -39,36 +42,31 @@ std::string OurWebThreeStubServer::shh_newIdentity()
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;
// To get the balance of the sender
cnote << "Sender has ETH: " << m_web3->ethereum()->postState().balance(_t.from);
Main* main; // don't know this yet, should be a member and set at construction time by Main, who will construct us.
QMessageBox userInput;
userInput.setText(QString::fromStdString(_title));
userInput.setInformativeText(QString::fromStdString(_text + "\n Do you wish to allow this?"));
userInput.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
userInput.button(QMessageBox::Ok)->setText("Allow");
userInput.button(QMessageBox::Cancel)->setText("Reject");
userInput.setDefaultButton(QMessageBox::Cancel);
return userInput.exec() == QMessageBox::Ok;
}
bool OurWebThreeStubServer::authenticate(dev::TransactionSkeleton const& _t) const
{
h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to);
if (contractCodeHash == EmptySHA3)
{
// recipient has no code - nothing special about this transaction.
// TODO: show basic message for value transfer.
return true; // or whatever.
}
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.
return true;
// 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 <libweb3jsonrpc/WebThreeStubServer.h>
class Main;
class OurWebThreeStubServer: public QObject, public WebThreeStubServer
{
Q_OBJECT
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 bool authenticate(dev::TransactionSkeleton const& _t) const;
@ -38,5 +41,8 @@ signals:
void onNewId(QString _s);
private:
bool showAuthenticationPopup(std::string const& _title, std::string const& _text) const;
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")
# specify Exception Handling Model in msvc
set(CMAKE_C_FLAGS "/EHsc")
set(CMAKE_CXX_FLAGS "/EHsc")
# disable unknown pragma warning (4068)
# disable unsafe function warning (4996)
# disable decorated name length exceeded, name was truncated (4503)
# disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests)
# declare Windows XP requirement
add_compile_options(/EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501)
# 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
set(ETH_STATIC 1)

13
eth/main.cpp

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

10
evmjit/libevmjit/Arith256.cpp

@ -178,7 +178,10 @@ extern "C"
auto arg1 = llvm2eth(*_arg1);
auto arg2 = llvm2eth(*_arg2);
auto arg3 = llvm2eth(*_arg3);
*o_result = eth2llvm(u256((bigint(arg1) * bigint(arg2)) % arg3));
if (arg3 != 0)
*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)
@ -186,7 +189,10 @@ extern "C"
auto arg1 = llvm2eth(*_arg1);
auto arg2 = llvm2eth(*_arg2);
auto arg3 = llvm2eth(*_arg3);
*o_result = eth2llvm(u256((bigint(arg1) + bigint(arg2)) % arg3));
if (arg3 != 0)
*o_result = eth2llvm(u256((bigint(arg1) + bigint(arg2)) % arg3));
else
*o_result = {};
}
}

15
evmjit/libevmjit/BasicBlock.cpp

@ -168,16 +168,13 @@ void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
if (val == nullptr)
continue;
assert(llvm::isa<llvm::PHINode>(val));
llvm::PHINode* phi = llvm::cast<llvm::PHINode>(val);
if (! phi->use_empty())
{
// Insert call to get() just before the PHI node and replace
// the uses of PHI with the uses of this new instruction.
m_builder.SetInsertPoint(phi);
auto newVal = _evmStack.get(idx);
phi->replaceAllUsesWith(newVal);
}
// Insert call to get() just before the PHI node and replace
// the uses of PHI with the uses of this new instruction.
m_builder.SetInsertPoint(phi);
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->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 mainFuncType = llvm::FunctionType::get(llvm::IntegerType::get(llvm::getGlobalContext(), 32), {}, false);
auto func = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get());
(void)func;
llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get());
}
return nullptr;
}
@ -69,9 +68,8 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory
cacheFile << _object->getBuffer();
}
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module)
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const*)
{
(void)_module;
auto o = lastObject;
lastObject = nullptr;
return o;

8
evmjit/libevmjit/Ext.cpp

@ -22,11 +22,9 @@ namespace jit
Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
RuntimeHelper(_runtimeManager),
m_memoryMan(_memoryMan)
// TODO: fix: either initialise properly or don't specify in constructor.
/*,
m_funcs{},
m_argAllocas{}*/
m_memoryMan(_memoryMan),
m_funcs({}), // The only std::array initialization that works in both Visual Studio & GCC
m_argAllocas({})
{
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
// 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");
auto words = m_builder.CreateUDiv(m_builder.CreateAdd(_dataLength, Constant::get(31)), Constant::get(32));
auto cost = m_builder.CreateNUWMul(Constant::get(c_sha3WordGas), words);
auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::lowPrecision);
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);
}

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 size = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataSize]));
assert(offset + size <= m_memory.size());
// TODO: Handle invalid data access by returning empty ref
assert(offset + size <= m_memory.size() || size == 0);
if (offset + size > m_memory.size())
return {};
auto dataBeg = m_memory.begin() + offset;
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(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.
/// @returns a byte array just big enough to represent @a _val.
template <class _T>

7
libethereum/Client.cpp

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

5
libethereum/Client.h

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

26
libjsqrc/ethereumjs/dist/ethereum.js

@ -85,6 +85,18 @@ var calcRealPadding = function (type, expected) {
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) {
return function (type, value) {
var expected = prefix;
@ -99,15 +111,7 @@ var setupInputTypes = function () {
if (prefix === "string")
return web3.fromAscii(value, padding).substr(2);
if (typeof value === "number")
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);
return padLeft(formatStandard(value), padding * 2);
};
};
@ -124,7 +128,7 @@ var setupInputTypes = function () {
};
var formatBool = function (value) {
return value ? '0x1' : '0x0';
return value ? '01' : '00';
};
return [
@ -134,7 +138,7 @@ var setupInputTypes = function () {
prefixedType('string', calcBytePadding),
prefixedType('real', calcRealPadding),
prefixedType('ureal', calcRealPadding),
namedType('address', 20),
namedType('address', 20, formatStandard),
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 () {
// 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) {
return function (type, value) {
var expected = prefix;
@ -98,15 +110,7 @@ var setupInputTypes = function () {
if (prefix === "string")
return web3.fromAscii(value, padding).substr(2);
if (typeof value === "number")
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);
return padLeft(formatStandard(value), padding * 2);
};
};
@ -123,7 +127,7 @@ var setupInputTypes = function () {
};
var formatBool = function (value) {
return value ? '0x1' : '0x0';
return value ? '01' : '00';
};
return [
@ -133,7 +137,7 @@ var setupInputTypes = function () {
prefixedType('string', calcBytePadding),
prefixedType('real', calcRealPadding),
prefixedType('ureal', calcRealPadding),
namedType('address', 20),
namedType('address', 20, formatStandard),
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);
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)
{

2
libserpent/rewriteutils.cpp

@ -54,7 +54,7 @@ bool isValidFunctionName(std::string f) {
vfMap[validFunctions[i][0]] = true;
}
}
return vfMap.count(f);
return vfMap.count(f) != 0;
}
// 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;
in.seekg(0, std::ios::end);
contents.resize(in.tellg());
contents.resize((unsigned)in.tellg());
in.seekg(0, std::ios::beg);
in.read(&contents[0], contents.size());
in.close();

53
libsolidity/AST.cpp

@ -50,18 +50,27 @@ void ContractDefinition::checkTypeRequirements()
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
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*> exportedFunctions;
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName())
{
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
auto res = exportedFunctions.insert(std::make_pair(hash,f.get()));
solAssert(res.second, "Hash collision at Function Definition Hash calculation");
}
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
map<FixedHash<4>, FunctionDefinition const*> exportedFunctions(exportedFunctionList.begin(),
exportedFunctionList.end());
solAssert(exportedFunctionList.size() == exportedFunctions.size(),
"Hash collision at Function Definition Hash calculation");
return exportedFunctions;
}
@ -74,6 +83,19 @@ FunctionDefinition const* ContractDefinition::getConstructor() const
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
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
@ -309,20 +331,13 @@ bool FunctionCall::isTypeConversion() const
void NewExpression::checkTypeRequirements()
{
m_contractName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
shared_ptr<ContractType const> type = make_shared<ContractType>(*m_contract);
m_type = type;
TypePointers const& parameterTypes = type->getConstructorType()->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
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."));
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
FunctionType::Location::CREATION);
}
void MemberAccess::checkTypeRequirements()

12
libsolidity/AST.h

@ -191,6 +191,8 @@ public:
FunctionDefinition const* getConstructor() const;
private:
std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> getInterfaceFunctionList() const;
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
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
{
public:
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName,
std::vector<ASTPointer<Expression>> const& _arguments):
Expression(_location), m_contractName(_contractName), m_arguments(_arguments) {}
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName):
Expression(_location), m_contractName(_contractName) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const 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.
ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; }
private:
ASTPointer<Identifier> m_contractName;
std::vector<ASTPointer<Expression>> m_arguments;
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)
{
if (_visitor.visit(*this))
{
m_contractName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void NewExpression::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_contractName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}

5
libsolidity/Compiler.cpp

@ -50,10 +50,9 @@ void Compiler::compileContract(ContractDefinition const& _contract, vector<Magic
function->accept(*this);
// Swap the runtime context with the creation-time context
CompilerContext runtimeContext;
swap(m_context, runtimeContext);
swap(m_context, m_runtimeContext);
initializeContext(_contract, _magicGlobals, _contracts);
packIntoContractCreator(_contract, runtimeContext);
packIntoContractCreator(_contract, m_runtimeContext);
}
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,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
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); }
private:
@ -70,6 +71,7 @@ private:
bool const m_optimize;
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_continueTags; ///< tag to jump to for a "continue" 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);
unsigned const size = _declaration.getType()->getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
int const size = _declaration.getType()->getSizeOnStack();
for (int i = 0; i < size; ++i)
*this << u256(0);
m_asm.adjustDeposit(-size);
}
@ -95,6 +95,11 @@ unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
return _baseOffset + m_asm.deposit();
}
unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
{
return -baseToCurrentStackOffset(-_offset);
}
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
{
auto it = m_stateVariables.find(&_declaration);

9
libsolidity/CompilerContext.h

@ -51,10 +51,10 @@ public:
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); }
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.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) != 0; }
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;
/// 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
/// the distance of that variable from the current top of the stack.
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;
/// 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/InterfaceHandler.h>
#include <libdevcrypto/SHA3.h>
using namespace std;
namespace dev
@ -39,7 +41,7 @@ namespace solidity
bool CompilerStack::addSource(string const& _name, string const& _content)
{
bool existed = m_sources.count(_name);
bool existed = m_sources.count(_name) != 0;
reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
return existed;
@ -117,6 +119,7 @@ void CompilerStack::compile(bool _optimize)
contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
}
@ -134,6 +137,16 @@ bytes const& CompilerStack::getBytecode(string const& _contractName) const
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
{
getContract(_contractName).compiler->streamAssembly(_outStream);

8
libsolidity/CompilerStack.h

@ -28,6 +28,7 @@
#include <memory>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
namespace dev {
namespace solidity {
@ -75,7 +76,13 @@ public:
/// @returns the compiled bytecode
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;
/// @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.
/// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
@ -121,6 +128,7 @@ private:
ContractDefinition const* contract = nullptr;
std::shared_ptr<Compiler> compiler;
bytes bytecode;
bytes runtimeBytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler;
mutable std::unique_ptr<std::string const> interface;
mutable std::unique_ptr<std::string const> solidityInterface;

204
libsolidity/ExpressionCompiler.cpp

@ -232,27 +232,71 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
case Location::EXTERNAL:
case Location::BARE:
_functionCall.getExpression().accept(*this);
appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE);
break;
case Location::CREATION:
{
FunctionCallOptions options;
options.bare = function.getLocation() == Location::BARE;
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
appendExternalFunctionCall(function, arguments, options);
_functionCall.getExpression().accept(*this);
solAssert(!function.gasSet(), "Gas limit set for contract creation.");
solAssert(function.getReturnParameterTypes().size() == 1, "");
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;
}
case Location::SEND:
case Location::SET_GAS:
{
FunctionCallOptions options;
options.bare = true;
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
options.obtainValue = [&]()
{
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(),
*function.getParameterTypes().front(), true);
};
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL), {}, options);
// 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);
appendTypeConversion(*arguments.front()->getType(),
*function.getParameterTypes().front(), true);
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{},
Location::EXTERNAL, true, true), {}, true);
break;
case Location::SUICIDE:
arguments.front()->accept(*this);
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},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
u256 contractAddress = contractAddresses.find(function.getLocation())->second;
FunctionCallOptions options;
options.bare = true;
options.obtainAddress = [&]() { m_context << contractAddress; };
appendExternalFunctionCall(function, arguments, options);
m_context << contractAddresses.find(function.getLocation())->second;
appendExternalFunctionCall(function, arguments, true);
break;
}
default:
@ -305,38 +346,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
bool ExpressionCompiler::visit(NewExpression const& _newExpression)
{
ContractType const* type = dynamic_cast<ContractType const*>(_newExpression.getType().get());
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;
// code is created for the function call (CREATION) only
return false;
}
@ -370,6 +380,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break;
case Type::Category::FUNCTION:
solAssert(!!_memberAccess.getExpression().getType()->getMemberType(member),
"Invalid member access to function.");
break;
case Type::Category::MAGIC:
// we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase")
@ -646,46 +660,60 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType,
vector<ASTPointer<Expression const>> const& _arguments,
FunctionCallOptions const& _options)
bool bare)
{
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
_options.obtainAddress();
if (!_options.bare)
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
// Assumed stack content here:
// <stack top>
// 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
for (unsigned i = 0; i < _arguments.size(); ++i)
unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
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);
Type const& type = *_functionType.getParameterTypes()[i];
appendTypeConversion(*_arguments[i]->getType(), type, true);
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);
// copy function identifier
m_context << eth::dupInstruction(gasValueSize + 1);
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
}
// 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
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
if (_options.obtainValue)
_options.obtainValue();
if (_functionType.valueSet())
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else
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
<< eth::Instruction::CALL
<< eth::Instruction::POP // @todo do not ignore failure indicator
<< eth::Instruction::POP; // pop contract address
if (_functionType.gasSet())
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
else
// 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)
{
@ -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,
unsigned _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.
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.
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.

4
libsolidity/InterfaceHandler.cpp

@ -106,7 +106,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear
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;
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;

24
libsolidity/Parser.cpp

@ -466,17 +466,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
{
ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken();
if (token == Token::NEW)
{
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))
if (Token::isUnaryOp(token) || Token::isCountOp(token))
{
// prefix expression
m_scanner->next();
@ -500,7 +490,17 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
ASTPointer<Expression> Parser::parseLeftHandSideExpression()
{
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)
{
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."));
}
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal)
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL)
{
TypePointers params;
TypePointers retParams;
@ -558,7 +559,6 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
retParams.push_back(var->getType());
swap(params, m_parameterTypes);
swap(retParams, m_returnParameterTypes);
m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
}
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(),
other.m_returnParameterTypes.cbegin(), typeCompare))
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;
}
@ -595,17 +598,45 @@ string FunctionType::toString() 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)
{
case Location::INTERNAL:
return 1;
case Location::EXTERNAL:
return 2;
case Location::CREATION:
case Location::ECRECOVER:
case Location::SHA256:
case Location::RIPEMD160:
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:
return 0;
return EmptyMemberList;
}
}
@ -628,6 +659,12 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
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
{
if (_other.getCategory() != getCategory())

31
libsolidity/Types.h

@ -178,7 +178,7 @@ public:
int getNumBits() const { return m_bits; }
bool isHash() const { return m_modifier == Modifier::HASH || 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;
@ -291,6 +291,8 @@ public:
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
/// is not used, as this type cannot be the type of a variable or expression.
std::shared_ptr<FunctionType const> const& getConstructorType() const;
@ -345,10 +347,15 @@ class FunctionType: public Type
{
public:
/// 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)
/// 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; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
@ -357,9 +364,10 @@ public:
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
_location) {}
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_location(_location) {}
m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {}
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
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 bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override;
virtual MemberList const& getMembers() const override;
Location const& getLocation() const { return m_location; }
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:
static TypePointers parseElementaryTypeVector(strings const& _types);
TypePointers m_parameterTypes;
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));
}
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
Assignment = Expression (AssignmentOp Expression)
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
NewExpression = 'new' Identifier '(' ( Expression ( ',' Expression )* ) ')'
NewExpression = 'new' Identifier
MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expresison ']'
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.Controls 1.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.2
import QtQuick.Dialogs 1.1
Window {

1
mix/qml/ProjectList.qml

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

4
mix/qml/js/ProjectModel.js

@ -130,11 +130,11 @@ function doCreateProject(title, path) {
var contractsFile = "contracts.sol";
var projectData = {
title: title,
files: [ indexFile, contractsFile ]
files: [ contractsFile, indexFile ]
};
//TODO: copy from template
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);
fileIo.writeFile(projectFile, json);
loadProject(dirPath);

6
test/CMakeLists.txt

@ -2,6 +2,7 @@ cmake_policy(SET CMP0015 NEW)
aux_source_directory(. SRC_LIST)
list(REMOVE_ITEM SRC_LIST "./createRandomTest.cpp")
list(REMOVE_ITEM SRC_LIST "./checkRandomTest.cpp")
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CRYPTOPP_INCLUDE_DIRS})
@ -12,6 +13,7 @@ include_directories(..)
file(GLOB HEADERS "*.h")
add_executable(testeth ${SRC_LIST} ${HEADERS})
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 ${CURL_LIBRARIES})
@ -29,3 +31,7 @@ endif()
target_link_libraries(createRandomTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(createRandomTest ethereum)
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 <test/solidityExecutionFramework.h>
#ifdef _MSC_VER
#pragma warning(disable: 4307) //integral constant overflow for high_bits_cleaning
#endif
using namespace std;
namespace dev
@ -1291,7 +1295,113 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses)
}
)";
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()

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

42
test/SolidityNatspecJSON.cpp

@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(user_basic_test)
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}"
" \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}"
"}}";
checkNatspec(sourceCode, natspec, true);
@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test)
char const* devNatspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7\"\n"
" }\n"
" }\n"
@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test)
char const* userNatspec = "{"
"\"methods\":{"
" \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}"
" \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}"
"}}";
checkNatspec(sourceCode, devNatspec, false);
@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(user_multiline_comment)
char const* natspec = "{"
"\"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);
@ -157,9 +157,9 @@ BOOST_AUTO_TEST_CASE(user_multiple_functions)
char const* natspec = "{"
"\"methods\":{"
" \"mul_and_add\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"},"
" \"divide\":{ \"notice\": \"Divides `input` by `div`\"},"
" \"sub\":{ \"notice\": \"Subtracts 3 from `input`\"}"
" \"mul_and_add(uint256,uint256)\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"},"
" \"divide(uint256,uint256)\":{ \"notice\": \"Divides `input` by `div`\"},"
" \"sub(int256)\":{ \"notice\": \"Subtracts 3 from `input`\"}"
"}}";
checkNatspec(sourceCode, natspec, true);
@ -205,7 +205,7 @@ BOOST_AUTO_TEST_CASE(dev_desc_after_nl)
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \" Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n"
@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params)
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n"
@ -252,7 +252,7 @@ BOOST_AUTO_TEST_CASE(dev_mutiline_param_description)
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\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 = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n"
" \"second\": \"Documentation for the second parameter\"\n"
" }\n"
" },\n"
" \"divide\":{ \n"
" \"divide(uint256,uint256)\":{ \n"
" \"details\": \"Divides 2 numbers\",\n"
" \"params\": {\n"
" \"input\": \"Documentation for the input parameter\",\n"
" \"div\": \"Documentation for the div parameter\"\n"
" }\n"
" },\n"
" \"sub\":{ \n"
" \"sub(int256)\":{ \n"
" \"details\": \"Subtracts 3 from `input`\",\n"
" \"params\": {\n"
" \"input\": \"Documentation for the input parameter\"\n"
@ -327,7 +327,7 @@ BOOST_AUTO_TEST_CASE(dev_return)
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\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 = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\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 = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\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 = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\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 = "{"
" \"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Mul function\"\n"
" }\n"
" }\n"
@ -454,7 +454,7 @@ BOOST_AUTO_TEST_CASE(dev_contract_doc)
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Mul function\"\n"
" }\n"
" }\n"
@ -477,7 +477,7 @@ BOOST_AUTO_TEST_CASE(dev_author_at_function)
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Mul function\",\n"
" \"author\": \"John Doe\",\n"
" }\n"
@ -501,7 +501,7 @@ BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul\":{ \n"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Mul function\"\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()
{
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 checkStorage(std::map<u256, u256> _expectedStore, std::map<u256, u256> _resultStore, Address _expectedAddr);
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);
std::string getTestPath();
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
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#define BOOST_DISABLE_WIN32 //disables SEH warning
#include <boost/test/included/unit_test.hpp>
#pragma warning(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));
boost::random::uniform_int_distribution<> lengthOfCodeDist(2, 16);
boost::random::uniform_int_distribution<> opcodeDist(0, 255);
boost::random::uniform_int_distribution<> BlockInfoOpcodeDist(0x40, 0x45);
boost::random::variate_generator<boost::mt19937&,
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);
string randomCode;
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)
if (dev::eth::isValidInstruction(dev::eth::Instruction(opcode)) || opcode == 0x10)
uint8_t opcode = randGen();
// disregard all invalid commands, except of one (0x0c)
if ((dev::eth::isValidInstruction(dev::eth::Instruction(opcode)) || (randGen() > 250)))
randomCode += toHex(toCompactBigEndian(opcode));
else
i--;
@ -76,10 +84,10 @@ int main(int argc, char *argv[])
\"randomVMtest\": {\n\
\"env\" : {\n\
\"previousHash\" : \"5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6\",\n\
\"currentNumber\" : \"0\",\n\
\"currentNumber\" : \"300\",\n\
\"currentGasLimit\" : \"1000000\",\n\
\"currentDifficulty\" : \"256\",\n\
\"currentTimestamp\" : 1,\n\
\"currentDifficulty\" : \"115792089237316195423570985008687907853269984665640564039457584007913129639935\",\n\
\"currentTimestamp\" : 2,\n\
\"currentCoinbase\" : \"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba\"\n\
},\n\
\"pre\" : {\n\
@ -106,7 +114,7 @@ int main(int argc, char *argv[])
read_string(s, v);
// 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
doMyTests(v);
@ -119,6 +127,8 @@ int main(int argc, char *argv[])
void doMyTests(json_spirit::mValue& v)
{
eth::VMFactory::setKind(eth::VMKind::Interpreter);
for (auto& i: v.get_obj())
{
cnote << i.first;

17
test/vm.cpp

@ -232,13 +232,13 @@ void FakeExtVM::importCallCreates(mArray& _callcreates)
for (mValue& v: _callcreates)
{
auto tx = v.get_obj();
BOOST_REQUIRE(tx.count("data") > 0);
BOOST_REQUIRE(tx.count("value") > 0);
BOOST_REQUIRE(tx.count("destination") > 0);
BOOST_REQUIRE(tx.count("gasLimit") > 0);
assert(tx.count("data") > 0);
assert(tx.count("value") > 0);
assert(tx.count("destination") > 0);
assert(tx.count("gasLimit") > 0);
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"]), Address(tx["destination"].get_str()), 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()), fromHex(tx["data"].get_str()));
callcreates.push_back(t);
}
}
@ -345,7 +345,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas();
}
catch (VMException const& _e)
catch (VMException const&)
{
cnote << "Safe VM Exception";
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);
BOOST_CHECK(test.callcreates == fev.callcreates);
checkCallCreates(fev.callcreates, test.callcreates);
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": {
"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": {
"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": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",

Loading…
Cancel
Save