diff --git a/.gitignore b/.gitignore index 6fde7ca22..2c9d286d2 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,5 @@ project.pbxproj evmjit +doc/html +*.autosave diff --git a/CMakeLists.txt b/CMakeLists.txt index db5ddb864..6e5be63eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ endfunction() function(createBuildInfo) # Set build platform; to be written to BuildInfo.h + 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,10 +65,10 @@ function(createBuildInfo) endif () #cmake build type may be not specified when using msvc - if (${CMAKE_BUILD_TYPE}) + if (CMAKE_BUILD_TYPE) set(_cmake_build_type ${CMAKE_BUILD_TYPE}) else() - set(_cmake_build_type "undefined") + set(_cmake_build_type "${CMAKE_CFG_INTDIR}") endif() # Generate header file containing useful build information @@ -156,7 +157,7 @@ endif () if (NOT HEADLESS) add_subdirectory(libjsqrc) - add_subdirectory(libqethereum) + add_subdirectory(libqwebthree) add_subdirectory(alethzero) add_subdirectory(third) add_subdirectory(mix) diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 8c8a37a42..219a8cb99 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -34,7 +34,7 @@ add_dependencies(${EXECUTABLE} BuildInfo.h) target_link_libraries(${EXECUTABLE} Qt5::Core) target_link_libraries(${EXECUTABLE} webthree) -target_link_libraries(${EXECUTABLE} qethereum) +target_link_libraries(${EXECUTABLE} qwebthree) target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 1fdf7de19..2ce00b0e3 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -165,6 +165,7 @@ + @@ -172,6 +173,7 @@ + @@ -2049,6 +2051,22 @@ font-size: 14pt New Identity + + + Clear Pe&nd&ing + + + + + true + + + false + + + Use &LLVM-EVM + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index b73a2cd0d..01b112b50 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -105,9 +106,9 @@ static QString contentsOfQResource(std::string const& res) return in.readAll(); } -Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f"); -Address c_newConfig = Address("d5f9d8d94886e70b06e474c3fb14fd43e2f23970"); -Address c_nameReg = Address("ddd1cea741d548f90d86fb87a3ae6492e18c03a1"); +//Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f"); +Address c_newConfig = Address("661005d2720d855f1d9976f88bb10c1a3398c77f"); +//Address c_nameReg = Address("ddd1cea741d548f90d86fb87a3ae6492e18c03a1"); Main::Main(QWidget *parent) : QMainWindow(parent), @@ -243,14 +244,14 @@ void Main::onKeysChanged() installBalancesWatch(); } -unsigned Main::installWatch(dev::eth::LogFilter const& _tf, std::function const& _f) +unsigned Main::installWatch(dev::eth::LogFilter const& _tf, WatchHandler const& _f) { auto ret = ethereum()->installWatch(_tf); m_handlers[ret] = _f; return ret; } -unsigned Main::installWatch(dev::h256 _tf, std::function const& _f) +unsigned Main::installWatch(dev::h256 _tf, WatchHandler const& _f) { auto ret = ethereum()->installWatch(_tf); m_handlers[ret] = _f; @@ -265,22 +266,32 @@ void Main::uninstallWatch(unsigned _w) void Main::installWatches() { - installWatch(dev::eth::LogFilter().address(c_config), [=]() { installNameRegWatch(); }); - installWatch(dev::eth::LogFilter().address(c_config), [=]() { installCurrenciesWatch(); }); - installWatch(dev::eth::PendingChangedFilter, [=](){ onNewPending(); }); - installWatch(dev::eth::ChainChangedFilter, [=](){ onNewBlock(); }); + installWatch(dev::eth::LogFilter().address(c_newConfig), [=](LocalisedLogEntries const&) { installNameRegWatch(); }); + installWatch(dev::eth::LogFilter().address(c_newConfig), [=](LocalisedLogEntries const&) { installCurrenciesWatch(); }); + installWatch(dev::eth::PendingChangedFilter, [=](LocalisedLogEntries const&){ onNewPending(); }); + installWatch(dev::eth::ChainChangedFilter, [=](LocalisedLogEntries const&){ onNewBlock(); }); +} + +Address Main::getNameReg() const +{ + return abiOut
(ethereum()->call(c_newConfig, abiIn("lookup(uint256)", (u256)1))); +} + +Address Main::getCurrencies() const +{ + return abiOut
(ethereum()->call(c_newConfig, abiIn("lookup(uint256)", (u256)2))); } void Main::installNameRegWatch() { uninstallWatch(m_nameRegFilter); - m_nameRegFilter = installWatch(dev::eth::LogFilter().address((u160)ethereum()->stateAt(c_config, 0)), [=](){ onNameRegChange(); }); + m_nameRegFilter = installWatch(dev::eth::LogFilter().address((u160)getNameReg()), [=](LocalisedLogEntries const&){ onNameRegChange(); }); } void Main::installCurrenciesWatch() { uninstallWatch(m_currenciesFilter); - m_currenciesFilter = installWatch(dev::eth::LogFilter().address((u160)ethereum()->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); }); + m_currenciesFilter = installWatch(dev::eth::LogFilter().address((u160)getCurrencies()), [=](LocalisedLogEntries const&){ onCurrenciesChange(); }); } void Main::installBalancesWatch() @@ -288,7 +299,9 @@ void Main::installBalancesWatch() dev::eth::LogFilter tf; vector
altCoins; - Address coinsAddr = right160(ethereum()->stateAt(c_config, 1)); + Address coinsAddr = getCurrencies(); + + // TODO: Update for new currencies reg. for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); for (auto i: m_myKeys) @@ -296,7 +309,7 @@ void Main::installBalancesWatch() tf.address(c).topic(h256(i.address(), h256::AlignRight)); uninstallWatch(m_balancesFilter); - m_balancesFilter = installWatch(tf, [=](){ onBalancesChange(); }); + m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); }); } void Main::onNameRegChange() @@ -450,37 +463,37 @@ static Public stringToPublic(QString const& _a) return Public(); } -static Address g_newNameReg; +//static Address g_newNameReg; QString Main::pretty(dev::Address _a) const { - static std::map s_memos; +/* static std::map s_memos; if (!s_memos.count(_a)) - { - if (!g_newNameReg) - g_newNameReg = abiOut
(ethereum()->call(c_newConfig, abiIn(1, (u256)1))); + {*/ +// if (!g_newNameReg) + auto g_newNameReg = getNameReg(); if (g_newNameReg) { - QString s = QString::fromStdString(toString(abiOut(ethereum()->call(g_newNameReg, abiIn(2, _a))))); - s_memos[_a] = s; + QString s = QString::fromStdString(toString(abiOut(ethereum()->call(g_newNameReg, abiIn("nameOf(address)", _a))))); +// s_memos[_a] = s; if (s.size()) return s; } - } +/* } else if (s_memos[_a].size()) - return s_memos[_a]; + return s_memos[_a];*/ h256 n; - +/* if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0)) n = ethereum()->stateAt(nameReg, (u160)(_a)); if (!n) n = ethereum()->stateAt(m_nameReg, (u160)(_a)); - +*/ return fromRaw(n); } @@ -505,21 +518,21 @@ Address Main::fromString(QString const& _n) const if (_n == "(Create Contract)") return Address(); - static std::map s_memos; +/* static std::map s_memos; if (!s_memos.count(_n)) - { - if (!g_newNameReg) - g_newNameReg = abiOut
(ethereum()->call(c_newConfig, abiIn(1, (u256)1))); + {*/ +// if (!g_newNameReg) + auto g_newNameReg = getNameReg(); if (g_newNameReg) { - Address a = abiOut
(ethereum()->call(g_newNameReg, abiIn(0, ::fromString(_n.toStdString())))); - s_memos[_n] = a; + Address a = abiOut
(ethereum()->call(g_newNameReg, abiIn("addressOf(string32)", ::fromString(_n.toStdString())))); +// s_memos[_n] = a; if (a) return a; } - } +/* } else if (s_memos[_n]) return s_memos[_n]; @@ -532,14 +545,13 @@ Address Main::fromString(QString const& _n) const memset(n.data() + sn.size(), 0, 32 - sn.size()); if (_n.size()) { - if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0)) if (h256 a = ethereum()->stateAt(nameReg, n)) return right160(a); if (h256 a = ethereum()->stateAt(m_nameReg, n)) return right160(a); - } + }*/ if (_n.size() == 40) return Address(fromHex(_n.toStdString())); @@ -566,8 +578,9 @@ QString Main::lookup(QString const& _a) const */ h256 ret; - if (h160 dnsReg = (u160)ethereum()->stateAt(c_config, 4, 0)) - ret = ethereum()->stateAt(dnsReg, n); + // TODO: fix with the new DNSreg contract +// if (h160 dnsReg = (u160)ethereum()->stateAt(c_config, 4, 0)) +// ret = ethereum()->stateAt(dnsReg, n); /* if (!ret) if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0, 0)) ret = ethereum()->stateAt(nameReg, n2); @@ -632,6 +645,7 @@ void Main::writeSettings() s.setValue("url", ui->urlEdit->text()); s.setValue("privateChain", m_privateChain); s.setValue("verbosity", ui->verbosity->value()); + s.setValue("jitvm", ui->jitvm->isChecked()); bytes d = m_webThree->saveNodes(); if (d.size()) @@ -706,6 +720,7 @@ void Main::readSettings(bool _skipGeometry) m_privateChain = s.value("privateChain", "").toString(); ui->usePrivate->setChecked(m_privateChain.size()); ui->verbosity->setValue(s.value("verbosity", 1).toInt()); + ui->jitvm->setChecked(s.value("jitvm", true).toBool()); ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html on_urlEdit_returnPressed(); @@ -805,6 +820,12 @@ void Main::on_usePrivate_triggered() on_killBlockchain_triggered(); } +void Main::on_jitvm_triggered() +{ + bool jit = ui->jitvm->isChecked(); + VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); +} + void Main::on_urlEdit_returnPressed() { QString s = ui->urlEdit->text(); @@ -861,8 +882,8 @@ void Main::refreshBalances() // update all the balance-dependent stuff. ui->ourAccounts->clear(); u256 totalBalance = 0; - map> altCoins; - Address coinsAddr = right160(ethereum()->stateAt(c_config, 1)); +/* map> altCoins; + Address coinsAddr = getCurrencies(); for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) { auto n = ethereum()->stateAt(coinsAddr, i + 1); @@ -872,7 +893,7 @@ void Main::refreshBalances() denom = 1; // cdebug << n << addr << denom << sha3(h256(n).asBytes()); altCoins[addr] = make_tuple(fromRaw(n), 0, denom); - } + }*/ for (auto i: m_myKeys) { u256 b = ethereum()->balanceAt(i.address()); @@ -880,18 +901,18 @@ void Main::refreshBalances() ->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size)); totalBalance += b; - for (auto& c: altCoins) - get<1>(c.second) += (u256)ethereum()->stateAt(c.first, (u160)i.address()); +// for (auto& c: altCoins) +// get<1>(c.second) += (u256)ethereum()->stateAt(c.first, (u160)i.address()); } QString b; - for (auto const& c: altCoins) +/* for (auto const& c: altCoins) if (get<1>(c.second)) { stringstream s; s << setw(toString(get<2>(c.second) - 1).size()) << setfill('0') << (get<1>(c.second) % get<2>(c.second)); b += QString::fromStdString(toString(get<1>(c.second) / get<2>(c.second)) + "." + s.str() + " ") + get<0>(c.second).toUpper() + " | "; - } + }*/ ui->balance->setText(b + QString::fromStdString(formatBalance(totalBalance))); } @@ -1150,8 +1171,11 @@ void Main::timerEvent(QTimerEvent*) m_qweb->poll(); for (auto const& i: m_handlers) - if (ethereum()->checkWatch(i.first)) - i.second(); + { + auto ls = ethereum()->checkWatch(i.first); + if (ls.size()) + i.second(ls); + } } string Main::renderDiff(dev::eth::StateDiff const& _d) const @@ -1224,6 +1248,7 @@ void Main::on_transactionQueue_currentItemChanged() if (i >= 0 && i < (int)ethereum()->pending().size()) { Transaction tx(ethereum()->pending()[i]); + TransactionReceipt receipt(ethereum()->postState().receipt(i)); auto ss = tx.safeSender(); h256 th = sha3(rlpList(ss, tx.nonce())); s << "

" << th << "

"; @@ -1246,12 +1271,15 @@ void Main::on_transactionQueue_currentItemChanged() if (tx.data().size()) s << dev::memDump(tx.data(), 16, true); } + s << "
Hex: " << toHex(tx.rlp()) << "
"; s << "
"; - + s << "
Log Bloom: " << receipt.bloom() << "
"; + auto r = receipt.rlp(); + s << "
Receipt: " << toString(RLP(r)) << "
"; + s << "
Receipt-Hex: " << toHex(receipt.rlp()) << "
"; + s << renderDiff(ethereum()->diff(i, 0)); // s << "Pre: " << fs.rootHash() << "
"; // s << "Post: " << ts.rootHash() << ""; - - s << renderDiff(ethereum()->diff(i, 0)); } ui->pendingInfo->setHtml(QString::fromStdString(s.str())); @@ -1364,11 +1392,6 @@ void Main::on_blocks_currentItemChanged() s << "
R: " << hex << nouppercase << tx.signature().r << ""; s << "
S: " << hex << nouppercase << tx.signature().s << ""; s << "
Msg: " << tx.sha3(eth::WithoutSignature) << ""; - s << "
Log Bloom: " << receipt.bloom() << "
"; - s << "
Hex: " << toHex(block[1][txi].data()) << "
"; - auto r = receipt.rlp(); - s << "
Receipt: " << toString(RLP(r)) << "
"; - s << "
Receipt-Hex: " << toHex(receipt.rlp()) << "
"; if (tx.isCreation()) { if (tx.data().size()) @@ -1379,6 +1402,12 @@ void Main::on_blocks_currentItemChanged() if (tx.data().size()) s << dev::memDump(tx.data(), 16, true); } + s << "
Hex: " << toHex(block[1][txi].data()) << "
"; + s << "
"; + s << "
Log Bloom: " << receipt.bloom() << "
"; + auto r = receipt.rlp(); + s << "
Receipt: " << toString(RLP(r)) << "
"; + s << "
Receipt-Hex: " << toHex(receipt.rlp()) << "
"; s << renderDiff(ethereum()->diff(txi, h)); ui->debugCurrent->setEnabled(true); ui->debugDumpState->setEnabled(true); @@ -1619,12 +1648,15 @@ void Main::on_data_textChanged() { m_data = fromHex(src); } - else if (src.substr(0, 8) == "contract") // improve this heuristic + else if (src.substr(0, 8) == "contract" || src.substr(0, 5) == "//sol") // improve this heuristic { dev::solidity::CompilerStack compiler; try { m_data = compiler.compile(src, m_enableOptimizer); + solidity = "

Solidity

"; + solidity += "
" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + "
"; + solidity += "
" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "
"; } catch (dev::Exception const& exception) { @@ -1699,6 +1731,18 @@ void Main::on_data_textChanged() updateFee(); } +void Main::on_clearPending_triggered() +{ + writeSettings(); + ui->mine->setChecked(false); + ui->net->setChecked(false); + web3()->stopNetwork(); + ethereum()->clearPending(); + readSettings(true); + installWatches(); + refreshAll(); +} + void Main::on_killBlockchain_triggered() { writeSettings(); @@ -1770,8 +1814,6 @@ void Main::on_net_triggered() web3()->setClientVersion(n); if (ui->net->isChecked()) { - // TODO: alter network stuff? - //ui->port->value(), string(), 0, NodeMode::Full, ui->idealPeers->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), m_privateChain.size() ? sha3(m_privateChain.toStdString()) : 0 web3()->setIdealPeerCount(ui->idealPeers->value()); web3()->setNetworkPreferences(netPrefs()); ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : 0); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index ed5d275eb..18cf5113b 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include namespace Ui { @@ -66,6 +66,8 @@ struct WorldState std::vector levels; }; +using WatchHandler = std::function; + class Main : public QMainWindow { Q_OBJECT @@ -128,6 +130,7 @@ private slots: void on_debugTimeline_valueChanged(); void on_jsInput_returnPressed(); void on_killBlockchain_triggered(); + void on_clearPending_triggered(); void on_importKey_triggered(); void on_exportKey_triggered(); void on_inject_triggered(); @@ -155,6 +158,7 @@ private slots: void on_importKeyFile_triggered(); void on_post_clicked(); void on_newIdentity_triggered(); + void on_jitvm_triggered(); void refreshWhisper(); void refreshBlockChain(); @@ -170,6 +174,8 @@ private: QString prettyU256(dev::u256 _n) const; QString lookup(QString const& _n) const; + dev::Address getNameReg() const; + dev::Address getCurrencies() const; void populateDebugger(dev::bytesConstRef r); void initDebugger(); @@ -191,8 +197,8 @@ private: dev::u256 value() const; dev::u256 gasPrice() const; - unsigned installWatch(dev::eth::LogFilter const& _tf, std::function const& _f); - unsigned installWatch(dev::h256 _tf, std::function const& _f); + unsigned installWatch(dev::eth::LogFilter const& _tf, WatchHandler const& _f); + unsigned installWatch(dev::h256 _tf, WatchHandler const& _f); void uninstallWatch(unsigned _w); void keysChanged(); @@ -225,7 +231,7 @@ private: std::unique_ptr m_webThree; - std::map> m_handlers; + std::map m_handlers; unsigned m_nameRegFilter = (unsigned)-1; unsigned m_currenciesFilter = (unsigned)-1; unsigned m_balancesFilter = (unsigned)-1; diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 136c86799..9b00a182f 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -113,6 +113,15 @@ if (NOT HEADLESS) message(" - macdeployqt path: ${MACDEPLOYQT_APP}") endif() +# TODO check node && npm version + find_program(ETH_NODE node) + string(REGEX REPLACE "node" "" ETH_NODE_DIRECTORY ${ETH_NODE}) + message(" - nodejs location : ${ETH_NODE}") + + find_program(ETH_NPM npm) + string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM}) + message(" - npm location : ${ETH_NPM}") + endif() #HEADLESS # use multithreaded boost libraries, with -mt suffix diff --git a/eth/main.cpp b/eth/main.cpp index dae61e114..dfe020763 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #if ETH_READLINE @@ -121,7 +122,11 @@ void help() << " -u,--public-ip Force public ip to given (default; auto)." << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl << " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl - << " -V,--version Show the version and exit." << endl; + << " -V,--version Show the version and exit." << endl +#if ETH_EVMJIT + << " --jit Use EVM JIT (default: off)." << endl +#endif + ; exit(0); } @@ -175,6 +180,12 @@ void sighandler(int) g_exit = true; } +enum class NodeMode +{ + PeerServer, + Full +}; + int main(int argc, char** argv) { unsigned short listenPort = 30303; @@ -193,6 +204,7 @@ int main(int argc, char** argv) bool upnp = true; bool useLocal = false; bool forceMining = false; + bool jit = false; string clientName; // Init defaults @@ -295,6 +307,15 @@ int main(int argc, char** argv) return -1; } } + else if (arg == "--jit") + { +#if ETH_EVMJIT + jit = true; +#else + cerr << "EVM JIT not enabled" << endl; + return -1; +#endif + } else if (arg == "-h" || arg == "--help") help(); else if (arg == "-V" || arg == "--version") @@ -308,9 +329,10 @@ int main(int argc, char** argv) cout << credits(); + VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); dev::WebThreeDirect web3( - "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), + "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""), dbPath, false, mode == NodeMode::Full ? set{"eth", "shh"} : set(), diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt index 26a8010d1..18601b921 100644 --- a/evmjit/CMakeLists.txt +++ b/evmjit/CMakeLists.txt @@ -9,6 +9,7 @@ if(LLVM_DIR) # local LLVM build find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + add_definitions(${LLVM_DEFINITIONS}) # TODO: bitwriter is needed only for evmcc llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen bitwriter) else() @@ -24,4 +25,4 @@ find_package(Boost REQUIRED) add_subdirectory(libevmjit) add_subdirectory(libevmjit-cpp) -add_subdirectory(evmcc) \ No newline at end of file +add_subdirectory(evmcc) diff --git a/evmjit/evmcc/evmcc.cpp b/evmjit/evmcc/evmcc.cpp index 3c43ab78b..e86f948f2 100644 --- a/evmjit/evmcc/evmcc.cpp +++ b/evmjit/evmcc/evmcc.cpp @@ -190,7 +190,6 @@ int main(int argc, char** argv) data.set(RuntimeData::CallValue, 0xabcd); data.set(RuntimeData::CallDataSize, 3); data.set(RuntimeData::GasPrice, 1003); - data.set(RuntimeData::PrevHash, 1003); data.set(RuntimeData::CoinBase, (u160)Address(101010101010101015)); data.set(RuntimeData::TimeStamp, 1005); data.set(RuntimeData::Number, 1006); diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp index 1dcd38162..6320aa222 100644 --- a/evmjit/libevmjit-cpp/Env.cpp +++ b/evmjit/libevmjit-cpp/Env.cpp @@ -42,16 +42,15 @@ extern "C" *o_value = eth2llvm(u); } - EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) + EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash) { - if (_env->depth == 1024) - jit::terminate(jit::ReturnCode::OutOfGas); - - assert(_env->depth < 1024); + *o_hash = _env->blockhash(llvm2eth(*_number)); + } + EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) + { auto endowment = llvm2eth(*_endowment); - - if (_env->balance(_env->myAddress) >= endowment) + if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) { _env->subBalance(endowment); auto gas = llvm2eth(*io_gas); @@ -66,13 +65,8 @@ extern "C" EXPORT bool env_call(ExtVMFace* _env, i256* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) { - if (_env->depth == 1024) - jit::terminate(jit::ReturnCode::OutOfGas); - - assert(_env->depth < 1024); - auto value = llvm2eth(*_value); - if (_env->balance(_env->myAddress) >= value) + if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) { _env->subBalance(value); auto receiveAddress = right160(*_receiveAddress); diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index 815aa6332..dda8133a8 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -20,7 +20,6 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t) m_data.set(RuntimeData::CallValue, _ext.value); m_data.set(RuntimeData::CallDataSize, _ext.data.size()); m_data.set(RuntimeData::GasPrice, _ext.gasPrice); - m_data.set(RuntimeData::PrevHash, _ext.previousBlock.hash); m_data.set(RuntimeData::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress)); m_data.set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp); m_data.set(RuntimeData::Number, _ext.currentBlock.number); diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index d233ea744..dda0fbc36 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -20,20 +20,21 @@ namespace jit const char* BasicBlock::NamePrefix = "Instr."; -BasicBlock::BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder) : - m_beginInstIdx(_beginInstIdx), - m_endInstIdx(_endInstIdx), - m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {NamePrefix, std::to_string(_beginInstIdx)}, _mainFunc)), +BasicBlock::BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : + m_begin(_begin), + m_end(_end), + // TODO: Add begin index to name + m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), NamePrefix, _mainFunc)), m_stack(*this), - m_builder(_builder) + m_builder(_builder), + m_isJumpDest(isJumpDest) {} -BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder) : - m_beginInstIdx(0), - m_endInstIdx(0), +BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)), m_stack(*this), - m_builder(_builder) + m_builder(_builder), + m_isJumpDest(isJumpDest) {} BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) : diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index f0643f342..7a5364a4e 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -1,9 +1,7 @@ #pragma once - #include - #include - +#include "Common.h" #include "Stack.h" namespace dev @@ -52,20 +50,21 @@ public: BasicBlock& m_bblock; }; - /// Basic block name prefix. The rest is beging instruction index. + /// Basic block name prefix. The rest is instruction index. static const char* NamePrefix; - explicit BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder); - explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder); + explicit BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); + explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); BasicBlock(const BasicBlock&) = delete; void operator=(const BasicBlock&) = delete; - operator llvm::BasicBlock*() { return m_llvmBB; } llvm::BasicBlock* llvm() { return m_llvmBB; } - ProgramCounter begin() { return m_beginInstIdx; } - ProgramCounter end() { return m_endInstIdx; } + bytes::const_iterator begin() { return m_begin; } + bytes::const_iterator end() { return m_end; } + + bool isJumpDest() const { return m_isJumpDest; } LocalStack& localStack() { return m_stack; } @@ -82,8 +81,8 @@ public: void dump(std::ostream& os, bool _dotOutput = false); private: - ProgramCounter const m_beginInstIdx; - ProgramCounter const m_endInstIdx; + bytes::const_iterator const m_begin; + bytes::const_iterator const m_end; llvm::BasicBlock* const m_llvmBB; @@ -99,16 +98,20 @@ private: /// the item below the top and so on. The stack grows as the code /// accesses more items on the EVM stack but once a value is put on /// the stack, it will never be replaced. - std::vector m_initialStack = {}; + std::vector m_initialStack; /// This stack tracks the contents of the EVM stack as the basic block /// executes. It may grow on both sides, as the code pushes items on /// top of the stack or changes existing items. - std::vector m_currentStack = {}; + std::vector m_currentStack; /// How many items higher is the current stack than the initial one. /// May be negative. int m_tosOffset = 0; + + /// Is the basic block a valid jump destination. + /// JUMPDEST is the first instruction of the basic block. + bool const m_isJumpDest = false; }; } diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp index a887d91e9..3ac9f4a73 100644 --- a/evmjit/libevmjit/Cache.cpp +++ b/evmjit/libevmjit/Cache.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,33 @@ ObjectCache* Cache::getObjectCache() return &objectCache; } +namespace +{ + llvm::MemoryBuffer* lastObject; +} + +std::unique_ptr Cache::getObject(std::string const& id) +{ + assert(!lastObject); + llvm::SmallString<256> cachePath; + llvm::sys::path::system_temp_directory(false, cachePath); + llvm::sys::path::append(cachePath, "evm_objs", id); + + if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) + lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); + else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) + std::cerr << r.getError().message(); // TODO: Add log + + if (lastObject) // if object found create fake module + { + auto module = std::unique_ptr(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; + } + return nullptr; +} + void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) { @@ -43,16 +71,10 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) { - auto&& id = _module->getModuleIdentifier(); - llvm::SmallString<256> cachePath; - llvm::sys::path::system_temp_directory(false, cachePath); - llvm::sys::path::append(cachePath, "evm_objs", id); - - if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) - return llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); - else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) - std::cerr << r.getError().message(); // TODO: Add log - return nullptr; + (void)_module; + auto o = lastObject; + lastObject = nullptr; + return o; } } diff --git a/evmjit/libevmjit/Cache.h b/evmjit/libevmjit/Cache.h index 80fe47ade..1cad537cd 100644 --- a/evmjit/libevmjit/Cache.h +++ b/evmjit/libevmjit/Cache.h @@ -2,7 +2,6 @@ #include #include -#include #include @@ -34,6 +33,7 @@ class Cache { public: static ObjectCache* getObjectCache(); + static std::unique_ptr getObject(std::string const& id); }; } diff --git a/evmjit/libevmjit/Common.h b/evmjit/libevmjit/Common.h index d98cc0acb..1d8451c74 100644 --- a/evmjit/libevmjit/Common.h +++ b/evmjit/libevmjit/Common.h @@ -37,13 +37,15 @@ enum class ReturnCode // TODO: Replace with h256 struct i256 { - uint64_t a; - uint64_t b; - uint64_t c; - uint64_t d; + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; }; static_assert(sizeof(i256) == 32, "Wrong i265 size"); +#define UNTESTED assert(false) + } } } diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 48dc50d60..bfcb9cea1 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -5,8 +5,6 @@ #include #include -#include - #include #include #include @@ -42,114 +40,87 @@ Compiler::Compiler(Options const& _options): void Compiler::createBasicBlocks(bytes const& _bytecode) { - std::set splitPoints; // Sorted collections of instruction indices where basic blocks start/end - - std::map directJumpTargets; - std::vector indirectJumpTargets; - boost::dynamic_bitset<> validJumpTargets(std::max(_bytecode.size(), size_t(1))); - - splitPoints.insert(0); // First basic block - validJumpTargets[0] = true; - - for (auto curr = _bytecode.begin(); curr != _bytecode.end(); ++curr) + /// Helper function that skips push data and finds next iterator (can be the end) + auto skipPushDataAndGetNext = [](bytes::const_iterator _curr, bytes::const_iterator _end) { - ProgramCounter currentPC = curr - _bytecode.begin(); - validJumpTargets[currentPC] = true; - - auto inst = Instruction(*curr); - switch (inst) - { + static const auto push1 = static_cast(Instruction::PUSH1); + static const auto push32 = static_cast(Instruction::PUSH32); + size_t offset = 1; + if (*_curr >= push1 && *_curr <= push32) + offset += std::min(*_curr - push1 + 1, (_end - _curr) - 1); + return _curr + offset; + }; + + auto begin = _bytecode.begin(); + bool nextJumpDest = false; + for (auto curr = begin, next = begin; curr != _bytecode.end(); curr = next) + { + next = skipPushDataAndGetNext(curr, _bytecode.end()); - case Instruction::ANY_PUSH: + bool isEnd = false; + switch (Instruction(*curr)) { - auto val = readPushData(curr, _bytecode.end()); - auto next = curr + 1; - if (next == _bytecode.end()) - break; - - auto nextInst = Instruction(*next); - if (nextInst == Instruction::JUMP || nextInst == Instruction::JUMPI) - { - // Create a block for the JUMP target. - ProgramCounter targetPC = val.ult(_bytecode.size()) ? val.getZExtValue() : _bytecode.size(); - splitPoints.insert(targetPC); - - ProgramCounter jumpPC = (next - _bytecode.begin()); - directJumpTargets[jumpPC] = targetPC; - } - break; - } - - case Instruction::JUMPDEST: - { - // A basic block starts here. - splitPoints.insert(currentPC); - indirectJumpTargets.push_back(currentPC); - break; - } - case Instruction::JUMP: case Instruction::JUMPI: case Instruction::RETURN: case Instruction::STOP: case Instruction::SUICIDE: - { - // Create a basic block starting at the following instruction. - if (curr + 1 < _bytecode.end()) - splitPoints.insert(currentPC + 1); + isEnd = true; + break; + + case Instruction::JUMPDEST: + nextJumpDest = true; break; - } default: break; } - } - // Remove split points generated from jumps out of code or into data. - for (auto it = splitPoints.cbegin(); it != splitPoints.cend();) - { - if (*it > _bytecode.size() || !validJumpTargets[*it]) - it = splitPoints.erase(it); - else - ++it; - } + assert(next <= _bytecode.end()); + if (next == _bytecode.end() || Instruction(*next) == Instruction::JUMPDEST) + isEnd = true; - for (auto it = splitPoints.cbegin(); it != splitPoints.cend();) - { - auto beginInstIdx = *it; - ++it; - auto endInstIdx = it != splitPoints.cend() ? *it : _bytecode.size(); - basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginInstIdx), std::forward_as_tuple(beginInstIdx, endInstIdx, m_mainFunc, m_builder)); + if (isEnd) + { + auto beginIdx = begin - _bytecode.begin(); + m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), + std::forward_as_tuple(begin, next, m_mainFunc, m_builder, nextJumpDest)); + nextJumpDest = false; + begin = next; + } } m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); - m_badJumpBlock = std::unique_ptr(new BasicBlock("BadJumpBlock", m_mainFunc, m_builder)); - m_jumpTableBlock = std::unique_ptr(new BasicBlock("JumpTableBlock", m_mainFunc, m_builder)); +} - for (auto it = directJumpTargets.cbegin(); it != directJumpTargets.cend(); ++it) +llvm::BasicBlock* Compiler::getJumpTableBlock() +{ + if (!m_jumpTableBlock) { - if (it->second >= _bytecode.size()) - { - // Jumping out of code means STOP - m_directJumpTargets[it->first] = m_stopBB; - continue; - } - - auto blockIter = basicBlocks.find(it->second); - if (blockIter != basicBlocks.end()) - { - m_directJumpTargets[it->first] = blockIter->second.llvm(); - } - else + m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true)); + InsertPointGuard g{m_builder}; + m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); + auto dest = m_jumpTableBlock->localStack().pop(); + auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock()); + for (auto&& p : m_basicBlocks) { - clog(JIT) << "Bad JUMP at PC " << it->first - << ": " << it->second << " is not a valid PC"; - m_directJumpTargets[it->first] = m_badJumpBlock->llvm(); + if (p.second.isJumpDest()) + switchInstr->addCase(Constant::get(p.first), p.second.llvm()); } } + return m_jumpTableBlock->llvm(); +} - for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it) - m_indirectJumpTargets.push_back(&basicBlocks.find(*it)->second); +llvm::BasicBlock* Compiler::getBadJumpBlock() +{ + if (!m_badJumpBlock) + { + m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder, true)); + InsertPointGuard g{m_builder}; + m_builder.SetInsertPoint(m_badJumpBlock->llvm()); + m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination)); + } + return m_badJumpBlock->llvm(); } std::unique_ptr Compiler::compile(bytes const& _bytecode, std::string const& _id) @@ -176,14 +147,14 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str Stack stack(m_builder, runtimeManager); Arith256 arith(m_builder); - m_builder.CreateBr(basicBlocks.begin()->second); + m_builder.CreateBr(m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm()); - for (auto basicBlockPairIt = basicBlocks.begin(); basicBlockPairIt != basicBlocks.end(); ++basicBlockPairIt) + for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) { auto& basicBlock = basicBlockPairIt->second; auto iterCopy = basicBlockPairIt; ++iterCopy; - auto nextBasicBlock = (iterCopy != basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; + auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; compileBasicBlock(basicBlock, _bytecode, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); } @@ -192,25 +163,6 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str m_builder.SetInsertPoint(m_stopBB); m_builder.CreateRet(Constant::get(ReturnCode::Stop)); - m_builder.SetInsertPoint(m_badJumpBlock->llvm()); - m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination)); - - m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); - if (m_indirectJumpTargets.size() > 0) - { - auto dest = m_jumpTableBlock->localStack().pop(); - auto switchInstr = m_builder.CreateSwitch(dest, m_badJumpBlock->llvm(), - m_indirectJumpTargets.size()); - for (auto it = m_indirectJumpTargets.cbegin(); it != m_indirectJumpTargets.cend(); ++it) - { - auto& bb = *it; - auto dest = Constant::get(bb->begin()); - switchInstr->addCase(dest, bb->llvm()); - } - } - else - m_builder.CreateBr(m_badJumpBlock->llvm()); - removeDeadBlocks(); dumpCFGifRequired("blocks-init.dot"); @@ -218,7 +170,7 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str if (m_options.optimizeStack) { std::vector blockList; - for (auto& entry : basicBlocks) + for (auto& entry : m_basicBlocks) blockList.push_back(&entry.second); if (m_jumpTableBlock) @@ -229,7 +181,7 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str dumpCFGifRequired("blocks-opt.dot"); } - for (auto& entry : basicBlocks) + for (auto& entry : m_basicBlocks) entry.second.synchronizeLocalStack(stack); if (m_jumpTableBlock) m_jumpTableBlock->synchronizeLocalStack(stack); @@ -259,9 +211,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode m_builder.SetInsertPoint(_basicBlock.llvm()); auto& stack = _basicBlock.localStack(); - for (auto currentPC = _basicBlock.begin(); currentPC != _basicBlock.end(); ++currentPC) + for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) { - auto inst = static_cast(_bytecode[currentPC]); + auto inst = Instruction(*it); _gasMeter.count(inst); @@ -523,10 +475,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::ANY_PUSH: { - auto curr = _bytecode.begin() + currentPC; // TODO: replace currentPC with iterator - auto value = readPushData(curr, _bytecode.end()); - currentPC = curr - _bytecode.begin(); - + auto value = readPushData(it, _basicBlock.end()); stack.push(Constant::get(value)); break; } @@ -596,46 +545,45 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::JUMP: case Instruction::JUMPI: { - // Generate direct jump iff: - // 1. this is not the first instruction in the block - // 2. m_directJumpTargets[currentPC] is defined (meaning that the previous instruction is a PUSH) - // Otherwise generate a indirect jump (a switch). llvm::BasicBlock* targetBlock = nullptr; - if (currentPC != _basicBlock.begin()) + auto target = stack.pop(); + if (auto constant = llvm::dyn_cast(target)) { - auto pairIter = m_directJumpTargets.find(currentPC); - if (pairIter != m_directJumpTargets.end()) - targetBlock = pairIter->second; + auto&& c = constant->getValue(); + auto targetIdx = c.getActiveBits() <= 64 ? c.getZExtValue() : -1; + auto it = m_basicBlocks.find(targetIdx); + targetBlock = (it != m_basicBlocks.end() && it->second.isJumpDest()) ? it->second.llvm() : getBadJumpBlock(); } + // TODO: Improve; check for constants if (inst == Instruction::JUMP) { if (targetBlock) { - // The target address is computed at compile time, - // just pop it without looking... - stack.pop(); m_builder.CreateBr(targetBlock); } else - m_builder.CreateBr(m_jumpTableBlock->llvm()); + { + stack.push(target); + m_builder.CreateBr(getJumpTableBlock()); + } } else // JUMPI { - stack.swap(1); auto val = stack.pop(); auto zero = Constant::get(0); auto cond = m_builder.CreateICmpNE(val, zero, "nonzero"); if (targetBlock) { - stack.pop(); m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock); } else - m_builder.CreateCondBr(cond, m_jumpTableBlock->llvm(), _nextBasicBlock); + { + stack.push(target); + m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock); + } } - break; } @@ -647,7 +595,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::PC: { - auto value = Constant::get(currentPC); + auto value = Constant::get(it - _bytecode.begin()); stack.push(value); break; } @@ -666,7 +614,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::CALLDATASIZE: case Instruction::CODESIZE: case Instruction::GASPRICE: - case Instruction::PREVHASH: case Instruction::COINBASE: case Instruction::TIMESTAMP: case Instruction::NUMBER: @@ -678,6 +625,14 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode break; } + case Instruction::BLOCKHASH: + { + auto number = stack.pop(); + auto hash = _ext.blockhash(number); + stack.push(hash); + break; + } + case Instruction::BALANCE: { auto address = stack.pop(); @@ -835,6 +790,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode default: // Invalid instruction - runtime exception { + // TODO: Replace with return statement _runtimeManager.raiseException(ReturnCode::BadInstruction); } @@ -857,13 +813,13 @@ void Compiler::removeDeadBlocks() do { sthErased = false; - for (auto it = basicBlocks.begin(); it != basicBlocks.end();) + for (auto it = m_basicBlocks.begin(); it != m_basicBlocks.end();) { auto llvmBB = it->second.llvm(); if (llvm::pred_begin(llvmBB) == llvm::pred_end(llvmBB)) { llvmBB->eraseFromParent(); - basicBlocks.erase(it++); + m_basicBlocks.erase(it++); sthErased = true; } else @@ -871,13 +827,6 @@ void Compiler::removeDeadBlocks() } } while (sthErased); - - // Remove jump table block if no predecessors - if (llvm::pred_begin(m_jumpTableBlock->llvm()) == llvm::pred_end(m_jumpTableBlock->llvm())) - { - m_jumpTableBlock->llvm()->eraseFromParent(); - m_jumpTableBlock.reset(); - } } void Compiler::dumpCFGifRequired(std::string const& _dotfilePath) @@ -898,7 +847,7 @@ void Compiler::dumpCFGtoStream(std::ostream& _out) << " entry [share=record, label=\"entry block\"];\n"; std::vector blocks; - for (auto& pair : basicBlocks) + for (auto& pair : m_basicBlocks) blocks.push_back(&pair.second); if (m_jumpTableBlock) blocks.push_back(m_jumpTableBlock.get()); @@ -937,7 +886,7 @@ void Compiler::dumpCFGtoStream(std::ostream& _out) void Compiler::dump() { - for (auto& entry : basicBlocks) + for (auto& entry : m_basicBlocks) entry.second.dump(); if (m_jumpTableBlock != nullptr) m_jumpTableBlock->dump(); diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index 8e3bf357c..720a48cf9 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -20,19 +20,13 @@ public: struct Options { /// Optimize stack operations between basic blocks - bool optimizeStack; + bool optimizeStack = true; /// Rewrite switch instructions to sequences of branches - bool rewriteSwitchToBranches; + bool rewriteSwitchToBranches = true; /// Dump CFG as a .dot file for graphviz - bool dumpCFG; - - Options(): - optimizeStack(true), - rewriteSwitchToBranches(true), - dumpCFG(false) - {} + bool dumpCFG = false; }; using ProgramCounter = uint64_t; @@ -47,6 +41,10 @@ private: void compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock); + llvm::BasicBlock* getJumpTableBlock(); + + llvm::BasicBlock* getBadJumpBlock(); + void removeDeadBlocks(); /// Dumps basic block graph in graphviz format to a file, if option dumpCFG is enabled. @@ -65,22 +63,16 @@ private: llvm::IRBuilder<> m_builder; /// Maps a program counter pc to a basic block that starts at pc (if any). - std::map basicBlocks = {}; - - /// Maps a pc at which there is a JUMP or JUMPI to the target block of the jump. - std::map m_directJumpTargets = {}; - - /// A list of possible blocks to which there may be indirect jumps. - std::vector m_indirectJumpTargets = {}; + std::map m_basicBlocks; /// Stop basic block - terminates execution with STOP code (0) llvm::BasicBlock* m_stopBB = nullptr; /// Block with a jump table. - std::unique_ptr m_jumpTableBlock = nullptr; + std::unique_ptr m_jumpTableBlock; - /// Default destination for indirect jumps. - std::unique_ptr m_badJumpBlock = nullptr; + /// Destination for invalid jumps + std::unique_ptr m_badJumpBlock; /// Main program function llvm::Function* m_mainFunc = nullptr; diff --git a/evmjit/libevmjit/CompilerHelper.cpp b/evmjit/libevmjit/CompilerHelper.cpp index badf9d889..9cccecd79 100644 --- a/evmjit/libevmjit/CompilerHelper.cpp +++ b/evmjit/libevmjit/CompilerHelper.cpp @@ -35,6 +35,11 @@ llvm::Function* CompilerHelper::getMainFunction() return nullptr; } +llvm::CallInst* CompilerHelper::createCall(llvm::Function* _func, std::initializer_list const& _args) +{ + return getBuilder().CreateCall(_func, {_args.begin(), _args.size()}); +} + RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager): CompilerHelper(_runtimeManager.getBuilder()), diff --git a/evmjit/libevmjit/CompilerHelper.h b/evmjit/libevmjit/CompilerHelper.h index 19315fe4a..62733ca72 100644 --- a/evmjit/libevmjit/CompilerHelper.h +++ b/evmjit/libevmjit/CompilerHelper.h @@ -31,12 +31,7 @@ protected: llvm::IRBuilder<>& m_builder; llvm::IRBuilder<>& getBuilder() { return m_builder; } - template - llvm::CallInst* createCall(llvm::Function* _func, _Args*... _args) - { - llvm::Value* args[] = {_args...}; - return getBuilder().CreateCall(_func, args); - } + llvm::CallInst* createCall(llvm::Function* _func, std::initializer_list const& _args); friend class RuntimeHelper; }; diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index 862586575..7e99932c3 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -4,8 +4,13 @@ #include #include +#pragma warning(push) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" #include #include +#pragma warning(pop) +#pragma GCC diagnostic pop #include #include #include @@ -69,7 +74,13 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en } else { - auto module = Compiler({}).compile(_code, mainFuncName); + bool objectCacheEnabled = true; + auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr; + std::unique_ptr module; + if (objectCache) + module = Cache::getObject(mainFuncName); + if (!module) + module = Compiler({}).compile(_code, mainFuncName); //module->dump(); if (!ee) { @@ -95,7 +106,8 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module memoryManager.release(); // and memory manager - ee->setObjectCache(Cache::getObjectCache()); + if (objectCache) + ee->setObjectCache(objectCache); entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); } else diff --git a/evmjit/libevmjit/Ext.cpp b/evmjit/libevmjit/Ext.cpp index 38adffd3f..b92a07bf8 100644 --- a/evmjit/libevmjit/Ext.cpp +++ b/evmjit/libevmjit/Ext.cpp @@ -23,114 +23,137 @@ 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{}*/ { - auto module = getModule(); - - m_args[0] = m_builder.CreateAlloca(Type::Word, nullptr, "ext.index"); - m_args[1] = m_builder.CreateAlloca(Type::Word, nullptr, "ext.value"); - m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg2"); - m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg3"); - m_arg4 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg4"); - m_arg5 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg5"); - m_arg6 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg6"); - m_arg7 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg7"); - m_arg8 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg8"); m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); +} - using Linkage = llvm::GlobalValue::LinkageTypes; - - llvm::Type* argsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr}; - - m_sload = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "env_sload", module); - m_sstore = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "env_sstore", module); - - llvm::Type* sha3ArgsTypes[] = {Type::BytePtr, Type::Size, Type::WordPtr}; - m_sha3 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, sha3ArgsTypes, false), Linkage::ExternalLinkage, "env_sha3", module); - - llvm::Type* createArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr}; - m_create = llvm::Function::Create(llvm::FunctionType::get(Type::Void, createArgsTypes, false), Linkage::ExternalLinkage, "env_create", module); - llvm::Type* callArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr}; - m_call = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, callArgsTypes, false), Linkage::ExternalLinkage, "env_call", module); +using FuncDesc = std::tuple; - llvm::Type* logArgsTypes[] = {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr}; - m_log = llvm::Function::Create(llvm::FunctionType::get(Type::Void, logArgsTypes, false), Linkage::ExternalLinkage, "env_log", module); +llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list const& _argsTypes) +{ + return llvm::FunctionType::get(_returnType, llvm::ArrayRef{_argsTypes.begin(), _argsTypes.size()}, false); +} - llvm::Type* getExtCodeArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()}; - m_getExtCode = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, getExtCodeArgsTypes, false), Linkage::ExternalLinkage, "env_getExtCode", module); +std::array::value> const& getEnvFuncDescs() +{ + static std::array::value> descs{{ + FuncDesc{"env_sload", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, + FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})}, + FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})}, + FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_getExtCode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, + FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})}, + }}; + + return descs; +} - // Helper function, not client Env interface - llvm::Type* callDataLoadArgsTypes[] = {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr}; - m_calldataload = llvm::Function::Create(llvm::FunctionType::get(Type::Void, callDataLoadArgsTypes, false), Linkage::ExternalLinkage, "ext_calldataload", module); +llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module) +{ + auto&& desc = getEnvFuncDescs()[static_cast(_id)]; + return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module); } -llvm::Function* Ext::getBalanceFunc() +llvm::Value* Ext::getArgAlloca() { - if (!m_balance) + auto& a = m_argAllocas[m_argCounter++]; + if (!a) { - llvm::Type* argsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr}; - m_balance = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argsTypes, false), llvm::Function::ExternalLinkage, "env_balance", getModule()); + // FIXME: Improve order and names + InsertPointGuard g{getBuilder()}; + getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI()); + a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg"); } - return m_balance; + + return a; +} + +llvm::Value* Ext::byPtr(llvm::Value* _value) +{ + auto a = getArgAlloca(); + getBuilder().CreateStore(_value, a); + return a; +} + +llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list const& _args) +{ + auto& func = m_funcs[static_cast(_funcId)]; + if (!func) + func = createFunc(_funcId, getModule()); + + m_argCounter = 0; + return getBuilder().CreateCall(func, {_args.begin(), _args.size()}); } llvm::Value* Ext::sload(llvm::Value* _index) { - m_builder.CreateStore(_index, m_args[0]); - m_builder.CreateCall3(m_sload, getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); // Uses native endianness - return m_builder.CreateLoad(m_args[1]); + auto ret = getArgAlloca(); + createCall(EnvFunc::sload, {getRuntimeManager().getEnvPtr(), byPtr(_index), ret}); // Uses native endianness + return m_builder.CreateLoad(ret); } void Ext::sstore(llvm::Value* _index, llvm::Value* _value) { - m_builder.CreateStore(_index, m_args[0]); - m_builder.CreateStore(_value, m_args[1]); - m_builder.CreateCall3(m_sstore, getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); // Uses native endianness + createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness } llvm::Value* Ext::calldataload(llvm::Value* _index) { - m_builder.CreateStore(_index, m_args[0]); - createCall(m_calldataload, getRuntimeManager().getDataPtr(), m_args[0], m_args[1]); - auto ret = m_builder.CreateLoad(m_args[1]); + auto ret = getArgAlloca(); + createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret}); + ret = m_builder.CreateLoad(ret); return Endianness::toNative(m_builder, ret); } llvm::Value* Ext::balance(llvm::Value* _address) { auto address = Endianness::toBE(m_builder, _address); - m_builder.CreateStore(address, m_args[0]); - createCall(getBalanceFunc(), getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); - return m_builder.CreateLoad(m_args[1]); + auto ret = getArgAlloca(); + createCall(EnvFunc::balance, {getRuntimeManager().getEnvPtr(), byPtr(address), ret}); + return m_builder.CreateLoad(ret); +} + +llvm::Value* Ext::blockhash(llvm::Value* _number) +{ + auto hash = getArgAlloca(); + createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash}); + hash = m_builder.CreateLoad(hash); + return Endianness::toNative(getBuilder(), hash); } llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize) { - m_builder.CreateStore(_gas, m_args[0]); - m_builder.CreateStore(_endowment, m_arg2); + auto gas = byPtr(_gas); + auto ret = getArgAlloca(); auto begin = m_memoryMan.getBytePtr(_initOff); auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size"); - createCall(m_create, getRuntimeManager().getEnvPtr(), m_args[0], m_arg2, begin, size, m_args[1]); - _gas = m_builder.CreateLoad(m_args[0]); // Return gas - llvm::Value* address = m_builder.CreateLoad(m_args[1]); + createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret}); + _gas = m_builder.CreateLoad(gas); // Return gas + llvm::Value* address = m_builder.CreateLoad(ret); address = Endianness::toNative(m_builder, address); return address; } llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress) { - m_builder.CreateStore(_gas, m_args[0]); + auto gas = byPtr(_gas); auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress); - m_builder.CreateStore(receiveAddress, m_arg2); - m_builder.CreateStore(_value, m_arg3); auto inBeg = m_memoryMan.getBytePtr(_inOff); auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size"); auto outBeg = m_memoryMan.getBytePtr(_outOff); auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size"); auto codeAddress = Endianness::toBE(m_builder, _codeAddress); - m_builder.CreateStore(codeAddress, m_arg8); - auto ret = createCall(m_call, getRuntimeManager().getEnvPtr(), m_args[0], m_arg2, m_arg3, inBeg, inSize, outBeg, outSize, m_arg8); - _gas = m_builder.CreateLoad(m_args[0]); // Return gas + auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), gas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)}); + _gas = m_builder.CreateLoad(gas); // Return gas return m_builder.CreateZExt(ret, Type::Word, "ret"); } @@ -138,8 +161,9 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) { auto begin = m_memoryMan.getBytePtr(_inOff); auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size"); - createCall(m_sha3, begin, size, m_args[1]); - llvm::Value* hash = m_builder.CreateLoad(m_args[1]); + auto ret = getArgAlloca(); + createCall(EnvFunc::sha3, {begin, size, ret}); + llvm::Value* hash = m_builder.CreateLoad(ret); hash = Endianness::toNative(m_builder, hash); return hash; } @@ -147,8 +171,7 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) MemoryRef Ext::getExtCode(llvm::Value* _addr) { auto addr = Endianness::toBE(m_builder, _addr); - m_builder.CreateStore(addr, m_args[0]); - auto code = createCall(m_getExtCode, getRuntimeManager().getEnvPtr(), m_args[0], m_size); + auto code = createCall(EnvFunc::getExtCode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size}); auto codeSize = m_builder.CreateLoad(m_size); auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word); return {code, codeSize256}; @@ -158,7 +181,7 @@ void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array } } diff --git a/evmjit/libevmjit/Ext.h b/evmjit/libevmjit/Ext.h index be71dc1ff..86a8a6190 100644 --- a/evmjit/libevmjit/Ext.h +++ b/evmjit/libevmjit/Ext.h @@ -18,6 +18,28 @@ struct MemoryRef llvm::Value* size; }; +template +struct sizeOf +{ + static const size_t value = static_cast(_EnumT::_size); +}; + +enum class EnvFunc +{ + sload, + sstore, + sha3, + balance, + create, + call, + log, + blockhash, + getExtCode, + calldataload, // Helper function, not client Env interface + + _size +}; + class Ext : public RuntimeHelper { public: @@ -30,6 +52,7 @@ public: llvm::Value* calldataload(llvm::Value* _index); llvm::Value* create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); llvm::Value* call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress); + llvm::Value* blockhash(llvm::Value* _number); llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); MemoryRef getExtCode(llvm::Value* _addr); @@ -39,27 +62,16 @@ public: private: Memory& m_memoryMan; - llvm::Value* m_args[2]; - llvm::Value* m_arg2; - llvm::Value* m_arg3; - llvm::Value* m_arg4; - llvm::Value* m_arg5; - llvm::Value* m_arg6; - llvm::Value* m_arg7; - llvm::Value* m_arg8; llvm::Value* m_size; llvm::Value* m_data = nullptr; - llvm::Function* m_sload; - llvm::Function* m_sstore; - llvm::Function* m_calldataload; - llvm::Function* m_balance = nullptr; - llvm::Function* m_create; - llvm::Function* m_call; - llvm::Function* m_sha3; - llvm::Function* m_getExtCode; - llvm::Function* m_log; - - llvm::Function* getBalanceFunc(); + + std::array::value> m_funcs; + std::array m_argAllocas; + size_t m_argCounter = 0; + + llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list const& _args); + llvm::Value* getArgAlloca(); + llvm::Value* byPtr(llvm::Value* _value); }; diff --git a/evmjit/libevmjit/GasMeter.cpp b/evmjit/libevmjit/GasMeter.cpp index c31942a45..84266111e 100644 --- a/evmjit/libevmjit/GasMeter.cpp +++ b/evmjit/libevmjit/GasMeter.cpp @@ -41,7 +41,7 @@ uint64_t const c_logDataGas = 1; uint64_t const c_logTopicGas = 32; uint64_t const c_copyGas = 1; -uint64_t getStepCost(Instruction inst) // TODO: Add this function to FeeSructure (pull request submitted) +uint64_t getStepCost(Instruction inst) { switch (inst) { @@ -119,7 +119,7 @@ void GasMeter::count(Instruction _inst) if (!m_checkCall) { // Create gas check call with mocked block cost at begining of current cost-block - m_checkCall = createCall(m_gasCheckFunc, m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word)); + m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word)}); } m_blockCost += getStepCost(_inst); @@ -127,7 +127,7 @@ void GasMeter::count(Instruction _inst) void GasMeter::count(llvm::Value* _cost) { - createCall(m_gasCheckFunc, m_runtimeManager.getRuntimePtr(), _cost); + createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost}); } void GasMeter::countExp(llvm::Value* _exponent) diff --git a/evmjit/libevmjit/Instruction.cpp b/evmjit/libevmjit/Instruction.cpp new file mode 100644 index 000000000..fdc40d043 --- /dev/null +++ b/evmjit/libevmjit/Instruction.cpp @@ -0,0 +1,40 @@ + +#include "Instruction.h" +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) +{ + auto pushInst = *_curr; + assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); + auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; + llvm::APInt value(256, 0); + ++_curr; // Point the data + for (decltype(numBytes) i = 0; i < numBytes; ++i) + { + byte b = (_curr != _end) ? *_curr++ : 0; + value <<= 8; + value |= b; + } + --_curr; // Point the last real byte read + return value; +} + +void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) +{ + auto pushInst = *_curr; + assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); + auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; + --_end; + for (decltype(numBytes) i = 0; i < numBytes && _curr < _end; ++i, ++_curr) {} +} + +} +} +} diff --git a/evmjit/libevmjit/Instruction.h b/evmjit/libevmjit/Instruction.h index 502c4b66e..158490dee 100644 --- a/evmjit/libevmjit/Instruction.h +++ b/evmjit/libevmjit/Instruction.h @@ -58,7 +58,7 @@ enum class Instruction: uint8_t EXTCODESIZE, ///< get external code size (from another contract) EXTCODECOPY, ///< copy external code (from another contract) - PREVHASH = 0x40, ///< get hash of most recent complete block + BLOCKHASH = 0x40, ///< get hash of most recent complete block COINBASE, ///< get the block's coinbase address TIMESTAMP, ///< get the block's timestamp NUMBER, ///< get the block's number @@ -160,9 +160,13 @@ enum class Instruction: uint8_t /// Reads PUSH data from pointed fragment of bytecode and constructs number out of it /// Reading out of bytecode means reading 0 -/// @param _curr is updates and points the last real byte read +/// @param _curr is updated and points the last real byte read llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end); +/// Skips PUSH data in pointed fragment of bytecode. +/// @param _curr is updated and points the last real byte skipped +void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end); + #define ANY_PUSH PUSH1: \ case Instruction::PUSH2: \ case Instruction::PUSH3: \ diff --git a/evmjit/libevmjit/Memory.cpp b/evmjit/libevmjit/Memory.cpp index c60a5e554..9f57c7a4b 100644 --- a/evmjit/libevmjit/Memory.cpp +++ b/evmjit/libevmjit/Memory.cpp @@ -146,18 +146,18 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMet llvm::Value* Memory::loadWord(llvm::Value* _addr) { - return createCall(m_loadWord, getRuntimeManager().getRuntimePtr(), _addr); + return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); } void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) { - createCall(m_storeWord, getRuntimeManager().getRuntimePtr(), _addr, _word); + createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); } void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) { auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); - createCall(m_storeByte, getRuntimeManager().getRuntimePtr(), _addr, byte); + createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); } llvm::Value* Memory::getData() @@ -181,7 +181,7 @@ llvm::Value* Memory::getBytePtr(llvm::Value* _index) void Memory::require(llvm::Value* _offset, llvm::Value* _size) { - createCall(m_require, getRuntimeManager().getRuntimePtr(), _offset, _size); + createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size}); } void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, diff --git a/evmjit/libevmjit/Runtime.cpp b/evmjit/libevmjit/Runtime.cpp index 27c81ea86..2522e8ace 100644 --- a/evmjit/libevmjit/Runtime.cpp +++ b/evmjit/libevmjit/Runtime.cpp @@ -11,29 +11,12 @@ namespace eth { namespace jit { -namespace -{ - jmp_buf_ref g_currJmpBuf; -} - -jmp_buf_ref Runtime::getCurrJmpBuf() -{ - return g_currJmpBuf; -} -Runtime::Runtime(RuntimeData* _data, Env* _env): +Runtime::Runtime(RuntimeData* _data, Env* _env) : m_data(*_data), m_env(*_env), - m_currJmpBuf(m_jmpBuf), - m_prevJmpBuf(g_currJmpBuf) -{ - g_currJmpBuf = m_jmpBuf; -} - -Runtime::~Runtime() -{ - g_currJmpBuf = m_prevJmpBuf; -} + m_currJmpBuf(m_jmpBuf) +{} bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy { diff --git a/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h index e11dac319..8cc5b7968 100644 --- a/evmjit/libevmjit/Runtime.h +++ b/evmjit/libevmjit/Runtime.h @@ -32,7 +32,6 @@ class Runtime { public: Runtime(RuntimeData* _data, Env* _env); - ~Runtime(); Runtime(const Runtime&) = delete; void operator=(const Runtime&) = delete; @@ -43,15 +42,13 @@ public: bytes getReturnData() const; jmp_buf_ref getJmpBuf() { return m_jmpBuf; } - static jmp_buf_ref getCurrJmpBuf(); private: RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract. Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract. jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract. byte* m_memoryData = nullptr; - i256 m_memorySize = {}; - jmp_buf_ref m_prevJmpBuf; + i256 m_memorySize; std::jmp_buf m_jmpBuf; StackImpl m_stack; MemoryImpl m_memory; diff --git a/evmjit/libevmjit/RuntimeData.h b/evmjit/libevmjit/RuntimeData.h index 89987bdeb..bb52f7864 100644 --- a/evmjit/libevmjit/RuntimeData.h +++ b/evmjit/libevmjit/RuntimeData.h @@ -21,7 +21,6 @@ struct RuntimeData CallValue, CallDataSize, GasPrice, - PrevHash, CoinBase, TimeStamp, Number, diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index 14280f80f..ea2fe20b5 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -63,7 +63,6 @@ llvm::Twine getName(RuntimeData::Index _index) case RuntimeData::CallValue: return "callvalue"; case RuntimeData::CallDataSize: return "calldatasize"; case RuntimeData::GasPrice: return "gasprice"; - case RuntimeData::PrevHash: return "prevhash"; case RuntimeData::CoinBase: return "coinbase"; case RuntimeData::TimeStamp: return "timestamp"; case RuntimeData::Number: return "number"; @@ -154,7 +153,6 @@ llvm::Value* RuntimeManager::get(Instruction _inst) case Instruction::CALLVALUE: return get(RuntimeData::CallValue); case Instruction::CALLDATASIZE: return get(RuntimeData::CallDataSize); case Instruction::GASPRICE: return get(RuntimeData::GasPrice); - case Instruction::PREVHASH: return get(RuntimeData::PrevHash); case Instruction::COINBASE: return get(RuntimeData::CoinBase); case Instruction::TIMESTAMP: return get(RuntimeData::TimeStamp); case Instruction::NUMBER: return get(RuntimeData::Number); diff --git a/evmjit/libevmjit/Utils.cpp b/evmjit/libevmjit/Utils.cpp index f1ffbf67f..0fd9c0e41 100644 --- a/evmjit/libevmjit/Utils.cpp +++ b/evmjit/libevmjit/Utils.cpp @@ -1,8 +1,5 @@ -#include #include "Utils.h" -#include "Instruction.h" -#include "Runtime.h" namespace dev { @@ -38,29 +35,6 @@ i256 eth2llvm(u256 _u) return i; } -llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) -{ - auto pushInst = *_curr; - assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); - auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; - llvm::APInt value(256, 0); - ++_curr; // Point the data - for (decltype(numBytes) i = 0; i < numBytes; ++i) - { - byte b = (_curr != _end) ? *_curr++ : 0; - value <<= 8; - value |= b; - } - --_curr; // Point the last real byte read - return value; -} - -void terminate(ReturnCode _returnCode) -{ - auto jmpBuf = Runtime::getCurrJmpBuf(); - std::longjmp(jmpBuf, static_cast(_returnCode)); -} - } } } diff --git a/evmjit/libevmjit/Utils.h b/evmjit/libevmjit/Utils.h index db0647fdf..f672365c6 100644 --- a/evmjit/libevmjit/Utils.h +++ b/evmjit/libevmjit/Utils.h @@ -17,8 +17,6 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } }; u256 llvm2eth(i256); i256 eth2llvm(u256); -void terminate(ReturnCode _returnCode); - } } } diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 0967596e2..9fefea45a 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -80,6 +80,9 @@ using StringMap = std::map; using u256Map = std::map; using HexMap = std::map; +// String types. +using strings = std::vector; + // Fixed-length string types. using string32 = std::array; diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index 5d03c195f..061f181b3 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -22,6 +22,7 @@ #pragma once #include +#include #include #include #include "CommonData.h" @@ -40,6 +41,7 @@ struct NoNetworking: virtual Exception {}; struct NoUPnPDevice: virtual Exception {}; struct RootNotFound: virtual Exception {}; struct FileError: virtual Exception {}; +struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): m_f("Interface " + _f + " not supported.") {} virtual const char* what() const noexcept { return m_f.c_str(); } private: std::string m_f; }; // error information to be added to exceptions typedef boost::error_info errinfo_invalidSymbol; diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index 2771c739a..0cd3f8064 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -28,7 +28,7 @@ public: bool contentsEqual(std::vector const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); } std::vector toVector() const { return std::vector(m_data, m_data + m_count); } - std::vector toBytes() const { return std::vector((unsigned char const*)m_data, m_data + m_count * sizeof(_T)); } + std::vector toBytes() const { return std::vector((unsigned char const*)m_data, (unsigned char const*)m_data + m_count * sizeof(_T)); } std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); } template operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>((_T2*)m_data, m_count * sizeof(_T) / sizeof(_T2)); } diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index f4803bd52..adcf70b61 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "SHA3.h" #include "FileSystem.h" #include "CryptoPP.h" @@ -139,7 +140,7 @@ h256 Nonce::get(bool _commit) static h256 s_seed; static string s_seedFile(getDataDir() + "/seed"); static mutex s_x; - lock_guard l(s_x); + Guard l(s_x); if (!s_seed) { static Nonce s_nonce; diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index d73e3fa43..acd59184f 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -20,6 +20,7 @@ */ #include "CryptoPP.h" +#include using namespace std; using namespace dev; @@ -40,7 +41,7 @@ void Secp256k1::encrypt(Public const& _k, bytes& io_cipher) ciphertext.resize(e.CiphertextLength(plen)); { - lock_guard l(x_rng); + Guard l(x_rng); e.Encrypt(m_rng, io_cipher.data(), plen, ciphertext.data()); } @@ -65,7 +66,7 @@ void Secp256k1::decrypt(Secret const& _k, bytes& io_text) DecodingResult r; { - lock_guard l(x_rng); + Guard l(x_rng); r = d.Decrypt(m_rng, io_text.data(), clen, plain.data()); } @@ -99,7 +100,7 @@ Signature Secp256k1::sign(Secret const& _key, h256 const& _hash) ECP::Point rp; Integer r; { - lock_guard l(x_params); + Guard l(x_params); rp = m_params.ExponentiateBase(k); r = m_params.ConvertElementToInteger(rp); } @@ -149,7 +150,7 @@ Public Secp256k1::recover(Signature _signature, bytesConstRef _message) ECP::Element x; { - lock_guard l(x_curve); + Guard l(x_curve); m_curve.DecodePoint(x, encodedpoint, 33); if (!m_curve.VerifyPoint(x)) return recovered; @@ -158,7 +159,7 @@ Public Secp256k1::recover(Signature _signature, bytesConstRef _message) // if (_signature[64] & 2) // { // r += m_q; -// lock_guard l(x_params); +// Guard l(x_params); // if (r >= m_params.GetMaxExponent()) // return recovered; // } @@ -171,7 +172,7 @@ Public Secp256k1::recover(Signature _signature, bytesConstRef _message) ECP::Point p; byte recoveredbytes[65]; { - lock_guard l(x_curve); + Guard l(x_curve); // todo: make generator member p = m_curve.CascadeMultiply(u2, x, u1, m_params.GetSubgroupGenerator()); m_curve.EncodePoint(recoveredbytes, p, false); @@ -210,7 +211,7 @@ void Secp256k1::exportPublicKey(CryptoPP::DL_PublicKey_EC const& bytes prefixedKey(_k.GetGroupParameters().GetEncodedElementSize(true)); { - lock_guard l(x_params); + Guard l(x_params); m_params.GetCurve().EncodePoint(prefixedKey.data(), _k.GetPublicElement(), false); assert(Public::size + 1 == _k.GetGroupParameters().GetEncodedElementSize(true)); } @@ -223,7 +224,7 @@ void Secp256k1::exponentToPublic(Integer const& _e, Public& o_p) CryptoPP::DL_PublicKey_EC pk; { - lock_guard l(x_params); + Guard l(x_params); pk.Initialize(m_params, m_params.ExponentiateBase(_e)); } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index d8967c084..c963ee401 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -159,9 +159,10 @@ void Client::clearPending() WriteGuard l(x_stateDB); if (!m_postMine.pending().size()) return; - for (unsigned i = 0; i < m_postMine.pending().size(); ++i) - appendFromNewPending(m_postMine.logBloom(i), changeds); +// for (unsigned i = 0; i < m_postMine.pending().size(); ++i) +// appendFromNewPending(m_postMine.logBloom(i), changeds); changeds.insert(PendingChangedFilter); + m_tq.clear(); m_postMine = m_preMine; } @@ -176,21 +177,29 @@ void Client::clearPending() unsigned Client::installWatch(h256 _h) { - auto ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; - m_watches[ret] = ClientWatch(_h); - cwatch << "+++" << ret << _h; + unsigned ret; + { + Guard l(m_filterLock); + ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; + m_watches[ret] = ClientWatch(_h); + cwatch << "+++" << ret << _h; + } + auto ch = logs(ret); + { + Guard l(m_filterLock); + swap(m_watches[ret].changes, ch); + } return ret; } unsigned Client::installWatch(LogFilter const& _f) { - lock_guard l(m_filterLock); - h256 h = _f.sha3(); - - if (!m_filters.count(h)) - m_filters.insert(make_pair(h, _f)); - + { + Guard l(m_filterLock); + if (!m_filters.count(h)) + m_filters.insert(make_pair(h, _f)); + } return installWatch(h); } @@ -198,7 +207,7 @@ void Client::uninstallWatch(unsigned _i) { cwatch << "XXX" << _i; - lock_guard l(m_filterLock); + Guard l(m_filterLock); auto it = m_watches.find(_i); if (it == m_watches.end()) @@ -214,33 +223,82 @@ void Client::uninstallWatch(unsigned _i) void Client::noteChanged(h256Set const& _filters) { - lock_guard l(m_filterLock); + Guard l(m_filterLock); + // accrue all changes left in each filter into the watches. for (auto& i: m_watches) if (_filters.count(i.second.id)) { // cwatch << "!!!" << i.first << i.second.id; - i.second.changes++; + try { + i.second.changes += m_filters.at(i.second.id).changes; + } catch(...){} } + // clear the filters now. + for (auto& i: m_filters) + i.second.changes.clear(); } -void Client::appendFromNewPending(LogBloom _bloom, h256Set& o_changed) const +LocalisedLogEntries Client::peekWatch(unsigned _watchId) const { - // TODO: more precise check on whether the txs match. - lock_guard l(m_filterLock); - for (pair const& i: m_filters) - if ((unsigned)i.second.filter.latest() > m_bc.number() && i.second.filter.matches(_bloom)) - o_changed.insert(i.first); + Guard l(m_filterLock); + + try { + return m_watches.at(_watchId).changes; + } catch (...) {} + return LocalisedLogEntries(); +} + +LocalisedLogEntries Client::checkWatch(unsigned _watchId) +{ + Guard l(m_filterLock); + LocalisedLogEntries ret; + + try { + std::swap(ret, m_watches.at(_watchId).changes); + } catch (...) {} + + return ret; } -void Client::appendFromNewBlock(h256 _block, h256Set& o_changed) const +void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed) +{ + Guard l(m_filterLock); + for (pair& i: m_filters) + if ((unsigned)i.second.filter.latest() > m_bc.number()) + { + // acceptable number. + auto m = i.second.filter.matches(_receipt); + if (m.size()) + { + // filter catches them + for (LogEntry const& l: m) + i.second.changes.push_back(LocalisedLogEntry(l, m_bc.number() + 1)); + io_changed.insert(i.first); + } + } +} + +void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed) { // TODO: more precise check on whether the txs match. auto d = m_bc.info(_block); + auto br = m_bc.receipts(_block); - lock_guard l(m_filterLock); - for (pair const& i: m_filters) + Guard l(m_filterLock); + for (pair& i: m_filters) if ((unsigned)i.second.filter.latest() >= d.number && (unsigned)i.second.filter.earliest() <= d.number && i.second.filter.matches(d.logBloom)) - o_changed.insert(i.first); + // acceptable number & looks like block may contain a matching log entry. + for (TransactionReceipt const& tr: br.receipts) + { + auto m = i.second.filter.matches(tr); + if (m.size()) + { + // filter catches them + for (LogEntry const& l: m) + i.second.changes.push_back(LocalisedLogEntry(l, (unsigned)d.number)); + io_changed.insert(i.first); + } + } } void Client::setForceMining(bool _enable) @@ -467,10 +525,10 @@ void Client::doWork() // returns h256s as blooms, once for each transaction. cwork << "postSTATE <== TQ"; - h512s newPendingBlooms = m_postMine.sync(m_bc, m_tq); - if (newPendingBlooms.size()) + TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq); + if (newPendingReceipts.size()) { - for (auto i: newPendingBlooms) + for (auto i: newPendingReceipts) appendFromNewPending(i, changeds); changeds.insert(PendingChangedFilter); @@ -534,7 +592,7 @@ eth::State Client::state(unsigned _txi) const StateDiff Client::diff(unsigned _txi, int _block) const { - State st = state(_block); + State st = asOf(_block); return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); } @@ -591,16 +649,16 @@ BlockInfo Client::uncle(h256 _blockHash, unsigned _i) const return BlockInfo::fromHeader(b[2][_i].data()); } -LogEntries Client::logs(LogFilter const& _f) const +LocalisedLogEntries Client::logs(LogFilter const& _f) const { - LogEntries ret; - unsigned begin = min(m_bc.number(), (unsigned)_f.latest()); - unsigned end = min(begin, (unsigned)_f.earliest()); + LocalisedLogEntries ret; + unsigned begin = min(m_bc.number() + 1, (unsigned)_f.latest()); + unsigned end = min(m_bc.number(), min(begin, (unsigned)_f.earliest())); unsigned m = _f.max(); unsigned s = _f.skip(); // Handle pending transactions differently as they're not on the block chain. - if (begin == m_bc.number()) + if (begin > m_bc.number()) { ReadGuard l(x_stateDB); for (unsigned i = 0; i < m_postMine.pending().size(); ++i) @@ -614,9 +672,10 @@ LogEntries Client::logs(LogFilter const& _f) const if (s) s--; else - ret.insert(ret.begin(), le[j]); + ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin)); } } + begin = m_bc.number(); } #if ETH_DEBUG @@ -648,7 +707,7 @@ LogEntries Client::logs(LogFilter const& _f) const if (s) s--; else - ret.insert(ret.begin(), le[j]); + ret.insert(ret.begin(), LocalisedLogEntry(le[j], n)); } } } diff --git a/libethereum/Client.h b/libethereum/Client.h index 6a3d7ec22..d0dce4cc1 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -57,12 +57,6 @@ enum ClientWorkState Deleted }; -enum class NodeMode -{ - PeerServer, - Full -}; - class VersionChecker { public: @@ -84,6 +78,7 @@ struct InstalledFilter LogFilter filter; unsigned refCount = 1; + LocalisedLogEntries changes; }; static const h256 PendingChangedFilter = u256(0); @@ -95,7 +90,7 @@ struct ClientWatch explicit ClientWatch(h256 _id): id(_id) {} h256 id; - unsigned changes = 1; + LocalisedLogEntries changes; }; struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; @@ -108,9 +103,9 @@ struct WorkChannel: public LogChannel { static const char* name() { return "-W-" #define cworkout dev::LogOutputStream() template struct ABISerialiser {}; -template struct ABISerialiser> { static bytes serialise(FixedHash const& _t) { return _t.asBytes(); } }; +template struct ABISerialiser> { static bytes serialise(FixedHash const& _t) { static_assert(N <= 32, "Cannot serialise hash > 32 bytes."); static_assert(N > 0, "Cannot serialise zero-length hash."); return bytes(32 - N, 0) + _t.asBytes(); } }; template <> struct ABISerialiser { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } }; -template <> struct ABISerialiser { static bytes serialise(u160 const& _t) { return h160(_t).asBytes(); } }; +template <> struct ABISerialiser { static bytes serialise(u160 const& _t) { return bytes(12, 0) + h160(_t).asBytes(); } }; template <> struct ABISerialiser { static bytes serialise(string32 const& _t) { return bytesConstRef((byte const*)_t.data(), 32).toBytes(); } }; inline bytes abiInAux() { return {}; } @@ -119,15 +114,15 @@ template bytes abiInAux(T const& _t, U const& ... _u) return ABISerialiser::serialise(_t) + abiInAux(_u ...); } -template bytes abiIn(byte _id, T const& ... _t) +template bytes abiIn(std::string _id, T const& ... _t) { - return bytes(1, _id) + abiInAux(_t ...); + return sha3(_id).ref().cropped(0, 4).toBytes() + abiInAux(_t ...); } template struct ABIDeserialiser {}; -template struct ABIDeserialiser> { static FixedHash deserialise(bytesConstRef& io_t) { FixedHash ret; io_t.cropped(0, N).populate(ret.ref()); return ret; } }; +template struct ABIDeserialiser> { static FixedHash deserialise(bytesConstRef& io_t) { static_assert(N <= 32, "Parameter sizes must be at most 32 bytes."); FixedHash ret; io_t.cropped(32 - N, N).populate(ret.ref()); io_t = io_t.cropped(32); return ret; } }; template <> struct ABIDeserialiser { static u256 deserialise(bytesConstRef& io_t) { u256 ret = fromBigEndian(io_t.cropped(0, 32)); io_t = io_t.cropped(32); return ret; } }; -template <> struct ABIDeserialiser { static u256 deserialise(bytesConstRef& io_t) { u160 ret = fromBigEndian(io_t.cropped(0, 20)); io_t = io_t.cropped(20); return ret; } }; +template <> struct ABIDeserialiser { static u160 deserialise(bytesConstRef& io_t) { u160 ret = fromBigEndian(io_t.cropped(12, 20)); io_t = io_t.cropped(32); return ret; } }; template <> struct ABIDeserialiser { static string32 deserialise(bytesConstRef& io_t) { string32 ret; io_t.cropped(0, 32).populate(vector_ref(ret.data(), 32)); io_t = io_t.cropped(32); return ret; } }; template T abiOut(bytes const& _data) @@ -188,11 +183,11 @@ public: virtual unsigned installWatch(LogFilter const& _filter); virtual unsigned installWatch(h256 _filterId); virtual void uninstallWatch(unsigned _watchId); - virtual bool peekWatch(unsigned _watchId) const { std::lock_guard l(m_filterLock); try { return m_watches.at(_watchId).changes != 0; } catch (...) { return false; } } - virtual bool checkWatch(unsigned _watchId) { std::lock_guard l(m_filterLock); bool ret = false; try { ret = m_watches.at(_watchId).changes != 0; m_watches.at(_watchId).changes = 0; } catch (...) {} return ret; } + virtual LocalisedLogEntries peekWatch(unsigned _watchId) const; + virtual LocalisedLogEntries checkWatch(unsigned _watchId); - virtual LogEntries logs(unsigned _watchId) const { try { std::lock_guard l(m_filterLock); return logs(m_filters.at(m_watches.at(_watchId).id).filter); } catch (...) { return LogEntries(); } } - virtual LogEntries logs(LogFilter const& _filter) const; + virtual LocalisedLogEntries logs(unsigned _watchId) const { try { Guard l(m_filterLock); return logs(m_filters.at(m_watches.at(_watchId).id).filter); } catch (...) { return LocalisedLogEntries(); } } + virtual LocalisedLogEntries logs(LogFilter const& _filter) const; // [EXTRA API]: @@ -292,11 +287,11 @@ private: /// Collate the changed filters for the bloom filter of the given pending transaction. /// Insert any filters that are activated into @a o_changed. - void appendFromNewPending(LogBloom _pendingTransactionBloom, h256Set& o_changed) const; + void appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed); /// Collate the changed filters for the hash of the given block. /// Insert any filters that are activated into @a o_changed. - void appendFromNewBlock(h256 _blockHash, h256Set& o_changed) const; + void appendFromNewBlock(h256 const& _blockHash, h256Set& io_changed); /// Record that the set of filters @a _filters have changed. /// This doesn't actually make any callbacks, but incrememnts some counters in m_watches. @@ -313,7 +308,7 @@ private: TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). - mutable boost::shared_mutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine. + mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine. OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. State m_preMine; ///< The present state of the client. State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). @@ -321,7 +316,7 @@ private: std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. std::vector m_miners; - mutable boost::shared_mutex x_miners; + mutable SharedMutex x_miners; bool m_paranoia = false; ///< Should we be paranoid about our state? bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping. bool m_forceMining = false; ///< Mine even when there are no transactions pending? diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 0901766bf..95e1aadda 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -317,6 +317,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) disable("Blacklisted client version."); else if (host()->isBanned(session()->id())) disable("Peer banned for previous bad behaviour."); + else + transition(Asking::Nothing); break; } case GetTransactionsPacket: break; // DEPRECATED. diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index f79bb4a3b..c4ce543d6 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -146,9 +146,17 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress) + _endowment, Account::ContractConception); // Execute _init. - m_vm = VMFactory::create(_gas); - m_ext = make_shared(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth); - return _init.empty(); + if (_init.empty()) + { + m_s.m_cache[m_newAddress].setCode({}); + m_endGas = _gas; + } + else + { + m_vm = VMFactory::create(_gas); + m_ext = make_shared(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth); + } + return !m_ext; } OnOpFunc Executive::simpleTrace() diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 28ac26819..35cd59663 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -86,15 +86,15 @@ public: // [LOGS API] - virtual LogEntries logs(unsigned _watchId) const = 0; - virtual LogEntries logs(LogFilter const& _filter) const = 0; + virtual LocalisedLogEntries logs(unsigned _watchId) const = 0; + virtual LocalisedLogEntries logs(LogFilter const& _filter) const = 0; /// Install, uninstall and query watches. virtual unsigned installWatch(LogFilter const& _filter) = 0; virtual unsigned installWatch(h256 _filterId) = 0; virtual void uninstallWatch(unsigned _watchId) = 0; - virtual bool peekWatch(unsigned _watchId) const = 0; - virtual bool checkWatch(unsigned _watchId) = 0; + virtual LocalisedLogEntries peekWatch(unsigned _watchId) const = 0; + virtual LocalisedLogEntries checkWatch(unsigned _watchId) = 0; // [BLOCK QUERY API] @@ -178,10 +178,9 @@ public: Watch(Interface& _c, LogFilter const& _tf): m_c(&_c), m_id(_c.installWatch(_tf)) {} ~Watch() { if (m_c) m_c->uninstallWatch(m_id); } - bool check() { return m_c ? m_c->checkWatch(m_id) : false; } - bool peek() { return m_c ? m_c->peekWatch(m_id) : false; } -// PastMessages messages() const { return m_c->messages(m_id); } - LogEntries logs() const { return m_c->logs(m_id); } + LocalisedLogEntries check() { return m_c ? m_c->checkWatch(m_id) : LocalisedLogEntries(); } + LocalisedLogEntries peek() { return m_c ? m_c->peekWatch(m_id) : LocalisedLogEntries(); } + LocalisedLogEntries logs() const { return m_c->logs(m_id); } private: Interface* m_c = nullptr; diff --git a/libethereum/LogFilter.cpp b/libethereum/LogFilter.cpp index 81cb439ba..eca428cfa 100644 --- a/libethereum/LogFilter.cpp +++ b/libethereum/LogFilter.cpp @@ -68,14 +68,15 @@ bool LogFilter::matches(State const& _s, unsigned _i) const LogEntries LogFilter::matches(TransactionReceipt const& _m) const { LogEntries ret; - for (LogEntry const& e: _m.log()) - { - if (!m_addresses.empty() && !m_addresses.count(e.address)) - continue; - for (auto const& t: m_topics) - if (!std::count(e.topics.begin(), e.topics.end(), t)) + if (matches(_m.bloom())) + for (LogEntry const& e: _m.log()) + { + if (!m_addresses.empty() && !m_addresses.count(e.address)) continue; - ret.push_back(e); - } + for (auto const& t: m_topics) + if (!std::count(e.topics.begin(), e.topics.end(), t)) + continue; + ret.push_back(e); + } return ret; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index e0705859f..3c59bf415 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -412,10 +412,10 @@ bool State::cull(TransactionQueue& _tq) const return ret; } -h512s State::sync(BlockChain const& _bc, TransactionQueue& _tq, bool* o_transactionQueueChanged) +TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, bool* o_transactionQueueChanged) { // TRANSACTIONS - h512s ret; + TransactionReceipts ret; auto ts = _tq.transactions(); auto lh = getLastHashes(_bc); @@ -432,7 +432,7 @@ h512s State::sync(BlockChain const& _bc, TransactionQueue& _tq, bool* o_transact uncommitToMine(); // boost::timer t; execute(lh, i.second); - ret.push_back(m_receipts.back().bloom()); + ret.push_back(m_receipts.back()); _tq.noteGood(i); ++goodTxs; // cnote << "TX took:" << t.elapsed() * 1000; diff --git a/libethereum/State.h b/libethereum/State.h index 921c82bb9..e7e1bbfab 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -140,10 +140,10 @@ public: // TODO: Cleaner interface. /// Sync our transactions, killing those from the queue that we have and assimilating those that we don't. - /// @returns a list of bloom filters one for each transaction placed from the queue into the state. + /// @returns a list of receipts one for each transaction placed from the queue into the state. /// @a o_transactionQueueChanged boolean pointer, the value of which will be set to true if the transaction queue /// changed and the pointer is non-null - h512s sync(BlockChain const& _bc, TransactionQueue& _tq, bool* o_transactionQueueChanged = nullptr); + TransactionReceipts sync(BlockChain const& _bc, TransactionQueue& _tq, bool* o_transactionQueueChanged = nullptr); /// Like sync but only operate on _tq, killing the invalid/old ones. bool cull(TransactionQueue& _tq) const; diff --git a/libethereum/Utility.cpp b/libethereum/Utility.cpp index b6653fe39..31c5e278a 100644 --- a/libethereum/Utility.cpp +++ b/libethereum/Utility.cpp @@ -23,6 +23,7 @@ #include #include +#include using namespace std; using namespace dev; using namespace dev::eth; @@ -32,7 +33,7 @@ bytes dev::eth::parseData(string const& _args) bytes m_data; boost::smatch what; - static const boost::regex r("(@|\\$)?\"([^\"]*)\"(\\s.*)?"); + static const boost::regex r("(!|#|@|\\$)?\"([^\"]*)\"(\\s.*)?"); static const boost::regex d("(@|\\$)?([0-9]+)(\\s*(ether)|(finney)|(szabo))?(\\s.*)?"); static const boost::regex h("(@|\\$)?(0x)?(([a-fA-F0-9])+)(\\s.*)?"); @@ -67,13 +68,15 @@ bytes dev::eth::parseData(string const& _args) } else if (boost::regex_match(s, what, r)) { - for (auto i: (string)what[2]) - m_data.push_back((byte)i); - if (what[1] != "$") - for (int i = what[2].length(); i < 32; ++i) - m_data.push_back(0); + bytes d = asBytes(what[2]); + if (what[1] == "!") + m_data += FixedHash<4>(sha3(d)).asBytes(); + else if (what[1] == "#") + m_data += sha3(d).asBytes(); + else if (what[1] == "$") + m_data += d + bytes{0}; else - m_data.push_back(0); + m_data += d + bytes(32 - what[2].length() % 32, 0); s = what[3]; } else diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 84cfe0a94..13e8712b8 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -60,6 +60,16 @@ struct LogEntry using LogEntries = std::vector; +struct LocalisedLogEntry: public LogEntry +{ + LocalisedLogEntry() {} + LocalisedLogEntry(LogEntry const& _le, unsigned _number): LogEntry(_le), number(_number) {} + + unsigned number = 0; +}; + +using LocalisedLogEntries = std::vector; + inline LogBloom bloom(LogEntries const& _logs) { LogBloom ret; diff --git a/libevm/VM.cpp b/libevm/VM.cpp index b8452e4f5..4307d9da0 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -31,3 +31,763 @@ void VM::reset(u256 _gas) noexcept m_curPC = 0; m_jumpDests.clear(); } + +bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) +{ + auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; + + if (m_jumpDests.empty()) + for (unsigned i = 0; i < _ext.code.size(); ++i) + if (_ext.code[i] == (byte)Instruction::JUMPDEST) + m_jumpDests.insert(i); + else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32) + i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1; + u256 nextPC = m_curPC + 1; + auto osteps = _steps; + for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1) + { + // INSTRUCTION... + Instruction inst = (Instruction)_ext.getCode(m_curPC); + + // FEES... + bigint runGas = c_stepGas; + bigint newTempSize = m_temp.size(); + bigint copySize = 0; + + auto onOperation = [&]() + { + if (_onOp) + _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext); + }; + // should work, but just seems to result in immediate errorless exit on initial execution. yeah. weird. + //m_onFail = std::function(onOperation); + + switch (inst) + { + case Instruction::STOP: + runGas = 0; + break; + + case Instruction::SUICIDE: + require(1); + runGas = 0; + break; + + case Instruction::SSTORE: + require(2); + if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2]) + runGas = c_sstoreSetGas; + else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) + { + runGas = 0; + _ext.sub.refunds += c_sstoreRefundGas; + } + else + runGas = c_sstoreResetGas; + break; + + case Instruction::SLOAD: + require(1); + runGas = c_sloadGas; + break; + + // These all operate on memory and therefore potentially expand it: + case Instruction::MSTORE: + require(2); + newTempSize = (bigint)m_stack.back() + 32; + break; + case Instruction::MSTORE8: + require(2); + newTempSize = (bigint)m_stack.back() + 1; + break; + case Instruction::MLOAD: + require(1); + newTempSize = (bigint)m_stack.back() + 32; + break; + case Instruction::RETURN: + require(2); + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); + break; + case Instruction::SHA3: + require(2); + runGas = c_sha3Gas + (m_stack[m_stack.size() - 2] + 31) / 32 * c_sha3WordGas; + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); + break; + case Instruction::CALLDATACOPY: + require(3); + copySize = m_stack[m_stack.size() - 3]; + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); + break; + case Instruction::CODECOPY: + require(3); + copySize = m_stack[m_stack.size() - 3]; + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); + break; + case Instruction::EXTCODECOPY: + require(4); + copySize = m_stack[m_stack.size() - 4]; + newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]); + break; + + case Instruction::BALANCE: + require(1); + runGas = c_balanceGas; + break; + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + { + unsigned n = (unsigned)inst - (unsigned)Instruction::LOG0; + require(n + 2); + runGas = c_logGas + c_logTopicGas * n + (bigint)c_logDataGas * m_stack[m_stack.size() - 2]; + newTempSize = memNeed(m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]); + break; + } + + case Instruction::CALL: + case Instruction::CALLCODE: + require(7); + runGas = (bigint)c_callGas + m_stack[m_stack.size() - 1]; + newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); + break; + + case Instruction::CREATE: + { + require(3); + newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3]); + runGas = c_createGas; + break; + } + case Instruction::EXP: + { + require(2); + auto expon = m_stack[m_stack.size() - 2]; + runGas = c_expGas + c_expByteGas * (32 - (h256(expon).firstBitSet() / 8)); + break; + } + + case Instruction::BLOCKHASH: + require(1); + break; + + case Instruction::PC: + case Instruction::MSIZE: + case Instruction::GAS: + case Instruction::JUMPDEST: + case Instruction::ADDRESS: + case Instruction::ORIGIN: + case Instruction::CALLER: + case Instruction::CALLVALUE: + case Instruction::CALLDATASIZE: + case Instruction::CODESIZE: + case Instruction::GASPRICE: + case Instruction::COINBASE: + case Instruction::TIMESTAMP: + case Instruction::NUMBER: + case Instruction::DIFFICULTY: + case Instruction::GASLIMIT: + case Instruction::PUSH1: + case Instruction::PUSH2: + case Instruction::PUSH3: + case Instruction::PUSH4: + case Instruction::PUSH5: + case Instruction::PUSH6: + case Instruction::PUSH7: + case Instruction::PUSH8: + case Instruction::PUSH9: + case Instruction::PUSH10: + case Instruction::PUSH11: + case Instruction::PUSH12: + case Instruction::PUSH13: + case Instruction::PUSH14: + case Instruction::PUSH15: + case Instruction::PUSH16: + case Instruction::PUSH17: + case Instruction::PUSH18: + case Instruction::PUSH19: + case Instruction::PUSH20: + case Instruction::PUSH21: + case Instruction::PUSH22: + case Instruction::PUSH23: + case Instruction::PUSH24: + case Instruction::PUSH25: + case Instruction::PUSH26: + case Instruction::PUSH27: + case Instruction::PUSH28: + case Instruction::PUSH29: + case Instruction::PUSH30: + case Instruction::PUSH31: + case Instruction::PUSH32: + break; + case Instruction::NOT: + case Instruction::ISZERO: + case Instruction::CALLDATALOAD: + case Instruction::EXTCODESIZE: + case Instruction::POP: + case Instruction::JUMP: + require(1); + break; + case Instruction::ADD: + case Instruction::MUL: + case Instruction::SUB: + case Instruction::DIV: + case Instruction::SDIV: + case Instruction::MOD: + case Instruction::SMOD: + case Instruction::LT: + case Instruction::GT: + case Instruction::SLT: + case Instruction::SGT: + case Instruction::EQ: + case Instruction::AND: + case Instruction::OR: + case Instruction::XOR: + case Instruction::BYTE: + case Instruction::JUMPI: + case Instruction::SIGNEXTEND: + require(2); + break; + case Instruction::ADDMOD: + case Instruction::MULMOD: + require(3); + break; + case Instruction::DUP1: + case Instruction::DUP2: + case Instruction::DUP3: + case Instruction::DUP4: + case Instruction::DUP5: + case Instruction::DUP6: + case Instruction::DUP7: + case Instruction::DUP8: + case Instruction::DUP9: + case Instruction::DUP10: + case Instruction::DUP11: + case Instruction::DUP12: + case Instruction::DUP13: + case Instruction::DUP14: + case Instruction::DUP15: + case Instruction::DUP16: + require(1 + (int)inst - (int)Instruction::DUP1); + break; + case Instruction::SWAP1: + case Instruction::SWAP2: + case Instruction::SWAP3: + case Instruction::SWAP4: + case Instruction::SWAP5: + case Instruction::SWAP6: + case Instruction::SWAP7: + case Instruction::SWAP8: + case Instruction::SWAP9: + case Instruction::SWAP10: + case Instruction::SWAP11: + case Instruction::SWAP12: + case Instruction::SWAP13: + case Instruction::SWAP14: + case Instruction::SWAP15: + case Instruction::SWAP16: + require((int)inst - (int)Instruction::SWAP1 + 2); + break; + default: + BOOST_THROW_EXCEPTION(BadInstruction()); + } + + newTempSize = (newTempSize + 31) / 32 * 32; + if (newTempSize > m_temp.size()) + runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32; + runGas += c_copyGas * (copySize + 31) / 32; + + onOperation(); +// if (_onOp) +// _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext); + + if (m_gas < runGas) + { + // Out of gas! + m_gas = 0; + BOOST_THROW_EXCEPTION(OutOfGas()); + } + + m_gas = (u256)((bigint)m_gas - runGas); + + if (newTempSize > m_temp.size()) + m_temp.resize((size_t)newTempSize); + + // EXECUTE... + switch (inst) + { + case Instruction::ADD: + //pops two items and pushes S[-1] + S[-2] mod 2^256. + m_stack[m_stack.size() - 2] += m_stack.back(); + m_stack.pop_back(); + break; + case Instruction::MUL: + //pops two items and pushes S[-1] * S[-2] mod 2^256. + m_stack[m_stack.size() - 2] *= m_stack.back(); + m_stack.pop_back(); + break; + case Instruction::SUB: + m_stack[m_stack.size() - 2] = m_stack.back() - m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + break; + case Instruction::DIV: + m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? m_stack.back() / m_stack[m_stack.size() - 2] : 0; + m_stack.pop_back(); + break; + case Instruction::SDIV: + m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0; + m_stack.pop_back(); + break; + case Instruction::MOD: + m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? m_stack.back() % m_stack[m_stack.size() - 2] : 0; + m_stack.pop_back(); + break; + case Instruction::SMOD: + m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0; + m_stack.pop_back(); + break; + case Instruction::EXP: + { + auto base = m_stack.back(); + auto expon = m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256); + break; + } + case Instruction::NOT: + m_stack.back() = ~m_stack.back(); + break; + case Instruction::LT: + m_stack[m_stack.size() - 2] = m_stack.back() < m_stack[m_stack.size() - 2] ? 1 : 0; + m_stack.pop_back(); + break; + case Instruction::GT: + m_stack[m_stack.size() - 2] = m_stack.back() > m_stack[m_stack.size() - 2] ? 1 : 0; + m_stack.pop_back(); + break; + case Instruction::SLT: + m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; + m_stack.pop_back(); + break; + case Instruction::SGT: + m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; + m_stack.pop_back(); + break; + case Instruction::EQ: + m_stack[m_stack.size() - 2] = m_stack.back() == m_stack[m_stack.size() - 2] ? 1 : 0; + m_stack.pop_back(); + break; + case Instruction::ISZERO: + m_stack.back() = m_stack.back() ? 0 : 1; + break; + case Instruction::AND: + m_stack[m_stack.size() - 2] = m_stack.back() & m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + break; + case Instruction::OR: + m_stack[m_stack.size() - 2] = m_stack.back() | m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + break; + case Instruction::XOR: + m_stack[m_stack.size() - 2] = m_stack.back() ^ m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + break; + case Instruction::BYTE: + m_stack[m_stack.size() - 2] = m_stack.back() < 32 ? (m_stack[m_stack.size() - 2] >> (unsigned)(8 * (31 - m_stack.back()))) & 0xff : 0; + 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.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.pop_back(); + m_stack.pop_back(); + break; + case Instruction::SIGNEXTEND: + if (m_stack.back() < 31) + { + unsigned const testBit(m_stack.back() * 8 + 7); + u256& number = m_stack[m_stack.size() - 2]; + u256 mask = ((u256(1) << testBit) - 1); + if (boost::multiprecision::bit_test(number, testBit)) + number |= ~mask; + else + number &= mask; + } + m_stack.pop_back(); + break; + case Instruction::SHA3: + { + unsigned inOff = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned inSize = (unsigned)m_stack.back(); + m_stack.pop_back(); + m_stack.push_back(sha3(bytesConstRef(m_temp.data() + inOff, inSize))); + break; + } + case Instruction::ADDRESS: + m_stack.push_back(fromAddress(_ext.myAddress)); + break; + case Instruction::ORIGIN: + m_stack.push_back(fromAddress(_ext.origin)); + break; + case Instruction::BALANCE: + { + m_stack.back() = _ext.balance(asAddress(m_stack.back())); + break; + } + case Instruction::CALLER: + m_stack.push_back(fromAddress(_ext.caller)); + break; + case Instruction::CALLVALUE: + m_stack.push_back(_ext.value); + break; + case Instruction::CALLDATALOAD: + { + if ((unsigned)m_stack.back() + (uint64_t)31 < _ext.data.size()) + m_stack.back() = (u256)*(h256 const*)(_ext.data.data() + (unsigned)m_stack.back()); + else + { + h256 r; + for (uint64_t i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + (uint64_t)32, j = 0; i < e; ++i, ++j) + r[j] = i < _ext.data.size() ? _ext.data[i] : 0; + m_stack.back() = (u256)r; + } + break; + } + case Instruction::CALLDATASIZE: + m_stack.push_back(_ext.data.size()); + break; + case Instruction::CODESIZE: + m_stack.push_back(_ext.code.size()); + break; + case Instruction::EXTCODESIZE: + m_stack.back() = _ext.codeAt(asAddress(m_stack.back())).size(); + break; + case Instruction::CALLDATACOPY: + case Instruction::CODECOPY: + case Instruction::EXTCODECOPY: + { + Address a; + if (inst == Instruction::EXTCODECOPY) + { + a = asAddress(m_stack.back()); + m_stack.pop_back(); + } + unsigned offset = (unsigned)m_stack.back(); + m_stack.pop_back(); + u256 index = m_stack.back(); + m_stack.pop_back(); + unsigned size = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned sizeToBeCopied; + switch(inst) + { + case Instruction::CALLDATACOPY: + sizeToBeCopied = index + (bigint)size > (u256)_ext.data.size() ? (u256)_ext.data.size() < index ? 0 : _ext.data.size() - (unsigned)index : size; + memcpy(m_temp.data() + offset, _ext.data.data() + (unsigned)index, sizeToBeCopied); + break; + case Instruction::CODECOPY: + sizeToBeCopied = index + (bigint)size > (u256)_ext.code.size() ? (u256)_ext.code.size() < index ? 0 : _ext.code.size() - (unsigned)index : size; + memcpy(m_temp.data() + offset, _ext.code.data() + (unsigned)index, sizeToBeCopied); + break; + case Instruction::EXTCODECOPY: + sizeToBeCopied = index + (bigint)size > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < index ? 0 : _ext.codeAt(a).size() - (unsigned)index : size; + memcpy(m_temp.data() + offset, _ext.codeAt(a).data() + (unsigned)index, sizeToBeCopied); + break; + default: + // this is unreachable, but if someone introduces a bug in the future, he may get here. + assert(false); + BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("CALLDATACOPY, CODECOPY or EXTCODECOPY instruction requested.")); + break; + } + memset(m_temp.data() + offset + sizeToBeCopied, 0, size - sizeToBeCopied); + break; + } + case Instruction::GASPRICE: + m_stack.push_back(_ext.gasPrice); + break; + case Instruction::BLOCKHASH: + m_stack.back() = (u256)_ext.blockhash(m_stack.back()); + break; + case Instruction::COINBASE: + m_stack.push_back((u160)_ext.currentBlock.coinbaseAddress); + break; + case Instruction::TIMESTAMP: + m_stack.push_back(_ext.currentBlock.timestamp); + break; + case Instruction::NUMBER: + m_stack.push_back(_ext.currentBlock.number); + break; + case Instruction::DIFFICULTY: + m_stack.push_back(_ext.currentBlock.difficulty); + break; + case Instruction::GASLIMIT: + m_stack.push_back(1000000); + break; + case Instruction::PUSH1: + case Instruction::PUSH2: + case Instruction::PUSH3: + case Instruction::PUSH4: + case Instruction::PUSH5: + case Instruction::PUSH6: + case Instruction::PUSH7: + case Instruction::PUSH8: + case Instruction::PUSH9: + case Instruction::PUSH10: + case Instruction::PUSH11: + case Instruction::PUSH12: + case Instruction::PUSH13: + case Instruction::PUSH14: + case Instruction::PUSH15: + case Instruction::PUSH16: + case Instruction::PUSH17: + case Instruction::PUSH18: + case Instruction::PUSH19: + case Instruction::PUSH20: + case Instruction::PUSH21: + case Instruction::PUSH22: + case Instruction::PUSH23: + case Instruction::PUSH24: + case Instruction::PUSH25: + case Instruction::PUSH26: + case Instruction::PUSH27: + case Instruction::PUSH28: + case Instruction::PUSH29: + case Instruction::PUSH30: + case Instruction::PUSH31: + case Instruction::PUSH32: + { + int i = (int)inst - (int)Instruction::PUSH1 + 1; + nextPC = m_curPC + 1; + m_stack.push_back(0); + for (; i--; nextPC++) + m_stack.back() = (m_stack.back() << 8) | _ext.getCode(nextPC); + break; + } + case Instruction::POP: + m_stack.pop_back(); + break; + case Instruction::DUP1: + case Instruction::DUP2: + case Instruction::DUP3: + case Instruction::DUP4: + case Instruction::DUP5: + case Instruction::DUP6: + case Instruction::DUP7: + case Instruction::DUP8: + case Instruction::DUP9: + case Instruction::DUP10: + case Instruction::DUP11: + case Instruction::DUP12: + case Instruction::DUP13: + case Instruction::DUP14: + case Instruction::DUP15: + case Instruction::DUP16: + { + auto n = 1 + (int)inst - (int)Instruction::DUP1; + m_stack.push_back(m_stack[m_stack.size() - n]); + break; + } + case Instruction::SWAP1: + case Instruction::SWAP2: + case Instruction::SWAP3: + case Instruction::SWAP4: + case Instruction::SWAP5: + case Instruction::SWAP6: + case Instruction::SWAP7: + case Instruction::SWAP8: + case Instruction::SWAP9: + case Instruction::SWAP10: + case Instruction::SWAP11: + case Instruction::SWAP12: + case Instruction::SWAP13: + case Instruction::SWAP14: + case Instruction::SWAP15: + case Instruction::SWAP16: + { + unsigned n = (int)inst - (int)Instruction::SWAP1 + 2; + auto d = m_stack.back(); + m_stack.back() = m_stack[m_stack.size() - n]; + m_stack[m_stack.size() - n] = d; + break; + } + case Instruction::MLOAD: + { + m_stack.back() = (u256)*(h256 const*)(m_temp.data() + (unsigned)m_stack.back()); + break; + } + case Instruction::MSTORE: + { + *(h256*)&m_temp[(unsigned)m_stack.back()] = (h256)m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + m_stack.pop_back(); + break; + } + case Instruction::MSTORE8: + { + m_temp[(unsigned)m_stack.back()] = (byte)(m_stack[m_stack.size() - 2] & 0xff); + m_stack.pop_back(); + m_stack.pop_back(); + break; + } + case Instruction::SLOAD: + m_stack.back() = _ext.store(m_stack.back()); + break; + case Instruction::SSTORE: + _ext.setStore(m_stack.back(), m_stack[m_stack.size() - 2]); + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::JUMP: + nextPC = m_stack.back(); + if (!m_jumpDests.count(nextPC)) + BOOST_THROW_EXCEPTION(BadJumpDestination()); + m_stack.pop_back(); + break; + case Instruction::JUMPI: + if (m_stack[m_stack.size() - 2]) + { + nextPC = m_stack.back(); + if (!m_jumpDests.count(nextPC)) + BOOST_THROW_EXCEPTION(BadJumpDestination()); + } + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::PC: + m_stack.push_back(m_curPC); + break; + case Instruction::MSIZE: + m_stack.push_back(m_temp.size()); + break; + case Instruction::GAS: + m_stack.push_back(m_gas); + break; + case Instruction::JUMPDEST: + break; +/* case Instruction::LOG0: + _ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + break; + case Instruction::LOG1: + _ext.log({m_stack[m_stack.size() - 1]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 2], (unsigned)m_stack[m_stack.size() - 3])); + break; + case Instruction::LOG2: + _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 3], (unsigned)m_stack[m_stack.size() - 4])); + break; + case Instruction::LOG3: + _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 4], (unsigned)m_stack[m_stack.size() - 5])); + break; + case Instruction::LOG4: + _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 5], (unsigned)m_stack[m_stack.size() - 6])); + break;*/ + case Instruction::LOG0: + _ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::LOG1: + _ext.log({m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + m_stack.pop_back(); + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::LOG2: + _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + m_stack.pop_back(); + m_stack.pop_back(); + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::LOG3: + _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + m_stack.pop_back(); + m_stack.pop_back(); + m_stack.pop_back(); + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::LOG4: + _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5], m_stack[m_stack.size() - 6]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + m_stack.pop_back(); + m_stack.pop_back(); + m_stack.pop_back(); + m_stack.pop_back(); + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::CREATE: + { + u256 endowment = m_stack.back(); + m_stack.pop_back(); + unsigned initOff = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned initSize = (unsigned)m_stack.back(); + m_stack.pop_back(); + + if (_ext.balance(_ext.myAddress) >= endowment && _ext.depth < 1024) + { + _ext.subBalance(endowment); + m_stack.push_back((u160)_ext.create(endowment, m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp)); + } + else + m_stack.push_back(0); + break; + } + case Instruction::CALL: + case Instruction::CALLCODE: + { + u256 gas = m_stack.back(); + m_stack.pop_back(); + Address receiveAddress = asAddress(m_stack.back()); + m_stack.pop_back(); + u256 value = m_stack.back(); + m_stack.pop_back(); + + unsigned inOff = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned inSize = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned outOff = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned outSize = (unsigned)m_stack.back(); + m_stack.pop_back(); + + if (_ext.balance(_ext.myAddress) >= value && _ext.depth < 1024) + { + _ext.subBalance(value); + m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, {}, receiveAddress)); + } + else + m_stack.push_back(0); + + m_gas += gas; + break; + } + case Instruction::RETURN: + { + unsigned b = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned s = (unsigned)m_stack.back(); + m_stack.pop_back(); + + return bytesConstRef(m_temp.data() + b, s); + } + case Instruction::SUICIDE: + { + Address dest = asAddress(m_stack.back()); + _ext.suicide(dest); + // ...follow through to... + } + case Instruction::STOP: + return bytesConstRef(); + } + } + if (_steps == (uint64_t)-1) + BOOST_THROW_EXCEPTION(StepsDone()); + return bytesConstRef(); +} diff --git a/libevm/VM.h b/libevm/VM.h index b8a33909c..ecf5de292 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -77,768 +77,5 @@ private: std::function m_onFail; }; -// TODO: Move it to cpp file. Not done to make review easier. -inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) -{ - auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; - - if (m_jumpDests.empty()) - for (unsigned i = 0; i < _ext.code.size(); ++i) - if (_ext.code[i] == (byte)Instruction::JUMPDEST) - m_jumpDests.insert(i); - else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32) - i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1; - u256 nextPC = m_curPC + 1; - auto osteps = _steps; - for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1) - { - // INSTRUCTION... - Instruction inst = (Instruction)_ext.getCode(m_curPC); - - // FEES... - bigint runGas = c_stepGas; - bigint newTempSize = m_temp.size(); - bigint copySize = 0; - - auto onOperation = [&]() - { - if (_onOp) - _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext); - }; - // should work, but just seems to result in immediate errorless exit on initial execution. yeah. weird. - //m_onFail = std::function(onOperation); - - switch (inst) - { - case Instruction::STOP: - runGas = 0; - break; - - case Instruction::SUICIDE: - require(1); - runGas = 0; - break; - - case Instruction::SSTORE: - require(2); - if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2]) - runGas = c_sstoreSetGas; - else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) - { - runGas = 0; - _ext.sub.refunds += c_sstoreRefundGas; - } - else - runGas = c_sstoreResetGas; - break; - - case Instruction::SLOAD: - require(1); - runGas = c_sloadGas; - break; - - // These all operate on memory and therefore potentially expand it: - case Instruction::MSTORE: - require(2); - newTempSize = (bigint)m_stack.back() + 32; - break; - case Instruction::MSTORE8: - require(2); - newTempSize = (bigint)m_stack.back() + 1; - break; - case Instruction::MLOAD: - require(1); - newTempSize = (bigint)m_stack.back() + 32; - break; - case Instruction::RETURN: - require(2); - newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); - break; - case Instruction::SHA3: - require(2); - runGas = c_sha3Gas + (m_stack[m_stack.size() - 2] + 31) / 32 * c_sha3WordGas; - newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); - break; - case Instruction::CALLDATACOPY: - require(3); - copySize = m_stack[m_stack.size() - 3]; - newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); - break; - case Instruction::CODECOPY: - require(3); - copySize = m_stack[m_stack.size() - 3]; - newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); - break; - case Instruction::EXTCODECOPY: - require(4); - copySize = m_stack[m_stack.size() - 4]; - newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]); - break; - - case Instruction::BALANCE: - require(1); - runGas = c_balanceGas; - break; - case Instruction::LOG0: - case Instruction::LOG1: - case Instruction::LOG2: - case Instruction::LOG3: - case Instruction::LOG4: - { - unsigned n = (unsigned)inst - (unsigned)Instruction::LOG0; - require(n + 2); - runGas = c_logGas + c_logTopicGas * n + (bigint)c_logDataGas * m_stack[m_stack.size() - 2]; - newTempSize = memNeed(m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]); - break; - } - - case Instruction::CALL: - case Instruction::CALLCODE: - require(7); - runGas = (bigint)c_callGas + m_stack[m_stack.size() - 1]; - newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); - break; - - case Instruction::CREATE: - { - require(3); - u256 inOff = m_stack[m_stack.size() - 2]; - u256 inSize = m_stack[m_stack.size() - 3]; - newTempSize = (bigint)inOff + inSize; - runGas = c_createGas; - break; - } - case Instruction::EXP: - { - require(2); - auto expon = m_stack[m_stack.size() - 2]; - runGas = c_expGas + c_expByteGas * (32 - (h256(expon).firstBitSet() / 8)); - break; - } - - case Instruction::BLOCKHASH: - require(1); - break; - - case Instruction::PC: - case Instruction::MSIZE: - case Instruction::GAS: - case Instruction::JUMPDEST: - case Instruction::ADDRESS: - case Instruction::ORIGIN: - case Instruction::CALLER: - case Instruction::CALLVALUE: - case Instruction::CALLDATASIZE: - case Instruction::CODESIZE: - case Instruction::GASPRICE: - case Instruction::COINBASE: - case Instruction::TIMESTAMP: - case Instruction::NUMBER: - case Instruction::DIFFICULTY: - case Instruction::GASLIMIT: - case Instruction::PUSH1: - case Instruction::PUSH2: - case Instruction::PUSH3: - case Instruction::PUSH4: - case Instruction::PUSH5: - case Instruction::PUSH6: - case Instruction::PUSH7: - case Instruction::PUSH8: - case Instruction::PUSH9: - case Instruction::PUSH10: - case Instruction::PUSH11: - case Instruction::PUSH12: - case Instruction::PUSH13: - case Instruction::PUSH14: - case Instruction::PUSH15: - case Instruction::PUSH16: - case Instruction::PUSH17: - case Instruction::PUSH18: - case Instruction::PUSH19: - case Instruction::PUSH20: - case Instruction::PUSH21: - case Instruction::PUSH22: - case Instruction::PUSH23: - case Instruction::PUSH24: - case Instruction::PUSH25: - case Instruction::PUSH26: - case Instruction::PUSH27: - case Instruction::PUSH28: - case Instruction::PUSH29: - case Instruction::PUSH30: - case Instruction::PUSH31: - case Instruction::PUSH32: - break; - case Instruction::NOT: - case Instruction::ISZERO: - case Instruction::CALLDATALOAD: - case Instruction::EXTCODESIZE: - case Instruction::POP: - case Instruction::JUMP: - require(1); - break; - case Instruction::ADD: - case Instruction::MUL: - case Instruction::SUB: - case Instruction::DIV: - case Instruction::SDIV: - case Instruction::MOD: - case Instruction::SMOD: - case Instruction::LT: - case Instruction::GT: - case Instruction::SLT: - case Instruction::SGT: - case Instruction::EQ: - case Instruction::AND: - case Instruction::OR: - case Instruction::XOR: - case Instruction::BYTE: - case Instruction::JUMPI: - case Instruction::SIGNEXTEND: - require(2); - break; - case Instruction::ADDMOD: - case Instruction::MULMOD: - require(3); - break; - case Instruction::DUP1: - case Instruction::DUP2: - case Instruction::DUP3: - case Instruction::DUP4: - case Instruction::DUP5: - case Instruction::DUP6: - case Instruction::DUP7: - case Instruction::DUP8: - case Instruction::DUP9: - case Instruction::DUP10: - case Instruction::DUP11: - case Instruction::DUP12: - case Instruction::DUP13: - case Instruction::DUP14: - case Instruction::DUP15: - case Instruction::DUP16: - require(1 + (int)inst - (int)Instruction::DUP1); - break; - case Instruction::SWAP1: - case Instruction::SWAP2: - case Instruction::SWAP3: - case Instruction::SWAP4: - case Instruction::SWAP5: - case Instruction::SWAP6: - case Instruction::SWAP7: - case Instruction::SWAP8: - case Instruction::SWAP9: - case Instruction::SWAP10: - case Instruction::SWAP11: - case Instruction::SWAP12: - case Instruction::SWAP13: - case Instruction::SWAP14: - case Instruction::SWAP15: - case Instruction::SWAP16: - require((int)inst - (int)Instruction::SWAP1 + 2); - break; - default: - BOOST_THROW_EXCEPTION(BadInstruction()); - } - - newTempSize = (newTempSize + 31) / 32 * 32; - if (newTempSize > m_temp.size()) - runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32; - runGas += c_copyGas * (copySize + 31) / 32; - - onOperation(); -// if (_onOp) -// _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext); - - if (m_gas < runGas) - { - // Out of gas! - m_gas = 0; - BOOST_THROW_EXCEPTION(OutOfGas()); - } - - m_gas = (u256)((bigint)m_gas - runGas); - - if (newTempSize > m_temp.size()) - m_temp.resize((size_t)newTempSize); - - // EXECUTE... - switch (inst) - { - case Instruction::ADD: - //pops two items and pushes S[-1] + S[-2] mod 2^256. - m_stack[m_stack.size() - 2] += m_stack.back(); - m_stack.pop_back(); - break; - case Instruction::MUL: - //pops two items and pushes S[-1] * S[-2] mod 2^256. - m_stack[m_stack.size() - 2] *= m_stack.back(); - m_stack.pop_back(); - break; - case Instruction::SUB: - m_stack[m_stack.size() - 2] = m_stack.back() - m_stack[m_stack.size() - 2]; - m_stack.pop_back(); - break; - case Instruction::DIV: - m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? m_stack.back() / m_stack[m_stack.size() - 2] : 0; - m_stack.pop_back(); - break; - case Instruction::SDIV: - m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0; - m_stack.pop_back(); - break; - case Instruction::MOD: - m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? m_stack.back() % m_stack[m_stack.size() - 2] : 0; - m_stack.pop_back(); - break; - case Instruction::SMOD: - m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0; - m_stack.pop_back(); - break; - case Instruction::EXP: - { - auto base = m_stack.back(); - auto expon = m_stack[m_stack.size() - 2]; - m_stack.pop_back(); - m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256); - break; - } - case Instruction::NOT: - m_stack.back() = ~m_stack.back(); - break; - case Instruction::LT: - m_stack[m_stack.size() - 2] = m_stack.back() < m_stack[m_stack.size() - 2] ? 1 : 0; - m_stack.pop_back(); - break; - case Instruction::GT: - m_stack[m_stack.size() - 2] = m_stack.back() > m_stack[m_stack.size() - 2] ? 1 : 0; - m_stack.pop_back(); - break; - case Instruction::SLT: - m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; - m_stack.pop_back(); - break; - case Instruction::SGT: - m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; - m_stack.pop_back(); - break; - case Instruction::EQ: - m_stack[m_stack.size() - 2] = m_stack.back() == m_stack[m_stack.size() - 2] ? 1 : 0; - m_stack.pop_back(); - break; - case Instruction::ISZERO: - m_stack.back() = m_stack.back() ? 0 : 1; - break; - case Instruction::AND: - m_stack[m_stack.size() - 2] = m_stack.back() & m_stack[m_stack.size() - 2]; - m_stack.pop_back(); - break; - case Instruction::OR: - m_stack[m_stack.size() - 2] = m_stack.back() | m_stack[m_stack.size() - 2]; - m_stack.pop_back(); - break; - case Instruction::XOR: - m_stack[m_stack.size() - 2] = m_stack.back() ^ m_stack[m_stack.size() - 2]; - m_stack.pop_back(); - break; - case Instruction::BYTE: - m_stack[m_stack.size() - 2] = m_stack.back() < 32 ? (m_stack[m_stack.size() - 2] >> (unsigned)(8 * (31 - m_stack.back()))) & 0xff : 0; - 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.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.pop_back(); - m_stack.pop_back(); - break; - case Instruction::SIGNEXTEND: - if (m_stack.back() < 31) - { - unsigned const testBit(m_stack.back() * 8 + 7); - u256& number = m_stack[m_stack.size() - 2]; - u256 mask = ((u256(1) << testBit) - 1); - if (boost::multiprecision::bit_test(number, testBit)) - number |= ~mask; - else - number &= mask; - } - m_stack.pop_back(); - break; - case Instruction::SHA3: - { - unsigned inOff = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned inSize = (unsigned)m_stack.back(); - m_stack.pop_back(); - m_stack.push_back(sha3(bytesConstRef(m_temp.data() + inOff, inSize))); - break; - } - case Instruction::ADDRESS: - m_stack.push_back(fromAddress(_ext.myAddress)); - break; - case Instruction::ORIGIN: - m_stack.push_back(fromAddress(_ext.origin)); - break; - case Instruction::BALANCE: - { - m_stack.back() = _ext.balance(asAddress(m_stack.back())); - break; - } - case Instruction::CALLER: - m_stack.push_back(fromAddress(_ext.caller)); - break; - case Instruction::CALLVALUE: - m_stack.push_back(_ext.value); - break; - case Instruction::CALLDATALOAD: - { - if ((unsigned)m_stack.back() + (uint64_t)31 < _ext.data.size()) - m_stack.back() = (u256)*(h256 const*)(_ext.data.data() + (unsigned)m_stack.back()); - else - { - h256 r; - for (uint64_t i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + (uint64_t)32, j = 0; i < e; ++i, ++j) - r[j] = i < _ext.data.size() ? _ext.data[i] : 0; - m_stack.back() = (u256)r; - } - break; - } - case Instruction::CALLDATASIZE: - m_stack.push_back(_ext.data.size()); - break; - case Instruction::CODESIZE: - m_stack.push_back(_ext.code.size()); - break; - case Instruction::EXTCODESIZE: - m_stack.back() = _ext.codeAt(asAddress(m_stack.back())).size(); - break; - case Instruction::CALLDATACOPY: - case Instruction::CODECOPY: - case Instruction::EXTCODECOPY: - { - Address a; - if (inst == Instruction::EXTCODECOPY) - { - a = asAddress(m_stack.back()); - m_stack.pop_back(); - } - unsigned offset = (unsigned)m_stack.back(); - m_stack.pop_back(); - u256 index = m_stack.back(); - m_stack.pop_back(); - unsigned size = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned sizeToBeCopied; - switch(inst) - { - case Instruction::CALLDATACOPY: - sizeToBeCopied = index + (bigint)size > (u256)_ext.data.size() ? (u256)_ext.data.size() < index ? 0 : _ext.data.size() - (unsigned)index : size; - memcpy(m_temp.data() + offset, _ext.data.data() + (unsigned)index, sizeToBeCopied); - break; - case Instruction::CODECOPY: - sizeToBeCopied = index + (bigint)size > (u256)_ext.code.size() ? (u256)_ext.code.size() < index ? 0 : _ext.code.size() - (unsigned)index : size; - memcpy(m_temp.data() + offset, _ext.code.data() + (unsigned)index, sizeToBeCopied); - break; - case Instruction::EXTCODECOPY: - sizeToBeCopied = index + (bigint)size > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < index ? 0 : _ext.codeAt(a).size() - (unsigned)index : size; - memcpy(m_temp.data() + offset, _ext.codeAt(a).data() + (unsigned)index, sizeToBeCopied); - break; - default: - // this is unreachable, but if someone introduces a bug in the future, he may get here. - assert(false); - BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("CALLDATACOPY, CODECOPY or EXTCODECOPY instruction requested.")); - break; - } - memset(m_temp.data() + offset + sizeToBeCopied, 0, size - sizeToBeCopied); - break; - } - case Instruction::GASPRICE: - m_stack.push_back(_ext.gasPrice); - break; - case Instruction::BLOCKHASH: - m_stack.back() = (u256)_ext.blockhash(m_stack.back()); - break; - case Instruction::COINBASE: - m_stack.push_back((u160)_ext.currentBlock.coinbaseAddress); - break; - case Instruction::TIMESTAMP: - m_stack.push_back(_ext.currentBlock.timestamp); - break; - case Instruction::NUMBER: - m_stack.push_back(_ext.currentBlock.number); - break; - case Instruction::DIFFICULTY: - m_stack.push_back(_ext.currentBlock.difficulty); - break; - case Instruction::GASLIMIT: - m_stack.push_back(1000000); - break; - case Instruction::PUSH1: - case Instruction::PUSH2: - case Instruction::PUSH3: - case Instruction::PUSH4: - case Instruction::PUSH5: - case Instruction::PUSH6: - case Instruction::PUSH7: - case Instruction::PUSH8: - case Instruction::PUSH9: - case Instruction::PUSH10: - case Instruction::PUSH11: - case Instruction::PUSH12: - case Instruction::PUSH13: - case Instruction::PUSH14: - case Instruction::PUSH15: - case Instruction::PUSH16: - case Instruction::PUSH17: - case Instruction::PUSH18: - case Instruction::PUSH19: - case Instruction::PUSH20: - case Instruction::PUSH21: - case Instruction::PUSH22: - case Instruction::PUSH23: - case Instruction::PUSH24: - case Instruction::PUSH25: - case Instruction::PUSH26: - case Instruction::PUSH27: - case Instruction::PUSH28: - case Instruction::PUSH29: - case Instruction::PUSH30: - case Instruction::PUSH31: - case Instruction::PUSH32: - { - int i = (int)inst - (int)Instruction::PUSH1 + 1; - nextPC = m_curPC + 1; - m_stack.push_back(0); - for (; i--; nextPC++) - m_stack.back() = (m_stack.back() << 8) | _ext.getCode(nextPC); - break; - } - case Instruction::POP: - m_stack.pop_back(); - break; - case Instruction::DUP1: - case Instruction::DUP2: - case Instruction::DUP3: - case Instruction::DUP4: - case Instruction::DUP5: - case Instruction::DUP6: - case Instruction::DUP7: - case Instruction::DUP8: - case Instruction::DUP9: - case Instruction::DUP10: - case Instruction::DUP11: - case Instruction::DUP12: - case Instruction::DUP13: - case Instruction::DUP14: - case Instruction::DUP15: - case Instruction::DUP16: - { - auto n = 1 + (int)inst - (int)Instruction::DUP1; - m_stack.push_back(m_stack[m_stack.size() - n]); - break; - } - case Instruction::SWAP1: - case Instruction::SWAP2: - case Instruction::SWAP3: - case Instruction::SWAP4: - case Instruction::SWAP5: - case Instruction::SWAP6: - case Instruction::SWAP7: - case Instruction::SWAP8: - case Instruction::SWAP9: - case Instruction::SWAP10: - case Instruction::SWAP11: - case Instruction::SWAP12: - case Instruction::SWAP13: - case Instruction::SWAP14: - case Instruction::SWAP15: - case Instruction::SWAP16: - { - unsigned n = (int)inst - (int)Instruction::SWAP1 + 2; - auto d = m_stack.back(); - m_stack.back() = m_stack[m_stack.size() - n]; - m_stack[m_stack.size() - n] = d; - break; - } - case Instruction::MLOAD: - { - m_stack.back() = (u256)*(h256 const*)(m_temp.data() + (unsigned)m_stack.back()); - break; - } - case Instruction::MSTORE: - { - *(h256*)&m_temp[(unsigned)m_stack.back()] = (h256)m_stack[m_stack.size() - 2]; - m_stack.pop_back(); - m_stack.pop_back(); - break; - } - case Instruction::MSTORE8: - { - m_temp[(unsigned)m_stack.back()] = (byte)(m_stack[m_stack.size() - 2] & 0xff); - m_stack.pop_back(); - m_stack.pop_back(); - break; - } - case Instruction::SLOAD: - m_stack.back() = _ext.store(m_stack.back()); - break; - case Instruction::SSTORE: - _ext.setStore(m_stack.back(), m_stack[m_stack.size() - 2]); - m_stack.pop_back(); - m_stack.pop_back(); - break; - case Instruction::JUMP: - nextPC = m_stack.back(); - if (!m_jumpDests.count(nextPC)) - BOOST_THROW_EXCEPTION(BadJumpDestination()); - m_stack.pop_back(); - break; - case Instruction::JUMPI: - if (m_stack[m_stack.size() - 2]) - { - nextPC = m_stack.back(); - if (!m_jumpDests.count(nextPC)) - BOOST_THROW_EXCEPTION(BadJumpDestination()); - } - m_stack.pop_back(); - m_stack.pop_back(); - break; - case Instruction::PC: - m_stack.push_back(m_curPC); - break; - case Instruction::MSIZE: - m_stack.push_back(m_temp.size()); - break; - case Instruction::GAS: - m_stack.push_back(m_gas); - break; - case Instruction::JUMPDEST: - break; -/* case Instruction::LOG0: - _ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); - break; - case Instruction::LOG1: - _ext.log({m_stack[m_stack.size() - 1]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 2], (unsigned)m_stack[m_stack.size() - 3])); - break; - case Instruction::LOG2: - _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 3], (unsigned)m_stack[m_stack.size() - 4])); - break; - case Instruction::LOG3: - _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 4], (unsigned)m_stack[m_stack.size() - 5])); - break; - case Instruction::LOG4: - _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 5], (unsigned)m_stack[m_stack.size() - 6])); - break;*/ - case Instruction::LOG0: - _ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); - m_stack.pop_back(); - m_stack.pop_back(); - break; - case Instruction::LOG1: - _ext.log({m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); - m_stack.pop_back(); - m_stack.pop_back(); - m_stack.pop_back(); - break; - case Instruction::LOG2: - _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); - m_stack.pop_back(); - m_stack.pop_back(); - m_stack.pop_back(); - m_stack.pop_back(); - break; - case Instruction::LOG3: - _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); - m_stack.pop_back(); - m_stack.pop_back(); - m_stack.pop_back(); - m_stack.pop_back(); - m_stack.pop_back(); - break; - case Instruction::LOG4: - _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5], m_stack[m_stack.size() - 6]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); - m_stack.pop_back(); - m_stack.pop_back(); - m_stack.pop_back(); - m_stack.pop_back(); - m_stack.pop_back(); - m_stack.pop_back(); - break; - case Instruction::CREATE: - { - u256 endowment = m_stack.back(); - m_stack.pop_back(); - unsigned initOff = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned initSize = (unsigned)m_stack.back(); - m_stack.pop_back(); - - if (_ext.balance(_ext.myAddress) >= endowment && _ext.depth < 1024) - { - _ext.subBalance(endowment); - m_stack.push_back((u160)_ext.create(endowment, m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp)); - } - else - m_stack.push_back(0); - break; - } - case Instruction::CALL: - case Instruction::CALLCODE: - { - u256 gas = m_stack.back(); - m_stack.pop_back(); - Address receiveAddress = asAddress(m_stack.back()); - m_stack.pop_back(); - u256 value = m_stack.back(); - m_stack.pop_back(); - - unsigned inOff = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned inSize = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned outOff = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned outSize = (unsigned)m_stack.back(); - m_stack.pop_back(); - - if (_ext.balance(_ext.myAddress) >= value && _ext.depth < 1024) - { - _ext.subBalance(value); - m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, {}, receiveAddress)); - } - else - m_stack.push_back(0); - - m_gas += gas; - break; - } - case Instruction::RETURN: - { - unsigned b = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned s = (unsigned)m_stack.back(); - m_stack.pop_back(); - - return bytesConstRef(m_temp.data() + b, s); - } - case Instruction::SUICIDE: - { - Address dest = asAddress(m_stack.back()); - _ext.suicide(dest); - // ...follow through to... - } - case Instruction::STOP: - return bytesConstRef(); - } - } - if (_steps == (uint64_t)-1) - BOOST_THROW_EXCEPTION(StepsDone()); - return bytesConstRef(); -} - } } diff --git a/libevmcore/Instruction.h b/libevmcore/Instruction.h index a28e8f8da..5cf002c4c 100644 --- a/libevmcore/Instruction.h +++ b/libevmcore/Instruction.h @@ -215,6 +215,14 @@ inline Instruction swapInstruction(unsigned _number) return Instruction(unsigned(Instruction::SWAP1) + _number - 1); } +/// @returns the LOG<_number> instruction +inline Instruction logInstruction(unsigned _number) +{ + if (asserts(_number <= 4)) + BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("Invalid LOG instruction requested.")); + return Instruction(unsigned(Instruction::LOG0) + _number); +} + /// Information structure for a particular instruction. struct InstructionInfo { diff --git a/libjsqrc/CMakeLists.txt b/libjsqrc/CMakeLists.txt index a6dbf023b..2635bc558 100644 --- a/libjsqrc/CMakeLists.txt +++ b/libjsqrc/CMakeLists.txt @@ -12,4 +12,14 @@ qt5_add_resources(JSQRC js.qrc) add_library(jsqrc STATIC ${JSQRC}) target_link_libraries(jsqrc Qt5::Core) +if (ETH_NODE AND ETH_NPM) + add_custom_target(ethereumjs) + add_custom_command(TARGET ethereumjs + POST_BUILD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND bash compilejs.sh ${ETH_NPM_DIRECTORY} ${ETH_NODE_DIRECTORY} + ) + add_dependencies(jsqrc ethereumjs) +endif() + install( TARGETS jsqrc ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) diff --git a/libjsqrc/compilejs.sh b/libjsqrc/compilejs.sh new file mode 100644 index 000000000..ba99e2415 --- /dev/null +++ b/libjsqrc/compilejs.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cd ethereumjs +export PATH=$PATH:$1:$2 +npm install +npm run-script build + diff --git a/libjsqrc/ethereumjs/.travis.yml b/libjsqrc/ethereumjs/.travis.yml index fafacbd5a..83b21d840 100644 --- a/libjsqrc/ethereumjs/.travis.yml +++ b/libjsqrc/ethereumjs/.travis.yml @@ -8,4 +8,6 @@ before_script: script: - "jshint *.js lib" after_script: - - npm run-script gulp + - npm run-script build + - npm test + diff --git a/libjsqrc/ethereumjs/README.md b/libjsqrc/ethereumjs/README.md index 865b62c6b..f18d382bd 100644 --- a/libjsqrc/ethereumjs/README.md +++ b/libjsqrc/ethereumjs/README.md @@ -50,13 +50,35 @@ web3.eth.coinbase.then(function(result){ For another example see `example/index.html`. +## Contribute! + +### Requirements + +* Node.js +* npm +* gulp (build) +* mocha (tests) + +```bash +sudo apt-get update +sudo apt-get install nodejs +sudo apt-get install npm +sudo apt-get install nodejs-legacy +``` + ## Building -* `gulp build` +```bash (gulp) +npm run-script build +``` ### Testing +```bash (mocha) +npm test +``` + **Please note this repo is in it's early stage.** If you'd like to run a WebSocket ethereum node check out @@ -76,4 +98,4 @@ ethereum -ws -loglevel=4 [dep-image]: https://david-dm.org/ethereum/ethereum.js.svg [dep-url]: https://david-dm.org/ethereum/ethereum.js [dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg -[dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies \ No newline at end of file +[dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies diff --git a/libjsqrc/ethereumjs/dist/ethereum.js b/libjsqrc/ethereumjs/dist/ethereum.js index e496c30f4..400a66815 100644 --- a/libjsqrc/ethereumjs/dist/ethereum.js +++ b/libjsqrc/ethereumjs/dist/ethereum.js @@ -93,6 +93,12 @@ var setupInputTypes = function () { } var padding = calcPadding(type, expected); + if (padding > 32) + return false; // not allowed to be so big. + padding = 32; // override as per the new ABI. + + if (prefix === "string") + return web3.fromAscii(value, padding).substr(2); if (typeof value === "number") value = value.toString(16); else if (typeof value === "string") @@ -111,6 +117,8 @@ var setupInputTypes = function () { return false; } + padding = 32; //override as per the new ABI. + return padLeft(formatter ? formatter(value) : value, padding * 2); }; }; @@ -141,7 +149,6 @@ var toAbiInput = function (json, methodName, params) { return; } - bytes = "0x" + padLeft(index.toString(16), 2); var method = json[index]; for (var i = 0; i < method.inputs.length; i++) { @@ -167,12 +174,16 @@ var setupOutputTypes = function () { } var padding = calcPadding(type, expected); + if (padding > 32) + return -1; // not allowed to be so big. + padding = 32; // override as per the new ABI. return padding * 2; }; }; var namedType = function (name, padding) { return function (type) { + padding = 32; // override as per the new ABI. return name === type ? padding * 2 : -1; }; }; @@ -260,11 +271,25 @@ var outputParser = function (json) { return parser; }; +var methodSignature = function (json, name) { + var method = json[findMethodIndex(json, name)]; + var result = name + '('; + var inputTypes = method.inputs.map(function (inp) { + return inp.type; + }); + result += inputTypes.join(','); + result += ')'; + + return web3.sha3(web3.fromAscii(result)); +}; + module.exports = { inputParser: inputParser, - outputParser: outputParser + outputParser: outputParser, + methodSignature: methodSignature }; + },{}],2:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -298,7 +323,7 @@ module.exports = { // TODO: is these line is supposed to be here? if ("build" !== 'build') {/* var WebSocket = require('ws'); // jshint ignore:line - var web3 = require('./main.js'); // jshint ignore:line + var web3 = require('./web3'); // jshint ignore:line */} var AutoProvider = function (userOptions) { @@ -399,6 +424,9 @@ if ("build" !== 'build') {/* var abi = require('./abi'); +// method signature length in bytes +var ETH_METHOD_SIGNATURE_LENGTH = 4; + var contract = function (address, desc) { var inputParser = abi.inputParser(desc); var outputParser = abi.outputParser(desc); @@ -418,14 +446,18 @@ var contract = function (address, desc) { call: function (extra) { extra = extra || {}; extra.to = address; - extra.data = parsed; - return web3.eth.call(extra).then(onSuccess); + return abi.methodSignature(desc, method.name).then(function (signature) { + extra.data = signature.slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2) + parsed; + return web3.eth.call(extra).then(onSuccess); + }); }, transact: function (extra) { extra = extra || {}; extra.to = address; - extra.data = parsed; - return web3.eth.transact(extra).then(onSuccess); + return abi.methodSignature(desc, method.name).then(function (signature) { + extra.data = signature.slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2) + parsed; + return web3.eth.transact(extra).then(onSuccess); + }); } }; }; @@ -549,6 +581,53 @@ module.exports = HttpRpcProvider; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ +/** @file qt.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * @date 2014 + */ + +var QtProvider = function() { + this.handlers = []; + + var self = this; + navigator.qt.onmessage = function (message) { + self.handlers.forEach(function (handler) { + handler.call(self, JSON.parse(message.data)); + }); + }; +}; + +QtProvider.prototype.send = function(payload) { + navigator.qt.postMessage(JSON.stringify(payload)); +}; + +Object.defineProperty(QtProvider.prototype, "onmessage", { + set: function(handler) { + this.handlers.push(handler); + } +}); + +module.exports = QtProvider; + +},{}],6:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ /** @file main.js * @authors: * Jeffrey Wilcke @@ -791,7 +870,7 @@ var web3 = { }, fromAscii: function(str, pad) { - pad = pad === undefined ? 32 : pad; + pad = pad === undefined ? 0 : pad; var hex = this.toHex(str); while(hex.length < pad*2) hex += "00"; @@ -1040,54 +1119,8 @@ function messageHandler(data) { } } -module.exports = web3; - -},{}],6:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file qt.js - * @authors: - * Jeffrey Wilcke - * Marek Kotewicz - * @date 2014 - */ - -var QtProvider = function() { - this.handlers = []; - - var self = this; - navigator.qt.onmessage = function (message) { - self.handlers.forEach(function (handler) { - handler.call(self, JSON.parse(message.data)); - }); - }; -}; - -QtProvider.prototype.send = function(payload) { - navigator.qt.postMessage(JSON.stringify(payload)); -}; - -Object.defineProperty(QtProvider.prototype, "onmessage", { - set: function(handler) { - this.handlers.push(handler); - } -}); - -module.exports = QtProvider; +if (typeof(module) !== "undefined") + module.exports = web3; },{}],7:[function(require,module,exports){ /* @@ -1166,10 +1199,11 @@ Object.defineProperty(WebSocketProvider.prototype, "onmessage", { set: function(provider) { this.onMessage(provider); } }); -module.exports = WebSocketProvider; +if (typeof(module) !== "undefined") + module.exports = WebSocketProvider; },{}],"web3":[function(require,module,exports){ -var web3 = require('./lib/main'); +var web3 = require('./lib/web3'); web3.providers.WebSocketProvider = require('./lib/websocket'); web3.providers.HttpRpcProvider = require('./lib/httprpc'); web3.providers.QtProvider = require('./lib/qt'); @@ -1178,7 +1212,7 @@ web3.contract = require('./lib/contract'); module.exports = web3; -},{"./lib/autoprovider":2,"./lib/contract":3,"./lib/httprpc":4,"./lib/main":5,"./lib/qt":6,"./lib/websocket":7}]},{},["web3"]) +},{"./lib/autoprovider":2,"./lib/contract":3,"./lib/httprpc":4,"./lib/qt":5,"./lib/web3":6,"./lib/websocket":7}]},{},["web3"]) //# sourceMappingURL=ethereum.js.map \ No newline at end of file diff --git a/libjsqrc/ethereumjs/dist/ethereum.js.map b/libjsqrc/ethereumjs/dist/ethereum.js.map index b54a294d7..e60f94922 100644 --- a/libjsqrc/ethereumjs/dist/ethereum.js.map +++ b/libjsqrc/ethereumjs/dist/ethereum.js.map @@ -6,24 +6,24 @@ "lib/autoprovider.js", "lib/contract.js", "lib/httprpc.js", - "lib/main.js", "lib/qt.js", + "lib/web3.js", "lib/websocket.js", "index.js" ], "names": [], - "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5fA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", + "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClSA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7fA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", "file": "generated.js", "sourceRoot": "", "sourcesContent": [ "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o.\n*/\n/** @file abi.js\n * @authors:\n * Marek Kotewicz \n * Gav Wood \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var web3 = require('./web3'); // jshint ignore:line\n*/}\n\n// TODO: make these be actually accurate instead of falling back onto JS's doubles.\nvar hexToDec = function (hex) {\n return parseInt(hex, 16).toString();\n};\n\nvar decToHex = function (dec) {\n return parseInt(dec).toString(16);\n};\n\nvar findIndex = function (array, callback) {\n var end = false;\n var i = 0;\n for (; i < array.length && !end; i++) {\n end = callback(array[i]);\n }\n return end ? i - 1 : -1;\n};\n\nvar findMethodIndex = function (json, methodName) {\n return findIndex(json, function (method) {\n return method.name === methodName;\n });\n};\n\nvar padLeft = function (string, chars) {\n return new Array(chars - string.length + 1).join(\"0\") + string;\n};\n\nvar calcBitPadding = function (type, expected) {\n var value = type.slice(expected.length);\n if (value === \"\") {\n return 32;\n }\n return parseInt(value) / 8;\n};\n\nvar calcBytePadding = function (type, expected) {\n var value = type.slice(expected.length);\n if (value === \"\") {\n return 32;\n }\n return parseInt(value);\n};\n\nvar calcRealPadding = function (type, expected) {\n var value = type.slice(expected.length);\n if (value === \"\") {\n return 32;\n }\n var sizes = value.split('x');\n for (var padding = 0, i = 0; i < sizes; i++) {\n padding += (sizes[i] / 8);\n }\n return padding;\n};\n\nvar setupInputTypes = function () {\n \n var prefixedType = function (prefix, calcPadding) {\n return function (type, value) {\n var expected = prefix;\n if (type.indexOf(expected) !== 0) {\n return false;\n }\n\n var padding = calcPadding(type, expected);\n if (typeof value === \"number\")\n value = value.toString(16);\n else if (typeof value === \"string\")\n value = web3.toHex(value); \n else if (value.indexOf('0x') === 0)\n value = value.substr(2);\n else\n value = (+value).toString(16);\n return padLeft(value, padding * 2);\n };\n };\n\n var namedType = function (name, padding, formatter) {\n return function (type, value) {\n if (type !== name) {\n return false;\n }\n\n return padLeft(formatter ? formatter(value) : value, padding * 2);\n };\n };\n\n var formatBool = function (value) {\n return value ? '0x1' : '0x0';\n };\n\n return [\n prefixedType('uint', calcBitPadding),\n prefixedType('int', calcBitPadding),\n prefixedType('hash', calcBitPadding),\n prefixedType('string', calcBytePadding),\n prefixedType('real', calcRealPadding),\n prefixedType('ureal', calcRealPadding),\n namedType('address', 20),\n namedType('bool', 1, formatBool),\n ];\n};\n\nvar inputTypes = setupInputTypes();\n\nvar toAbiInput = function (json, methodName, params) {\n var bytes = \"\";\n var index = findMethodIndex(json, methodName);\n\n if (index === -1) {\n return;\n }\n\n bytes = \"0x\" + padLeft(index.toString(16), 2);\n var method = json[index];\n\n for (var i = 0; i < method.inputs.length; i++) {\n var found = false;\n for (var j = 0; j < inputTypes.length && !found; j++) {\n found = inputTypes[j](method.inputs[i].type, params[i]);\n }\n if (!found) {\n console.error('unsupported json type: ' + method.inputs[i].type);\n }\n bytes += found;\n }\n return bytes;\n};\n\nvar setupOutputTypes = function () {\n\n var prefixedType = function (prefix, calcPadding) {\n return function (type) {\n var expected = prefix;\n if (type.indexOf(expected) !== 0) {\n return -1;\n }\n\n var padding = calcPadding(type, expected);\n return padding * 2;\n };\n };\n\n var namedType = function (name, padding) {\n return function (type) {\n return name === type ? padding * 2 : -1;\n };\n };\n\n var formatInt = function (value) {\n return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value);\n };\n\n var formatHash = function (value) {\n return \"0x\" + value;\n };\n\n var formatBool = function (value) {\n return value === '1' ? true : false;\n };\n\n var formatString = function (value) {\n return web3.toAscii(value);\n };\n\n return [\n { padding: prefixedType('uint', calcBitPadding), format: formatInt },\n { padding: prefixedType('int', calcBitPadding), format: formatInt },\n { padding: prefixedType('hash', calcBitPadding), format: formatHash },\n { padding: prefixedType('string', calcBytePadding), format: formatString },\n { padding: prefixedType('real', calcRealPadding), format: formatInt },\n { padding: prefixedType('ureal', calcRealPadding), format: formatInt },\n { padding: namedType('address', 20) },\n { padding: namedType('bool', 1), format: formatBool }\n ];\n};\n\nvar outputTypes = setupOutputTypes();\n\nvar fromAbiOutput = function (json, methodName, output) {\n var index = findMethodIndex(json, methodName);\n\n if (index === -1) {\n return;\n }\n\n output = output.slice(2);\n\n var result = [];\n var method = json[index];\n for (var i = 0; i < method.outputs.length; i++) {\n var padding = -1;\n for (var j = 0; j < outputTypes.length && padding === -1; j++) {\n padding = outputTypes[j].padding(method.outputs[i].type);\n }\n\n if (padding === -1) {\n // not found output parsing\n continue;\n }\n var res = output.slice(0, padding);\n var formatter = outputTypes[j - 1].format;\n result.push(formatter ? formatter(res) : (\"0x\" + res));\n output = output.slice(padding);\n }\n\n return result;\n};\n\nvar inputParser = function (json) {\n var parser = {};\n json.forEach(function (method) {\n parser[method.name] = function () {\n var params = Array.prototype.slice.call(arguments);\n return toAbiInput(json, method.name, params);\n };\n });\n\n return parser;\n};\n\nvar outputParser = function (json) {\n var parser = {};\n json.forEach(function (method) {\n parser[method.name] = function (output) {\n return fromAbiOutput(json, method.name, output);\n };\n });\n\n return parser;\n};\n\nmodule.exports = {\n inputParser: inputParser,\n outputParser: outputParser\n};\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file autoprovider.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\n/*\n * @brief if qt object is available, uses QtProvider,\n * if not tries to connect over websockets\n * if it fails, it uses HttpRpcProvider\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var WebSocket = require('ws'); // jshint ignore:line\n var web3 = require('./main.js'); // jshint ignore:line\n*/}\n\nvar AutoProvider = function (userOptions) {\n if (web3.haveProvider()) {\n return;\n }\n\n // before we determine what provider we are, we have to cache request\n this.sendQueue = [];\n this.onmessageQueue = [];\n\n if (navigator.qt) {\n this.provider = new web3.providers.QtProvider();\n return;\n }\n\n userOptions = userOptions || {};\n var options = {\n httprpc: userOptions.httprpc || 'http://localhost:8080',\n websockets: userOptions.websockets || 'ws://localhost:40404/eth'\n };\n\n var self = this;\n var closeWithSuccess = function (success) {\n ws.close();\n if (success) {\n self.provider = new web3.providers.WebSocketProvider(options.websockets);\n } else {\n self.provider = new web3.providers.HttpRpcProvider(options.httprpc);\n self.poll = self.provider.poll.bind(self.provider);\n }\n self.sendQueue.forEach(function (payload) {\n self.provider(payload);\n });\n self.onmessageQueue.forEach(function (handler) {\n self.provider.onmessage = handler;\n });\n };\n\n var ws = new WebSocket(options.websockets);\n\n ws.onopen = function() {\n closeWithSuccess(true);\n };\n\n ws.onerror = function() {\n closeWithSuccess(false);\n };\n};\n\nAutoProvider.prototype.send = function (payload) {\n if (this.provider) {\n this.provider.send(payload);\n return;\n }\n this.sendQueue.push(payload);\n};\n\nObject.defineProperty(AutoProvider.prototype, 'onmessage', {\n set: function (handler) {\n if (this.provider) {\n this.provider.onmessage = handler;\n return;\n }\n this.onmessageQueue.push(handler);\n }\n});\n\nmodule.exports = AutoProvider;\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file contract.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var web3 = require('./web3'); // jshint ignore:line\n*/}\n\nvar abi = require('./abi');\n\nvar contract = function (address, desc) {\n var inputParser = abi.inputParser(desc);\n var outputParser = abi.outputParser(desc);\n\n var contract = {};\n\n desc.forEach(function (method) {\n contract[method.name] = function () {\n var params = Array.prototype.slice.call(arguments);\n var parsed = inputParser[method.name].apply(null, params);\n\n var onSuccess = function (result) {\n return outputParser[method.name](result);\n };\n\n return {\n call: function (extra) {\n extra = extra || {};\n extra.to = address;\n extra.data = parsed;\n return web3.eth.call(extra).then(onSuccess);\n },\n transact: function (extra) {\n extra = extra || {};\n extra.to = address;\n extra.data = parsed;\n return web3.eth.transact(extra).then(onSuccess);\n }\n };\n };\n });\n\n return contract;\n};\n\nmodule.exports = contract;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file abi.js\n * @authors:\n * Marek Kotewicz \n * Gav Wood \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var web3 = require('./web3'); // jshint ignore:line\n*/}\n\n// TODO: make these be actually accurate instead of falling back onto JS's doubles.\nvar hexToDec = function (hex) {\n return parseInt(hex, 16).toString();\n};\n\nvar decToHex = function (dec) {\n return parseInt(dec).toString(16);\n};\n\nvar findIndex = function (array, callback) {\n var end = false;\n var i = 0;\n for (; i < array.length && !end; i++) {\n end = callback(array[i]);\n }\n return end ? i - 1 : -1;\n};\n\nvar findMethodIndex = function (json, methodName) {\n return findIndex(json, function (method) {\n return method.name === methodName;\n });\n};\n\nvar padLeft = function (string, chars) {\n return new Array(chars - string.length + 1).join(\"0\") + string;\n};\n\nvar calcBitPadding = function (type, expected) {\n var value = type.slice(expected.length);\n if (value === \"\") {\n return 32;\n }\n return parseInt(value) / 8;\n};\n\nvar calcBytePadding = function (type, expected) {\n var value = type.slice(expected.length);\n if (value === \"\") {\n return 32;\n }\n return parseInt(value);\n};\n\nvar calcRealPadding = function (type, expected) {\n var value = type.slice(expected.length);\n if (value === \"\") {\n return 32;\n }\n var sizes = value.split('x');\n for (var padding = 0, i = 0; i < sizes; i++) {\n padding += (sizes[i] / 8);\n }\n return padding;\n};\n\nvar setupInputTypes = function () {\n \n var prefixedType = function (prefix, calcPadding) {\n return function (type, value) {\n var expected = prefix;\n if (type.indexOf(expected) !== 0) {\n return false;\n }\n\n var padding = calcPadding(type, expected);\n if (padding > 32)\n return false; // not allowed to be so big.\n padding = 32; // override as per the new ABI.\n\n if (prefix === \"string\")\n return web3.fromAscii(value, padding).substr(2);\n if (typeof value === \"number\")\n value = value.toString(16);\n else if (typeof value === \"string\")\n value = web3.toHex(value); \n else if (value.indexOf('0x') === 0)\n value = value.substr(2);\n else\n value = (+value).toString(16);\n return padLeft(value, padding * 2);\n };\n };\n\n var namedType = function (name, padding, formatter) {\n return function (type, value) {\n if (type !== name) {\n return false;\n }\n\n padding = 32; //override as per the new ABI.\n\n return padLeft(formatter ? formatter(value) : value, padding * 2);\n };\n };\n\n var formatBool = function (value) {\n return value ? '0x1' : '0x0';\n };\n\n return [\n prefixedType('uint', calcBitPadding),\n prefixedType('int', calcBitPadding),\n prefixedType('hash', calcBitPadding),\n prefixedType('string', calcBytePadding),\n prefixedType('real', calcRealPadding),\n prefixedType('ureal', calcRealPadding),\n namedType('address', 20),\n namedType('bool', 1, formatBool),\n ];\n};\n\nvar inputTypes = setupInputTypes();\n\nvar toAbiInput = function (json, methodName, params) {\n var bytes = \"\";\n var index = findMethodIndex(json, methodName);\n\n if (index === -1) {\n return;\n }\n\n var method = json[index];\n\n for (var i = 0; i < method.inputs.length; i++) {\n var found = false;\n for (var j = 0; j < inputTypes.length && !found; j++) {\n found = inputTypes[j](method.inputs[i].type, params[i]);\n }\n if (!found) {\n console.error('unsupported json type: ' + method.inputs[i].type);\n }\n bytes += found;\n }\n return bytes;\n};\n\nvar setupOutputTypes = function () {\n\n var prefixedType = function (prefix, calcPadding) {\n return function (type) {\n var expected = prefix;\n if (type.indexOf(expected) !== 0) {\n return -1;\n }\n\n var padding = calcPadding(type, expected);\n if (padding > 32)\n return -1; // not allowed to be so big.\n padding = 32; // override as per the new ABI.\n return padding * 2;\n };\n };\n\n var namedType = function (name, padding) {\n return function (type) {\n padding = 32; // override as per the new ABI.\n return name === type ? padding * 2 : -1;\n };\n };\n\n var formatInt = function (value) {\n return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value);\n };\n\n var formatHash = function (value) {\n return \"0x\" + value;\n };\n\n var formatBool = function (value) {\n return value === '1' ? true : false;\n };\n\n var formatString = function (value) {\n return web3.toAscii(value);\n };\n\n return [\n { padding: prefixedType('uint', calcBitPadding), format: formatInt },\n { padding: prefixedType('int', calcBitPadding), format: formatInt },\n { padding: prefixedType('hash', calcBitPadding), format: formatHash },\n { padding: prefixedType('string', calcBytePadding), format: formatString },\n { padding: prefixedType('real', calcRealPadding), format: formatInt },\n { padding: prefixedType('ureal', calcRealPadding), format: formatInt },\n { padding: namedType('address', 20) },\n { padding: namedType('bool', 1), format: formatBool }\n ];\n};\n\nvar outputTypes = setupOutputTypes();\n\nvar fromAbiOutput = function (json, methodName, output) {\n var index = findMethodIndex(json, methodName);\n\n if (index === -1) {\n return;\n }\n\n output = output.slice(2);\n\n var result = [];\n var method = json[index];\n for (var i = 0; i < method.outputs.length; i++) {\n var padding = -1;\n for (var j = 0; j < outputTypes.length && padding === -1; j++) {\n padding = outputTypes[j].padding(method.outputs[i].type);\n }\n\n if (padding === -1) {\n // not found output parsing\n continue;\n }\n var res = output.slice(0, padding);\n var formatter = outputTypes[j - 1].format;\n result.push(formatter ? formatter(res) : (\"0x\" + res));\n output = output.slice(padding);\n }\n\n return result;\n};\n\nvar inputParser = function (json) {\n var parser = {};\n json.forEach(function (method) {\n parser[method.name] = function () {\n var params = Array.prototype.slice.call(arguments);\n return toAbiInput(json, method.name, params);\n };\n });\n\n return parser;\n};\n\nvar outputParser = function (json) {\n var parser = {};\n json.forEach(function (method) {\n parser[method.name] = function (output) {\n return fromAbiOutput(json, method.name, output);\n };\n });\n\n return parser;\n};\n\nvar methodSignature = function (json, name) {\n var method = json[findMethodIndex(json, name)];\n var result = name + '(';\n var inputTypes = method.inputs.map(function (inp) {\n return inp.type;\n });\n result += inputTypes.join(',');\n result += ')';\n\n return web3.sha3(web3.fromAscii(result));\n};\n\nmodule.exports = {\n inputParser: inputParser,\n outputParser: outputParser,\n methodSignature: methodSignature\n};\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file autoprovider.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\n/*\n * @brief if qt object is available, uses QtProvider,\n * if not tries to connect over websockets\n * if it fails, it uses HttpRpcProvider\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var WebSocket = require('ws'); // jshint ignore:line\n var web3 = require('./web3'); // jshint ignore:line\n*/}\n\nvar AutoProvider = function (userOptions) {\n if (web3.haveProvider()) {\n return;\n }\n\n // before we determine what provider we are, we have to cache request\n this.sendQueue = [];\n this.onmessageQueue = [];\n\n if (navigator.qt) {\n this.provider = new web3.providers.QtProvider();\n return;\n }\n\n userOptions = userOptions || {};\n var options = {\n httprpc: userOptions.httprpc || 'http://localhost:8080',\n websockets: userOptions.websockets || 'ws://localhost:40404/eth'\n };\n\n var self = this;\n var closeWithSuccess = function (success) {\n ws.close();\n if (success) {\n self.provider = new web3.providers.WebSocketProvider(options.websockets);\n } else {\n self.provider = new web3.providers.HttpRpcProvider(options.httprpc);\n self.poll = self.provider.poll.bind(self.provider);\n }\n self.sendQueue.forEach(function (payload) {\n self.provider(payload);\n });\n self.onmessageQueue.forEach(function (handler) {\n self.provider.onmessage = handler;\n });\n };\n\n var ws = new WebSocket(options.websockets);\n\n ws.onopen = function() {\n closeWithSuccess(true);\n };\n\n ws.onerror = function() {\n closeWithSuccess(false);\n };\n};\n\nAutoProvider.prototype.send = function (payload) {\n if (this.provider) {\n this.provider.send(payload);\n return;\n }\n this.sendQueue.push(payload);\n};\n\nObject.defineProperty(AutoProvider.prototype, 'onmessage', {\n set: function (handler) {\n if (this.provider) {\n this.provider.onmessage = handler;\n return;\n }\n this.onmessageQueue.push(handler);\n }\n});\n\nmodule.exports = AutoProvider;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file contract.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var web3 = require('./web3'); // jshint ignore:line\n*/}\n\nvar abi = require('./abi');\n\n// method signature length in bytes\nvar ETH_METHOD_SIGNATURE_LENGTH = 4;\n\nvar contract = function (address, desc) {\n var inputParser = abi.inputParser(desc);\n var outputParser = abi.outputParser(desc);\n\n var contract = {};\n\n desc.forEach(function (method) {\n contract[method.name] = function () {\n var params = Array.prototype.slice.call(arguments);\n var parsed = inputParser[method.name].apply(null, params);\n\n var onSuccess = function (result) {\n return outputParser[method.name](result);\n };\n\n return {\n call: function (extra) {\n extra = extra || {};\n extra.to = address;\n return abi.methodSignature(desc, method.name).then(function (signature) {\n extra.data = signature.slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2) + parsed;\n return web3.eth.call(extra).then(onSuccess);\n });\n },\n transact: function (extra) {\n extra = extra || {};\n extra.to = address;\n return abi.methodSignature(desc, method.name).then(function (signature) {\n extra.data = signature.slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2) + parsed;\n return web3.eth.transact(extra).then(onSuccess);\n });\n }\n };\n };\n });\n\n return contract;\n};\n\nmodule.exports = contract;\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file httprpc.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line\n*/}\n\nvar HttpRpcProvider = function (host) {\n this.handlers = [];\n this.host = host;\n};\n\nfunction formatJsonRpcObject(object) {\n return {\n jsonrpc: '2.0',\n method: object.call,\n params: object.args,\n id: object._id\n };\n}\n\nfunction formatJsonRpcMessage(message) {\n var object = JSON.parse(message);\n\n return {\n _id: object.id,\n data: object.result,\n error: object.error\n };\n}\n\nHttpRpcProvider.prototype.sendRequest = function (payload, cb) {\n var data = formatJsonRpcObject(payload);\n\n var request = new XMLHttpRequest();\n request.open(\"POST\", this.host, true);\n request.send(JSON.stringify(data));\n request.onreadystatechange = function () {\n if (request.readyState === 4 && cb) {\n cb(request);\n }\n };\n};\n\nHttpRpcProvider.prototype.send = function (payload) {\n var self = this;\n this.sendRequest(payload, function (request) {\n self.handlers.forEach(function (handler) {\n handler.call(self, formatJsonRpcMessage(request.responseText));\n });\n });\n};\n\nHttpRpcProvider.prototype.poll = function (payload, id) {\n var self = this;\n this.sendRequest(payload, function (request) {\n var parsed = JSON.parse(request.responseText);\n if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) {\n return;\n }\n self.handlers.forEach(function (handler) {\n handler.call(self, {_event: payload.call, _id: id, data: parsed.result});\n });\n });\n};\n\nObject.defineProperty(HttpRpcProvider.prototype, \"onmessage\", {\n set: function (handler) {\n this.handlers.push(handler);\n }\n});\n\nmodule.exports = HttpRpcProvider;\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file main.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nfunction flattenPromise (obj) {\n if (obj instanceof Promise) {\n return Promise.resolve(obj);\n }\n\n if (obj instanceof Array) {\n return new Promise(function (resolve) {\n var promises = obj.map(function (o) {\n return flattenPromise(o);\n });\n\n return Promise.all(promises).then(function (res) {\n for (var i = 0; i < obj.length; i++) {\n obj[i] = res[i];\n }\n resolve(obj);\n });\n });\n }\n\n if (obj instanceof Object) {\n return new Promise(function (resolve) {\n var keys = Object.keys(obj);\n var promises = keys.map(function (key) {\n return flattenPromise(obj[key]);\n });\n\n return Promise.all(promises).then(function (res) {\n for (var i = 0; i < keys.length; i++) {\n obj[keys[i]] = res[i];\n }\n resolve(obj);\n });\n });\n }\n\n return Promise.resolve(obj);\n}\n\nvar web3Methods = function () {\n return [\n { name: 'sha3', call: 'web3_sha3' }\n ];\n};\n\nvar ethMethods = function () {\n var blockCall = function (args) {\n return typeof args[0] === \"string\" ? \"eth_blockByHash\" : \"eth_blockByNumber\";\n };\n\n var transactionCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_transactionByHash' : 'eth_transactionByNumber';\n };\n\n var uncleCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_uncleByHash' : 'eth_uncleByNumber';\n };\n\n var methods = [\n { name: 'balanceAt', call: 'eth_balanceAt' },\n { name: 'stateAt', call: 'eth_stateAt' },\n { name: 'storageAt', call: 'eth_storageAt' },\n { name: 'countAt', call: 'eth_countAt'},\n { name: 'codeAt', call: 'eth_codeAt' },\n { name: 'transact', call: 'eth_transact' },\n { name: 'call', call: 'eth_call' },\n { name: 'block', call: blockCall },\n { name: 'transaction', call: transactionCall },\n { name: 'uncle', call: uncleCall },\n { name: 'compilers', call: 'eth_compilers' },\n { name: 'lll', call: 'eth_lll' },\n { name: 'solidity', call: 'eth_solidity' },\n { name: 'serpent', call: 'eth_serpent' },\n { name: 'logs', call: 'eth_logs' }\n ];\n return methods;\n};\n\nvar ethProperties = function () {\n return [\n { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },\n { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },\n { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },\n { name: 'gasPrice', getter: 'eth_gasPrice' },\n { name: 'account', getter: 'eth_account' },\n { name: 'accounts', getter: 'eth_accounts' },\n { name: 'peerCount', getter: 'eth_peerCount' },\n { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },\n { name: 'number', getter: 'eth_number'}\n ];\n};\n\nvar dbMethods = function () {\n return [\n { name: 'put', call: 'db_put' },\n { name: 'get', call: 'db_get' },\n { name: 'putString', call: 'db_putString' },\n { name: 'getString', call: 'db_getString' }\n ];\n};\n\nvar shhMethods = function () {\n return [\n { name: 'post', call: 'shh_post' },\n { name: 'newIdentity', call: 'shh_newIdentity' },\n { name: 'haveIdentity', call: 'shh_haveIdentity' },\n { name: 'newGroup', call: 'shh_newGroup' },\n { name: 'addToGroup', call: 'shh_addToGroup' }\n ];\n};\n\nvar ethWatchMethods = function () {\n var newFilter = function (args) {\n return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';\n };\n\n return [\n { name: 'newFilter', call: newFilter },\n { name: 'uninstallFilter', call: 'eth_uninstallFilter' },\n { name: 'getMessages', call: 'eth_filterLogs' }\n ];\n};\n\nvar shhWatchMethods = function () {\n return [\n { name: 'newFilter', call: 'shh_newFilter' },\n { name: 'uninstallFilter', call: 'shh_uninstallFilter' },\n { name: 'getMessage', call: 'shh_getMessages' }\n ];\n};\n\nvar setupMethods = function (obj, methods) {\n methods.forEach(function (method) {\n obj[method.name] = function () {\n return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) {\n var call = typeof method.call === \"function\" ? method.call(args) : method.call;\n return {call: call, args: args};\n }).then(function (request) {\n return new Promise(function (resolve, reject) {\n web3.provider.send(request, function (err, result) {\n if (!err) {\n resolve(result);\n return;\n }\n reject(err);\n });\n });\n }).catch(function(err) {\n console.error(err);\n });\n };\n });\n};\n\nvar setupProperties = function (obj, properties) {\n properties.forEach(function (property) {\n var proto = {};\n proto.get = function () {\n return new Promise(function(resolve, reject) {\n web3.provider.send({call: property.getter}, function(err, result) {\n if (!err) {\n resolve(result);\n return;\n }\n reject(err);\n });\n });\n };\n if (property.setter) {\n proto.set = function (val) {\n return flattenPromise([val]).then(function (args) {\n return new Promise(function (resolve) {\n web3.provider.send({call: property.setter, args: args}, function (err, result) {\n if (!err) {\n resolve(result);\n return;\n }\n reject(err);\n });\n });\n }).catch(function (err) {\n console.error(err);\n });\n };\n }\n Object.defineProperty(obj, property.name, proto);\n });\n};\n\n// TODO: import from a dependency, don't duplicate.\nvar hexToDec = function (hex) {\n return parseInt(hex, 16).toString();\n};\n\nvar decToHex = function (dec) {\n return parseInt(dec).toString(16);\n};\n\n\nvar web3 = {\n _callbacks: {},\n _events: {},\n providers: {},\n\n toHex: function(str) {\n var hex = \"\";\n for(var i = 0; i < str.length; i++) {\n var n = str.charCodeAt(i).toString(16);\n hex += n.length < 2 ? '0' + n : n;\n }\n\n return hex;\n },\n\n toAscii: function(hex) {\n // Find termination\n var str = \"\";\n var i = 0, l = hex.length;\n if (hex.substring(0, 2) === '0x')\n i = 2;\n for(; i < l; i+=2) {\n var code = hex.charCodeAt(i);\n if(code === 0) {\n break;\n }\n\n str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));\n }\n\n return str;\n },\n\n fromAscii: function(str, pad) {\n pad = pad === undefined ? 32 : pad;\n var hex = this.toHex(str);\n while(hex.length < pad*2)\n hex += \"00\";\n return \"0x\" + hex;\n },\n\n toDecimal: function (val) {\n return hexToDec(val.substring(2));\n },\n\n fromDecimal: function (val) {\n return \"0x\" + decToHex(val);\n },\n\n toEth: function(str) {\n var val = typeof str === \"string\" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;\n var unit = 0;\n var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ];\n while (val > 3000 && unit < units.length - 1)\n {\n val /= 1000;\n unit++;\n }\n var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);\n var replaceFunction = function($0, $1, $2) {\n return $1 + ',' + $2;\n };\n\n while (true) {\n var o = s;\n s = s.replace(/(\\d)(\\d\\d\\d[\\.\\,])/, replaceFunction);\n if (o === s)\n break;\n }\n return s + ' ' + units[unit];\n },\n\n eth: {\n prototype: Object(), // jshint ignore:line\n watch: function (params) {\n return new Filter(params, ethWatch);\n }\n },\n\n db: {\n prototype: Object() // jshint ignore:line\n },\n\n shh: {\n prototype: Object(), // jshint ignore:line\n watch: function (params) {\n return new Filter(params, shhWatch);\n }\n },\n\n on: function(event, id, cb) {\n if(web3._events[event] === undefined) {\n web3._events[event] = {};\n }\n\n web3._events[event][id] = cb;\n return this;\n },\n\n off: function(event, id) {\n if(web3._events[event] !== undefined) {\n delete web3._events[event][id];\n }\n\n return this;\n },\n\n trigger: function(event, id, data) {\n var callbacks = web3._events[event];\n if (!callbacks || !callbacks[id]) {\n return;\n }\n var cb = callbacks[id];\n cb(data);\n }\n};\n\nsetupMethods(web3, web3Methods());\nsetupMethods(web3.eth, ethMethods());\nsetupProperties(web3.eth, ethProperties());\nsetupMethods(web3.db, dbMethods());\nsetupMethods(web3.shh, shhMethods());\n\nvar ethWatch = {\n changed: 'eth_changed'\n};\nsetupMethods(ethWatch, ethWatchMethods());\nvar shhWatch = {\n changed: 'shh_changed'\n};\nsetupMethods(shhWatch, shhWatchMethods());\n\nvar ProviderManager = function() {\n this.queued = [];\n this.polls = [];\n this.ready = false;\n this.provider = undefined;\n this.id = 1;\n\n var self = this;\n var poll = function () {\n if (self.provider && self.provider.poll) {\n self.polls.forEach(function (data) {\n data.data._id = self.id;\n self.id++;\n self.provider.poll(data.data, data.id);\n });\n }\n setTimeout(poll, 12000);\n };\n poll();\n};\n\nProviderManager.prototype.send = function(data, cb) {\n data._id = this.id;\n if (cb) {\n web3._callbacks[data._id] = cb;\n }\n\n data.args = data.args || [];\n this.id++;\n\n if(this.provider !== undefined) {\n this.provider.send(data);\n } else {\n console.warn(\"provider is not set\");\n this.queued.push(data);\n }\n};\n\nProviderManager.prototype.set = function(provider) {\n if(this.provider !== undefined && this.provider.unload !== undefined) {\n this.provider.unload();\n }\n\n this.provider = provider;\n this.ready = true;\n};\n\nProviderManager.prototype.sendQueued = function() {\n for(var i = 0; this.queued.length; i++) {\n // Resend\n this.send(this.queued[i]);\n }\n};\n\nProviderManager.prototype.installed = function() {\n return this.provider !== undefined;\n};\n\nProviderManager.prototype.startPolling = function (data, pollId) {\n if (!this.provider || !this.provider.poll) {\n return;\n }\n this.polls.push({data: data, id: pollId});\n};\n\nProviderManager.prototype.stopPolling = function (pollId) {\n for (var i = this.polls.length; i--;) {\n var poll = this.polls[i];\n if (poll.id === pollId) {\n this.polls.splice(i, 1);\n }\n }\n};\n\nweb3.provider = new ProviderManager();\n\nweb3.setProvider = function(provider) {\n provider.onmessage = messageHandler;\n web3.provider.set(provider);\n web3.provider.sendQueued();\n};\n\nweb3.haveProvider = function() {\n return !!web3.provider.provider;\n};\n\nvar Filter = function(options, impl) {\n this.impl = impl;\n this.callbacks = [];\n\n var self = this;\n this.promise = impl.newFilter(options);\n this.promise.then(function (id) {\n self.id = id;\n web3.on(impl.changed, id, self.trigger.bind(self));\n web3.provider.startPolling({call: impl.changed, args: [id]}, id);\n });\n};\n\nFilter.prototype.arrived = function(callback) {\n this.changed(callback);\n};\n\nFilter.prototype.changed = function(callback) {\n var self = this;\n this.promise.then(function(id) {\n self.callbacks.push(callback);\n });\n};\n\nFilter.prototype.trigger = function(messages) {\n for(var i = 0; i < this.callbacks.length; i++) {\n this.callbacks[i].call(this, messages);\n }\n};\n\nFilter.prototype.uninstall = function() {\n var self = this;\n this.promise.then(function (id) {\n self.impl.uninstallFilter(id);\n web3.provider.stopPolling(id);\n web3.off(impl.changed, id);\n });\n};\n\nFilter.prototype.messages = function() {\n var self = this;\n return this.promise.then(function (id) {\n return self.impl.getMessages(id);\n });\n};\n\nFilter.prototype.logs = function () {\n return this.messages();\n};\n\nfunction messageHandler(data) {\n if(data._event !== undefined) {\n web3.trigger(data._event, data._id, data.data);\n return;\n }\n\n if(data._id) {\n var cb = web3._callbacks[data._id];\n if (cb) {\n cb.call(this, data.error, data.data);\n delete web3._callbacks[data._id];\n }\n }\n}\n\nmodule.exports = web3;\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file qt.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * @date 2014\n */\n\nvar QtProvider = function() {\n this.handlers = [];\n\n var self = this;\n navigator.qt.onmessage = function (message) {\n self.handlers.forEach(function (handler) {\n handler.call(self, JSON.parse(message.data));\n });\n };\n};\n\nQtProvider.prototype.send = function(payload) {\n navigator.qt.postMessage(JSON.stringify(payload));\n};\n\nObject.defineProperty(QtProvider.prototype, \"onmessage\", {\n set: function(handler) {\n this.handlers.push(handler);\n }\n});\n\nmodule.exports = QtProvider;\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file websocket.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var WebSocket = require('ws'); // jshint ignore:line\n*/}\n\nvar WebSocketProvider = function(host) {\n // onmessage handlers\n this.handlers = [];\n // queue will be filled with messages if send is invoked before the ws is ready\n this.queued = [];\n this.ready = false;\n\n this.ws = new WebSocket(host);\n\n var self = this;\n this.ws.onmessage = function(event) {\n for(var i = 0; i < self.handlers.length; i++) {\n self.handlers[i].call(self, JSON.parse(event.data), event);\n }\n };\n\n this.ws.onopen = function() {\n self.ready = true;\n\n for(var i = 0; i < self.queued.length; i++) {\n // Resend\n self.send(self.queued[i]);\n }\n };\n};\n\nWebSocketProvider.prototype.send = function(payload) {\n if(this.ready) {\n var data = JSON.stringify(payload);\n\n this.ws.send(data);\n } else {\n this.queued.push(payload);\n }\n};\n\nWebSocketProvider.prototype.onMessage = function(handler) {\n this.handlers.push(handler);\n};\n\nWebSocketProvider.prototype.unload = function() {\n this.ws.close();\n};\nObject.defineProperty(WebSocketProvider.prototype, \"onmessage\", {\n set: function(provider) { this.onMessage(provider); }\n});\n\nmodule.exports = WebSocketProvider;\n", - "var web3 = require('./lib/main');\nweb3.providers.WebSocketProvider = require('./lib/websocket');\nweb3.providers.HttpRpcProvider = require('./lib/httprpc');\nweb3.providers.QtProvider = require('./lib/qt');\nweb3.providers.AutoProvider = require('./lib/autoprovider');\nweb3.contract = require('./lib/contract');\n\nmodule.exports = web3;\n" + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file main.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nfunction flattenPromise (obj) {\n if (obj instanceof Promise) {\n return Promise.resolve(obj);\n }\n\n if (obj instanceof Array) {\n return new Promise(function (resolve) {\n var promises = obj.map(function (o) {\n return flattenPromise(o);\n });\n\n return Promise.all(promises).then(function (res) {\n for (var i = 0; i < obj.length; i++) {\n obj[i] = res[i];\n }\n resolve(obj);\n });\n });\n }\n\n if (obj instanceof Object) {\n return new Promise(function (resolve) {\n var keys = Object.keys(obj);\n var promises = keys.map(function (key) {\n return flattenPromise(obj[key]);\n });\n\n return Promise.all(promises).then(function (res) {\n for (var i = 0; i < keys.length; i++) {\n obj[keys[i]] = res[i];\n }\n resolve(obj);\n });\n });\n }\n\n return Promise.resolve(obj);\n}\n\nvar web3Methods = function () {\n return [\n { name: 'sha3', call: 'web3_sha3' }\n ];\n};\n\nvar ethMethods = function () {\n var blockCall = function (args) {\n return typeof args[0] === \"string\" ? \"eth_blockByHash\" : \"eth_blockByNumber\";\n };\n\n var transactionCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_transactionByHash' : 'eth_transactionByNumber';\n };\n\n var uncleCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_uncleByHash' : 'eth_uncleByNumber';\n };\n\n var methods = [\n { name: 'balanceAt', call: 'eth_balanceAt' },\n { name: 'stateAt', call: 'eth_stateAt' },\n { name: 'storageAt', call: 'eth_storageAt' },\n { name: 'countAt', call: 'eth_countAt'},\n { name: 'codeAt', call: 'eth_codeAt' },\n { name: 'transact', call: 'eth_transact' },\n { name: 'call', call: 'eth_call' },\n { name: 'block', call: blockCall },\n { name: 'transaction', call: transactionCall },\n { name: 'uncle', call: uncleCall },\n { name: 'compilers', call: 'eth_compilers' },\n { name: 'lll', call: 'eth_lll' },\n { name: 'solidity', call: 'eth_solidity' },\n { name: 'serpent', call: 'eth_serpent' },\n { name: 'logs', call: 'eth_logs' }\n ];\n return methods;\n};\n\nvar ethProperties = function () {\n return [\n { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },\n { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },\n { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },\n { name: 'gasPrice', getter: 'eth_gasPrice' },\n { name: 'account', getter: 'eth_account' },\n { name: 'accounts', getter: 'eth_accounts' },\n { name: 'peerCount', getter: 'eth_peerCount' },\n { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },\n { name: 'number', getter: 'eth_number'}\n ];\n};\n\nvar dbMethods = function () {\n return [\n { name: 'put', call: 'db_put' },\n { name: 'get', call: 'db_get' },\n { name: 'putString', call: 'db_putString' },\n { name: 'getString', call: 'db_getString' }\n ];\n};\n\nvar shhMethods = function () {\n return [\n { name: 'post', call: 'shh_post' },\n { name: 'newIdentity', call: 'shh_newIdentity' },\n { name: 'haveIdentity', call: 'shh_haveIdentity' },\n { name: 'newGroup', call: 'shh_newGroup' },\n { name: 'addToGroup', call: 'shh_addToGroup' }\n ];\n};\n\nvar ethWatchMethods = function () {\n var newFilter = function (args) {\n return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';\n };\n\n return [\n { name: 'newFilter', call: newFilter },\n { name: 'uninstallFilter', call: 'eth_uninstallFilter' },\n { name: 'getMessages', call: 'eth_filterLogs' }\n ];\n};\n\nvar shhWatchMethods = function () {\n return [\n { name: 'newFilter', call: 'shh_newFilter' },\n { name: 'uninstallFilter', call: 'shh_uninstallFilter' },\n { name: 'getMessage', call: 'shh_getMessages' }\n ];\n};\n\nvar setupMethods = function (obj, methods) {\n methods.forEach(function (method) {\n obj[method.name] = function () {\n return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) {\n var call = typeof method.call === \"function\" ? method.call(args) : method.call;\n return {call: call, args: args};\n }).then(function (request) {\n return new Promise(function (resolve, reject) {\n web3.provider.send(request, function (err, result) {\n if (!err) {\n resolve(result);\n return;\n }\n reject(err);\n });\n });\n }).catch(function(err) {\n console.error(err);\n });\n };\n });\n};\n\nvar setupProperties = function (obj, properties) {\n properties.forEach(function (property) {\n var proto = {};\n proto.get = function () {\n return new Promise(function(resolve, reject) {\n web3.provider.send({call: property.getter}, function(err, result) {\n if (!err) {\n resolve(result);\n return;\n }\n reject(err);\n });\n });\n };\n if (property.setter) {\n proto.set = function (val) {\n return flattenPromise([val]).then(function (args) {\n return new Promise(function (resolve) {\n web3.provider.send({call: property.setter, args: args}, function (err, result) {\n if (!err) {\n resolve(result);\n return;\n }\n reject(err);\n });\n });\n }).catch(function (err) {\n console.error(err);\n });\n };\n }\n Object.defineProperty(obj, property.name, proto);\n });\n};\n\n// TODO: import from a dependency, don't duplicate.\nvar hexToDec = function (hex) {\n return parseInt(hex, 16).toString();\n};\n\nvar decToHex = function (dec) {\n return parseInt(dec).toString(16);\n};\n\n\nvar web3 = {\n _callbacks: {},\n _events: {},\n providers: {},\n\n toHex: function(str) {\n var hex = \"\";\n for(var i = 0; i < str.length; i++) {\n var n = str.charCodeAt(i).toString(16);\n hex += n.length < 2 ? '0' + n : n;\n }\n\n return hex;\n },\n\n toAscii: function(hex) {\n // Find termination\n var str = \"\";\n var i = 0, l = hex.length;\n if (hex.substring(0, 2) === '0x')\n i = 2;\n for(; i < l; i+=2) {\n var code = hex.charCodeAt(i);\n if(code === 0) {\n break;\n }\n\n str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));\n }\n\n return str;\n },\n\n fromAscii: function(str, pad) {\n pad = pad === undefined ? 0 : pad;\n var hex = this.toHex(str);\n while(hex.length < pad*2)\n hex += \"00\";\n return \"0x\" + hex;\n },\n\n toDecimal: function (val) {\n return hexToDec(val.substring(2));\n },\n\n fromDecimal: function (val) {\n return \"0x\" + decToHex(val);\n },\n\n toEth: function(str) {\n var val = typeof str === \"string\" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;\n var unit = 0;\n var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ];\n while (val > 3000 && unit < units.length - 1)\n {\n val /= 1000;\n unit++;\n }\n var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);\n var replaceFunction = function($0, $1, $2) {\n return $1 + ',' + $2;\n };\n\n while (true) {\n var o = s;\n s = s.replace(/(\\d)(\\d\\d\\d[\\.\\,])/, replaceFunction);\n if (o === s)\n break;\n }\n return s + ' ' + units[unit];\n },\n\n eth: {\n prototype: Object(), // jshint ignore:line\n watch: function (params) {\n return new Filter(params, ethWatch);\n }\n },\n\n db: {\n prototype: Object() // jshint ignore:line\n },\n\n shh: {\n prototype: Object(), // jshint ignore:line\n watch: function (params) {\n return new Filter(params, shhWatch);\n }\n },\n\n on: function(event, id, cb) {\n if(web3._events[event] === undefined) {\n web3._events[event] = {};\n }\n\n web3._events[event][id] = cb;\n return this;\n },\n\n off: function(event, id) {\n if(web3._events[event] !== undefined) {\n delete web3._events[event][id];\n }\n\n return this;\n },\n\n trigger: function(event, id, data) {\n var callbacks = web3._events[event];\n if (!callbacks || !callbacks[id]) {\n return;\n }\n var cb = callbacks[id];\n cb(data);\n }\n};\n\nsetupMethods(web3, web3Methods());\nsetupMethods(web3.eth, ethMethods());\nsetupProperties(web3.eth, ethProperties());\nsetupMethods(web3.db, dbMethods());\nsetupMethods(web3.shh, shhMethods());\n\nvar ethWatch = {\n changed: 'eth_changed'\n};\nsetupMethods(ethWatch, ethWatchMethods());\nvar shhWatch = {\n changed: 'shh_changed'\n};\nsetupMethods(shhWatch, shhWatchMethods());\n\nvar ProviderManager = function() {\n this.queued = [];\n this.polls = [];\n this.ready = false;\n this.provider = undefined;\n this.id = 1;\n\n var self = this;\n var poll = function () {\n if (self.provider && self.provider.poll) {\n self.polls.forEach(function (data) {\n data.data._id = self.id;\n self.id++;\n self.provider.poll(data.data, data.id);\n });\n }\n setTimeout(poll, 12000);\n };\n poll();\n};\n\nProviderManager.prototype.send = function(data, cb) {\n data._id = this.id;\n if (cb) {\n web3._callbacks[data._id] = cb;\n }\n\n data.args = data.args || [];\n this.id++;\n\n if(this.provider !== undefined) {\n this.provider.send(data);\n } else {\n console.warn(\"provider is not set\");\n this.queued.push(data);\n }\n};\n\nProviderManager.prototype.set = function(provider) {\n if(this.provider !== undefined && this.provider.unload !== undefined) {\n this.provider.unload();\n }\n\n this.provider = provider;\n this.ready = true;\n};\n\nProviderManager.prototype.sendQueued = function() {\n for(var i = 0; this.queued.length; i++) {\n // Resend\n this.send(this.queued[i]);\n }\n};\n\nProviderManager.prototype.installed = function() {\n return this.provider !== undefined;\n};\n\nProviderManager.prototype.startPolling = function (data, pollId) {\n if (!this.provider || !this.provider.poll) {\n return;\n }\n this.polls.push({data: data, id: pollId});\n};\n\nProviderManager.prototype.stopPolling = function (pollId) {\n for (var i = this.polls.length; i--;) {\n var poll = this.polls[i];\n if (poll.id === pollId) {\n this.polls.splice(i, 1);\n }\n }\n};\n\nweb3.provider = new ProviderManager();\n\nweb3.setProvider = function(provider) {\n provider.onmessage = messageHandler;\n web3.provider.set(provider);\n web3.provider.sendQueued();\n};\n\nweb3.haveProvider = function() {\n return !!web3.provider.provider;\n};\n\nvar Filter = function(options, impl) {\n this.impl = impl;\n this.callbacks = [];\n\n var self = this;\n this.promise = impl.newFilter(options);\n this.promise.then(function (id) {\n self.id = id;\n web3.on(impl.changed, id, self.trigger.bind(self));\n web3.provider.startPolling({call: impl.changed, args: [id]}, id);\n });\n};\n\nFilter.prototype.arrived = function(callback) {\n this.changed(callback);\n};\n\nFilter.prototype.changed = function(callback) {\n var self = this;\n this.promise.then(function(id) {\n self.callbacks.push(callback);\n });\n};\n\nFilter.prototype.trigger = function(messages) {\n for(var i = 0; i < this.callbacks.length; i++) {\n this.callbacks[i].call(this, messages);\n }\n};\n\nFilter.prototype.uninstall = function() {\n var self = this;\n this.promise.then(function (id) {\n self.impl.uninstallFilter(id);\n web3.provider.stopPolling(id);\n web3.off(impl.changed, id);\n });\n};\n\nFilter.prototype.messages = function() {\n var self = this;\n return this.promise.then(function (id) {\n return self.impl.getMessages(id);\n });\n};\n\nFilter.prototype.logs = function () {\n return this.messages();\n};\n\nfunction messageHandler(data) {\n if(data._event !== undefined) {\n web3.trigger(data._event, data._id, data.data);\n return;\n }\n\n if(data._id) {\n var cb = web3._callbacks[data._id];\n if (cb) {\n cb.call(this, data.error, data.data);\n delete web3._callbacks[data._id];\n }\n }\n}\n\nif (typeof(module) !== \"undefined\")\n module.exports = web3;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file websocket.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var WebSocket = require('ws'); // jshint ignore:line\n*/}\n\nvar WebSocketProvider = function(host) {\n // onmessage handlers\n this.handlers = [];\n // queue will be filled with messages if send is invoked before the ws is ready\n this.queued = [];\n this.ready = false;\n\n this.ws = new WebSocket(host);\n\n var self = this;\n this.ws.onmessage = function(event) {\n for(var i = 0; i < self.handlers.length; i++) {\n self.handlers[i].call(self, JSON.parse(event.data), event);\n }\n };\n\n this.ws.onopen = function() {\n self.ready = true;\n\n for(var i = 0; i < self.queued.length; i++) {\n // Resend\n self.send(self.queued[i]);\n }\n };\n};\n\nWebSocketProvider.prototype.send = function(payload) {\n if(this.ready) {\n var data = JSON.stringify(payload);\n\n this.ws.send(data);\n } else {\n this.queued.push(payload);\n }\n};\n\nWebSocketProvider.prototype.onMessage = function(handler) {\n this.handlers.push(handler);\n};\n\nWebSocketProvider.prototype.unload = function() {\n this.ws.close();\n};\nObject.defineProperty(WebSocketProvider.prototype, \"onmessage\", {\n set: function(provider) { this.onMessage(provider); }\n});\n\nif (typeof(module) !== \"undefined\")\n module.exports = WebSocketProvider;\n", + "var web3 = require('./lib/web3');\nweb3.providers.WebSocketProvider = require('./lib/websocket');\nweb3.providers.HttpRpcProvider = require('./lib/httprpc');\nweb3.providers.QtProvider = require('./lib/qt');\nweb3.providers.AutoProvider = require('./lib/autoprovider');\nweb3.contract = require('./lib/contract');\n\nmodule.exports = web3;\n" ] } \ No newline at end of file diff --git a/libjsqrc/ethereumjs/dist/ethereum.min.js b/libjsqrc/ethereumjs/dist/ethereum.min.js index f9eac6a9d..1d909e464 100644 --- a/libjsqrc/ethereumjs/dist/ethereum.min.js +++ b/libjsqrc/ethereumjs/dist/ethereum.min.js @@ -1 +1 @@ -require=function t(e,n,r){function o(a,s){if(!n[a]){if(!e[a]){var u="function"==typeof require&&require;if(!s&&u)return u(a,!0);if(i)return i(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;ai;i++)o+=r[i]/8;return o},c=function(){var t=function(t,e){return function(n,r){var o=t;if(0!==n.indexOf(o))return!1;var a=e(n,o);return r="number"==typeof r?r.toString(16):"string"==typeof r?web3.toHex(r):0===r.indexOf("0x")?r.substr(2):(+r).toString(16),i(r,2*a)}},e=function(t,e,n){return function(r,o){return r!==t?!1:i(n?n(o):o,2*e)}},n=function(t){return t?"0x1":"0x0"};return[t("uint",a),t("int",a),t("hash",a),t("string",s),t("real",u),t("ureal",u),e("address",20),e("bool",1,n)]},l=c(),h=function(t,e,n){var r="",a=o(t,e);if(-1!==a){r="0x"+i(a.toString(16),2);for(var s=t[a],u=0;un;n+=2){var o=t.charCodeAt(n);if(0===o)break;e+=String.fromCharCode(parseInt(t.substr(n,2),16))}return e},fromAscii:function(t,e){e=void 0===e?32:e;for(var n=this.toHex(t);n.length<2*e;)n+="00";return"0x"+n},toDecimal:function(t){return p(t.substring(2))},fromDecimal:function(t){return"0x"+d(t)},toEth:function(t){for(var e="string"==typeof t?0===t.indexOf("0x")?parseInt(t.substr(2),16):parseInt(t):t,n=0,r=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e>3e3&&ni;i++)o+=r[i]/8;return o},c=function(){var t=function(t,e){return function(n,r){var o=t;if(0!==n.indexOf(o))return!1;var a=e(n,o);return a>32?!1:(a=32,"string"===t?web3.fromAscii(r,a).substr(2):(r="number"==typeof r?r.toString(16):"string"==typeof r?web3.toHex(r):0===r.indexOf("0x")?r.substr(2):(+r).toString(16),i(r,2*a)))}},e=function(t,e,n){return function(r,o){return r!==t?!1:(e=32,i(n?n(o):o,2*e))}},n=function(t){return t?"0x1":"0x0"};return[t("uint",a),t("int",a),t("hash",a),t("string",s),t("real",u),t("ureal",u),e("address",20),e("bool",1,n)]},l=c(),h=function(t,e,n){var r="",i=o(t,e);if(-1!==i){for(var a=t[i],s=0;s32?-1:(o=32,2*o)}},e=function(t,e){return function(n){return e=32,t===n?2*e:-1}},r=function(t){return t.length<=8?+parseInt(t,16):n(t)},o=function(t){return"0x"+t},i=function(t){return"1"===t?!0:!1},c=function(t){return web3.toAscii(t)};return[{padding:t("uint",a),format:r},{padding:t("int",a),format:r},{padding:t("hash",a),format:o},{padding:t("string",s),format:c},{padding:t("real",u),format:r},{padding:t("ureal",u),format:r},{padding:e("address",20)},{padding:e("bool",1),format:i}]},p=f(),d=function(t,e,n){var r=o(t,e);if(-1!==r){n=n.slice(2);for(var i=[],a=t[r],s=0;sn;n+=2){var o=t.charCodeAt(n);if(0===o)break;e+=String.fromCharCode(parseInt(t.substr(n,2),16))}return e},fromAscii:function(t,e){e=void 0===e?0:e;for(var n=this.toHex(t);n.length<2*e;)n+="00";return"0x"+n},toDecimal:function(t){return p(t.substring(2))},fromDecimal:function(t){return"0x"+d(t)},toEth:function(t){for(var e="string"==typeof t?0===t.indexOf("0x")?parseInt(t.substr(2),16):parseInt(t):t,n=0,r=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e>3e3&&n 32) + return false; // not allowed to be so big. + padding = 32; // override as per the new ABI. + + if (prefix === "string") + return web3.fromAscii(value, padding).substr(2); if (typeof value === "number") value = value.toString(16); else if (typeof value === "string") @@ -110,6 +116,8 @@ var setupInputTypes = function () { return false; } + padding = 32; //override as per the new ABI. + return padLeft(formatter ? formatter(value) : value, padding * 2); }; }; @@ -140,7 +148,6 @@ var toAbiInput = function (json, methodName, params) { return; } - bytes = "0x" + padLeft(index.toString(16), 2); var method = json[index]; for (var i = 0; i < method.inputs.length; i++) { @@ -166,12 +173,16 @@ var setupOutputTypes = function () { } var padding = calcPadding(type, expected); + if (padding > 32) + return -1; // not allowed to be so big. + padding = 32; // override as per the new ABI. return padding * 2; }; }; var namedType = function (name, padding) { return function (type) { + padding = 32; // override as per the new ABI. return name === type ? padding * 2 : -1; }; }; @@ -259,7 +270,21 @@ var outputParser = function (json) { return parser; }; +var methodSignature = function (json, name) { + var method = json[findMethodIndex(json, name)]; + var result = name + '('; + var inputTypes = method.inputs.map(function (inp) { + return inp.type; + }); + result += inputTypes.join(','); + result += ')'; + + return web3.sha3(web3.fromAscii(result)); +}; + module.exports = { inputParser: inputParser, - outputParser: outputParser + outputParser: outputParser, + methodSignature: methodSignature }; + diff --git a/libjsqrc/ethereumjs/lib/autoprovider.js b/libjsqrc/ethereumjs/lib/autoprovider.js index 2f0a3e627..113873674 100644 --- a/libjsqrc/ethereumjs/lib/autoprovider.js +++ b/libjsqrc/ethereumjs/lib/autoprovider.js @@ -30,7 +30,7 @@ // TODO: is these line is supposed to be here? if (process.env.NODE_ENV !== 'build') { var WebSocket = require('ws'); // jshint ignore:line - var web3 = require('./main.js'); // jshint ignore:line + var web3 = require('./web3'); // jshint ignore:line } var AutoProvider = function (userOptions) { diff --git a/libjsqrc/ethereumjs/lib/contract.js b/libjsqrc/ethereumjs/lib/contract.js index b10339003..eb7fbf018 100644 --- a/libjsqrc/ethereumjs/lib/contract.js +++ b/libjsqrc/ethereumjs/lib/contract.js @@ -27,6 +27,9 @@ if (process.env.NODE_ENV !== 'build') { var abi = require('./abi'); +// method signature length in bytes +var ETH_METHOD_SIGNATURE_LENGTH = 4; + var contract = function (address, desc) { var inputParser = abi.inputParser(desc); var outputParser = abi.outputParser(desc); @@ -46,14 +49,18 @@ var contract = function (address, desc) { call: function (extra) { extra = extra || {}; extra.to = address; - extra.data = parsed; - return web3.eth.call(extra).then(onSuccess); + return abi.methodSignature(desc, method.name).then(function (signature) { + extra.data = signature.slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2) + parsed; + return web3.eth.call(extra).then(onSuccess); + }); }, transact: function (extra) { extra = extra || {}; extra.to = address; - extra.data = parsed; - return web3.eth.transact(extra).then(onSuccess); + return abi.methodSignature(desc, method.name).then(function (signature) { + extra.data = signature.slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2) + parsed; + return web3.eth.transact(extra).then(onSuccess); + }); } }; }; diff --git a/libjsqrc/ethereumjs/lib/main.js b/libjsqrc/ethereumjs/lib/web3.js similarity index 99% rename from libjsqrc/ethereumjs/lib/main.js rename to libjsqrc/ethereumjs/lib/web3.js index b240bdae2..4220114f9 100644 --- a/libjsqrc/ethereumjs/lib/main.js +++ b/libjsqrc/ethereumjs/lib/web3.js @@ -256,7 +256,7 @@ var web3 = { }, fromAscii: function(str, pad) { - pad = pad === undefined ? 32 : pad; + pad = pad === undefined ? 0 : pad; var hex = this.toHex(str); while(hex.length < pad*2) hex += "00"; @@ -505,4 +505,5 @@ function messageHandler(data) { } } -module.exports = web3; +if (typeof(module) !== "undefined") + module.exports = web3; diff --git a/libjsqrc/ethereumjs/lib/websocket.js b/libjsqrc/ethereumjs/lib/websocket.js index ddb44aed5..5b40075e4 100644 --- a/libjsqrc/ethereumjs/lib/websocket.js +++ b/libjsqrc/ethereumjs/lib/websocket.js @@ -74,4 +74,5 @@ Object.defineProperty(WebSocketProvider.prototype, "onmessage", { set: function(provider) { this.onMessage(provider); } }); -module.exports = WebSocketProvider; +if (typeof(module) !== "undefined") + module.exports = WebSocketProvider; diff --git a/libjsqrc/ethereumjs/package.json b/libjsqrc/ethereumjs/package.json index 24141ea2e..b4f180d10 100644 --- a/libjsqrc/ethereumjs/package.json +++ b/libjsqrc/ethereumjs/package.json @@ -1,7 +1,7 @@ { "name": "ethereum.js", "namespace": "ethereum", - "version": "0.0.5", + "version": "0.0.7", "description": "Ethereum Compatible JavaScript API", "main": "./index.js", "directories": { @@ -25,12 +25,14 @@ "jshint": ">=2.5.0", "uglifyify": "^2.6.0", "unreachable-branch-transform": "^0.1.0", - "vinyl-source-stream": "^1.0.0" + "vinyl-source-stream": "^1.0.0", + "mocha": ">=2.1.0" }, "scripts": { "build": "gulp", "watch": "gulp watch", - "lint": "gulp lint" + "lint": "gulp lint", + "test": "mocha" }, "repository": { "type": "git", diff --git a/libjsqrc/ethereumjs/test/abi.parsers.js b/libjsqrc/ethereumjs/test/abi.parsers.js new file mode 100644 index 000000000..06a77fb86 --- /dev/null +++ b/libjsqrc/ethereumjs/test/abi.parsers.js @@ -0,0 +1,37 @@ +var assert = require('assert'); +var abi = require('../lib/abi.js'); + +describe('abi', function() { + describe('inputParser', function() { + it('should parse ...', function() { + + var desc = [{ + "name": "multiply", + "inputs": [ + { + "name": "a", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "d", + "type": "uint256" + } + ] + }]; + + var iParser = abi.inputParser(desc); + assert.equal(iParser.multiply(1), "0x000000000000000000000000000000000000000000000000000000000000000001"); + + }); + }); + + + describe('outputParser', function() { + it('parse ...', function() { + + }); + }); +}); + diff --git a/libjsqrc/ethereumjs/test/db.methods.js b/libjsqrc/ethereumjs/test/db.methods.js new file mode 100644 index 000000000..b4abfc4d7 --- /dev/null +++ b/libjsqrc/ethereumjs/test/db.methods.js @@ -0,0 +1,18 @@ +require('es6-promise').polyfill(); + +var assert = require('assert'); +var web3 = require('../index.js'); +var u = require('./utils.js'); +web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider + +describe('web3', function() { + describe('db', function() { + it('should have all methods implemented', function() { + u.methodExists(web3.db, 'put'); + u.methodExists(web3.db, 'get'); + u.methodExists(web3.db, 'putString'); + u.methodExists(web3.db, 'getString'); + }); + }); +}); + diff --git a/libjsqrc/ethereumjs/test/eth.methods.js b/libjsqrc/ethereumjs/test/eth.methods.js new file mode 100644 index 000000000..7190b27d2 --- /dev/null +++ b/libjsqrc/ethereumjs/test/eth.methods.js @@ -0,0 +1,42 @@ +require('es6-promise').polyfill(); + +var assert = require('assert'); +var web3 = require('../index.js'); +var u = require('./utils.js'); +web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider + +describe('web3', function() { + describe('eth', function() { + it('should have all methods implemented', function() { + u.methodExists(web3.eth, 'balanceAt'); + u.methodExists(web3.eth, 'stateAt'); + u.methodExists(web3.eth, 'storageAt'); + u.methodExists(web3.eth, 'countAt'); + u.methodExists(web3.eth, 'codeAt'); + u.methodExists(web3.eth, 'transact'); + u.methodExists(web3.eth, 'call'); + u.methodExists(web3.eth, 'block'); + u.methodExists(web3.eth, 'transaction'); + u.methodExists(web3.eth, 'uncle'); + u.methodExists(web3.eth, 'compilers'); + u.methodExists(web3.eth, 'lll'); + u.methodExists(web3.eth, 'solidity'); + u.methodExists(web3.eth, 'serpent'); + u.methodExists(web3.eth, 'logs'); + }); + + it('should have all properties implemented', function () { + u.propertyExists(web3.eth, 'coinbase'); + u.propertyExists(web3.eth, 'listening'); + u.propertyExists(web3.eth, 'mining'); + u.propertyExists(web3.eth, 'gasPrice'); + u.propertyExists(web3.eth, 'account'); + u.propertyExists(web3.eth, 'accounts'); + u.propertyExists(web3.eth, 'peerCount'); + u.propertyExists(web3.eth, 'defaultBlock'); + u.propertyExists(web3.eth, 'number'); + }); + }); +}); + + diff --git a/libjsqrc/ethereumjs/test/mocha.opts b/libjsqrc/ethereumjs/test/mocha.opts new file mode 100644 index 000000000..b2db8d5a7 --- /dev/null +++ b/libjsqrc/ethereumjs/test/mocha.opts @@ -0,0 +1,2 @@ +--reporter Spec + diff --git a/libjsqrc/ethereumjs/test/shh.methods.js b/libjsqrc/ethereumjs/test/shh.methods.js new file mode 100644 index 000000000..08f573a3c --- /dev/null +++ b/libjsqrc/ethereumjs/test/shh.methods.js @@ -0,0 +1,19 @@ +require('es6-promise').polyfill(); + +var assert = require('assert'); +var web3 = require('../index.js'); +var u = require('./utils.js'); +web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider + +describe('web3', function() { + describe('shh', function() { + it('should have all methods implemented', function() { + u.methodExists(web3.shh, 'post'); + u.methodExists(web3.shh, 'newIdentity'); + u.methodExists(web3.shh, 'haveIdentity'); + u.methodExists(web3.shh, 'newGroup'); + u.methodExists(web3.shh, 'addToGroup'); + }); + }); +}); + diff --git a/libjsqrc/ethereumjs/test/utils.js b/libjsqrc/ethereumjs/test/utils.js new file mode 100644 index 000000000..4c508da67 --- /dev/null +++ b/libjsqrc/ethereumjs/test/utils.js @@ -0,0 +1,15 @@ +var assert = require('assert'); + +var methodExists = function (object, method) { + assert.equal('function', typeof object[method], 'method ' + method + ' is not implemented'); +}; + +var propertyExists = function (object, property) { + assert.equal('object', typeof object[property], 'property ' + property + ' is not implemented'); +}; + +module.exports = { + methodExists: methodExists, + propertyExists: propertyExists +}; + diff --git a/libjsqrc/ethereumjs/test/web3.methods.js b/libjsqrc/ethereumjs/test/web3.methods.js new file mode 100644 index 000000000..a7e020978 --- /dev/null +++ b/libjsqrc/ethereumjs/test/web3.methods.js @@ -0,0 +1,18 @@ +require('es6-promise').polyfill(); + +var assert = require('assert'); +var web3 = require('../index.js'); +var u = require('./utils.js'); +web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider + +describe('web3', function() { + it('should have all methods implemented', function() { + u.methodExists(web3, 'sha3'); + u.methodExists(web3, 'toAscii'); + u.methodExists(web3, 'fromAscii'); + u.methodExists(web3, 'toFixed'); + u.methodExists(web3, 'fromFixed'); + u.methodExists(web3, 'offset'); + }); +}); + diff --git a/libjsqrc/js.qrc b/libjsqrc/js.qrc index eee8f6652..896ab472f 100644 --- a/libjsqrc/js.qrc +++ b/libjsqrc/js.qrc @@ -1,7 +1,7 @@ - - - es6-promise-2.0.0.js - setup.js - ethereumjs/dist/ethereum.js - + + + es6-promise-2.0.0.js + setup.js + ethereumjs/dist/ethereum.js + diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index d1c3cd19b..4be6f228f 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -558,7 +558,7 @@ void Host::prunePeers() for (auto i: m_peers) if (!dc.count(i.first)) if (auto p = i.second.lock()) - if (/*(m_mode != NodeMode::Host || p->m_caps != 0x01) &&*/ chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers. + if (chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers. { ++agedPeers; if ((!worst || p->rating() < worst->rating() || (p->rating() == worst->rating() && p->m_connect > worst->m_connect))) // kill older ones diff --git a/libqethereum/CMakeLists.txt b/libqwebthree/CMakeLists.txt similarity index 98% rename from libqethereum/CMakeLists.txt rename to libqwebthree/CMakeLists.txt index 683bfec56..4de131ead 100644 --- a/libqethereum/CMakeLists.txt +++ b/libqwebthree/CMakeLists.txt @@ -15,7 +15,7 @@ aux_source_directory(. SRC_LIST) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(..) -set(EXECUTABLE qethereum) +set(EXECUTABLE qwebthree) file(GLOB HEADERS "*.h") diff --git a/libqethereum/QEthereum.cpp b/libqwebthree/QWebThree.cpp similarity index 89% rename from libqethereum/QEthereum.cpp rename to libqwebthree/QWebThree.cpp index daf773a22..891d84ffa 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqwebthree/QWebThree.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file QEthereum.cpp +/** @file QWebThree.cpp * @authors: * Gav Wood * Marek Kotewicz @@ -22,7 +22,7 @@ */ #include -#include "QEthereum.h" +#include "QWebThree.h" using namespace std; @@ -134,24 +134,7 @@ void QWebThree::onDataProcessed(QString _json, QString _addInfo) if (!_addInfo.compare("internal")) return; - if (!_addInfo.compare("eth_changed")) - { - QJsonArray resultsArray = QJsonDocument::fromJson(_json.toUtf8()).array(); - for (int i = 0; i < resultsArray.size(); i++) - { - QJsonObject elem = resultsArray[i].toObject(); - if (elem.contains("result") && elem["result"].toBool() == true) - { - QJsonObject res; - res["_event"] = _addInfo; - res["_id"] = (int)m_watches[i]; // we can do that couse poll is synchronous - response(QString::fromUtf8(QJsonDocument(res).toJson())); - } - } - return; - } - - if (!_addInfo.compare("shh_changed")) + if (!_addInfo.compare("shh_changed") || !_addInfo.compare("eth_changed")) { QJsonArray resultsArray = QJsonDocument::fromJson(_json.toUtf8()).array(); for (int i = 0; i < resultsArray.size(); i++) @@ -161,13 +144,18 @@ void QWebThree::onDataProcessed(QString _json, QString _addInfo) { QJsonObject res; res["_event"] = _addInfo; - res["_id"] = (int)m_shhWatches[i]; + + if (!_addInfo.compare("shh_changed")) + res["_id"] = (int)m_shhWatches[i]; // we can do that couse poll is synchronous + else + res["_id"] = (int)m_watches[i]; + res["data"] = elem["result"].toArray(); response(QString::fromUtf8(QJsonDocument(res).toJson())); } } } - + QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object(); if ((!_addInfo.compare("eth_newFilter") || !_addInfo.compare("eth_newFilterString")) && f.contains("result")) diff --git a/libqethereum/QEthereum.h b/libqwebthree/QWebThree.h similarity index 96% rename from libqethereum/QEthereum.h rename to libqwebthree/QWebThree.h index 657986b36..b223d45d0 100644 --- a/libqethereum/QEthereum.h +++ b/libqwebthree/QWebThree.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file QEthereum.h +/** @file QWebThree.h * @authors: * Gav Wood * Marek Kotewicz @@ -85,7 +85,7 @@ private: _frame->addToJavaScriptWindowObject("_web3", qweb, QWebFrame::ScriptOwnership); \ _frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \ _frame->evaluateJavaScript(contentsOfQResource(":/js/es6-promise-2.0.0.js")); \ - _frame->evaluateJavaScript(contentsOfQResource(":/js/ethereum.js")); \ + _frame->evaluateJavaScript(contentsOfQResource(":/js/webthree.js")); \ _frame->evaluateJavaScript(contentsOfQResource(":/js/setup.js")); \ } diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 047711067..d171006a8 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -27,6 +27,8 @@ #include #include +#include + using namespace std; namespace dev @@ -50,18 +52,17 @@ void ContractDefinition::checkTypeRequirements() function->checkTypeRequirements(); } -vector ContractDefinition::getInterfaceFunctions() const +map, FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const { - vector exportedFunctions; + map, FunctionDefinition const*> exportedFunctions; for (ASTPointer const& f: m_definedFunctions) if (f->isPublic() && f->getName() != getName()) - exportedFunctions.push_back(f.get()); - auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b) - { - return _a->getName().compare(_b->getName()) < 0; - }; + { + 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"); + } - sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames); return exportedFunctions; } @@ -173,7 +174,15 @@ void VariableDefinition::checkTypeRequirements() { // no type declared and no previous assignment, infer the type m_value->checkTypeRequirements(); - m_variable->setType(m_value->getType()); + TypePointer type = m_value->getType(); + if (type->getCategory() == Type::Category::INTEGER_CONSTANT) + { + auto intType = dynamic_pointer_cast(type)->getIntegerType(); + if (!intType) + BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString())); + type = intType; + } + m_variable->setType(type); } } } @@ -192,16 +201,22 @@ void Assignment::checkTypeRequirements() { // compound assignment m_rightHandSide->checkTypeRequirements(); - TypePointer resultType = Type::binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), - m_type, m_rightHandSide->getType()); + TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), + m_rightHandSide->getType()); if (!resultType || *resultType != *m_type) - BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type.")); + BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_assigmentOperator)) + + " not compatible with types " + + m_type->toString() + " and " + + m_rightHandSide->getType()->toString())); } } void ExpressionStatement::checkTypeRequirements() { m_expression->checkTypeRequirements(); + if (m_expression->getType()->getCategory() == Type::Category::INTEGER_CONSTANT) + if (!dynamic_pointer_cast(m_expression->getType())->getIntegerType()) + BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant.")); } void Expression::expectType(Type const& _expectedType) @@ -227,8 +242,8 @@ void UnaryOperation::checkTypeRequirements() m_subExpression->checkTypeRequirements(); if (m_operator == Token::Value::INC || m_operator == Token::Value::DEC || m_operator == Token::Value::DELETE) m_subExpression->requireLValue(); - m_type = m_subExpression->getType(); - if (!m_type->acceptsUnaryOperator(m_operator)) + m_type = m_subExpression->getType()->unaryOperatorResult(m_operator); + if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type.")); } @@ -236,7 +251,7 @@ void BinaryOperation::checkTypeRequirements() { m_left->checkTypeRequirements(); m_right->checkTypeRequirements(); - m_commonType = Type::binaryOperatorResult(m_operator, m_left->getType(), m_right->getType()); + m_commonType = m_left->getType()->binaryOperatorResult(m_operator, m_right->getType()); if (!m_commonType) BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) + " not compatible with types " + @@ -300,7 +315,7 @@ void NewExpression::checkTypeRequirements() m_contract = dynamic_cast(m_contractName->getReferencedDeclaration()); if (!m_contract) BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); - shared_ptr type = make_shared(*m_contract); + shared_ptr type = make_shared(*m_contract); m_type = type; TypePointers const& parameterTypes = type->getConstructorType()->getParameterTypes(); if (parameterTypes.size() != m_arguments.size()) @@ -351,7 +366,7 @@ void Identifier::checkTypeRequirements() if (structDef) { // note that we do not have a struct type here - m_type = make_shared(make_shared(*structDef)); + m_type = make_shared(make_shared(*structDef)); return; } FunctionDefinition const* functionDef = dynamic_cast(m_referencedDeclaration); @@ -360,13 +375,13 @@ void Identifier::checkTypeRequirements() // a function reference is not a TypeType, because calling a TypeType converts to the type. // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type // conversion. - m_type = make_shared(*functionDef); + m_type = make_shared(*functionDef); return; } ContractDefinition const* contractDef = dynamic_cast(m_referencedDeclaration); if (contractDef) { - m_type = make_shared(make_shared(*contractDef)); + m_type = make_shared(make_shared(*contractDef)); return; } MagicVariableDeclaration const* magicVariable = dynamic_cast(m_referencedDeclaration); @@ -380,14 +395,14 @@ void Identifier::checkTypeRequirements() void ElementaryTypeNameExpression::checkTypeRequirements() { - m_type = make_shared(Type::fromElementaryTypeName(m_typeToken)); + m_type = make_shared(Type::fromElementaryTypeName(m_typeToken)); } void Literal::checkTypeRequirements() { m_type = Type::forLiteral(*this); if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Literal value too large.")); + BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); } } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 8493d4323..95121d4cb 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -183,8 +183,9 @@ public: /// Can contain a nullptr in which case indicates absence of documentation ASTPointer const& getDocumentation() const { return m_documentation; } - /// Returns the functions that make up the calling interface in the intended order. - std::vector getInterfaceFunctions() const; + /// @returns a map of canonical function signatures to FunctionDefinitions + /// as intended for use by the ABI. + std::map, FunctionDefinition const*> getInterfaceFunctions() const; /// Returns the constructor or nullptr if no constructor was specified FunctionDefinition const* getConstructor() const; diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 0a0b62bdd..9c0b50775 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -28,6 +28,7 @@ endif() target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcore) +target_link_libraries(${EXECUTABLE} devcrypto) install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 394ae5f84..782a7efe2 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -95,12 +95,12 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program unsigned argumentSize = 0; for (ASTPointer const& var: _constructor.getParameters()) - argumentSize += var->getType()->getCalldataEncodedSize(); + argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize()); if (argumentSize > 0) { m_context << u256(argumentSize); m_context.appendProgramSize(); - m_context << u256(1); // copy it to byte one as expected for ABI calls + m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls m_context << eth::Instruction::CODECOPY; appendCalldataUnpacker(_constructor, true); } @@ -118,35 +118,27 @@ set Compiler::getFunctionsNeededByConstructor(Functio void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { - vector interfaceFunctions = _contract.getInterfaceFunctions(); - vector callDataUnpackerEntryPoints; - - if (interfaceFunctions.size() > 255) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract.")); - - // retrieve the first byte of the call data, which determines the called function - // @todo This code had a jump table in a previous version which was more efficient but also - // error prone (due to the optimizer and variable length tag addresses) - m_context << u256(1) << u256(0) // some constants - << eth::dupInstruction(1) << eth::Instruction::CALLDATALOAD - << eth::dupInstruction(2) << eth::Instruction::BYTE - << eth::dupInstruction(2); - - // stack here: 1 0 0, stack top will be counted up until it matches funid - for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid) + map, FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions(); + map, const eth::AssemblyItem> callDataUnpackerEntryPoints; + + // retrieve the function signature hash from the calldata + m_context << u256(1) << u256(0); + CompilerUtils(m_context).loadFromMemory(0, 4, false, true); + + // stack now is: 1 0 + // for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it) + for (auto const& it: interfaceFunctions) { - callDataUnpackerEntryPoints.push_back(m_context.newTag()); - m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ; - m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back()); - if (funid < interfaceFunctions.size() - 1) - m_context << eth::dupInstruction(4) << eth::Instruction::ADD; + callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag())); + m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ; + m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); } m_context << eth::Instruction::STOP; // function not found - for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid) + for (auto const& it: interfaceFunctions) { - FunctionDefinition const& function = *interfaceFunctions[funid]; - m_context << callDataUnpackerEntryPoints[funid]; + FunctionDefinition const& function = *it.second; + m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(function); m_context.appendJumpTo(m_context.getFunctionEntryLabel(function)); @@ -158,18 +150,19 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory) { // We do not check the calldata size, everything is zero-padded. - unsigned dataOffset = 1; + unsigned dataOffset = CompilerUtils::dataStartOffset; // the 4 bytes of the function hash signature //@todo this can be done more efficiently, saving some CALLDATALOAD calls for (ASTPointer const& var: _function.getParameters()) { - unsigned const numBytes = var->getType()->getCalldataEncodedSize(); - if (numBytes > 32) + unsigned const c_numBytes = var->getType()->getCalldataEncodedSize(); + if (c_numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(var->getLocation()) << errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); - bool leftAligned = var->getType()->getCategory() == Type::Category::STRING; - CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory); - dataOffset += numBytes; + bool const c_leftAligned = var->getType()->getCategory() == Type::Category::STRING; + bool const c_padToWords = true; + dataOffset += CompilerUtils(m_context).loadFromMemory(dataOffset, c_numBytes, c_leftAligned, + !_fromMemory, c_padToWords); } return dataOffset; } @@ -189,10 +182,11 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function) << errinfo_sourceLocation(parameters[i]->getLocation()) << errinfo_comment("Type " + paramType.toString() + " not yet supported.")); CompilerUtils(m_context).copyToStackTop(stackDepth, paramType); - bool const leftAligned = paramType.getCategory() == Type::Category::STRING; - CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned); + ExpressionCompiler::appendTypeConversion(m_context, paramType, paramType, true); + bool const c_leftAligned = paramType.getCategory() == Type::Category::STRING; + bool const c_padToWords = true; + dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, c_leftAligned, c_padToWords); stackDepth -= paramType.getSizeOnStack(); - dataOffset += numBytes; } // note that the stack is not cleaned up here m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN; @@ -238,16 +232,16 @@ bool Compiler::visit(FunctionDefinition const& _function) // Note that the fact that the return arguments are of increasing index is vital for this // algorithm to work. - unsigned const argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters()); - unsigned const returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters()); - unsigned const localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables()); + unsigned const c_argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters()); + unsigned const c_returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters()); + unsigned const c_localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables()); vector stackLayout; - stackLayout.push_back(returnValuesSize); // target of return address - stackLayout += vector(argumentsSize, -1); // discard all arguments - for (unsigned i = 0; i < returnValuesSize; ++i) + stackLayout.push_back(c_returnValuesSize); // target of return address + stackLayout += vector(c_argumentsSize, -1); // discard all arguments + for (unsigned i = 0; i < c_returnValuesSize; ++i) stackLayout.push_back(i); - stackLayout += vector(localVariablesSize, -1); + stackLayout += vector(c_localVariablesSize, -1); while (stackLayout.back() != int(stackLayout.size() - 1)) if (stackLayout.back() < 0) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 79716fdec..174f9cd22 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -16,6 +16,7 @@ */ /** * @author Christian + * @author Gav Wood * @date 2014 * Full-stack compiler that converts a source code string to bytecode. */ @@ -140,10 +141,15 @@ void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractN string const& CompilerStack::getInterface(string const& _contractName) const { - return getJsonDocumentation(_contractName, DocumentationType::ABI_INTERFACE); + return getMetadata(_contractName, DocumentationType::ABI_INTERFACE); } -string const& CompilerStack::getJsonDocumentation(string const& _contractName, DocumentationType _type) const +string const& CompilerStack::getSolidityInterface(string const& _contractName) const +{ + return getMetadata(_contractName, DocumentationType::ABI_SOLIDITY_INTERFACE); +} + +string const& CompilerStack::getMetadata(string const& _contractName, DocumentationType _type) const { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); @@ -162,6 +168,9 @@ string const& CompilerStack::getJsonDocumentation(string const& _contractName, D case DocumentationType::ABI_INTERFACE: doc = &contract.interface; break; + case DocumentationType::ABI_SOLIDITY_INTERFACE: + doc = &contract.solidityInterface; + break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); } diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index e7143b7bb..afc9a5162 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -16,6 +16,7 @@ */ /** * @author Christian + * @author Gav Wood * @date 2014 * Full-stack compiler that converts a source code string to bytecode. */ @@ -43,7 +44,8 @@ enum class DocumentationType: uint8_t { NATSPEC_USER = 1, NATSPEC_DEV, - ABI_INTERFACE + ABI_INTERFACE, + ABI_SOLIDITY_INTERFACE }; /** @@ -81,11 +83,14 @@ public: /// Returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. std::string const& getInterface(std::string const& _contractName = "") const; + /// Returns a string representing the contract interface in Solidity. + /// Prerequisite: Successful call to parse or compile. + std::string const& getSolidityInterface(std::string const& _contractName = "") const; /// Returns a string representing the contract's documentation in JSON. /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. - /// Can be one of 3 types defined at @c DocumentationType - std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const; + /// Can be one of 4 types defined at @c DocumentationType + std::string const& getMetadata(std::string const& _contractName, DocumentationType _type) const; /// @returns the previously used scanner, useful for counting lines during error reporting. Scanner const& getScanner(std::string const& _sourceName = "") const; @@ -118,6 +123,7 @@ private: bytes bytecode; std::shared_ptr interfaceHandler; mutable std::unique_ptr interface; + mutable std::unique_ptr solidityInterface; mutable std::unique_ptr userDocumentation; mutable std::unique_ptr devDocumentation; diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index 680e9190b..3101c1b44 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -31,17 +31,23 @@ namespace dev namespace solidity { -void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata) +const unsigned int CompilerUtils::dataStartOffset = 4; + +unsigned CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, + bool _fromCalldata, bool _padToWordBoundaries) { if (_bytes == 0) { m_context << u256(0); - return; + return 0; } eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD; solAssert(_bytes <= 32, "Memory load of more than 32 bytes requested."); - if (_bytes == 32) + if (_bytes == 32 || _padToWordBoundaries) + { m_context << u256(_offset) << load; + return 32; + } else { // load data and add leading or trailing zeros by dividing/multiplying depending on alignment @@ -52,21 +58,24 @@ void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _left m_context << u256(_offset) << load << eth::Instruction::DIV; if (_leftAligned) m_context << eth::Instruction::MUL; + return _bytes; } } -void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned) +unsigned CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, + bool _padToWordBoundaries) { if (_bytes == 0) { m_context << eth::Instruction::POP; - return; + return 0; } solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested."); - if (_bytes != 32 && !_leftAligned) + if (_bytes != 32 && !_leftAligned && !_padToWordBoundaries) // shift the value accordingly before storing m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL; m_context << u256(_offset) << eth::Instruction::MSTORE; + return _padToWordBoundaries ? 32 : _bytes; } void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h index 928f0e2d3..b5a695282 100644 --- a/libsolidity/CompilerUtils.h +++ b/libsolidity/CompilerUtils.h @@ -40,12 +40,23 @@ public: /// @param _bytes number of bytes to load /// @param _leftAligned if true, store left aligned on stack (otherwise right aligned) /// @param _fromCalldata if true, load from calldata, not from memory - void loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, bool _fromCalldata = false); + /// @param _padToWordBoundaries if true, assume the data is padded to word (32 byte) boundaries + /// @returns the number of bytes consumed in memory (can be different from _bytes if + /// _padToWordBoundaries is true) + unsigned loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, + bool _fromCalldata = false, bool _padToWordBoundaries = false); /// Stores data from stack in memory. /// @param _offset offset in memory /// @param _bytes number of bytes to store /// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned) - void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false); + /// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries + /// @returns the number of bytes written to memory (can be different from _bytes if + /// _padToWordBoundaries is true) + unsigned storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, + bool _padToWordBoundaries = false); + /// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the + /// padded calldata) + static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; } /// Moves the value that is at the top of the stack to a stack variable. void moveToStackVariable(VariableDeclaration const& _variable); @@ -58,10 +69,14 @@ public: static unsigned getSizeOnStack(std::vector const& _variables); static unsigned getSizeOnStack(std::vector> const& _variableTypes); + /// Bytes we need to the start of call data. + /// - The size in bytes of the function (hash) identifier. + static const unsigned int dataStartOffset; private: CompilerContext& m_context; }; + template unsigned CompilerUtils::getSizeOnStack(std::vector const& _variables) { diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index aa7406132..1c02f4f32 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -41,11 +41,11 @@ void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression _expression.accept(compiler); } -void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, - Type const& _typeOnStack, Type const& _targetType) +void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, + Type const& _targetType, bool _cleanupNeeded) { ExpressionCompiler compiler(_context); - compiler.appendTypeConversion(_typeOnStack, _targetType); + compiler.appendTypeConversion(_typeOnStack, _targetType, _cleanupNeeded); } bool ExpressionCompiler::visit(Assignment const& _assignment) @@ -71,12 +71,20 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) return false; } -void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation) +bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) { //@todo type checking and creating code for an operator should be in the same place: // the operator should know how to convert itself and to which types it applies, so // put this code together with "Type::acceptsBinary/UnaryOperator" into a class that // represents the operator + if (_unaryOperation.getType()->getCategory() == Type::Category::INTEGER_CONSTANT) + { + m_context << _unaryOperation.getType()->literalValue(nullptr); + return false; + } + + _unaryOperation.getSubExpression().accept(*this); + switch (_unaryOperation.getOperator()) { case Token::NOT: // ! @@ -128,6 +136,7 @@ void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid unary operator: " + string(Token::toString(_unaryOperation.getOperator())))); } + return false; } bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) @@ -135,21 +144,23 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) Expression const& leftExpression = _binaryOperation.getLeftExpression(); Expression const& rightExpression = _binaryOperation.getRightExpression(); Type const& commonType = _binaryOperation.getCommonType(); - Token::Value const op = _binaryOperation.getOperator(); + Token::Value const c_op = _binaryOperation.getOperator(); - if (op == Token::AND || op == Token::OR) // special case: short-circuiting + if (c_op == Token::AND || c_op == Token::OR) // special case: short-circuiting appendAndOrOperatorCode(_binaryOperation); + else if (commonType.getCategory() == Type::Category::INTEGER_CONSTANT) + m_context << commonType.literalValue(nullptr); else { - bool cleanupNeeded = false; - if (commonType.getCategory() == Type::Category::INTEGER) - if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD) - cleanupNeeded = true; + bool cleanupNeeded = commonType.getCategory() == Type::Category::INTEGER && + (Token::isCompareOp(c_op) || c_op == Token::DIV || c_op == Token::MOD); // for commutative operators, push the literal as late as possible to allow improved optimization - //@todo this has to be extended for literal expressions - bool swap = (m_optimize && Token::isCommutativeOp(op) && dynamic_cast(&rightExpression) - && !dynamic_cast(&leftExpression)); + auto isLiteral = [](Expression const& _e) + { + return dynamic_cast(&_e) || _e.getType()->getCategory() == Type::Category::INTEGER_CONSTANT; + }; + bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); if (swap) { leftExpression.accept(*this); @@ -164,10 +175,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) leftExpression.accept(*this); appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); } - if (Token::isCompareOp(op)) - appendCompareOperatorCode(op, commonType); + if (Token::isCompareOp(c_op)) + appendCompareOperatorCode(c_op, commonType); else - appendOrdinaryBinaryOperatorCode(op, commonType); + appendOrdinaryBinaryOperatorCode(c_op, commonType); } // do not visit the child nodes, we already did that explicitly @@ -183,13 +194,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(_functionCall.getArguments().size() == 1, ""); Expression const& firstArgument = *_functionCall.getArguments().front(); firstArgument.accept(*this); - if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT && - _functionCall.getType()->getCategory() == Type::Category::INTEGER) - { - // explicit type conversion contract -> address, nothing to do. - } - else - appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); + appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); } else { @@ -239,13 +244,17 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) FunctionCallOptions options; options.bare = true; options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); }; - options.obtainValue = [&]() { arguments.front()->accept(*this); }; - appendExternalFunctionCall(FunctionType({}, {}, Location::EXTERNAL), {}, options); + options.obtainValue = [&]() + { + arguments.front()->accept(*this); + appendTypeConversion(*arguments.front()->getType(), + *function.getParameterTypes().front(), true); + }; + appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL), {}, options); break; } case Location::SUICIDE: arguments.front()->accept(*this); - //@todo might not be necessary appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); m_context << eth::Instruction::SUICIDE; break; @@ -256,6 +265,23 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) CompilerUtils(m_context).storeInMemory(0); m_context << u256(32) << u256(0) << eth::Instruction::SHA3; break; + case Location::LOG0: + case Location::LOG1: + case Location::LOG2: + case Location::LOG3: + case Location::LOG4: + { + unsigned logNumber = int(function.getLocation()) - int(Location::LOG0); + for (int arg = logNumber; arg >= 0; --arg) + { + arguments[arg]->accept(*this); + appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true); + } + // @todo move this once we actually use memory + CompilerUtils(m_context).storeInMemory(0); + m_context << u256(32) << u256(0) << eth::logInstruction(logNumber); + break; + } case Location::ECRECOVER: case Location::SHA256: case Location::RIPEMD160: @@ -267,7 +293,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) FunctionCallOptions options; options.bare = true; options.obtainAddress = [&]() { m_context << contractAddress; }; - options.packDensely = false; appendExternalFunctionCall(function, arguments, options); break; } @@ -299,15 +324,16 @@ bool ExpressionCompiler::visit(NewExpression const& _newExpression) for (unsigned i = 0; i < arguments.size(); ++i) { arguments[i]->accept(*this); - appendTypeConversion(*arguments[i]->getType(), *types[i]); - unsigned const numBytes = types[i]->getCalldataEncodedSize(); - if (numBytes > 32) + 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 leftAligned = types[i]->getCategory() == Type::Category::STRING; - CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned); - dataOffset += numBytes; + 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; @@ -319,6 +345,18 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) ASTString const& member = _memberAccess.getMemberName(); switch (_memberAccess.getExpression().getType()->getCategory()) { + case Type::Category::CONTRACT: + { + ContractType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); + u256 identifier = type.getFunctionIdentifier(member); + if (identifier != Invalid256) + { + appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true); + m_context << identifier; + break; + } + // fall-through to "integer" otherwise (address) + } case Type::Category::INTEGER: if (member == "balance") { @@ -332,12 +370,6 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); break; - case Type::Category::CONTRACT: - { - ContractType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - m_context << type.getFunctionIndex(member); - break; - } case Type::Category::MAGIC: // we can ignore the kind of magic and only look at the name of the member if (member == "coinbase") @@ -423,10 +455,10 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { switch (_literal.getType()->getCategory()) { - case Type::Category::INTEGER: + case Type::Category::INTEGER_CONSTANT: case Type::Category::BOOL: case Type::Category::STRING: - m_context << _literal.getType()->literalValue(_literal); + m_context << _literal.getType()->literalValue(&_literal); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now.")); @@ -435,12 +467,12 @@ void ExpressionCompiler::endVisit(Literal const& _literal) void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation) { - Token::Value const op = _binaryOperation.getOperator(); - solAssert(op == Token::OR || op == Token::AND, ""); + Token::Value const c_op = _binaryOperation.getOperator(); + solAssert(c_op == Token::OR || c_op == Token::AND, ""); _binaryOperation.getLeftExpression().accept(*this); m_context << eth::Instruction::DUP1; - if (op == Token::AND) + if (c_op == Token::AND) m_context << eth::Instruction::ISZERO; eth::AssemblyItem endLabel = m_context.appendConditionalJump(); m_context << eth::Instruction::POP; @@ -459,23 +491,23 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type else { IntegerType const& type = dynamic_cast(_type); - bool const isSigned = type.isSigned(); + bool const c_isSigned = type.isSigned(); switch (_operator) { case Token::GTE: - m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT) + m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT) << eth::Instruction::ISZERO; break; case Token::LTE: - m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT) + m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT) << eth::Instruction::ISZERO; break; case Token::GT: - m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT); + m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT); break; case Token::LT: - m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT); + m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator.")); @@ -498,7 +530,7 @@ void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type) { IntegerType const& type = dynamic_cast(_type); - bool const isSigned = type.isSigned(); + bool const c_isSigned = type.isSigned(); switch (_operator) { @@ -512,10 +544,10 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty m_context << eth::Instruction::MUL; break; case Token::DIV: - m_context << (isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV); + m_context << (c_isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV); break; case Token::MOD: - m_context << (isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD); + m_context << (c_isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator.")); @@ -562,10 +594,38 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con if (_typeOnStack == _targetType && !_cleanupNeeded) return; - if (_typeOnStack.getCategory() == Type::Category::INTEGER) - appendHighBitsCleanup(dynamic_cast(_typeOnStack)); - else if (_typeOnStack.getCategory() == Type::Category::STRING) + Type::Category stackTypeCategory = _typeOnStack.getCategory(); + Type::Category targetTypeCategory = _targetType.getCategory(); + if (stackTypeCategory == Type::Category::INTEGER || stackTypeCategory == Type::Category::CONTRACT || + stackTypeCategory == Type::Category::INTEGER_CONSTANT) + { + solAssert(targetTypeCategory == Type::Category::INTEGER || targetTypeCategory == Type::Category::CONTRACT, ""); + IntegerType addressType(0, IntegerType::Modifier::ADDRESS); + IntegerType const& targetType = targetTypeCategory == Type::Category::INTEGER + ? dynamic_cast(_targetType) : addressType; + if (stackTypeCategory == Type::Category::INTEGER_CONSTANT) + { + IntegerConstantType const& constType = dynamic_cast(_typeOnStack); + // We know that the stack is clean, we only have to clean for a narrowing conversion + // where cleanup is forced. + if (targetType.getNumBits() < constType.getIntegerType()->getNumBits() && _cleanupNeeded) + appendHighBitsCleanup(targetType); + } + else + { + IntegerType const& typeOnStack = stackTypeCategory == Type::Category::INTEGER + ? dynamic_cast(_typeOnStack) : addressType; + // Widening: clean up according to source type width + // Non-widening and force: clean up according to target type bits + if (targetType.getNumBits() > typeOnStack.getNumBits()) + appendHighBitsCleanup(typeOnStack); + else if (_cleanupNeeded) + appendHighBitsCleanup(targetType); + } + } + else if (stackTypeCategory == Type::Category::STRING) { + solAssert(targetTypeCategory == Type::Category::STRING, ""); // nothing to do, strings are high-order-bit-aligned //@todo clear lower-order bytes if we allow explicit conversion to shorter strings } @@ -590,43 +650,47 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio { solAssert(_arguments.size() == _functionType.getParameterTypes().size(), ""); - unsigned dataOffset = _options.bare ? 0 : 1; // reserve one byte for the function index + _options.obtainAddress(); + if (!_options.bare) + CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset); + + unsigned dataOffset = _options.bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier for (unsigned i = 0; i < _arguments.size(); ++i) { _arguments[i]->accept(*this); Type const& type = *_functionType.getParameterTypes()[i]; - appendTypeConversion(*_arguments[i]->getType(), type); - unsigned const numBytes = _options.packDensely ? type.getCalldataEncodedSize() : 32; - if (numBytes == 0 || numBytes > 32) + 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 leftAligned = type.getCategory() == Type::Category::STRING; - CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned); - dataOffset += numBytes; + 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); } //@todo only return the first return value for now Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : _functionType.getReturnParameterTypes().front().get(); - unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0; - if (!_options.packDensely && retSize > 0) - retSize = 32; + 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(); else m_context << u256(0); - _options.obtainAddress(); - if (!_options.bare) - m_context << u256(0) << eth::Instruction::MSTORE8; + m_context << eth::dupInstruction(6); //copy contract address + 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 // @todo do not ignore failure indicator + << eth::Instruction::POP; // pop contract address + if (retSize > 0) { - bool const leftAligned = firstType->getCategory() == Type::Category::STRING; - CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned); + bool const c_leftAligned = firstType->getCategory() == Type::Category::STRING; + CompilerUtils(m_context).loadFromMemory(0, retSize, c_leftAligned, false, true); } } diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index c0ee4ab48..98f58c854 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -16,6 +16,7 @@ */ /** * @author Christian + * @author Gav Wood * @date 2014 * Solidity AST to EVM bytecode compiler for expressions. */ @@ -50,14 +51,15 @@ public: static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false); /// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type. - static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType); + static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, + Type const& _targetType, bool _cleanupNeeded = false); private: explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false): m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {} virtual bool visit(Assignment const& _assignment) override; - virtual void endVisit(UnaryOperation const& _unaryOperation) override; + virtual bool visit(UnaryOperation const& _unaryOperation) override; virtual bool visit(BinaryOperation const& _binaryOperation) override; virtual bool visit(FunctionCall const& _functionCall) override; virtual bool visit(NewExpression const& _newExpression) override; @@ -95,9 +97,6 @@ private: std::function obtainValue; /// If true, do not prepend function index to call data bool bare = false; - /// If false, use calling convention that all arguments and return values are packed as - /// 32 byte values with padding. - bool packDensely = true; }; /// Appends code to call a function of the given type with the given arguments. diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index f4805b1f7..92ca9548a 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -16,6 +16,7 @@ */ /** * @author Christian + * @author Gav Wood * @date 2014 * Container of the (implicit and explicit) global objects. */ @@ -34,32 +35,28 @@ namespace solidity GlobalContext::GlobalContext(): m_magicVariables(vector>{make_shared("block", make_shared(MagicType::Kind::BLOCK)), - make_shared("msg", make_shared(MagicType::Kind::MSG)), - make_shared("tx", make_shared(MagicType::Kind::TX)), - make_shared("suicide", - make_shared(TypePointers({std::make_shared(0, - IntegerType::Modifier::ADDRESS)}), - TypePointers(), - FunctionType::Location::SUICIDE)), - make_shared("sha3", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - FunctionType::Location::SHA3)), - make_shared("sha256", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - FunctionType::Location::SHA256)), - make_shared("ecrecover", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH), - std::make_shared(8, IntegerType::Modifier::HASH), - std::make_shared(256, IntegerType::Modifier::HASH), - std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers({std::make_shared(0, IntegerType::Modifier::ADDRESS)}), - FunctionType::Location::ECRECOVER)), - make_shared("ripemd160", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers({std::make_shared(160, IntegerType::Modifier::HASH)}), - FunctionType::Location::RIPEMD160))}) + make_shared("msg", make_shared(MagicType::Kind::MSG)), + make_shared("tx", make_shared(MagicType::Kind::TX)), + make_shared("suicide", + make_shared(strings{"address"}, strings{}, FunctionType::Location::SUICIDE)), + make_shared("sha3", + make_shared(strings{"hash"}, strings{"hash"}, FunctionType::Location::SHA3)), + make_shared("log0", + make_shared(strings{"hash"},strings{}, FunctionType::Location::LOG0)), + make_shared("log1", + make_shared(strings{"hash", "hash"},strings{}, FunctionType::Location::LOG1)), + make_shared("log2", + make_shared(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::LOG2)), + make_shared("log3", + make_shared(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::LOG3)), + make_shared("log4", + make_shared(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::LOG4)), + make_shared("sha256", + make_shared(strings{"hash"}, strings{"hash"}, FunctionType::Location::SHA256)), + make_shared("ecrecover", + make_shared(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRECOVER)), + make_shared("ripemd160", + make_shared(strings{"hash"}, strings{"hash160"}, FunctionType::Location::RIPEMD160))}) { } diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 224234cbd..a5c6f4a1d 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -2,6 +2,7 @@ #include #include #include +using namespace std; namespace dev { @@ -26,6 +27,8 @@ std::unique_ptr InterfaceHandler::getDocumentation(ContractDefiniti return getDevDocumentation(_contractDef); case DocumentationType::ABI_INTERFACE: return getABIInterface(_contractDef); + case DocumentationType::ABI_SOLIDITY_INTERFACE: + return getABISolidityInterface(_contractDef); } BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); @@ -36,7 +39,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio { Json::Value methods(Json::arrayValue); - for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) + for (auto const& it: _contractDef.getInterfaceFunctions()) { Json::Value method; Json::Value inputs(Json::arrayValue); @@ -55,24 +58,47 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio return params; }; - method["name"] = f->getName(); - method["constant"] = f->isDeclaredConst(); - method["inputs"] = populateParameters(f->getParameters()); - method["outputs"] = populateParameters(f->getReturnParameters()); + method["name"] = it.second->getName(); + method["constant"] = it.second->isDeclaredConst(); + method["inputs"] = populateParameters(it.second->getParameters()); + method["outputs"] = populateParameters(it.second->getReturnParameters()); methods.append(method); } return std::unique_ptr(new std::string(m_writer.write(methods))); } +unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef) +{ + string ret = "contract " + _contractDef.getName() + "{"; + for (auto const& it: _contractDef.getInterfaceFunctions()) + { + FunctionDefinition const* f = it.second; + auto populateParameters = [](vector> const& _vars) + { + string r = ""; + for (ASTPointer const& var: _vars) + r += (r.size() ? "," : "(") + var->getType()->toString() + " " + var->getName(); + return r.size() ? r + ")" : "()"; + }; + ret += "function " + f->getName() + populateParameters(f->getParameters()) + (f->isDeclaredConst() ? "constant " : ""); + if (f->getReturnParameters().size()) + ret += "returns" + populateParameters(f->getReturnParameters()); + else if (ret.back() == ' ') + ret.pop_back(); + ret += "{}"; + } + return unique_ptr(new string(ret + "}")); +} + std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef) { Json::Value doc; Json::Value methods(Json::objectValue); - for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) + for (auto const& it: _contractDef.getInterfaceFunctions()) { Json::Value user; - auto strPtr = f->getDocumentation(); + auto strPtr = it.second->getDocumentation(); if (strPtr) { resetUser(); @@ -80,7 +106,7 @@ std::unique_ptr 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[f->getName()] = user; + methods[it.second->getName()] = user; } } } @@ -110,10 +136,10 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin doc["title"] = m_title; } - for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) + for (auto const& it: _contractDef.getInterfaceFunctions()) { Json::Value method; - auto strPtr = f->getDocumentation(); + auto strPtr = it.second->getDocumentation(); if (strPtr) { resetDev(); @@ -136,7 +162,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["return"] = m_return; if (!method.empty()) // add the function, only if we have any documentation to add - methods[f->getName()] = method; + methods[it.second->getName()] = method; } } doc["methods"] = methods; @@ -323,8 +349,13 @@ void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _ } else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag currPos = appendDocTag(currPos, end, _owner); - else if (currPos != end) // skip the line if a newline was found + else if (currPos != end) + { + if (nlPos == end) //end of text + return; + // else skip the line if a newline was found currPos = nlPos + 1; + } } } diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index c8399d71f..2b62cabdf 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -74,6 +74,7 @@ public: /// @return A unique pointer contained string with the json /// representation of the contract's ABI Interface std::unique_ptr getABIInterface(ContractDefinition const& _contractDef); + std::unique_ptr getABISolidityInterface(ContractDefinition const& _contractDef); /// Get the User documentation of the contract /// @param _contractDef The contract definition /// @return A unique pointer contained string with the json diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 6433b5262..b283ca10e 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -455,7 +455,7 @@ void Scanner::scanToken() token = Token::ADD; break; case '-': - // - -- -= Number + // - -- -= advance(); if (m_char == '-') { @@ -464,8 +464,6 @@ void Scanner::scanToken() } else if (m_char == '=') token = selectToken(Token::ASSIGN_SUB); - else if (m_char == '.' || isDecimalDigit(m_char)) - token = scanNumber('-'); else token = Token::SUB; break; @@ -650,8 +648,7 @@ Token::Value Scanner::scanNumber(char _charSeen) } else { - if (_charSeen == '-') - addLiteralChar('-'); + solAssert(_charSeen == 0, ""); // if the first character is '0' we must check for octals and hex if (m_char == '0') { @@ -703,24 +700,6 @@ Token::Value Scanner::scanNumber(char _charSeen) return Token::NUMBER; } - -// ---------------------------------------------------------------------------- -// Keyword Matcher - - -static Token::Value keywordOrIdentifierToken(string const& _input) -{ - // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored - // and keywords to be put inside the keywords variable. -#define KEYWORD(name, string, precedence) {string, Token::name}, -#define TOKEN(name, string, precedence) - static const map keywords({TOKEN_LIST(TOKEN, KEYWORD)}); -#undef KEYWORD -#undef TOKEN - auto it = keywords.find(_input); - return it == keywords.end() ? Token::IDENTIFIER : it->second; -} - Token::Value Scanner::scanIdentifierOrKeyword() { solAssert(isIdentifierStart(m_char), ""); @@ -730,7 +709,7 @@ Token::Value Scanner::scanIdentifierOrKeyword() while (isIdentifierPart(m_char)) addLiteralCharAndAdvance(); literal.complete(); - return keywordOrIdentifierToken(m_nextToken.literal); + return Token::fromIdentifierOrKeyword(m_nextToken.literal); } char CharStream::advanceAndGet(size_t _chars) diff --git a/libsolidity/Token.cpp b/libsolidity/Token.cpp index 093bd9c1d..7dc56c327 100644 --- a/libsolidity/Token.cpp +++ b/libsolidity/Token.cpp @@ -40,8 +40,11 @@ // You should have received a copy of the GNU General Public License // along with cpp-ethereum. If not, see . +#include #include +using namespace std; + namespace dev { namespace solidity @@ -77,6 +80,20 @@ char const Token::m_tokenType[] = { TOKEN_LIST(KT, KK) }; + +Token::Value Token::fromIdentifierOrKeyword(const std::string& _name) +{ + // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored + // and keywords to be put inside the keywords variable. +#define KEYWORD(name, string, precedence) {string, Token::name}, +#define TOKEN(name, string, precedence) + static const map keywords({TOKEN_LIST(TOKEN, KEYWORD)}); +#undef KEYWORD +#undef TOKEN + auto it = keywords.find(_name); + return it == keywords.end() ? Token::IDENTIFIER : it->second; +} + #undef KT #undef KK diff --git a/libsolidity/Token.h b/libsolidity/Token.h index f2ffd076a..2d4441d08 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -386,6 +386,8 @@ public: return m_precedence[tok]; } + static Token::Value fromIdentifierOrKeyword(std::string const& _name); + private: static char const* const m_name[NUM_TOKENS]; static char const* const m_string[NUM_TOKENS]; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 7a81b65f6..7ca1dc6d5 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -35,7 +35,7 @@ namespace solidity shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) { - solAssert(Token::isElementaryTypeName(_typeToken), ""); + solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) { @@ -44,17 +44,17 @@ shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) if (bytes == 0) bytes = 32; int modifier = offset / 33; - return make_shared(bytes * 8, + return make_shared(bytes * 8, modifier == 0 ? IntegerType::Modifier::SIGNED : modifier == 1 ? IntegerType::Modifier::UNSIGNED : IntegerType::Modifier::HASH); } else if (_typeToken == Token::ADDRESS) - return make_shared(0, IntegerType::Modifier::ADDRESS); + return make_shared(0, IntegerType::Modifier::ADDRESS); else if (_typeToken == Token::BOOL) - return make_shared(); + return make_shared(); else if (Token::STRING0 <= _typeToken && _typeToken <= Token::STRING32) - return make_shared(int(_typeToken) - int(Token::STRING0)); + return make_shared(int(_typeToken) - int(Token::STRING0)); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type.")); @@ -64,11 +64,11 @@ shared_ptr Type::fromUserDefinedTypeName(UserDefinedTypeName const& { Declaration const* declaration = _typeName.getReferencedDeclaration(); if (StructDefinition const* structDef = dynamic_cast(declaration)) - return make_shared(*structDef); + return make_shared(*structDef); else if (FunctionDefinition const* function = dynamic_cast(declaration)) - return make_shared(*function); + return make_shared(*function); else if (ContractDefinition const* contract = dynamic_cast(declaration)) - return make_shared(*contract); + return make_shared(*contract); return shared_ptr(); } @@ -80,7 +80,7 @@ shared_ptr Type::fromMapping(Mapping const& _typeName) shared_ptr valueType = _typeName.getValueType().toType(); if (!valueType) BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); - return make_shared(keyType, valueType); + return make_shared(keyType, valueType); } shared_ptr Type::forLiteral(Literal const& _literal) @@ -89,14 +89,14 @@ shared_ptr Type::forLiteral(Literal const& _literal) { case Token::TRUE_LITERAL: case Token::FALSE_LITERAL: - return make_shared(); + return make_shared(); case Token::NUMBER: - return IntegerType::smallestTypeForLiteral(_literal.getValue()); + return IntegerConstantType::fromLiteral(_literal.getValue()); case Token::STRING_LITERAL: //@todo put larger strings into dynamic strings return StaticStringType::smallestTypeForLiteral(_literal.getValue()); default: - return shared_ptr(); + return shared_ptr(); } } @@ -112,19 +112,6 @@ TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) const MemberList Type::EmptyMemberList = MemberList(); -shared_ptr IntegerType::smallestTypeForLiteral(string const& _literal) -{ - bigint value(_literal); - bool isSigned = value < 0 || (!_literal.empty() && _literal.front() == '-'); - if (isSigned) - // convert to positive number of same bit requirements - value = ((-value) - 1) << 1; - unsigned bytes = max(bytesRequired(value), 1u); - if (bytes > 32) - return shared_ptr(); - return make_shared(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED); -} - IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): m_bits(_bits), m_modifier(_modifier) { @@ -156,18 +143,26 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT; } -bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const +TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const { + // "delete" is ok for all integer types if (_operator == Token::DELETE) - return true; - if (isAddress()) - return false; - if (_operator == Token::BIT_NOT) - return true; - if (isHash()) - return false; - return _operator == Token::ADD || _operator == Token::SUB || - _operator == Token::INC || _operator == Token::DEC; + return shared_from_this(); + // no further unary operators for addresses + else if (isAddress()) + return TypePointer(); + // "~" is ok for all other types + else if (_operator == Token::BIT_NOT) + return shared_from_this(); + // nothing else for hashes + else if (isHash()) + return TypePointer(); + // for non-hash integers, we allow +, -, ++ and -- + else if (_operator == Token::ADD || _operator == Token::SUB || + _operator == Token::INC || _operator == Token::DEC) + return shared_from_this(); + else + return TypePointer(); } bool IntegerType::operator==(Type const& _other) const @@ -186,17 +181,11 @@ string IntegerType::toString() const return prefix + dev::toString(m_bits); } -u256 IntegerType::literalValue(Literal const& _literal) const -{ - bigint value(_literal.getValue()); - return u256(value); -} - -TypePointer IntegerType::binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const +TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (getCategory() != _other->getCategory()) + if (_other->getCategory() != Category::INTEGER_CONSTANT && _other->getCategory() != getCategory()) return TypePointer(); - auto commonType = dynamic_pointer_cast(Type::commonType(_this, _other)); + auto commonType = dynamic_pointer_cast(Type::commonType(shared_from_this(), _other)); if (!commonType) return TypePointer(); @@ -215,18 +204,147 @@ TypePointer IntegerType::binaryOperatorResultImpl(Token::Value _operator, TypePo } const MemberList IntegerType::AddressMemberList = - MemberList({{"balance", - make_shared(256)}, - {"callstring32", - make_shared(TypePointers({make_shared(32)}), - TypePointers(), FunctionType::Location::BARE)}, - {"callstring32string32", - make_shared(TypePointers({make_shared(32), - make_shared(32)}), - TypePointers(), FunctionType::Location::BARE)}, - {"send", - make_shared(TypePointers({make_shared(256)}), - TypePointers(), FunctionType::Location::SEND)}}); + MemberList({{"balance", make_shared(256)}, + {"callstring32", make_shared(strings{"string32"}, strings{}, + FunctionType::Location::BARE)}, + {"callstring32string32", make_shared(strings{"string32", "string32"}, + strings{}, FunctionType::Location::BARE)}, + {"send", make_shared(strings{"uint"}, strings{}, FunctionType::Location::SEND)}}); + +shared_ptr IntegerConstantType::fromLiteral(string const& _literal) +{ + return make_shared(bigint(_literal)); +} + +bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + TypePointer integerType = getIntegerType(); + return integerType && integerType->isImplicitlyConvertibleTo(_convertTo); +} + +bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + TypePointer integerType = getIntegerType(); + return integerType && integerType->isExplicitlyConvertibleTo(_convertTo); +} + +TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) const +{ + bigint value; + switch (_operator) + { + case Token::BIT_NOT: + value = ~m_value; + break; + case Token::ADD: + value = m_value; + break; + case Token::SUB: + value = -m_value; + break; + default: + return TypePointer(); + } + return make_shared(value); +} + +TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + if (_other->getCategory() == Category::INTEGER) + { + shared_ptr integerType = getIntegerType(); + if (!integerType) + return TypePointer(); + return integerType->binaryOperatorResult(_operator, _other); + } + else if (_other->getCategory() != getCategory()) + return TypePointer(); + + IntegerConstantType const& other = dynamic_cast(*_other); + if (Token::isCompareOp(_operator)) + { + shared_ptr thisIntegerType = getIntegerType(); + shared_ptr otherIntegerType = other.getIntegerType(); + if (!thisIntegerType || !otherIntegerType) + return TypePointer(); + return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType); + } + else + { + bigint value; + switch (_operator) + { + case Token::BIT_OR: + value = m_value | other.m_value; + break; + case Token::BIT_XOR: + value = m_value ^ other.m_value; + break; + case Token::BIT_AND: + value = m_value & other.m_value; + break; + case Token::ADD: + value = m_value + other.m_value; + break; + case Token::SUB: + value = m_value - other.m_value; + break; + case Token::MUL: + value = m_value * other.m_value; + break; + case Token::DIV: + if (other.m_value == 0) + return TypePointer(); + value = m_value / other.m_value; + break; + case Token::MOD: + if (other.m_value == 0) + return TypePointer(); + value = m_value % other.m_value; + break; + default: + return TypePointer(); + } + return make_shared(value); + } +} + +bool IntegerConstantType::operator==(Type const& _other) const +{ + if (_other.getCategory() != getCategory()) + return false; + return m_value == dynamic_cast(_other).m_value; +} + +string IntegerConstantType::toString() const +{ + return "int_const " + m_value.str(); +} + +u256 IntegerConstantType::literalValue(Literal const*) const +{ + // we ignore the literal and hope that the type was correctly determined + solAssert(m_value <= u256(-1), "Integer constant too large."); + solAssert(m_value >= -(bigint(1) << 255), "Integer constant too small."); + if (m_value >= 0) + return u256(m_value); + else + return s2u(s256(m_value)); +} + +shared_ptr IntegerConstantType::getIntegerType() const +{ + bigint value = m_value; + bool negative = (value < 0); + if (negative) // convert to positive number of same bit requirements + value = ((-value) - 1) << 1; + if (value > u256(-1)) + return shared_ptr(); + else + return make_shared(max(bytesRequired(value), 1u) * 8, + negative ? IntegerType::Modifier::SIGNED + : IntegerType::Modifier::UNSIGNED); +} shared_ptr StaticStringType::smallestTypeForLiteral(string const& _literal) { @@ -257,12 +375,13 @@ bool StaticStringType::operator==(Type const& _other) const return other.m_bytes == m_bytes; } -u256 StaticStringType::literalValue(const Literal& _literal) const +u256 StaticStringType::literalValue(const Literal* _literal) const { + solAssert(_literal, ""); u256 value = 0; - for (char c: _literal.getValue()) + for (char c: _literal->getValue()) value = (value << 8) | byte(c); - return value << ((32 - _literal.getValue().length()) * 8); + return value << ((32 - _literal->getValue().length()) * 8); } bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -278,35 +397,41 @@ bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const return isImplicitlyConvertibleTo(_convertTo); } -u256 BoolType::literalValue(Literal const& _literal) const +u256 BoolType::literalValue(Literal const* _literal) const { - if (_literal.getToken() == Token::TRUE_LITERAL) + solAssert(_literal, ""); + if (_literal->getToken() == Token::TRUE_LITERAL) return u256(1); - else if (_literal.getToken() == Token::FALSE_LITERAL) + else if (_literal->getToken() == Token::FALSE_LITERAL) return u256(0); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); } -TypePointer BoolType::binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const +TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { if (getCategory() != _other->getCategory()) return TypePointer(); if (Token::isCompareOp(_operator) || _operator == Token::AND || _operator == Token::OR) - return _this; + return _other; else return TypePointer(); } -bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const +bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (isImplicitlyConvertibleTo(_convertTo)) + if (*this == _convertTo) return true; if (_convertTo.getCategory() == Category::INTEGER) return dynamic_cast(_convertTo).isAddress(); return false; } +bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::INTEGER; +} + bool ContractType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -333,9 +458,11 @@ MemberList const& ContractType::getMembers() const // We need to lazy-initialize it because of recursive references. if (!m_members) { - map> members; - for (FunctionDefinition const* function: m_contract.getInterfaceFunctions()) - members[function->getName()] = make_shared(*function, false); + // All address members and all interface functions + map> members(IntegerType::AddressMemberList.begin(), + IntegerType::AddressMemberList.end()); + for (auto const& it: m_contract.getInterfaceFunctions()) + members[it.second->getName()] = make_shared(*it.second, false); m_members.reset(new MemberList(members)); } return *m_members; @@ -347,23 +474,21 @@ shared_ptr const& ContractType::getConstructorType() const { FunctionDefinition const* constructor = m_contract.getConstructor(); if (constructor) - m_constructorType = make_shared(*constructor); + m_constructorType = make_shared(*constructor); else - m_constructorType = make_shared(TypePointers(), TypePointers()); + m_constructorType = make_shared(TypePointers(), TypePointers()); } return m_constructorType; } -unsigned ContractType::getFunctionIndex(string const& _functionName) const +u256 ContractType::getFunctionIdentifier(string const& _functionName) const { - unsigned index = 0; - for (FunctionDefinition const* function: m_contract.getInterfaceFunctions()) - { - if (function->getName() == _functionName) - return index; - ++index; - } - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index of non-existing contract function requested.")); + auto interfaceFunctions = m_contract.getInterfaceFunctions(); + for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it) + if (it->second->getName() == _functionName) + return FixedHash<4>::Arith(it->first); + + return Invalid256; } bool StructType::operator==(Type const& _other) const @@ -494,6 +619,15 @@ string FunctionType::getCanonicalSignature() const return ret + ")"; } +TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) +{ + TypePointers pointers; + pointers.reserve(_types.size()); + for (string const& type: _types) + pointers.push_back(Type::fromElementaryTypeName(Token::fromIdentifierOrKeyword(type))); + return pointers; +} + bool MappingType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -521,21 +655,21 @@ MagicType::MagicType(MagicType::Kind _kind): switch (m_kind) { case Kind::BLOCK: - m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::ADDRESS)}, - {"timestamp", make_shared(256)}, - {"prevhash", make_shared(256, IntegerType::Modifier::HASH)}, - {"difficulty", make_shared(256)}, - {"number", make_shared(256)}, - {"gaslimit", make_shared(256)}}); + m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::ADDRESS)}, + {"timestamp", make_shared(256)}, + {"prevhash", make_shared(256, IntegerType::Modifier::HASH)}, + {"difficulty", make_shared(256)}, + {"number", make_shared(256)}, + {"gaslimit", make_shared(256)}}); break; case Kind::MSG: - m_members = MemberList({{"sender", make_shared(0, IntegerType::Modifier::ADDRESS)}, - {"gas", make_shared(256)}, - {"value", make_shared(256)}}); + m_members = MemberList({{"sender", make_shared(0, IntegerType::Modifier::ADDRESS)}, + {"gas", make_shared(256)}, + {"value", make_shared(256)}}); break; case Kind::TX: - m_members = MemberList({{"origin", make_shared(0, IntegerType::Modifier::ADDRESS)}, - {"gasprice", make_shared(256)}}); + m_members = MemberList({{"origin", make_shared(0, IntegerType::Modifier::ADDRESS)}, + {"gasprice", make_shared(256)}}); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index a91a6c24e..deabe1160 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -70,12 +70,12 @@ private: /** * Abstract base class that forms the root of the type hierarchy. */ -class Type: private boost::noncopyable +class Type: private boost::noncopyable, public std::enable_shared_from_this { public: enum class Category { - INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC + INTEGER, INTEGER_CONSTANT, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC }; ///@{ @@ -92,12 +92,6 @@ public: static TypePointer forLiteral(Literal const& _literal); /// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise static TypePointer commonType(TypePointer const& _a, TypePointer const& _b); - /// @returns the resulting type of applying the given operator or an empty pointer if this is not possible. - /// The default implementation allows comparison operators if a common type exists - static TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _a, TypePointer const& _b) - { - return _a->binaryOperatorResultImpl(_operator, _a, _b); - } virtual Category getCategory() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } @@ -105,13 +99,24 @@ public: { return isImplicitlyConvertibleTo(_convertTo); } - virtual bool acceptsUnaryOperator(Token::Value) const { return false; } + /// @returns the resulting type of applying the given unary operator or an empty pointer if + /// this is not possible. + /// The default implementation does not allow any unary operator. + virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); } + /// @returns the resulting type of applying the given binary operator or an empty pointer if + /// this is not possible. + /// The default implementation allows comparison operators if a common type exists + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const + { + return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer(); + } virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); } virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); } /// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding - /// is not a simple big-endian encoding or the type cannot be stored on the stack. + /// is not a simple big-endian encoding or the type cannot be stored in calldata. + /// Note that irrespective of this size, each calldata element is padded to a multiple of 32 bytes. virtual unsigned getCalldataEncodedSize() const { return 0; } /// @returns number of bytes required to hold this value in storage. /// For dynamically "allocated" types, it returns the size of the statically allocated head, @@ -131,18 +136,13 @@ public: TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); } virtual std::string toString() const = 0; - virtual u256 literalValue(Literal const&) const + virtual u256 literalValue(Literal const*) const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested " "for type without literals.")); } protected: - virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _a, TypePointer const& _b) const - { - return Token::isCompareOp(_operator) ? commonType(_a, _b) : TypePointer(); - } - /// Convenience object used when returning an empty member list. static const MemberList EmptyMemberList; }; @@ -159,15 +159,12 @@ public: }; virtual Category getCategory() const override { return Category::INTEGER; } - /// @returns the smallest integer type for the given literal or an empty pointer - /// if no type fits. - static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); - explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED); virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool acceptsUnaryOperator(Token::Value _operator) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; virtual bool operator==(Type const& _other) const override; @@ -177,20 +174,51 @@ public: virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; } virtual std::string toString() const override; - virtual u256 literalValue(Literal const& _literal) const override; 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; } -protected: - virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override; + static const MemberList AddressMemberList; private: int m_bits; Modifier m_modifier; - static const MemberList AddressMemberList; +}; + +/** + * Integer constants either literals or computed. Example expressions: 2, 2+10, ~10. + * There is one distinct type per value. + */ +class IntegerConstantType: public Type +{ +public: + virtual Category getCategory() const override { return Category::INTEGER_CONSTANT; } + + static std::shared_ptr fromLiteral(std::string const& _literal); + + explicit IntegerConstantType(bigint _value): m_value(_value) {} + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + + virtual bool operator==(Type const& _other) const override; + + virtual bool canBeStored() const override { return false; } + virtual bool canLiveOutsideStorage() const override { return false; } + virtual unsigned getSizeOnStack() const override { return 1; } + + virtual std::string toString() const override; + virtual u256 literalValue(Literal const* _literal) const override; + + /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. + std::shared_ptr getIntegerType() const; + +private: + bigint m_value; }; /** @@ -214,7 +242,7 @@ public: virtual bool isValueType() const override { return true; } virtual std::string toString() const override { return "string" + dev::toString(m_bytes); } - virtual u256 literalValue(Literal const& _literal) const override; + virtual u256 literalValue(Literal const* _literal) const override; int getNumBytes() const { return m_bytes; } @@ -231,19 +259,17 @@ public: BoolType() {} virtual Category getCategory() const { return Category::BOOL; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool acceptsUnaryOperator(Token::Value _operator) const override + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override { - return _operator == Token::NOT || _operator == Token::DELETE; + return (_operator == Token::NOT || _operator == Token::DELETE) ? shared_from_this() : TypePointer(); } + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; virtual unsigned getCalldataEncodedSize() const { return 1; } virtual bool isValueType() const override { return true; } virtual std::string toString() const override { return "bool"; } - virtual u256 literalValue(Literal const& _literal) const override; - -protected: - virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override; + virtual u256 literalValue(Literal const* _literal) const override; }; /** @@ -254,7 +280,9 @@ class ContractType: public Type public: virtual Category getCategory() const override { return Category::CONTRACT; } ContractType(ContractDefinition const& _contract): m_contract(_contract) {} - /// Contracts can be converted to themselves and to addresses. + /// Contracts can be implicitly converted to super classes and to addresses. + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + /// Contracts can be converted to themselves and to integers. virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool operator==(Type const& _other) const override; virtual u256 getStorageSize() const override; @@ -267,7 +295,9 @@ public: /// is not used, as this type cannot be the type of a variable or expression. std::shared_ptr const& getConstructorType() const; - unsigned getFunctionIndex(std::string const& _functionName) const; + /// @returns the identifier of the function with the given name or Invalid256 if such a name does + /// not exist. + u256 getFunctionIdentifier(std::string const& _functionName) const; private: ContractDefinition const& m_contract; @@ -285,9 +315,9 @@ class StructType: public Type public: virtual Category getCategory() const override { return Category::STRUCT; } StructType(StructDefinition const& _struct): m_struct(_struct) {} - virtual bool acceptsUnaryOperator(Token::Value _operator) const override + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override { - return _operator == Token::DELETE; + return _operator == Token::DELETE ? shared_from_this() : TypePointer(); } virtual bool operator==(Type const& _other) const override; @@ -318,10 +348,14 @@ public: /// INTERNAL: jump tag, EXTERNAL: contract address + function index, /// 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, BARE }; + enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, LOG0, LOG1, LOG2, LOG3, LOG4, BARE }; virtual Category getCategory() const override { return Category::FUNCTION; } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); + FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes, + Location _location = Location::INTERNAL): + FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), + _location) {} FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, Location _location = Location::INTERNAL): m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), @@ -341,6 +375,8 @@ public: std::string getCanonicalSignature() const; private: + static TypePointers parseElementaryTypeVector(strings const& _types); + TypePointers m_parameterTypes; TypePointers m_returnParameterTypes; Location m_location; @@ -378,20 +414,12 @@ public: virtual Category getCategory() const override { return Category::VOID; } VoidType() {} + virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual std::string toString() const override { return "void"; } virtual bool canBeStored() const override { return false; } virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 0; } - -protected: - virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override - { - (void)_operator; - (void)_this; - (void)_other; - return TypePointer(); - } }; /** @@ -403,24 +431,15 @@ class TypeType: public Type public: virtual Category getCategory() const override { return Category::TYPE; } TypeType(TypePointer const& _actualType): m_actualType(_actualType) {} - TypePointer const& getActualType() const { return m_actualType; } + virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } virtual bool canLiveOutsideStorage() const override { return false; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } -protected: - virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override - { - (void)_operator; - (void)_this; - (void)_other; - return TypePointer(); - } - private: TypePointer m_actualType; }; @@ -437,6 +456,12 @@ public: virtual Category getCategory() const override { return Category::MAGIC; } MagicType(Kind _kind); + + virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override + { + return TypePointer(); + } + virtual bool operator==(Type const& _other) const; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -445,15 +470,6 @@ public: virtual std::string toString() const override; -protected: - virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override - { - (void)_operator; - (void)_this; - (void)_other; - return TypePointer(); - } - private: Kind m_kind; diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 874c14331..f728a29f5 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -21,198 +21,19 @@ * @date 2014 */ -#include -#include -#include -#include "WebThreeStubServer.h" -#include -#include -#include -#include -#include #include +#include #include -#include -#include -#include +#include "WebThreeStubServer.h" using namespace std; using namespace dev; using namespace dev::eth; -static Json::Value toJson(dev::eth::BlockInfo const& _bi) -{ - Json::Value res; - res["hash"] = boost::lexical_cast(_bi.hash); - res["parentHash"] = toJS(_bi.parentHash); - res["sha3Uncles"] = toJS(_bi.sha3Uncles); - res["miner"] = toJS(_bi.coinbaseAddress); - res["stateRoot"] = toJS(_bi.stateRoot); - res["transactionsRoot"] = toJS(_bi.transactionsRoot); - res["difficulty"] = toJS(_bi.difficulty); - res["number"] = (int)_bi.number; - res["gasLimit"] = (int)_bi.gasLimit; - res["timestamp"] = (int)_bi.timestamp; - res["extraData"] = jsFromBinary(_bi.extraData); - res["nonce"] = toJS(_bi.nonce); - return res; -} - -static Json::Value toJson(dev::eth::Transaction const& _t) -{ - Json::Value res; - res["hash"] = toJS(_t.sha3()); - res["input"] = jsFromBinary(_t.data()); - res["to"] = toJS(_t.receiveAddress()); - res["from"] = toJS(_t.sender()); - res["gas"] = (int)_t.gas(); - res["gasPrice"] = toJS(_t.gasPrice()); - res["nonce"] = toJS(_t.nonce()); - res["value"] = toJS(_t.value()); - return res; -} - -static Json::Value toJson(dev::eth::LogEntry const& _e) -{ - Json::Value res; - - res["data"] = jsFromBinary(_e.data); - res["address"] = toJS(_e.address); - for (auto const& t: _e.topics) - res["topics"].append(toJS(t)); - return res; -} - -static Json::Value toJson(dev::eth::LogEntries const& _es) // commented to avoid warning. Uncomment once in use @ poC-7. -{ - Json::Value res; - for (dev::eth::LogEntry const& e: _es) - res.append(toJson(e)); - return res; -} - -static Json::Value toJson(std::map const& _storage) -{ - Json::Value res(Json::objectValue); - for (auto i: _storage) - res[toJS(i.first)] = toJS(i.second); - return res; -} - -static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to avoid warning. Uncomment once in use @ PoC-7. -{ - dev::eth::LogFilter filter; - if (!_json.isObject() || _json.empty()) - return filter; - - if (_json["earliest"].isInt()) - filter.withEarliest(_json["earliest"].asInt()); - if (_json["latest"].isInt()) - filter.withLatest(_json["lastest"].asInt()); - if (_json["max"].isInt()) - filter.withMax(_json["max"].asInt()); - if (_json["skip"].isInt()) - filter.withSkip(_json["skip"].asInt()); - if (!_json["address"].empty()) - { - if (_json["address"].isArray()) - { - for (auto i : _json["address"]) - if (i.isString()) - filter.address(jsToAddress(i.asString())); - } - else if (_json["address"].isString()) - filter.address(jsToAddress(_json["address"].asString())); - } - if (!_json["topics"].empty()) - { - if (_json["topics"].isArray()) - { - for (auto i: _json["topics"]) - if (i.isString()) - filter.topic(jsToU256(i.asString())); - } - else if(_json["topics"].isString()) - filter.topic(jsToU256(_json["topics"].asString())); - } - return filter; -} - -static shh::Message toMessage(Json::Value const& _json) -{ - shh::Message ret; - if (_json["from"].isString()) - ret.setFrom(jsToPublic(_json["from"].asString())); - if (_json["to"].isString()) - ret.setTo(jsToPublic(_json["to"].asString())); - if (_json["payload"].isString()) - ret.setPayload(jsToBytes(_json["payload"].asString())); - return ret; -} - -static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, Secret _from) -{ - unsigned ttl = 50; - unsigned workToProve = 50; - shh::BuildTopic bt; - - if (_json["ttl"].isInt()) - ttl = _json["ttl"].asInt(); - if (_json["workToProve"].isInt()) - workToProve = _json["workToProve"].asInt(); - if (!_json["topic"].empty()) - { - if (_json["topic"].isString()) - bt.shift(jsToBytes(_json["topic"].asString())); - else if (_json["topic"].isArray()) - for (auto i: _json["topic"]) - if (i.isString()) - bt.shift(jsToBytes(i.asString())); - } - return _m.seal(_from, bt, ttl, workToProve); -} - -static pair toWatch(Json::Value const& _json) -{ - shh::BuildTopicMask bt; - Public to; - - if (_json["to"].isString()) - to = jsToPublic(_json["to"].asString()); - - if (!_json["topic"].empty()) - { - if (_json["topic"].isString()) - bt.shift(jsToBytes(_json["topic"].asString())); - else if (_json["topic"].isArray()) - for (auto i: _json["topic"]) - if (i.isString()) - bt.shift(jsToBytes(i.asString())); - } - return make_pair(bt.toTopicMask(), to); -} - -static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m) -{ - Json::Value res; - res["hash"] = toJS(_h); - res["expiry"] = (int)_e.expiry(); - res["sent"] = (int)_e.sent(); - res["ttl"] = (int)_e.ttl(); - res["workProved"] = (int)_e.workProved(); - for (auto const& t: _e.topics()) - res["topics"].append(toJS(t)); - res["payload"] = toJS(_m.payload()); - res["from"] = toJS(_m.from()); - res["to"] = toJS(_m.to()); - return res; -} - WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, std::vector const& _accounts): - AbstractWebThreeStubServer(_conn), + WebThreeStubServerBase(_conn, _accounts), m_web3(_web3) { - setAccounts(_accounts); auto path = getDataDir() + "/.web3"; boost::filesystem::create_directories(path); ldb::Options o; @@ -220,181 +41,27 @@ WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, ldb::DB::Open(o, path, &m_db); } -void WebThreeStubServer::setAccounts(std::vector const& _accounts) -{ - m_accounts.clear(); - for (auto i: _accounts) - m_accounts[i.address()] = i.secret(); -} - -void WebThreeStubServer::setIdentities(std::vector const& _ids) -{ - m_ids.clear(); - for (auto i: _ids) - m_ids[i.pub()] = i.secret(); -} - -dev::eth::Interface* WebThreeStubServer::client() const +dev::eth::Interface* WebThreeStubServer::client() { return m_web3.ethereum(); } -std::shared_ptr WebThreeStubServer::face() const +std::shared_ptr WebThreeStubServer::face() { return m_web3.whisper(); } -std::string WebThreeStubServer::web3_sha3(std::string const& _param1) -{ - return toJS(sha3(jsToBytes(_param1))); -} - -Json::Value WebThreeStubServer::eth_accounts() -{ - Json::Value ret(Json::arrayValue); - for (auto i: m_accounts) - ret.append(toJS(i.first)); - return ret; -} - -std::string WebThreeStubServer::shh_addToGroup(std::string const& _group, std::string const& _who) -{ - (void)_group; - (void)_who; - return ""; -} - -std::string WebThreeStubServer::eth_balanceAt(string const& _address) -{ - return toJS(client()->balanceAt(jsToAddress(_address), client()->getDefault())); -} - -Json::Value WebThreeStubServer::eth_blockByHash(std::string const& _hash) -{ - return toJson(client()->blockInfo(jsToFixed<32>(_hash))); -} - -Json::Value WebThreeStubServer::eth_blockByNumber(int const& _number) -{ - return toJson(client()->blockInfo(client()->hashFromNumber(_number))); -} - -static TransactionSkeleton toTransaction(Json::Value const& _json) -{ - TransactionSkeleton ret; - if (!_json.isObject() || _json.empty()) - return ret; - - if (_json["from"].isString()) - ret.from = jsToAddress(_json["from"].asString()); - if (_json["to"].isString()) - ret.to = jsToAddress(_json["to"].asString()); - if (!_json["value"].empty()) - { - if (_json["value"].isString()) - ret.value = jsToU256(_json["value"].asString()); - else if (_json["value"].isInt()) - ret.value = u256(_json["value"].asInt()); - } - if (!_json["gas"].empty()) - { - if (_json["gas"].isString()) - ret.gas = jsToU256(_json["gas"].asString()); - else if (_json["gas"].isInt()) - ret.gas = u256(_json["gas"].asInt()); - } - if (!_json["gasPrice"].empty()) - { - if (_json["gasPrice"].isString()) - ret.gasPrice = jsToU256(_json["gasPrice"].asString()); - else if (_json["gasPrice"].isInt()) - ret.gas = u256(_json["gas"].asInt()); - } - if (!_json["data"].empty()) - { - if (_json["data"].isString()) // ethereum.js has preconstructed the data array - ret.data = jsToBytes(_json["data"].asString()); - else if (_json["data"].isArray()) // old style: array of 32-byte-padded values. TODO: remove PoC-8 - for (auto i: _json["data"]) - dev::operator +=(ret.data, padded(jsToBytes(i.asString()), 32)); - } - - if (_json["code"].isString()) - ret.data = jsToBytes(_json["code"].asString()); - return ret; -} - -std::string WebThreeStubServer::eth_call(Json::Value const& _json) -{ - std::string ret; - TransactionSkeleton t = toTransaction(_json); - if (!t.from && m_accounts.size()) - { - auto b = m_accounts.begin()->first; - for (auto a: m_accounts) - if (client()->balanceAt(a.first) > client()->balanceAt(b)) - b = a.first; - t.from = b; - } - if (!m_accounts.count(t.from)) - return ret; - if (!t.gasPrice) - t.gasPrice = 10 * dev::eth::szabo; - if (!t.gas) - t.gas = min(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice); - ret = toJS(client()->call(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice)); - return ret; -} - -bool WebThreeStubServer::eth_changed(int const& _id) -{ - return client()->checkWatch(_id); -} - -std::string WebThreeStubServer::eth_codeAt(string const& _address) +dev::WebThreeNetworkFace* WebThreeStubServer::network() { - return jsFromBinary(client()->codeAt(jsToAddress(_address), client()->getDefault())); + return &m_web3; } -std::string WebThreeStubServer::eth_coinbase() +dev::WebThreeStubDatabaseFace* WebThreeStubServer::db() { - return toJS(client()->address()); + return this; } -double WebThreeStubServer::eth_countAt(string const& _address) -{ - return (double)(uint64_t)client()->countAt(jsToAddress(_address), client()->getDefault()); -} - -int WebThreeStubServer::eth_defaultBlock() -{ - return client()->getDefault(); -} - -std::string WebThreeStubServer::eth_gasPrice() -{ - return toJS(10 * dev::eth::szabo); -} - -std::string WebThreeStubServer::db_get(std::string const& _name, std::string const& _key) -{ - bytes k = sha3(_name).asBytes() + sha3(_key).asBytes(); - string ret; - m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret); - return toJS(dev::asBytes(ret)); -} - -Json::Value WebThreeStubServer::eth_filterLogs(int const& _id) -{ - return toJson(client()->logs(_id)); -} - -Json::Value WebThreeStubServer::eth_logs(Json::Value const& _json) -{ - return toJson(client()->logs(toLogFilter(_json))); -} - -std::string WebThreeStubServer::db_getString(std::string const& _name, std::string const& _key) +std::string WebThreeStubServer::get(std::string const& _name, std::string const& _key) { bytes k = sha3(_name).asBytes() + sha3(_key).asBytes(); string ret; @@ -402,289 +69,9 @@ std::string WebThreeStubServer::db_getString(std::string const& _name, std::stri return ret; } -bool WebThreeStubServer::shh_haveIdentity(std::string const& _id) -{ - return m_ids.count(jsToPublic(_id)) > 0; -} - -bool WebThreeStubServer::eth_listening() -{ - return m_web3.isNetworkStarted(); -} - -bool WebThreeStubServer::eth_mining() -{ - return client()->isMining(); -} - -int WebThreeStubServer::eth_newFilter(Json::Value const& _json) -{ - unsigned ret = -1; - ret = client()->installWatch(toLogFilter(_json)); - return ret; -} - -int WebThreeStubServer::eth_newFilterString(std::string const& _filter) -{ - unsigned ret = -1; - if (_filter.compare("chain") == 0) - ret = client()->installWatch(dev::eth::ChainChangedFilter); - else if (_filter.compare("pending") == 0) - ret = client()->installWatch(dev::eth::PendingChangedFilter); - return ret; -} - -std::string WebThreeStubServer::shh_newGroup(std::string const& _id, std::string const& _who) -{ - (void)_id; - (void)_who; - return ""; -} - -std::string WebThreeStubServer::shh_newIdentity() -{ -// cnote << this << m_ids; - KeyPair kp = KeyPair::create(); - m_ids[kp.pub()] = kp.secret(); - return toJS(kp.pub()); -} - -Json::Value WebThreeStubServer::eth_compilers() -{ - Json::Value ret(Json::arrayValue); - ret.append("lll"); - ret.append("solidity"); - ret.append("serpent"); - return ret; -} - -std::string WebThreeStubServer::eth_lll(std::string const& _code) -{ - string res; - vector errors; - res = toJS(dev::eth::compileLLL(_code, true, &errors)); - cwarn << "LLL compilation errors: " << errors; - return res; -} - -std::string WebThreeStubServer::eth_serpent(std::string const& _code) -{ - string res; - try - { - res = toJS(dev::asBytes(::compile(_code))); - } - catch (string err) - { - cwarn << "Solidity compilation error: " << err; - } - catch (...) - { - cwarn << "Uncought serpent compilation exception"; - } - return res; -} - -std::string WebThreeStubServer::eth_solidity(std::string const& _code) -{ - string res; - dev::solidity::CompilerStack compiler; - try - { - res = toJS(compiler.compile(_code, true)); - } - catch (dev::Exception const& exception) - { - ostringstream error; - solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); - cwarn << "Solidity compilation error: " << error.str(); - } - catch (...) - { - cwarn << "Uncought solidity compilation exception"; - } - return res; -} - -int WebThreeStubServer::eth_number() -{ - return client()->number() + 1; -} - -int WebThreeStubServer::eth_peerCount() -{ - return m_web3.peerCount(); -} - -bool WebThreeStubServer::shh_post(Json::Value const& _json) -{ - shh::Message m = toMessage(_json); - Secret from; - - if (m.from() && m_ids.count(m.from())) - { - cwarn << "Silently signing message from identity" << m.from().abridged() << ": User validation hook goes here."; - // TODO: insert validification hook here. - from = m_ids[m.from()]; - } - - face()->inject(toSealed(_json, m, from)); - return true; -} - -bool WebThreeStubServer::db_put(std::string const& _name, std::string const& _key, std::string const& _value) +void WebThreeStubServer::put(std::string const& _name, std::string const& _key, std::string const& _value) { bytes k = sha3(_name).asBytes() + sha3(_key).asBytes(); - bytes v = jsToBytes(_value); - m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size())); - return true; -} - -bool WebThreeStubServer::db_putString(std::string const& _name, std::string const& _key, std::string const& _value) -{ - bytes k = sha3(_name).asBytes() + sha3(_key).asBytes(); - string v = _value; - m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size())); - return true; -} - -bool WebThreeStubServer::eth_setCoinbase(std::string const& _address) -{ - client()->setAddress(jsToAddress(_address)); - return true; -} - -bool WebThreeStubServer::eth_setDefaultBlock(int const& _block) -{ - client()->setDefault(_block); - return true; -} - -bool WebThreeStubServer::eth_setListening(bool const& _listening) -{ - if (_listening) - m_web3.startNetwork(); - else - m_web3.stopNetwork(); - return true; -} - -bool WebThreeStubServer::eth_setMining(bool const& _mining) -{ - if (_mining) - client()->startMining(); - else - client()->stopMining(); - return true; -} - -Json::Value WebThreeStubServer::shh_changed(int const& _id) -{ - Json::Value ret(Json::arrayValue); - auto pub = m_shhWatches[_id]; - if (!pub || m_ids.count(pub)) - for (h256 const& h: face()->checkWatch(_id)) - { - auto e = face()->envelope(h); - shh::Message m; - if (pub) - { - cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here."; - m = e.open(m_ids[pub]); - if (!m) - continue; - } - else - m = e.open(); - ret.append(toJson(h, e, m)); - } - - return ret; -} - -int WebThreeStubServer::shh_newFilter(Json::Value const& _json) -{ - auto w = toWatch(_json); - auto ret = face()->installWatch(w.first); - m_shhWatches.insert(make_pair(ret, w.second)); - return ret; -} - -bool WebThreeStubServer::shh_uninstallFilter(int const& _id) -{ - face()->uninstallWatch(_id); - return true; -} - -std::string WebThreeStubServer::eth_stateAt(string const& _address, string const& _storage) -{ - return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), client()->getDefault())); -} - -Json::Value WebThreeStubServer::eth_storageAt(string const& _address) -{ - return toJson(client()->storageAt(jsToAddress(_address))); -} - -std::string WebThreeStubServer::eth_transact(Json::Value const& _json) -{ - std::string ret; - TransactionSkeleton t = toTransaction(_json); - if (!t.from && m_accounts.size()) - { - auto b = m_accounts.begin()->first; - for (auto a: m_accounts) - if (client()->balanceAt(a.first) > client()->balanceAt(b)) - b = a.first; - t.from = b; - } - if (!m_accounts.count(t.from)) - return ret; - if (!t.gasPrice) - t.gasPrice = 10 * dev::eth::szabo; - if (!t.gas) - t.gas = min(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice); - if (authenticate(t)) - { - if (t.to) - // TODO: from qethereum, insert validification hook here. - client()->transact(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice); - else - ret = toJS(client()->transact(m_accounts[t.from].secret(), t.value, t.data, t.gas, t.gasPrice)); - client()->flushTransactions(); - } - return ret; -} - -bool WebThreeStubServer::authenticate(TransactionSkeleton const& _t) const -{ - cwarn << "Silently signing transaction from address" << _t.from.abridged() << ": User validation hook goes here."; - return true; -} - -Json::Value WebThreeStubServer::eth_transactionByHash(std::string const& _hash, int const& _i) -{ - return toJson(client()->transaction(jsToFixed<32>(_hash), _i)); -} - -Json::Value WebThreeStubServer::eth_transactionByNumber(int const& _number, int const& _i) -{ - return toJson(client()->transaction(client()->hashFromNumber(_number), _i)); -} - -Json::Value WebThreeStubServer::eth_uncleByHash(std::string const& _hash, int const& _i) -{ - return toJson(client()->uncle(jsToFixed<32>(_hash), _i)); -} - -Json::Value WebThreeStubServer::eth_uncleByNumber(int const& _number, int const& _i) -{ - return toJson(client()->uncle(client()->hashFromNumber(_number), _i)); -} - -bool WebThreeStubServer::eth_uninstallFilter(int const& _id) -{ - client()->uninstallWatch(_id); - return true; + m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)_value.data(), _value.size())); } diff --git a/libweb3jsonrpc/WebThreeStubServer.h b/libweb3jsonrpc/WebThreeStubServer.h index 0f81fce9d..d401c38fc 100644 --- a/libweb3jsonrpc/WebThreeStubServer.h +++ b/libweb3jsonrpc/WebThreeStubServer.h @@ -28,111 +28,33 @@ #include #pragma warning(pop) -#include -#include -#include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#include "abstractwebthreestubserver.h" -#pragma GCC diagnostic pop - -namespace ldb = leveldb; +#include "WebThreeStubServerBase.h" namespace dev { class WebThreeDirect; -class KeyPair; -class TransactionSkeleton; -namespace eth -{ -class Interface; -} -namespace shh -{ -class Interface; -} } /** - * @brief JSON-RPC api implementation - * @todo filters should work on unsigned instead of int - * unsigned are not supported in json-rpc-cpp and there are bugs with double in json-rpc-cpp version 0.2.1 - * @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols. - * @todo modularise everything so additional subprotocols don't need to change this file. + * @brief JSON-RPC api implementation for WebThreeDirect */ -class WebThreeStubServer: public AbstractWebThreeStubServer +class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace { public: WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector const& _accounts); - virtual std::string web3_sha3(std::string const& _param1); - virtual Json::Value eth_accounts(); - virtual std::string eth_balanceAt(std::string const& _address); - virtual Json::Value eth_blockByHash(std::string const& _hash); - virtual Json::Value eth_blockByNumber(int const& _number); - virtual std::string eth_call(Json::Value const& _json); - virtual bool eth_changed(int const& _id); - virtual std::string eth_codeAt(std::string const& _address); - virtual std::string eth_coinbase(); - virtual Json::Value eth_compilers(); - virtual double eth_countAt(std::string const& _address); - virtual int eth_defaultBlock(); - virtual std::string eth_gasPrice(); - virtual Json::Value eth_filterLogs(int const& _id); - virtual Json::Value eth_logs(Json::Value const& _json); - virtual bool eth_listening(); - virtual bool eth_mining(); - virtual int eth_newFilter(Json::Value const& _json); - virtual int eth_newFilterString(std::string const& _filter); - virtual int eth_number(); - virtual int eth_peerCount(); - virtual bool eth_setCoinbase(std::string const& _address); - virtual bool eth_setDefaultBlock(int const& _block); - virtual bool eth_setListening(bool const& _listening); - virtual std::string eth_lll(std::string const& _s); - virtual std::string eth_serpent(std::string const& _s); - virtual bool eth_setMining(bool const& _mining); - virtual std::string eth_solidity(std::string const& _code); - virtual std::string eth_stateAt(std::string const& _address, std::string const& _storage); - virtual Json::Value eth_storageAt(std::string const& _address); - virtual std::string eth_transact(Json::Value const& _json); - virtual Json::Value eth_transactionByHash(std::string const& _hash, int const& _i); - virtual Json::Value eth_transactionByNumber(int const& _number, int const& _i); - virtual Json::Value eth_uncleByHash(std::string const& _hash, int const& _i); - virtual Json::Value eth_uncleByNumber(int const& _number, int const& _i); - virtual bool eth_uninstallFilter(int const& _id); - - virtual std::string db_get(std::string const& _name, std::string const& _key); - virtual std::string db_getString(std::string const& _name, std::string const& _key); - virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value); - virtual bool db_putString(std::string const& _name, std::string const& _key, std::string const& _value); - - virtual std::string shh_addToGroup(std::string const& _group, std::string const& _who); - virtual Json::Value shh_changed(int const& _id); - virtual bool shh_haveIdentity(std::string const& _id); - virtual int shh_newFilter(Json::Value const& _json); - virtual std::string shh_newGroup(std::string const& _id, std::string const& _who); - virtual std::string shh_newIdentity(); - virtual bool shh_post(Json::Value const& _json); - virtual bool shh_uninstallFilter(int const& _id); - - void setAccounts(std::vector const& _accounts); - void setIdentities(std::vector const& _ids); - std::map const& ids() const { return m_ids; } +private: + dev::eth::Interface* client() override; + std::shared_ptr face() override; + dev::WebThreeNetworkFace* network() override; + dev::WebThreeStubDatabaseFace* db() override; -protected: - virtual bool authenticate(dev::TransactionSkeleton const& _t) const; + std::string get(std::string const& _name, std::string const& _key) override; + void put(std::string const& _name, std::string const& _key, std::string const& _value) override; private: - dev::eth::Interface* client() const; - std::shared_ptr face() const; dev::WebThreeDirect& m_web3; - std::map m_accounts; - - ldb::ReadOptions m_readOptions; - ldb::WriteOptions m_writeOptions; - ldb::DB* m_db; - - std::map m_ids; - std::map m_shhWatches; + leveldb::ReadOptions m_readOptions; + leveldb::WriteOptions m_writeOptions; + leveldb::DB* m_db; }; diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp new file mode 100644 index 000000000..5003454f0 --- /dev/null +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -0,0 +1,665 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file WebThreeStubServerBase.cpp + * @authors: + * Gav Wood + * Marek Kotewicz + * @date 2014 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "WebThreeStubServerBase.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +static Json::Value toJson(dev::eth::BlockInfo const& _bi) +{ + Json::Value res; + res["hash"] = boost::lexical_cast(_bi.hash); + res["parentHash"] = toJS(_bi.parentHash); + res["sha3Uncles"] = toJS(_bi.sha3Uncles); + res["miner"] = toJS(_bi.coinbaseAddress); + res["stateRoot"] = toJS(_bi.stateRoot); + res["transactionsRoot"] = toJS(_bi.transactionsRoot); + res["difficulty"] = toJS(_bi.difficulty); + res["number"] = (int)_bi.number; + res["gasLimit"] = (int)_bi.gasLimit; + res["timestamp"] = (int)_bi.timestamp; + res["extraData"] = jsFromBinary(_bi.extraData); + res["nonce"] = toJS(_bi.nonce); + return res; +} + +static Json::Value toJson(dev::eth::Transaction const& _t) +{ + Json::Value res; + res["hash"] = toJS(_t.sha3()); + res["input"] = jsFromBinary(_t.data()); + res["to"] = toJS(_t.receiveAddress()); + res["from"] = toJS(_t.sender()); + res["gas"] = (int)_t.gas(); + res["gasPrice"] = toJS(_t.gasPrice()); + res["nonce"] = toJS(_t.nonce()); + res["value"] = toJS(_t.value()); + return res; +} + +static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e) +{ + Json::Value res; + + res["data"] = jsFromBinary(_e.data); + res["address"] = toJS(_e.address); + for (auto const& t: _e.topics) + res["topics"].append(toJS(t)); + res["number"] = _e.number; + return res; +} + +static Json::Value toJson(dev::eth::LocalisedLogEntries const& _es) // commented to avoid warning. Uncomment once in use @ poC-7. +{ + Json::Value res; + for (dev::eth::LocalisedLogEntry const& e: _es) + res.append(toJson(e)); + return res; +} + +static Json::Value toJson(std::map const& _storage) +{ + Json::Value res(Json::objectValue); + for (auto i: _storage) + res[toJS(i.first)] = toJS(i.second); + return res; +} + +static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to avoid warning. Uncomment once in use @ PoC-7. +{ + dev::eth::LogFilter filter; + if (!_json.isObject() || _json.empty()) + return filter; + + if (_json["earliest"].isInt()) + filter.withEarliest(_json["earliest"].asInt()); + if (_json["latest"].isInt()) + filter.withLatest(_json["lastest"].asInt()); + if (_json["max"].isInt()) + filter.withMax(_json["max"].asInt()); + if (_json["skip"].isInt()) + filter.withSkip(_json["skip"].asInt()); + if (!_json["address"].empty()) + { + if (_json["address"].isArray()) + { + for (auto i : _json["address"]) + if (i.isString()) + filter.address(jsToAddress(i.asString())); + } + else if (_json["address"].isString()) + filter.address(jsToAddress(_json["address"].asString())); + } + if (!_json["topics"].empty()) + { + if (_json["topics"].isArray()) + { + for (auto i: _json["topics"]) + if (i.isString()) + filter.topic(jsToU256(i.asString())); + } + else if(_json["topics"].isString()) + filter.topic(jsToU256(_json["topics"].asString())); + } + return filter; +} + +static shh::Message toMessage(Json::Value const& _json) +{ + shh::Message ret; + if (_json["from"].isString()) + ret.setFrom(jsToPublic(_json["from"].asString())); + if (_json["to"].isString()) + ret.setTo(jsToPublic(_json["to"].asString())); + if (_json["payload"].isString()) + ret.setPayload(jsToBytes(_json["payload"].asString())); + return ret; +} + +static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, Secret _from) +{ + unsigned ttl = 50; + unsigned workToProve = 50; + shh::BuildTopic bt; + + if (_json["ttl"].isInt()) + ttl = _json["ttl"].asInt(); + if (_json["workToProve"].isInt()) + workToProve = _json["workToProve"].asInt(); + if (!_json["topic"].empty()) + { + if (_json["topic"].isString()) + bt.shift(jsToBytes(_json["topic"].asString())); + else if (_json["topic"].isArray()) + for (auto i: _json["topic"]) + if (i.isString()) + bt.shift(jsToBytes(i.asString())); + } + return _m.seal(_from, bt, ttl, workToProve); +} + +static pair toWatch(Json::Value const& _json) +{ + shh::BuildTopicMask bt; + Public to; + + if (_json["to"].isString()) + to = jsToPublic(_json["to"].asString()); + + if (!_json["topic"].empty()) + { + if (_json["topic"].isString()) + bt.shift(jsToBytes(_json["topic"].asString())); + else if (_json["topic"].isArray()) + for (auto i: _json["topic"]) + if (i.isString()) + bt.shift(jsToBytes(i.asString())); + } + return make_pair(bt.toTopicMask(), to); +} + +static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m) +{ + Json::Value res; + res["hash"] = toJS(_h); + res["expiry"] = (int)_e.expiry(); + res["sent"] = (int)_e.sent(); + res["ttl"] = (int)_e.ttl(); + res["workProved"] = (int)_e.workProved(); + for (auto const& t: _e.topics()) + res["topics"].append(toJS(t)); + res["payload"] = toJS(_m.payload()); + res["from"] = toJS(_m.from()); + res["to"] = toJS(_m.to()); + return res; +} + +WebThreeStubServerBase::WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts): + AbstractWebThreeStubServer(_conn) +{ + setAccounts(_accounts); +} + +void WebThreeStubServerBase::setAccounts(std::vector const& _accounts) +{ + m_accounts.clear(); + for (auto i: _accounts) + m_accounts[i.address()] = i.secret(); +} + +void WebThreeStubServerBase::setIdentities(std::vector const& _ids) +{ + m_ids.clear(); + for (auto i: _ids) + m_ids[i.pub()] = i.secret(); +} + +std::string WebThreeStubServerBase::web3_sha3(std::string const& _param1) +{ + return toJS(sha3(jsToBytes(_param1))); +} + +Json::Value WebThreeStubServerBase::eth_accounts() +{ + Json::Value ret(Json::arrayValue); + for (auto i: m_accounts) + ret.append(toJS(i.first)); + return ret; +} + +std::string WebThreeStubServerBase::shh_addToGroup(std::string const& _group, std::string const& _who) +{ + (void)_group; + (void)_who; + return ""; +} + +std::string WebThreeStubServerBase::eth_balanceAt(string const& _address) +{ + return toJS(client()->balanceAt(jsToAddress(_address), client()->getDefault())); +} + +Json::Value WebThreeStubServerBase::eth_blockByHash(std::string const& _hash) +{ + return toJson(client()->blockInfo(jsToFixed<32>(_hash))); +} + +Json::Value WebThreeStubServerBase::eth_blockByNumber(int const& _number) +{ + return toJson(client()->blockInfo(client()->hashFromNumber(_number))); +} + +static TransactionSkeleton toTransaction(Json::Value const& _json) +{ + TransactionSkeleton ret; + if (!_json.isObject() || _json.empty()) + return ret; + + if (_json["from"].isString()) + ret.from = jsToAddress(_json["from"].asString()); + if (_json["to"].isString()) + ret.to = jsToAddress(_json["to"].asString()); + if (!_json["value"].empty()) + { + if (_json["value"].isString()) + ret.value = jsToU256(_json["value"].asString()); + else if (_json["value"].isInt()) + ret.value = u256(_json["value"].asInt()); + } + if (!_json["gas"].empty()) + { + if (_json["gas"].isString()) + ret.gas = jsToU256(_json["gas"].asString()); + else if (_json["gas"].isInt()) + ret.gas = u256(_json["gas"].asInt()); + } + if (!_json["gasPrice"].empty()) + { + if (_json["gasPrice"].isString()) + ret.gasPrice = jsToU256(_json["gasPrice"].asString()); + else if (_json["gasPrice"].isInt()) + ret.gas = u256(_json["gas"].asInt()); + } + if (!_json["data"].empty()) + { + if (_json["data"].isString()) // ethereum.js has preconstructed the data array + ret.data = jsToBytes(_json["data"].asString()); + else if (_json["data"].isArray()) // old style: array of 32-byte-padded values. TODO: remove PoC-8 + for (auto i: _json["data"]) + dev::operator +=(ret.data, padded(jsToBytes(i.asString()), 32)); + } + + if (_json["code"].isString()) + ret.data = jsToBytes(_json["code"].asString()); + return ret; +} + +std::string WebThreeStubServerBase::eth_call(Json::Value const& _json) +{ + std::string ret; + TransactionSkeleton t = toTransaction(_json); + if (!t.from && m_accounts.size()) + { + auto b = m_accounts.begin()->first; + for (auto a: m_accounts) + if (client()->balanceAt(a.first) > client()->balanceAt(b)) + b = a.first; + t.from = b; + } + if (!m_accounts.count(t.from)) + return ret; + if (!t.gasPrice) + t.gasPrice = 10 * dev::eth::szabo; + if (!t.gas) + t.gas = min(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice); + ret = toJS(client()->call(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice)); + return ret; +} + +Json::Value WebThreeStubServerBase::eth_changed(int const& _id) +{ + return toJson(client()->checkWatch(_id)); +} + +std::string WebThreeStubServerBase::eth_codeAt(string const& _address) +{ + return jsFromBinary(client()->codeAt(jsToAddress(_address), client()->getDefault())); +} + +std::string WebThreeStubServerBase::eth_coinbase() +{ + return toJS(client()->address()); +} + +double WebThreeStubServerBase::eth_countAt(string const& _address) +{ + return (double)(uint64_t)client()->countAt(jsToAddress(_address), client()->getDefault()); +} + +int WebThreeStubServerBase::eth_defaultBlock() +{ + return client()->getDefault(); +} + +std::string WebThreeStubServerBase::eth_gasPrice() +{ + return toJS(10 * dev::eth::szabo); +} + +std::string WebThreeStubServerBase::db_get(std::string const& _name, std::string const& _key) +{ + string ret = db()->get(_name, _key); + return toJS(dev::asBytes(ret)); +} + +Json::Value WebThreeStubServerBase::eth_filterLogs(int const& _id) +{ + return toJson(client()->logs(_id)); +} + +Json::Value WebThreeStubServerBase::eth_logs(Json::Value const& _json) +{ + return toJson(client()->logs(toLogFilter(_json))); +} + +std::string WebThreeStubServerBase::db_getString(std::string const& _name, std::string const& _key) +{ + return db()->get(_name, _key);; +} + +bool WebThreeStubServerBase::shh_haveIdentity(std::string const& _id) +{ + return m_ids.count(jsToPublic(_id)) > 0; +} + +bool WebThreeStubServerBase::eth_listening() +{ + return network()->isNetworkStarted(); +} + +bool WebThreeStubServerBase::eth_mining() +{ + return client()->isMining(); +} + +int WebThreeStubServerBase::eth_newFilter(Json::Value const& _json) +{ + unsigned ret = -1; + ret = client()->installWatch(toLogFilter(_json)); + return ret; +} + +int WebThreeStubServerBase::eth_newFilterString(std::string const& _filter) +{ + unsigned ret = -1; + if (_filter.compare("chain") == 0) + ret = client()->installWatch(dev::eth::ChainChangedFilter); + else if (_filter.compare("pending") == 0) + ret = client()->installWatch(dev::eth::PendingChangedFilter); + return ret; +} + +std::string WebThreeStubServerBase::shh_newGroup(std::string const& _id, std::string const& _who) +{ + (void)_id; + (void)_who; + return ""; +} + +std::string WebThreeStubServerBase::shh_newIdentity() +{ +// cnote << this << m_ids; + KeyPair kp = KeyPair::create(); + m_ids[kp.pub()] = kp.secret(); + return toJS(kp.pub()); +} + +Json::Value WebThreeStubServerBase::eth_compilers() +{ + Json::Value ret(Json::arrayValue); + ret.append("lll"); + ret.append("solidity"); + ret.append("serpent"); + return ret; +} + +std::string WebThreeStubServerBase::eth_lll(std::string const& _code) +{ + string res; + vector errors; + res = toJS(dev::eth::compileLLL(_code, true, &errors)); + cwarn << "LLL compilation errors: " << errors; + return res; +} + +std::string WebThreeStubServerBase::eth_serpent(std::string const& _code) +{ + string res; + try + { + res = toJS(dev::asBytes(::compile(_code))); + } + catch (string err) + { + cwarn << "Solidity compilation error: " << err; + } + catch (...) + { + cwarn << "Uncought serpent compilation exception"; + } + return res; +} + +std::string WebThreeStubServerBase::eth_solidity(std::string const& _code) +{ + string res; + dev::solidity::CompilerStack compiler; + try + { + res = toJS(compiler.compile(_code, true)); + } + catch (dev::Exception const& exception) + { + ostringstream error; + solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); + cwarn << "Solidity compilation error: " << error.str(); + } + catch (...) + { + cwarn << "Uncought solidity compilation exception"; + } + return res; +} + +int WebThreeStubServerBase::eth_number() +{ + return client()->number() + 1; +} + +int WebThreeStubServerBase::eth_peerCount() +{ + return network()->peerCount(); +} + +bool WebThreeStubServerBase::shh_post(Json::Value const& _json) +{ + shh::Message m = toMessage(_json); + Secret from; + + if (m.from() && m_ids.count(m.from())) + { + cwarn << "Silently signing message from identity" << m.from().abridged() << ": User validation hook goes here."; + // TODO: insert validification hook here. + from = m_ids[m.from()]; + } + + face()->inject(toSealed(_json, m, from)); + return true; +} + +bool WebThreeStubServerBase::db_put(std::string const& _name, std::string const& _key, std::string const& _value) +{ + string v = asString(jsToBytes(_value)); + db()->put(_name, _key, v); + return true; +} + +bool WebThreeStubServerBase::db_putString(std::string const& _name, std::string const& _key, std::string const& _value) +{ + db()->put(_name, _key,_value); + return true; +} + +bool WebThreeStubServerBase::eth_setCoinbase(std::string const& _address) +{ + client()->setAddress(jsToAddress(_address)); + return true; +} + +bool WebThreeStubServerBase::eth_setDefaultBlock(int const& _block) +{ + client()->setDefault(_block); + return true; +} + +bool WebThreeStubServerBase::eth_setListening(bool const& _listening) +{ + if (_listening) + network()->startNetwork(); + else + network()->stopNetwork(); + return true; +} + +bool WebThreeStubServerBase::eth_setMining(bool const& _mining) +{ + if (_mining) + client()->startMining(); + else + client()->stopMining(); + return true; +} + +Json::Value WebThreeStubServerBase::shh_changed(int const& _id) +{ + Json::Value ret(Json::arrayValue); + auto pub = m_shhWatches[_id]; + if (!pub || m_ids.count(pub)) + for (h256 const& h: face()->checkWatch(_id)) + { + auto e = face()->envelope(h); + shh::Message m; + if (pub) + { + cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here."; + m = e.open(m_ids[pub]); + if (!m) + continue; + } + else + m = e.open(); + ret.append(toJson(h, e, m)); + } + + return ret; +} + +int WebThreeStubServerBase::shh_newFilter(Json::Value const& _json) +{ + auto w = toWatch(_json); + auto ret = face()->installWatch(w.first); + m_shhWatches.insert(make_pair(ret, w.second)); + return ret; +} + +bool WebThreeStubServerBase::shh_uninstallFilter(int const& _id) +{ + face()->uninstallWatch(_id); + return true; +} + +std::string WebThreeStubServerBase::eth_stateAt(string const& _address, string const& _storage) +{ + return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), client()->getDefault())); +} + +Json::Value WebThreeStubServerBase::eth_storageAt(string const& _address) +{ + return toJson(client()->storageAt(jsToAddress(_address))); +} + +std::string WebThreeStubServerBase::eth_transact(Json::Value const& _json) +{ + std::string ret; + TransactionSkeleton t = toTransaction(_json); + if (!t.from && m_accounts.size()) + { + auto b = m_accounts.begin()->first; + for (auto a: m_accounts) + if (client()->balanceAt(a.first) > client()->balanceAt(b)) + b = a.first; + t.from = b; + } + if (!m_accounts.count(t.from)) + return ret; + if (!t.gasPrice) + t.gasPrice = 10 * dev::eth::szabo; + if (!t.gas) + t.gas = min(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice); + if (authenticate(t)) + { + if (t.to) + // TODO: from qethereum, insert validification hook here. + client()->transact(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice); + else + ret = toJS(client()->transact(m_accounts[t.from].secret(), t.value, t.data, t.gas, t.gasPrice)); + client()->flushTransactions(); + } + return ret; +} + +bool WebThreeStubServerBase::authenticate(TransactionSkeleton const& _t) const +{ + cwarn << "Silently signing transaction from address" << _t.from.abridged() << ": User validation hook goes here."; + return true; +} + +Json::Value WebThreeStubServerBase::eth_transactionByHash(std::string const& _hash, int const& _i) +{ + return toJson(client()->transaction(jsToFixed<32>(_hash), _i)); +} + +Json::Value WebThreeStubServerBase::eth_transactionByNumber(int const& _number, int const& _i) +{ + return toJson(client()->transaction(client()->hashFromNumber(_number), _i)); +} + +Json::Value WebThreeStubServerBase::eth_uncleByHash(std::string const& _hash, int const& _i) +{ + return toJson(client()->uncle(jsToFixed<32>(_hash), _i)); +} + +Json::Value WebThreeStubServerBase::eth_uncleByNumber(int const& _number, int const& _i) +{ + return toJson(client()->uncle(client()->hashFromNumber(_number), _i)); +} + +bool WebThreeStubServerBase::eth_uninstallFilter(int const& _id) +{ + client()->uninstallWatch(_id); + return true; +} + diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h new file mode 100644 index 000000000..24a6a9962 --- /dev/null +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -0,0 +1,138 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file WebThreeStubServer.h + * @authors: + * Gav Wood + * Marek Kotewicz + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include "abstractwebthreestubserver.h" +#pragma GCC diagnostic pop + + +namespace dev +{ +class WebThreeNetworkFace; +class KeyPair; +struct TransactionSkeleton; +namespace eth +{ +class Interface; +} +namespace shh +{ +class Interface; +} + +class WebThreeStubDatabaseFace +{ +public: + virtual std::string get(std::string const& _name, std::string const& _key) = 0; + virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) = 0; +}; + +/** + * @brief JSON-RPC api implementation + * @todo filters should work on unsigned instead of int + * unsigned are not supported in json-rpc-cpp and there are bugs with double in json-rpc-cpp version 0.2.1 + * @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols. + * @todo modularise everything so additional subprotocols don't need to change this file. + */ +class WebThreeStubServerBase: public AbstractWebThreeStubServer +{ +public: + WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts); + + virtual std::string web3_sha3(std::string const& _param1); + virtual Json::Value eth_accounts(); + virtual std::string eth_balanceAt(std::string const& _address); + virtual Json::Value eth_blockByHash(std::string const& _hash); + virtual Json::Value eth_blockByNumber(int const& _number); + virtual std::string eth_call(Json::Value const& _json); + virtual Json::Value eth_changed(int const& _id); + virtual std::string eth_codeAt(std::string const& _address); + virtual std::string eth_coinbase(); + virtual Json::Value eth_compilers(); + virtual double eth_countAt(std::string const& _address); + virtual int eth_defaultBlock(); + virtual std::string eth_gasPrice(); + virtual Json::Value eth_filterLogs(int const& _id); + virtual Json::Value eth_logs(Json::Value const& _json); + virtual bool eth_listening(); + virtual bool eth_mining(); + virtual int eth_newFilter(Json::Value const& _json); + virtual int eth_newFilterString(std::string const& _filter); + virtual int eth_number(); + virtual int eth_peerCount(); + virtual bool eth_setCoinbase(std::string const& _address); + virtual bool eth_setDefaultBlock(int const& _block); + virtual bool eth_setListening(bool const& _listening); + virtual std::string eth_lll(std::string const& _s); + virtual std::string eth_serpent(std::string const& _s); + virtual bool eth_setMining(bool const& _mining); + virtual std::string eth_solidity(std::string const& _code); + virtual std::string eth_stateAt(std::string const& _address, std::string const& _storage); + virtual Json::Value eth_storageAt(std::string const& _address); + virtual std::string eth_transact(Json::Value const& _json); + virtual Json::Value eth_transactionByHash(std::string const& _hash, int const& _i); + virtual Json::Value eth_transactionByNumber(int const& _number, int const& _i); + virtual Json::Value eth_uncleByHash(std::string const& _hash, int const& _i); + virtual Json::Value eth_uncleByNumber(int const& _number, int const& _i); + virtual bool eth_uninstallFilter(int const& _id); + + virtual std::string db_get(std::string const& _name, std::string const& _key); + virtual std::string db_getString(std::string const& _name, std::string const& _key); + virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value); + virtual bool db_putString(std::string const& _name, std::string const& _key, std::string const& _value); + + virtual std::string shh_addToGroup(std::string const& _group, std::string const& _who); + virtual Json::Value shh_changed(int const& _id); + virtual bool shh_haveIdentity(std::string const& _id); + virtual int shh_newFilter(Json::Value const& _json); + virtual std::string shh_newGroup(std::string const& _id, std::string const& _who); + virtual std::string shh_newIdentity(); + virtual bool shh_post(Json::Value const& _json); + virtual bool shh_uninstallFilter(int const& _id); + + void setAccounts(std::vector const& _accounts); + void setIdentities(std::vector const& _ids); + std::map const& ids() const { return m_ids; } + +protected: + virtual bool authenticate(dev::TransactionSkeleton const& _t) const; + +protected: + virtual dev::eth::Interface* client() = 0; + virtual std::shared_ptr face() = 0; + virtual dev::WebThreeNetworkFace* network() = 0; + virtual dev::WebThreeStubDatabaseFace* db() = 0; + + std::map m_accounts; + + std::map m_ids; + std::map m_shhWatches; +}; + +} //namespace dev diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 779d8bec3..15c53e0fa 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -45,7 +45,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(new jsonrpc::Procedure("eth_newFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_newFilterI); this->bindAndAddMethod(new jsonrpc::Procedure("eth_newFilterString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_newFilterStringI); this->bindAndAddMethod(new jsonrpc::Procedure("eth_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_uninstallFilterI); - this->bindAndAddMethod(new jsonrpc::Procedure("eth_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_changedI); + this->bindAndAddMethod(new jsonrpc::Procedure("eth_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_changedI); this->bindAndAddMethod(new jsonrpc::Procedure("eth_filterLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_filterLogsI); this->bindAndAddMethod(new jsonrpc::Procedure("eth_logs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_logsI); this->bindAndAddMethod(new jsonrpc::Procedure("db_put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putI); @@ -287,7 +287,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer peers() = 0; + + /// Same as peers().size(), but more efficient. + virtual size_t peerCount() const = 0; + + /// Connect to a particular peer. + virtual void connect(std::string const& _seedHost, unsigned short _port) = 0; + + /// Save peers + virtual dev::bytes saveNodes() = 0; + + /// Restore peers + virtual void restoreNodes(bytesConstRef _saved) = 0; + + /// Sets the ideal number of peers. + virtual void setIdealPeerCount(size_t _n) = 0; + + virtual bool haveNetwork() const = 0; + + virtual void setNetworkPreferences(p2p::NetworkPreferences const& _n) = 0; + + virtual p2p::NodeId id() const = 0; + + /// Gets the nodes. + virtual p2p::Nodes nodes() const = 0; + + /// Start the network subsystem. + virtual void startNetwork() = 0; + + /// Stop the network subsystem. + virtual void stopNetwork() = 0; + + /// Is network working? there may not be any peers yet. + virtual bool isNetworkStarted() const = 0; +}; + + /** * @brief Main API hub for interfacing with Web 3 components. This doesn't do any local multiplexing, so you can only have one * running on any given machine for the provided DB path. @@ -61,7 +101,7 @@ namespace bzz { class Interface; } * * Provides a baseline for the multiplexed multi-protocol session class, WebThree. */ -class WebThreeDirect +class WebThreeDirect : public WebThreeNetworkFace { public: /// Constructor for private instance. If there is already another process on the machine using @a _dbPath, then this will throw an exception. @@ -84,40 +124,40 @@ public: // Network stuff: /// Get information on the current peer set. - std::vector peers(); + std::vector peers() override; /// Same as peers().size(), but more efficient. - size_t peerCount() const; + size_t peerCount() const override; /// Connect to a particular peer. - void connect(std::string const& _seedHost, unsigned short _port = 30303); + void connect(std::string const& _seedHost, unsigned short _port = 30303) override; /// Save peers - dev::bytes saveNodes(); + dev::bytes saveNodes() override; /// Restore peers - void restoreNodes(bytesConstRef _saved); + void restoreNodes(bytesConstRef _saved) override; /// Sets the ideal number of peers. - void setIdealPeerCount(size_t _n); + void setIdealPeerCount(size_t _n) override; - bool haveNetwork() const { return m_net.isStarted(); } + bool haveNetwork() const override { return m_net.isStarted(); } - void setNetworkPreferences(p2p::NetworkPreferences const& _n); + void setNetworkPreferences(p2p::NetworkPreferences const& _n) override; - p2p::NodeId id() const { return m_net.id(); } + p2p::NodeId id() const override { return m_net.id(); } /// Gets the nodes. - p2p::Nodes nodes() const { return m_net.nodes(); } + p2p::Nodes nodes() const override { return m_net.nodes(); } /// Start the network subsystem. - void startNetwork() { m_net.start(); } + void startNetwork() override { m_net.start(); } /// Stop the network subsystem. - void stopNetwork() { m_net.stop(); } + void stopNetwork() override { m_net.stop(); } /// Is network working? there may not be any peers yet. - bool isNetworkStarted() { return m_net.isStarted(); } + bool isNetworkStarted() const override { return m_net.isStarted(); } private: std::string m_clientVersion; ///< Our end-application client's name/version. diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp index ff2d10d01..4c8e61510 100644 --- a/mix/AppContext.cpp +++ b/mix/AppContext.cpp @@ -27,13 +27,11 @@ #include #include #include -#include -#include -#include -#include -#include -#include "AppContext.h" #include "CodeModel.h" +#include "FileIo.h" +#include "ClientModel.h" +#include "AppContext.h" +#include using namespace dev; using namespace dev::eth; @@ -45,28 +43,19 @@ AppContext::AppContext(QQmlApplicationEngine* _engine) { m_applicationEngine = _engine; //m_webThree = std::unique_ptr(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"})); - m_codeModel = std::unique_ptr(new CodeModel(this)); - m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get()); + m_codeModel.reset(new CodeModel(this)); + m_clientModel.reset(new ClientModel(this)); + m_fileIo.reset(new FileIo()); m_applicationEngine->rootContext()->setContextProperty("appContext", this); -} + qmlRegisterType("org.ethereum.qml", 1, 0, "FileIo"); + qmlRegisterSingletonType(QUrl("qrc:/qml/ProjectModel.qml"), "org.ethereum.qml.ProjectModel", 1, 0, "ProjectModel"); + m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get()); + m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get()); -AppContext::~AppContext() -{ } -void AppContext::loadProject() +AppContext::~AppContext() { - QString path = QStandardPaths::locate(QStandardPaths::DataLocation, c_projectFileName); - if (!path.isEmpty()) - { - QFile file(path); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - QTextStream stream(&file); - QString json = stream.readAll(); - emit projectLoaded(json); - } - } } QQmlApplicationEngine* AppContext::appEngine() @@ -86,19 +75,3 @@ void AppContext::displayMessageDialog(QString _title, QString _message) dialogWin->findChild("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message); QMetaObject::invokeMethod(dialogWin, "open"); } - -void AppContext::saveProject(QString const& _json) -{ - QDir dirPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); - QString path = QDir(dirPath).filePath(c_projectFileName); - if (!path.isEmpty()) - { - dirPath.mkpath(dirPath.path()); - QFile file(path); - if (file.open(QIODevice::WriteOnly | QIODevice::Text)) - { - QTextStream stream(&file); - stream << _json; - } - } -} diff --git a/mix/AppContext.h b/mix/AppContext.h index 7435c7777..dec3b319e 100644 --- a/mix/AppContext.h +++ b/mix/AppContext.h @@ -47,6 +47,8 @@ namespace mix { class CodeModel; +class ClientModel; +class FileIo; /** * @brief Provides access to application scope variable. */ @@ -62,23 +64,25 @@ public: QQmlApplicationEngine* appEngine(); /// Get code model CodeModel* codeModel() { return m_codeModel.get(); } + /// Get client model + ClientModel* clientModel() { return m_clientModel.get(); } /// Display an alert message. void displayMessageDialog(QString _title, QString _message); - /// Load project settings - void loadProject(); + signals: - void projectLoaded(QString const& _json); + /// Triggered once components have been loaded + void appLoaded(); private: QQmlApplicationEngine* m_applicationEngine; //owned by app std::unique_ptr m_webThree; std::unique_ptr m_codeModel; + std::unique_ptr m_clientModel; + std::unique_ptr m_fileIo; public slots: /// Delete the current instance when application quit. void quitApplication() {} - /// Write json to a settings file - void saveProject(QString const& _json); }; } diff --git a/mix/AssemblyDebuggerControl.cpp b/mix/AssemblyDebuggerControl.cpp index 12dfe533b..a3553043d 100644 --- a/mix/AssemblyDebuggerControl.cpp +++ b/mix/AssemblyDebuggerControl.cpp @@ -17,53 +17,30 @@ * display opcode debugging. */ -#include #include #include #include -#include -#include -#include -#include "AssemblyDebuggerModel.h" -#include "AssemblyDebuggerControl.h" #include "AppContext.h" -#include "DebuggingStateWrapper.h" -#include "QContractDefinition.h" -#include "QVariableDeclaration.h" -#include "ContractCallDataEncoder.h" -#include "CodeModel.h" +#include "ClientModel.h" +#include "AssemblyDebuggerControl.h" -using namespace dev::eth; using namespace dev::mix; -/// @todo Move this to QML -dev::u256 fromQString(QString const& _s) -{ - return dev::jsToU256(_s.toStdString()); -} - -/// @todo Move this to QML -QString toQString(dev::u256 _value) -{ - std::ostringstream s; - s << _value; - return QString::fromStdString(s.str()); -} - AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context): - Extension(_context, ExtensionDisplayBehavior::RightView), m_running(false) + Extension(_context, ExtensionDisplayBehavior::RightView) { + /* qRegisterMetaType("QVariableDefinition*"); qRegisterMetaType("QVariableDefinitionList*"); qRegisterMetaType>("QList"); qRegisterMetaType>("QList"); qRegisterMetaType("QVariableDeclaration*"); qRegisterMetaType("AssemblyDebuggerData"); - connect(this, &AssemblyDebuggerControl::dataAvailable, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection); m_modelDebugger = std::unique_ptr(new AssemblyDebuggerModel); + _context->appEngine()->rootContext()->setContextProperty("debugModel", this);*/ - _context->appEngine()->rootContext()->setContextProperty("debugModel", this); + connect(_context->clientModel(), &ClientModel::showDebuggerWindow, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection); } QString AssemblyDebuggerControl::contentUrl() const @@ -80,138 +57,8 @@ void AssemblyDebuggerControl::start() const { } -void AssemblyDebuggerControl::debugDeployment() +void AssemblyDebuggerControl::showDebugger() { - executeSequence(std::vector(), 10000000 * ether); -} - -void AssemblyDebuggerControl::debugState(QVariantMap _state) -{ - u256 balance = fromQString(_state.value("balance").toString()); - QVariantList transactions = _state.value("transactions").toList(); - - std::vector transactionSequence; - - for (auto const& t: transactions) - { - QVariantMap transaction = t.toMap(); - - QString functionId = transaction.value("functionId").toString(); - u256 value = fromQString(transaction.value("value").toString()); - u256 gas = fromQString(transaction.value("gas").toString()); - u256 gasPrice = fromQString(transaction.value("gasPrice").toString()); - QVariantMap params = transaction.value("parameters").toMap(); - TransactionSettings transactionSettings(functionId, value, gas, gasPrice); - - for (auto p = params.cbegin(); p != params.cend(); ++p) - transactionSettings.parameterValues.insert(std::make_pair(p.key(), fromQString(p.value().toString()))); - - transactionSequence.push_back(transactionSettings); - } - executeSequence(transactionSequence, balance); -} - -void AssemblyDebuggerControl::executeSequence(std::vector const& _sequence, u256 _balance) -{ - if (m_running) - throw (std::logic_error("debugging already running")); - auto compilerRes = m_ctx->codeModel()->code(); - std::shared_ptr contractDef = compilerRes->sharedContract(); - m_running = true; - - emit runStarted(); - emit stateChanged(); - - //run sequence - QtConcurrent::run([=]() - { - try - { - bytes contractCode = compilerRes->bytes(); - std::vector transactonData; - QFunctionDefinition* f; - ContractCallDataEncoder c; - //encode data for all transactions - for (auto const& t: _sequence) - { - f = nullptr; - for (int tf = 0; tf < contractDef->functionsList().size(); tf++) - { - if (contractDef->functionsList().at(tf)->name() == t.functionId) - { - f = contractDef->functionsList().at(tf); - break; - } - } - if (!f) - throw std::runtime_error("function " + t.functionId.toStdString() + " not found"); - - c.encode(f->index()); - for (int p = 0; p < f->parametersList().size(); p++) - { - QVariableDeclaration* var = (QVariableDeclaration*)f->parametersList().at(p); - u256 value = 0; - auto v = t.parameterValues.find(var->name()); - if (v != t.parameterValues.cend()) - value = v->second; - c.encode(var, value); - } - transactonData.emplace_back(c.encodedData()); - } - - //run contract creation first - m_modelDebugger->resetState(_balance); - DebuggingContent debuggingContent = m_modelDebugger->deployContract(contractCode); - Address address = debuggingContent.contractAddress; - for (unsigned i = 0; i < _sequence.size(); ++i) - debuggingContent = m_modelDebugger->callContract(address, transactonData.at(i), _sequence.at(i)); - - if (f && (_sequence.size() > 0)) - debuggingContent.returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue); - - //we need to wrap states in a QObject before sending to QML. - QList wStates; - for (int i = 0; i < debuggingContent.machineStates.size(); i++) - { - QPointer s(new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes())); - s->setState(debuggingContent.machineStates.at(i)); - wStates.append(s); - } - //collect states for last transaction - AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode); - emit dataAvailable(debuggingContent.returnParameters, wStates, code); - emit runComplete(); - } - catch(boost::exception const&) - { - emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); - } - catch(std::exception const& e) - { - emit runFailed(e.what()); - } - m_running = false; - emit stateChanged(); - }); -} - -void AssemblyDebuggerControl::showDebugger(QList const& _returnParam, QList const& _wStates, AssemblyDebuggerData const& _code) -{ - m_appEngine->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates)); - m_appEngine->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code))); - m_appEngine->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code))); - m_appEngine->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam))); - updateDebugPanel(); -} - -void AssemblyDebuggerControl::updateDebugPanel() -{ - QVariant returnValue; QObject* debugPanel = m_view->findChild("debugPanel", Qt::FindChildrenRecursively); - QMetaObject::invokeMethod(debugPanel, "update", Q_RETURN_ARG(QVariant, returnValue)); -} - -void AssemblyDebuggerControl::showDebugError(QString const& _error) -{ - m_ctx->displayMessageDialog(QApplication::tr("Debugger"), _error); + QMetaObject::invokeMethod(debugPanel, "update"); } diff --git a/mix/AssemblyDebuggerControl.h b/mix/AssemblyDebuggerControl.h index b8bd344e5..2e552f63a 100644 --- a/mix/AssemblyDebuggerControl.h +++ b/mix/AssemblyDebuggerControl.h @@ -20,14 +20,7 @@ #pragma once #include -#include #include "Extension.h" -#include "AssemblyDebuggerModel.h" - -using AssemblyDebuggerData = std::tuple, dev::mix::QQMLMap*>; - -Q_DECLARE_METATYPE(AssemblyDebuggerData) -Q_DECLARE_METATYPE(dev::mix::DebuggingContent) class AppContext; @@ -37,12 +30,11 @@ namespace mix { /** - * @brief Extension which display transaction creation or transaction call debugging. handle: F5 to deploy contract, F6 to reset state. + * @brief Extension which display transaction creation or transaction call debugging. */ class AssemblyDebuggerControl: public Extension { Q_OBJECT - Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged) public: AssemblyDebuggerControl(AppContext* _context); @@ -50,41 +42,10 @@ public: void start() const override; QString title() const override; QString contentUrl() const override; - /// show panel without managing machine states result. Displayed in the right side tab. - Q_INVOKABLE void updateDebugPanel(); - -private: - void executeSequence(std::vector const& _sequence, u256 _balance); - - std::unique_ptr m_modelDebugger; - std::atomic m_running; - -public slots: - /// Run the contract constructor and show debugger window. - void debugDeployment(); - /// Setup state, run transaction sequence, show debugger for the last transaction - /// @param _state JS object with state configuration - void debugState(QVariantMap _state); private slots: /// Update UI with machine states result. Displayed in the right side tab. - void showDebugger(QList const& _returnParams = QList(), QList const& _wStates = QList(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); - /// Update UI with transaction run error. - void showDebugError(QString const& _error); - -signals: - /// Transaction execution started - void runStarted(); - /// Transaction execution completed successfully - void runComplete(); - /// Transaction execution completed with error - /// @param _message Error message - void runFailed(QString const& _message); - /// Execution state changed - void stateChanged(); - - /// Emited when machine states are available. - void dataAvailable(QList const& _returnParams = QList(), QList const& _wStates = QList(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); + void showDebugger(); }; } diff --git a/mix/AssemblyDebuggerModel.cpp b/mix/AssemblyDebuggerModel.cpp deleted file mode 100644 index e822d0a3f..000000000 --- a/mix/AssemblyDebuggerModel.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** @file AssemblyDebuggerModel.cpp - * @author Yann yann@ethdev.com - * @date 2014 - * used as a model to debug contract assembly code. - */ - -#include -#include -#include -#include -#include -#include -#include "AssemblyDebuggerModel.h" -#include "AppContext.h" -#include "DebuggingStateWrapper.h" - -using namespace std; -using namespace dev; -using namespace dev::eth; - -namespace dev -{ -namespace mix -{ - -AssemblyDebuggerModel::AssemblyDebuggerModel(): - m_userAccount(KeyPair::create()) -{ - resetState(10000000 * ether); -} - -DebuggingContent AssemblyDebuggerModel::executeTransaction(bytesConstRef const& _rawTransaction) -{ - QList machineStates; - eth::Executive execution(m_executiveState, LastHashes(), 0); - execution.setup(_rawTransaction); - std::vector levels; - bytes code; - bytesConstRef data; - bool firstIteration = true; - auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) - { - VM& vm = *(VM*)voidVM; - ExtVM const& ext = *(ExtVM const*)voidExt; - - if (firstIteration) - { - code = ext.code; - data = ext.data; - firstIteration = false; - } - - if (levels.size() < ext.depth) - levels.push_back(&machineStates.back()); - else - levels.resize(ext.depth); - - machineStates.append(DebuggingState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), - vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); - }; - - execution.go(onOp); - execution.finalize(); - - DebuggingContent d; - d.returnValue = execution.out().toVector(); - d.machineStates = machineStates; - d.executionCode = code; - d.executionData = data; - d.contentAvailable = true; - d.message = "ok"; - return d; -} - -DebuggingContent AssemblyDebuggerModel::deployContract(bytes const& _code) -{ - u256 gasPrice = 10000000000000; - u256 gas = 1000000; - u256 amount = 100; - Transaction _tr(amount, gasPrice, min(gas, m_executiveState.gasLimitRemaining()), _code, m_executiveState.transactionsFrom(dev::toAddress(m_userAccount.secret())), m_userAccount.secret()); - bytes b = _tr.rlp(); - dev::bytesConstRef bytesRef = &b; - DebuggingContent d = executeTransaction(bytesRef); - h256 th = sha3(rlpList(_tr.sender(), _tr.nonce())); - d.contractAddress = right160(th); - return d; -} - -DebuggingContent AssemblyDebuggerModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) -{ - Transaction tr = Transaction(_tr.value, _tr.gasPrice, min(_tr.gas, m_executiveState.gasLimitRemaining()), _contract, _data, m_executiveState.transactionsFrom(dev::toAddress(m_userAccount.secret())), m_userAccount.secret()); - bytes b = tr.rlp(); - dev::bytesConstRef bytesRef = &b; - DebuggingContent d = executeTransaction(bytesRef); - d.contractAddress = tr.receiveAddress(); - return d; -} - -void AssemblyDebuggerModel::resetState(u256 _balance) -{ - m_executiveState = eth::State(Address(), m_overlayDB, BaseState::Empty); - m_executiveState.addBalance(m_userAccount.address(), _balance); -} - -} -} diff --git a/mix/AssemblyDebuggerModel.h b/mix/AssemblyDebuggerModel.h deleted file mode 100644 index c12e711c6..000000000 --- a/mix/AssemblyDebuggerModel.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** @file AssemblyDebuggerModel.h - * @author Yann yann@ethdev.com - * @date 2014 - * Used as a model to debug contract assembly code. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include "DebuggingStateWrapper.h" - -namespace dev -{ -namespace mix -{ - -/// Backend transaction config class -struct TransactionSettings -{ - TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice): - functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {} - - /// Contract function name - QString functionId; - /// Transaction value - u256 value; - /// Gas - u256 gas; - /// Gas price - u256 gasPrice; - /// Mapping from contract function parameter name to value - std::map parameterValues; -}; - - -/** - * @brief Long-life object for managing all executions. - */ -class AssemblyDebuggerModel -{ -public: - AssemblyDebuggerModel(); - /// Call function in a already deployed contract. - DebuggingContent callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); - /// Deploy the contract described by _code. - DebuggingContent deployContract(bytes const& _code); - /// Reset state to the empty state with given balance. - void resetState(u256 _balance); - -private: - KeyPair m_userAccount; - OverlayDB m_overlayDB; - eth::State m_executiveState; - DebuggingContent executeTransaction(dev::bytesConstRef const& _rawTransaction); -}; - -} -} diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index 555f6290f..b916cd0fd 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -27,7 +27,7 @@ eth_add_executable(${EXECUTABLE} target_link_libraries(${EXECUTABLE} Qt5::Core) target_link_libraries(${EXECUTABLE} Qt5::Gui) target_link_libraries(${EXECUTABLE} webthree) -target_link_libraries(${EXECUTABLE} qethereum) +target_link_libraries(${EXECUTABLE} qwebthree) target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) @@ -46,3 +46,6 @@ eth_install_executable(${EXECUTABLE} QMLDIR ${CMAKE_CURRENT_SOURCE_DIR}/qml ) +#add qml files to project tree in Qt creator +file(GLOB_RECURSE QMLFILES "qml/*.*") +add_custom_target(dummy SOURCES ${QMLFILES}) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp new file mode 100644 index 000000000..bcbed1fac --- /dev/null +++ b/mix/ClientModel.cpp @@ -0,0 +1,222 @@ +/* + This file is part of cpp-ethereum. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ClientModel.cpp + * @author Yann yann@ethdev.com + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include +#include +#include "ClientModel.h" +#include "AppContext.h" +#include "DebuggingStateWrapper.h" +#include "QContractDefinition.h" +#include "QVariableDeclaration.h" +#include "ContractCallDataEncoder.h" +#include "CodeModel.h" +#include "ClientModel.h" + +using namespace dev; +using namespace dev::eth; +using namespace dev::mix; + +/// @todo Move this to QML +dev::u256 fromQString(QString const& _s) +{ + return dev::jsToU256(_s.toStdString()); +} + +/// @todo Move this to QML +QString toQString(dev::u256 _value) +{ + std::ostringstream s; + s << _value; + return QString::fromStdString(s.str()); +} + +ClientModel::ClientModel(AppContext* _context): + m_context(_context), m_running(false) +{ + qRegisterMetaType("QVariableDefinition*"); + qRegisterMetaType("QVariableDefinitionList*"); + qRegisterMetaType>("QList"); + qRegisterMetaType>("QList"); + qRegisterMetaType("QVariableDeclaration*"); + qRegisterMetaType("AssemblyDebuggerData"); + + connect(this, &ClientModel::dataAvailable, this, &ClientModel::showDebugger, Qt::QueuedConnection); + m_client.reset(new MixClient()); + + _context->appEngine()->rootContext()->setContextProperty("clientModel", this); +} + +void ClientModel::debugDeployment() +{ + executeSequence(std::vector(), 10000000 * ether); +} + +void ClientModel::debugState(QVariantMap _state) +{ + u256 balance = fromQString(_state.value("balance").toString()); + QVariantList transactions = _state.value("transactions").toList(); + + std::vector transactionSequence; + + for (auto const& t: transactions) + { + QVariantMap transaction = t.toMap(); + + QString functionId = transaction.value("functionId").toString(); + u256 value = fromQString(transaction.value("value").toString()); + u256 gas = fromQString(transaction.value("gas").toString()); + u256 gasPrice = fromQString(transaction.value("gasPrice").toString()); + QVariantMap params = transaction.value("parameters").toMap(); + TransactionSettings transactionSettings(functionId, value, gas, gasPrice); + + for (auto p = params.cbegin(); p != params.cend(); ++p) + transactionSettings.parameterValues.insert(std::make_pair(p.key(), fromQString(p.value().toString()))); + + transactionSequence.push_back(transactionSettings); + } + executeSequence(transactionSequence, balance); +} + +void ClientModel::executeSequence(std::vector const& _sequence, u256 _balance) +{ + if (m_running) + throw (std::logic_error("debugging already running")); + auto compilerRes = m_context->codeModel()->code(); + std::shared_ptr contractDef = compilerRes->sharedContract(); + m_running = true; + + emit runStarted(); + emit stateChanged(); + + //run sequence + QtConcurrent::run([=]() + { + try + { + bytes contractCode = compilerRes->bytes(); + std::vector transactonData; + QFunctionDefinition* f; + ContractCallDataEncoder c; + //encode data for all transactions + for (auto const& t: _sequence) + { + f = nullptr; + for (int tf = 0; tf < contractDef->functionsList().size(); tf++) + { + if (contractDef->functionsList().at(tf)->name() == t.functionId) + { + f = contractDef->functionsList().at(tf); + break; + } + } + if (!f) + throw std::runtime_error("function " + t.functionId.toStdString() + " not found"); + + c.encode(f); + for (int p = 0; p < f->parametersList().size(); p++) + { + QVariableDeclaration* var = (QVariableDeclaration*)f->parametersList().at(p); + u256 value = 0; + auto v = t.parameterValues.find(var->name()); + if (v != t.parameterValues.cend()) + value = v->second; + c.encode(var, value); + } + transactonData.emplace_back(c.encodedData()); + } + + //run contract creation first + m_client->resetState(_balance); + ExecutionResult debuggingContent = deployContract(contractCode); + Address address = debuggingContent.contractAddress; + for (unsigned i = 0; i < _sequence.size(); ++i) + debuggingContent = callContract(address, transactonData.at(i), _sequence.at(i)); + + QList returnParameters; + + if (f) + returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue); + + //we need to wrap states in a QObject before sending to QML. + QList wStates; + for (unsigned i = 0; i < debuggingContent.machineStates.size(); i++) + { + QPointer s(new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes())); + s->setState(debuggingContent.machineStates[i]); + wStates.append(s); + } + //collect states for last transaction + AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode); + emit dataAvailable(returnParameters, wStates, code); + emit runComplete(); + } + catch(boost::exception const&) + { + emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); + } + + catch(std::exception const& e) + { + emit runFailed(e.what()); + } + m_running = false; + emit stateChanged(); + }); +} + +void ClientModel::showDebugger(QList const& _returnParam, QList const& _wStates, AssemblyDebuggerData const& _code) +{ + m_context->appEngine()->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates)); + m_context->appEngine()->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code))); + m_context->appEngine()->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code))); + m_context->appEngine()->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam))); + showDebuggerWindow(); +} + +void ClientModel::showDebugError(QString const& _error) +{ + //TODO: change that to a signal + m_context->displayMessageDialog(tr("Debugger"), _error); +} + +ExecutionResult ClientModel::deployContract(bytes const& _code) +{ + u256 gasPrice = 10000000000000; + u256 gas = 125000; + u256 amount = 100; + + Address contractAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice); + ExecutionResult r = m_client->lastExecutionResult(); + r.contractAddress = contractAddress; + return r; +} + +ExecutionResult ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) +{ + m_client->transact(m_client->userAccount().secret(), _tr.value, _contract, _data, _tr.gas, _tr.gasPrice); + ExecutionResult r = m_client->lastExecutionResult(); + r.contractAddress = _contract; + return r; +} + diff --git a/mix/ClientModel.h b/mix/ClientModel.h new file mode 100644 index 000000000..509a5995b --- /dev/null +++ b/mix/ClientModel.h @@ -0,0 +1,113 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ClientModel.h + * @author Yann yann@ethdev.com + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include +#include "DebuggingStateWrapper.h" +#include "MixClient.h" + +using AssemblyDebuggerData = std::tuple, dev::mix::QQMLMap*>; + +Q_DECLARE_METATYPE(AssemblyDebuggerData) +Q_DECLARE_METATYPE(dev::mix::ExecutionResult) + +namespace dev +{ +namespace mix +{ + +class AppContext; + +/// Backend transaction config class +struct TransactionSettings +{ + TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice): + functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {} + + /// Contract function name + QString functionId; + /// Transaction value + u256 value; + /// Gas + u256 gas; + /// Gas price + u256 gasPrice; + /// Mapping from contract function parameter name to value + std::map parameterValues; +}; + + +/** + * @brief Ethereum state control + */ +class ClientModel: public QObject +{ + Q_OBJECT + +public: + ClientModel(AppContext* _context); + + Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged) + +public slots: + /// Run the contract constructor and show debugger window. + void debugDeployment(); + /// Setup state, run transaction sequence, show debugger for the last transaction + /// @param _state JS object with state configuration + void debugState(QVariantMap _state); + +private slots: + /// Update UI with machine states result. Display a modal dialog. + void showDebugger(QList const& _returnParams = QList(), QList const& _wStates = QList(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); + /// Update UI with transaction run error. + void showDebugError(QString const& _error); + +signals: + /// Transaction execution started + void runStarted(); + /// Transaction execution completed successfully + void runComplete(); + /// Transaction execution completed with error + /// @param _message Error message + void runFailed(QString const& _message); + /// Execution state changed + void stateChanged(); + /// Show debugger window request + void showDebuggerWindow(); + + /// Emited when machine states are available. + void dataAvailable(QList const& _returnParams = QList(), QList const& _wStates = QList(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); + +private: + void executeSequence(std::vector const& _sequence, u256 _balance); + ExecutionResult deployContract(bytes const& _code); + ExecutionResult callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); + + AppContext* m_context; + std::atomic m_running; + std::unique_ptr m_client; +}; + +} +} diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index 6c5b23af7..f47dcf4ed 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -31,6 +31,7 @@ #include "AppContext.h" #include "MixApplication.h" #include "CodeModel.h" +#include "ClientModel.h" #include "CodeHighlighter.h" #include "CodeEditorExtensionManager.h" @@ -51,15 +52,6 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) if (!_editor) return; - QVariant doc = _editor->property("textDocument"); - if (doc.canConvert()) - { - QQuickTextDocument* qqdoc = doc.value(); - if (qqdoc) - { - m_doc = qqdoc->textDocument(); - } - } } void CodeEditorExtensionManager::initExtensions() @@ -67,7 +59,7 @@ void CodeEditorExtensionManager::initExtensions() std::shared_ptr output = std::make_shared(m_appContext); std::shared_ptr debug = std::make_shared(m_appContext); std::shared_ptr stateList = std::make_shared(m_appContext); - QObject::connect(m_doc, &QTextDocument::contentsChange, this, &CodeEditorExtensionManager::onCodeChange); + //QObject::connect(m_appContext->clientModel(), &ClientModel::runFailed, output.get(), &sta::displayError); QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); initExtension(output); @@ -96,35 +88,15 @@ void CodeEditorExtensionManager::initExtension(std::shared_ptr _ext) m_features.append(_ext); } -void CodeEditorExtensionManager::setEditor(QQuickItem* _editor) -{ - this->loadEditor(_editor); - this->initExtensions(); - - auto args = QApplication::arguments(); - if (args.length() > 1) - { - QString path = args[1]; - QFile file(path); - if (file.exists() && file.open(QFile::ReadOnly)) - m_doc->setPlainText(file.readAll()); - } -} - -void CodeEditorExtensionManager::onCodeChange() -{ - m_appContext->codeModel()->updateFormatting(m_doc); //update old formatting - m_appContext->codeModel()->registerCodeChange(m_doc->toPlainText()); -} - void CodeEditorExtensionManager::applyCodeHighlight() { - m_appContext->codeModel()->updateFormatting(m_doc); + //TODO: reimplement } void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView) { m_rightView = _rightView; + initExtensions(); //TODO: move this to a proper place } void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView) diff --git a/mix/CodeEditorExtensionManager.h b/mix/CodeEditorExtensionManager.h index da7d0486d..0455a1beb 100644 --- a/mix/CodeEditorExtensionManager.h +++ b/mix/CodeEditorExtensionManager.h @@ -43,7 +43,6 @@ class CodeEditorExtensionManager: public QObject { Q_OBJECT - Q_PROPERTY(QQuickItem* editor MEMBER m_editor WRITE setEditor) Q_PROPERTY(QQuickItem* headerView MEMBER m_headerView WRITE setHeaderView) Q_PROPERTY(QQuickItem* rightView MEMBER m_rightView WRITE setRightView) @@ -54,19 +53,15 @@ public: void initExtensions(); /// Initialize extension. void initExtension(std::shared_ptr); - /// Set current text editor. - void setEditor(QQuickItem*); /// Set current tab view void setHeaderView(QQuickItem*); /// Set current right tab view. void setRightView(QQuickItem*); private slots: - void onCodeChange(); void applyCodeHighlight(); private: - QQuickItem* m_editor; QVector> m_features; QQuickItem* m_headerView; QQuickItem* m_rightView; diff --git a/mix/CodeHighlighter.cpp b/mix/CodeHighlighter.cpp index 5c126d8e2..ab8a61ff5 100644 --- a/mix/CodeHighlighter.cpp +++ b/mix/CodeHighlighter.cpp @@ -64,7 +64,7 @@ namespace }; } -CodeHighlighter::FormatRange::FormatRange(CodeHighlighterSettings::Token _t, solidity::Location const& _location): +CodeHighlighter::FormatRange::FormatRange(CodeHighlighterSettings::Token _t, dev::solidity::Location const& _location): token(_t), start(_location.start), length(_location.end - _location.start) {} @@ -91,7 +91,7 @@ void CodeHighlighter::processSource(std::string const& _source) std::sort(m_formats.begin(), m_formats.end()); } -void CodeHighlighter::processAST(solidity::ASTNode const& _ast) +void CodeHighlighter::processAST(dev::solidity::ASTNode const& _ast) { HighlightVisitor visitor(&m_formats); _ast.accept(visitor); diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 35fe3440a..699e0753d 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -48,7 +48,7 @@ CompilationResult::CompilationResult(): m_codeHighlighter(new CodeHighlighter()) {} -CompilationResult::CompilationResult(const solidity::CompilerStack& _compiler): +CompilationResult::CompilationResult(const dev::solidity::CompilerStack& _compiler): QObject(nullptr), m_successful(true), m_codeHash(qHash(QString())) @@ -162,13 +162,13 @@ void CodeModel::onCompilationComplete(CompilationResult*_newResult) m_result.reset(_newResult); emit compilationComplete(); emit stateChanged(); - if (m_result->successfull()) + if (m_result->successful()) emit codeChanged(); } bool CodeModel::hasContract() const { - return m_result->successfull(); + return m_result->successful(); } void CodeModel::updateFormatting(QTextDocument* _document) diff --git a/mix/CodeModel.h b/mix/CodeModel.h index 8592d6d87..0c1de2a90 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -66,12 +66,12 @@ class CompilationResult: public QObject Q_OBJECT Q_PROPERTY(QContractDefinition* contract READ contract) Q_PROPERTY(QString compilerMessage READ compilerMessage CONSTANT) - Q_PROPERTY(bool successfull READ successfull CONSTANT) + Q_PROPERTY(bool successful READ successful CONSTANT) public: /// Empty compilation result constructor CompilationResult(); - /// Successfull compilation result constructor + /// Successful compilation result constructor CompilationResult(solidity::CompilerStack const& _compiler); /// Failed compilation result constructor CompilationResult(CompilationResult const& _prev, QString const& _compilerMessage); @@ -80,9 +80,9 @@ public: QContractDefinition* contract() { return m_contract.get(); } /// @returns contract definition std::shared_ptr sharedContract() { return m_contract; } - /// Indicates if the compilation was successfull - bool successfull() const { return m_successful; } - /// @returns compiler error message in case of unsuccessfull compilation + /// Indicates if the compilation was successful + bool successful() const { return m_successful; } + /// @returns compiler error message in case of unsuccessful compilation QString compilerMessage() const { return m_compilerMessage; } /// @returns contract bytecode dev::bytes const& bytes() const { return m_bytes; } diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 757f7243c..7a38db22b 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -27,6 +27,7 @@ #include #include "QVariableDeclaration.h" #include "QVariableDefinition.h" +#include "QFunctionDefinition.h" #include "ContractCallDataEncoder.h" using namespace dev; using namespace dev::solidity; @@ -37,10 +38,10 @@ bytes ContractCallDataEncoder::encodedData() return m_encodedData; } -void ContractCallDataEncoder::encode(int _functionIndex) +void ContractCallDataEncoder::encode(QFunctionDefinition const* _function) { - bytes i = jsToBytes(std::to_string(_functionIndex)); - m_encodedData.insert(m_encodedData.end(), i.begin(), i.end()); + bytes hash = _function->hash().asBytes(); + m_encodedData.insert(m_encodedData.end(), hash.begin(), hash.end()); } void ContractCallDataEncoder::encode(QVariableDeclaration const* _dec, bool _value) @@ -91,7 +92,7 @@ int ContractCallDataEncoder::padding(QString type) else if (type.indexOf("bool") != -1) return 1; else if ((type.indexOf("address") != -1)) - return 20; + return 32; else return 0; } diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index fd67a7b7a..49410a7cd 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -30,6 +30,10 @@ namespace dev namespace mix { +class QFunctionDefinition; +class QVariableDeclaration; +class QVariableDefinition; + /** * @brief Encode/Decode data to be sent to a transaction or to be displayed in a view. */ @@ -43,8 +47,8 @@ public: void encode(QVariableDeclaration const* _dec, u256 _value); /// Encode variable in order to be sent as parameter. void encode(QVariableDeclaration const* _dec, bool _value); - /// Encode index of the function to call. - void encode(int _functionIndex); + /// Encode hash of the function to call. + void encode(QFunctionDefinition const* _function); /// Decode variable in order to be sent to QML view. QList decode(QList _dec, bytes _value); /// Get all encoded data encoded by encode function. diff --git a/mix/DebuggingStateWrapper.h b/mix/DebuggingStateWrapper.h index 76795f132..931aac284 100644 --- a/mix/DebuggingStateWrapper.h +++ b/mix/DebuggingStateWrapper.h @@ -28,45 +28,13 @@ #include #include #include "QVariableDefinition.h" +#include "MixClient.h" namespace dev { namespace mix { -/** - * @brief Store information about a machine state. - */ -struct DebuggingState -{ - uint64_t steps; - dev::Address cur; - dev::u256 curPC; - dev::eth::Instruction inst; - dev::bigint newMemSize; - dev::u256 gas; - dev::u256s stack; - dev::bytes memory; - dev::bigint gasCost; - std::map storage; - std::vector levels; -}; - -/** - * @brief Store information about a machine states. - */ -struct DebuggingContent -{ - QList machineStates; - bytes executionCode; - bytesConstRef executionData; - Address contractAddress; - bool contentAvailable; - QString message; - bytes returnValue; - QList returnParameters; -}; - /** * @brief Contains the line nb of the assembly code and the corresponding index in the code bytes array. */ @@ -156,14 +124,14 @@ public: /// Get all previous steps. QStringList levels(); /// Get the current processed machine state. - DebuggingState state() { return m_state; } + MachineState state() { return m_state; } /// Set the current processed machine state. - void setState(DebuggingState _state) { m_state = _state; } + void setState(MachineState _state) { m_state = _state; } /// Convert all machine state in human readable code. static std::tuple, QQMLMap*> getHumanReadableCode(bytes const& _code); private: - DebuggingState m_state; + MachineState m_state; bytes m_code; bytes m_data; QStringList fillList(QStringList _list, QString _emptyValue); diff --git a/mix/FileIo.cpp b/mix/FileIo.cpp new file mode 100644 index 000000000..fed9909e6 --- /dev/null +++ b/mix/FileIo.cpp @@ -0,0 +1,95 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file FileIo.cpp + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include +#include +#include "FileIo.h" + +using namespace dev::mix; + +void FileIo::makeDir(QString const& _url) +{ + QUrl url(_url); + QDir dirPath(url.path()); + if (!dirPath.exists()) + dirPath.mkpath(dirPath.path()); +} + +QString FileIo::readFile(QString const& _url) +{ + QUrl url(_url); + QFile file(url.path()); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QTextStream stream(&file); + QString data = stream.readAll(); + return data; + } + else + error(tr("Error reading file %1").arg(_url)); + return QString(); +} + +void FileIo::writeFile(QString const& _url, QString const& _data) +{ + QUrl url(_url); + QFile file(url.path()); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QTextStream stream(&file); + stream << _data; + } + else + error(tr("Error writing file %1").arg(_url)); +} + +void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl) +{ + QUrl sourceUrl(_sourceUrl); + QUrl destUrl(_destUrl); + if (!QFile::copy(sourceUrl.path(), destUrl.path())) + error(tr("Error copying file %1 to %2").arg(_sourceUrl).arg(_destUrl)); +} + +QString FileIo::getHomePath() const +{ + return QDir::homePath(); +} + +void FileIo::moveFile(QString const& _sourceUrl, QString const& _destUrl) +{ + QUrl sourceUrl(_sourceUrl); + QUrl destUrl(_destUrl); + if (!QFile::rename(sourceUrl.path(), destUrl.path())) + error(tr("Error moving file %1 to %2").arg(_sourceUrl).arg(_destUrl)); +} + +bool FileIo::fileExists(QString const& _url) +{ + QUrl url(_url); + QFile file(url.path()); + return file.exists(); +} diff --git a/mix/FileIo.h b/mix/FileIo.h new file mode 100644 index 000000000..08d49e099 --- /dev/null +++ b/mix/FileIo.h @@ -0,0 +1,61 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file FileIo.h + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include + +namespace dev +{ +namespace mix +{ + +///File services for QML +class FileIo: public QObject +{ + Q_OBJECT + Q_PROPERTY(QString homePath READ getHomePath CONSTANT) + +signals: + /// Signalled in case of IO error + void error(QString const& _errorText); + +public: + /// Create a directory if it does not exist. Signals on failure. + Q_INVOKABLE void makeDir(QString const& _url); + /// Read file contents to a string. Signals on failure. + Q_INVOKABLE QString readFile(QString const& _url); + /// Write contents to a file. Signals on failure. + Q_INVOKABLE void writeFile(QString const& _url, QString const& _data); + /// Copy a file from _sourcePath to _destPath. Signals on failure. + Q_INVOKABLE void copyFile(QString const& _sourceUrl, QString const& _destUrl); + /// Move (rename) a file from _sourcePath to _destPath. Signals on failure. + Q_INVOKABLE void moveFile(QString const& _sourceUrl, QString const& _destUrl); + /// Check if file exists + Q_INVOKABLE bool fileExists(QString const& _url); + +private: + QString getHomePath() const; +}; + +} +} diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index 5cf71aa7d..2cdc4b30d 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -35,7 +35,7 @@ MixApplication::MixApplication(int _argc, char* _argv[]): qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff m_engine->load(QUrl("qrc:/qml/main.qml")); - m_appContext->loadProject(); + m_appContext->appLoaded(); } MixApplication::~MixApplication() diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp new file mode 100644 index 000000000..db272a3d0 --- /dev/null +++ b/mix/MixClient.cpp @@ -0,0 +1,336 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file MixClient.cpp + * @author Yann yann@ethdev.com + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "MixClient.h" + +using namespace dev; +using namespace dev::eth; +using namespace dev::mix; + +MixClient::MixClient(): + m_userAccount(KeyPair::create()) +{ + resetState(10000000 * ether); +} + +void MixClient::resetState(u256 _balance) +{ + WriteGuard l(x_state); + m_state = eth::State(Address(), m_stateDB, BaseState::Empty); + m_state.addBalance(m_userAccount.address(), _balance); +} + +void MixClient::executeTransaction(bytesConstRef _rlp, State& _state) +{ + Executive execution(_state, LastHashes(), 0); + execution.setup(_rlp); + bytes code; + bytesConstRef data; + bool firstIteration = true; + std::vector machineStates; + std::vector levels; + auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) + { + VM& vm = *(VM*)voidVM; + ExtVM const& ext = *(ExtVM const*)voidExt; + + if (firstIteration) + { + code = ext.code; + data = ext.data; + firstIteration = false; + } + + if (levels.size() < ext.depth) + levels.push_back(&machineStates.back()); + else + levels.resize(ext.depth); + + machineStates.push_back(MachineState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), + vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); + }; + + execution.go(onOp); + execution.finalize(); + + ExecutionResult d; + d.returnValue = execution.out().toVector(); + d.machineStates = machineStates; + d.executionCode = code; + d.executionData = data; + d.contentAvailable = true; + d.message = "ok"; + m_lastExecutionResult = d; +} + +void MixClient::validateBlock(int _block) const +{ + //TODO: throw exception here if _block != 0 + (void)_block; +} + +void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +{ + WriteGuard l(x_state); + u256 n = m_state.transactionsFrom(toAddress(_secret)); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + bytes rlp = t.rlp(); + executeTransaction(&rlp, m_state); +} + +Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) +{ + WriteGuard l(x_state); + u256 n = m_state.transactionsFrom(toAddress(_secret)); + eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); + bytes rlp = t.rlp(); + executeTransaction(&rlp, m_state); + return right160(sha3(rlpList(t.sender(), t.nonce()))); +} + +void MixClient::inject(bytesConstRef _rlp) +{ + WriteGuard l(x_state); + executeTransaction(_rlp, m_state); +} + +void MixClient::flushTransactions() +{ +} + +bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +{ + bytes out; + u256 n; + State temp; + { + ReadGuard lr(x_state); + temp = m_state; + n = temp.transactionsFrom(toAddress(_secret)); + } + Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + bytes rlp = t.rlp(); + WriteGuard lw(x_state); //TODO: lock is required only for last executoin state + executeTransaction(&rlp, temp); + return m_lastExecutionResult.returnValue; +} + +u256 MixClient::balanceAt(Address _a, int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + return m_state.balance(_a); +} + +u256 MixClient::countAt(Address _a, int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + return m_state.transactionsFrom(_a); +} + +u256 MixClient::stateAt(Address _a, u256 _l, int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + return m_state.storage(_a, _l); +} + +bytes MixClient::codeAt(Address _a, int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + return m_state.code(_a); +} + +std::map MixClient::storageAt(Address _a, int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + return m_state.storage(_a); +} + +eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const +{ + (void)_watchId; + return LocalisedLogEntries(); +} + +eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _filter) const +{ + (void)_filter; + return LocalisedLogEntries(); +} + +unsigned MixClient::installWatch(eth::LogFilter const& _filter) +{ + (void)_filter; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::installWatch")); +} + +unsigned MixClient::installWatch(h256 _filterId) +{ + (void)_filterId; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::installWatch")); +} + +void MixClient::uninstallWatch(unsigned _watchId) +{ + (void)_watchId; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uninstallWatch")); +} + +eth::LocalisedLogEntries MixClient::peekWatch(unsigned _watchId) const +{ + (void)_watchId; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::peekWatch")); +} + +eth::LocalisedLogEntries MixClient::checkWatch(unsigned _watchId) +{ + (void)_watchId; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::checkWatch")); +} + +h256 MixClient::hashFromNumber(unsigned _number) const +{ + (void)_number; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::hashFromNumber")); +} + +eth::BlockInfo MixClient::blockInfo(h256 _hash) const +{ + (void)_hash; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockInfo")); +} + +eth::BlockDetails MixClient::blockDetails(h256 _hash) const +{ + (void)_hash; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockDetails")); +} + +eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const +{ + (void)_blockHash; + (void)_i; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::transaction")); +} + +eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const +{ + (void)_blockHash; + (void)_i; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uncle")); +} + +unsigned MixClient::number() const +{ + return 0; +} + +eth::Transactions MixClient::pending() const +{ + return eth::Transactions(); +} + +eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const +{ + (void)_txi; + (void)_block; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff")); +} + +eth::StateDiff MixClient::diff(unsigned _txi, int _block) const +{ + (void)_txi; + (void)_block; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff")); +} + +Addresses MixClient::addresses(int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + Addresses ret; + for (auto const& i: m_state.addresses()) + ret.push_back(i.first); + return ret; +} + +u256 MixClient::gasLimitRemaining() const +{ + ReadGuard l(x_state); + return m_state.gasLimitRemaining(); +} + +void MixClient::setAddress(Address _us) +{ + WriteGuard l(x_state); + m_state.setAddress(_us); +} + +Address MixClient::address() const +{ + ReadGuard l(x_state); + return m_state.address(); +} + +void MixClient::setMiningThreads(unsigned _threads) +{ + (void)_threads; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::setMiningThreads")); +} + +unsigned MixClient::miningThreads() const +{ + return 0; +} + +void MixClient::startMining() +{ + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::startMining")); +} + +void MixClient::stopMining() +{ + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::stopMining")); +} + +bool MixClient::isMining() +{ + return false; +} + +eth::MineProgress MixClient::miningProgress() const +{ + return eth::MineProgress(); +} diff --git a/mix/MixClient.h b/mix/MixClient.h new file mode 100644 index 000000000..b7ba7fbf3 --- /dev/null +++ b/mix/MixClient.h @@ -0,0 +1,125 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file MixClient.h + * @author Yann yann@ethdev.com + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include + +namespace dev +{ +namespace mix +{ + +/** + * @brief Store information about a machine state. + */ +struct MachineState +{ + uint64_t steps; + dev::Address cur; + dev::u256 curPC; + dev::eth::Instruction inst; + dev::bigint newMemSize; + dev::u256 gas; + dev::u256s stack; + dev::bytes memory; + dev::bigint gasCost; + std::map storage; + std::vector levels; +}; + +/** + * @brief Store information about a machine states. + */ +struct ExecutionResult +{ + std::vector machineStates; + bytes executionCode; + bytesConstRef executionData; + Address contractAddress; + bool contentAvailable; + std::string message; + bytes returnValue; +}; + +class MixClient: public dev::eth::Interface +{ +public: + MixClient(); + /// Reset state to the empty state with given balance. + void resetState(u256 _balance); + const KeyPair& userAccount() const { return m_userAccount; } + const ExecutionResult lastExecutionResult() const { ReadGuard l(x_state); return m_lastExecutionResult; } + + //dev::eth::Interface + void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; + Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override; + void inject(bytesConstRef _rlp) override; + void flushTransactions() override; + bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; + u256 balanceAt(Address _a, int _block) const override; + u256 countAt(Address _a, int _block) const override; + u256 stateAt(Address _a, u256 _l, int _block) const override; + bytes codeAt(Address _a, int _block) const override; + std::map storageAt(Address _a, int _block) const override; + eth::LocalisedLogEntries logs(unsigned _watchId) const override; + eth::LocalisedLogEntries logs(eth::LogFilter const& _filter) const override; + unsigned installWatch(eth::LogFilter const& _filter) override; + unsigned installWatch(h256 _filterId) override; + void uninstallWatch(unsigned _watchId) override; + eth::LocalisedLogEntries peekWatch(unsigned _watchId) const override; + eth::LocalisedLogEntries checkWatch(unsigned _watchId) override; + h256 hashFromNumber(unsigned _number) const override; + eth::BlockInfo blockInfo(h256 _hash) const override; + eth::BlockDetails blockDetails(h256 _hash) const override; + eth::Transaction transaction(h256 _blockHash, unsigned _i) const override; + eth::BlockInfo uncle(h256 _blockHash, unsigned _i) const override; + unsigned number() const override; + eth::Transactions pending() const override; + eth::StateDiff diff(unsigned _txi, h256 _block) const override; + eth::StateDiff diff(unsigned _txi, int _block) const override; + Addresses addresses(int _block) const override; + u256 gasLimitRemaining() const override; + void setAddress(Address _us) override; + Address address() const override; + void setMiningThreads(unsigned _threads) override; + unsigned miningThreads() const override; + void startMining() override; + void stopMining() override; + bool isMining() override; + eth::MineProgress miningProgress() const override; + +private: + void executeTransaction(bytesConstRef _rlp, eth::State& _state); + void validateBlock(int _block) const; + + KeyPair m_userAccount; + eth::State m_state; + OverlayDB m_stateDB; + mutable boost::shared_mutex x_state; + ExecutionResult m_lastExecutionResult; +}; + +} + +} diff --git a/mix/QContractDefinition.cpp b/mix/QContractDefinition.cpp index 72f96dbcc..bee9cfe49 100644 --- a/mix/QContractDefinition.cpp +++ b/mix/QContractDefinition.cpp @@ -33,11 +33,9 @@ using namespace dev::mix; QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract) { - std::vector functions = _contract->getInterfaceFunctions(); - for (unsigned i = 0; i < functions.size(); i++) - { - FunctionDefinition const* func = functions.at(i); - m_functions.append(new QFunctionDefinition(func, i)); - } + auto interfaceFunctions = _contract->getInterfaceFunctions(); + unsigned i = 0; + for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it, ++i) + m_functions.append(new QFunctionDefinition(it->second, i)); } diff --git a/mix/QFunctionDefinition.cpp b/mix/QFunctionDefinition.cpp index 7149809af..97ce0ff58 100644 --- a/mix/QFunctionDefinition.cpp +++ b/mix/QFunctionDefinition.cpp @@ -20,12 +20,14 @@ */ #include +#include #include "QVariableDeclaration.h" #include "QFunctionDefinition.h" + using namespace dev::solidity; using namespace dev::mix; -QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDefinition const* _f, int _index): QBasicNodeDefinition(_f), m_index(_index) +QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDefinition const* _f, int _index): QBasicNodeDefinition(_f), m_index(_index), m_hash(dev::sha3(_f->getCanonicalSignature())) { std::vector> parameters = _f->getParameterList().getParameters(); for (unsigned i = 0; i < parameters.size(); i++) diff --git a/mix/QFunctionDefinition.h b/mix/QFunctionDefinition.h index b2d0cd0b7..7f606c8a1 100644 --- a/mix/QFunctionDefinition.h +++ b/mix/QFunctionDefinition.h @@ -49,9 +49,12 @@ public: QList returnParameters() const { return m_returnParameters; } /// Get the index of this function on the contract ABI. int index() const { return m_index; } + /// Get the hash of this function declaration on the contract ABI. + FixedHash<4> hash() const { return m_hash; } private: int m_index; + FixedHash<4> m_hash; QList m_parameters; QList m_returnParameters; void initQParameters(); diff --git a/mix/Web3Server.cpp b/mix/Web3Server.cpp new file mode 100644 index 000000000..469ca907d --- /dev/null +++ b/mix/Web3Server.cpp @@ -0,0 +1,55 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Web3Server.h.cpp + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + + +#include +#include "Web3Server.h" + +using namespace dev::mix; + +Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts, dev::eth::Interface* _client): + WebThreeStubServerBase(_conn, _accounts), + m_client(_client) +{ +} + +std::shared_ptr Web3Server::face() +{ + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::shh::Interface")); +} + +dev::WebThreeNetworkFace* Web3Server::network() +{ + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::WebThreeNetworkFace")); +} + +std::string Web3Server::get(std::string const& _name, std::string const& _key) +{ + std::string k(_name + "/" + _key); + return m_db[k]; +} + +void Web3Server::put(std::string const& _name, std::string const& _key, std::string const& _value) +{ + std::string k(_name + "/" + _key); + m_db[k] = _value; +} diff --git a/mix/Web3Server.h b/mix/Web3Server.h new file mode 100644 index 000000000..c603b48a2 --- /dev/null +++ b/mix/Web3Server.h @@ -0,0 +1,56 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Web3Server.h + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include +#include +#include + +namespace dev +{ + +namespace mix +{ + +class Web3Server: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace +{ +public: + Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts, dev::eth::Interface* _client); + +private: + dev::eth::Interface* client() override { return m_client; } + std::shared_ptr face() override; + dev::WebThreeNetworkFace* network() override; + dev::WebThreeStubDatabaseFace* db() override { return this; } + + std::string get(std::string const& _name, std::string const& _key) override; + void put(std::string const& _name, std::string const& _key, std::string const& _value) override; + +private: + dev::eth::Interface* m_client; + std::map m_db; +}; + +} + +} diff --git a/mix/qml.qrc b/mix/qml.qrc index 29d890417..44589e70d 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -1,15 +1,13 @@ - qml/BasicContent.qml qml/main.qml - qml/MainContent.qml - qml/TabStyle.qml - qml/Debugger.qml - qml/js/Debugger.js + qml/AlertMessageDialog.qml + qml/BasicContent.qml qml/BasicMessage.qml - qml/TransactionDialog.qml + qml/Debugger.qml + qml/MainContent.qml qml/ModalDialog.qml - qml/AlertMessageDialog.qml + qml/ProjectList.qml qml/StateDialog.qml qml/StateList.qml qml/js/main.js @@ -34,5 +32,14 @@ qml/DebugInfoList.qml qml/ItemDelegateDataDump.qml qml/StatusPane.qml + qml/TabStyle.qml + qml/TransactionDialog.qml + qml/js/Debugger.js + qml/NewProjectDialog.qml + qml/ProjectModel.qml + qml/CodeEditor.qml + qml/CodeEditorView.qml + qml/js/ProjectModel.js + qml/WebPreview.qml diff --git a/mix/qml/CodeEditor.qml b/mix/qml/CodeEditor.qml new file mode 100644 index 000000000..78b44074b --- /dev/null +++ b/mix/qml/CodeEditor.qml @@ -0,0 +1,82 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.1 + +Component { + Item { + signal editorTextChanged + + function setText(text) { + codeEditor.text = text; + } + + function getText() { + return codeEditor.text; + } + + anchors.fill: parent + id: contentView + width: parent.width + height: parent.height * 0.7 + Rectangle { + id: lineColumn + property int rowHeight: codeEditor.font.pixelSize + 3 + color: "#202020" + width: 50 + height: parent.height + Column { + y: -codeEditor.flickableItem.contentY + 4 + width: parent.width + Repeater { + model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) + delegate: Text { + id: text + color: codeEditor.textColor + font: codeEditor.font + width: lineColumn.width - 4 + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + height: lineColumn.rowHeight + renderType: Text.NativeRendering + text: index + 1 + } + } + } + } + + TextArea { + id: codeEditor + textColor: "#EEE8D5" + style: TextAreaStyle { + backgroundColor: "#002B36" + } + + anchors.left: lineColumn.right + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + wrapMode: TextEdit.NoWrap + frameVisible: false + + height: parent.height + font.family: "Monospace" + font.pointSize: 12 + width: parent.width + + tabChangesFocus: false + Keys.onPressed: { + if (event.key === Qt.Key_Tab) { + codeEditor.insert(codeEditor.cursorPosition, "\t"); + event.accepted = true; + } + } + onTextChanged: { + console.log("textchange") + editorTextChanged(); + } + + } + } +} diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml new file mode 100644 index 000000000..3fce9aec6 --- /dev/null +++ b/mix/qml/CodeEditorView.qml @@ -0,0 +1,92 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import org.ethereum.qml.ProjectModel 1.0 + +Item { + + property string currentDocumentId: "" + + function getDocumentText(documentId) { + for (i = 0; i < editorListModel.count; i++) { + if (editorListModel.get(i).documentId === documentId) { + return editors.itemAt(i).getText(); + } + } + return ""; + } + + function openDocument(document) { + loadDocument(document); + currentDocumentId = document.documentId; + } + + function loadDocument(document) { + for (var i = 0; i < editorListModel.count; i++) + if (editorListModel.get(i).documentId === document.documentId) + return; //already open + + editorListModel.append(document); + } + + function doLoadDocument(editor, document) { + var data = fileIo.readFile(document.path); + console.log(document.isContract) + if (document.isContract) + editor.onEditorTextChanged.connect(function() { + codeModel.registerCodeChange(editor.getText()); + }); + editor.setText(data); + } + + Connections { + target: ProjectModel + onDocumentOpened: { + openDocument(document); + } + onProjectSaving: { + for (var i = 0; i < editorListModel.count; i++) + fileIo.writeFile(editorListModel.get(i).path, editors.itemAt(i).item.getText()); + } + onProjectClosed: { + for (var i = 0; i < editorListModel.count; i++) { + editors.itemAt(i).visible = false; + } + editorListModel.clear(); + currentDocumentId = ""; + } + } + + CodeEditor { + id: codeEditor + } + + Repeater { + id: editors + model: editorListModel + delegate: Loader { + active: false; + asynchronous: true + anchors.fill: parent + sourceComponent: codeEditor + visible: (index >= 0 && index < editorListModel.count && currentDocumentId === editorListModel.get(index).documentId) + onVisibleChanged: { + loadIfNotLoaded() + } + Component.onCompleted: { + loadIfNotLoaded() + } + onLoaded: { doLoadDocument(item, editorListModel.get(index)) } + + function loadIfNotLoaded () { + if(visible && !active) { + active = true; + } + } + } + } + ListModel { + id: editorListModel + } +} diff --git a/mix/qml/DebugBasicInfo.qml b/mix/qml/DebugBasicInfo.qml index 5cfea55ac..3e28aa4c1 100644 --- a/mix/qml/DebugBasicInfo.qml +++ b/mix/qml/DebugBasicInfo.qml @@ -3,107 +3,33 @@ import QtQuick.Controls 1.1 import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.1 -ColumnLayout { - property string currentStep - property string mem - property string stepCost - property string gasSpent - spacing: 0 - RowLayout { - width: parent.width - height: parent.height / 4 - Rectangle { - width: parent.width / 2 - height: parent.height - color: "#e5e5e5" - Text - { - font.pixelSize: 12 - anchors.centerIn: parent - color: "#a2a2a2" - text: qsTr("Current step") - font.family: "Sans Serif" - } - } - Text - { - font.pixelSize: 13 - id: currentStepValue - text: currentStep - } - } +RowLayout { + property string titleStr + width: parent.width + height: parent.height / 4 - RowLayout { - width: parent.width - height: parent.height / 4 - Rectangle { - width: parent.width / 2 - height: parent.height - color: "#e5e5e5" - Text - { - font.pixelSize: 12 - anchors.centerIn: parent - color: "#a2a2a2" - text: qsTr("Adding memory") - font.family: "Sans Serif" - } - } - - Text - { - font.pixelSize: 13 - id: memValue - text: mem - } + function update(_value) + { + currentStepValue.text = _value; } - RowLayout { - width: parent.width - height: parent.height / 4 - Rectangle { - width: parent.width / 2 - height: parent.height - color: "#e5e5e5" - Text - { - font.pixelSize: 12 - anchors.centerIn: parent - color: "#a2a2a2" - text: qsTr("Step cost") - font.family: "Sans Serif" - } - } + Rectangle { + width: parent.width / 2 + height: parent.height + color: "#e5e5e5" Text { - font.pixelSize: 13 - id: stepCostValue - text: stepCost + id: title + font.pixelSize: 12 + anchors.centerIn: parent + color: "#a2a2a2" + font.family: "Sans Serif" + text: titleStr } } - - RowLayout { - width: parent.width - height: parent.height / 4 - Rectangle { - width: parent.width / 2 - height: parent.height - color: "#e5e5e5" - Text - { - font.pixelSize: 12 - anchors.centerIn: parent - color: "#a2a2a2" - text: qsTr("Total gas spent") - font.family: "Sans Serif" - } - } - Text - { - font.pixelSize: 13 - id: gasSpentValue - text: gasSpent - } + Text + { + font.pixelSize: 13 + id: currentStepValue } } - diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index ed9134cdd..c0a1dc4ba 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -22,7 +22,7 @@ Rectangle { function update() { - if (statusPane.result.successfull) + if (statusPane.result.successful) { Debugger.init(); debugScrollArea.visible = true; @@ -39,7 +39,11 @@ Rectangle { errorDetail.text = errorInfo.errorDetail; errorLine.text = errorInfo.errorLine; } - forceActiveFocus(); + } + + Connections { + target: codeModel + onCompilationComplete: update() } Rectangle @@ -281,10 +285,26 @@ Rectangle { id: basicInfoColumn height: 125 color: "transparent" - DebugBasicInfo { - id: basicInfo - height: parent.height + ColumnLayout { + spacing: 0 width: parent.width + height: parent.height + DebugBasicInfo { + id: currentStep + titleStr: qsTr("Current step") + } + DebugBasicInfo { + id: mem + titleStr: qsTr("Adding memory") + } + DebugBasicInfo { + id: stepCost + titleStr: qsTr("Step cost") + } + DebugBasicInfo { + id: gasSpent + titleStr: qsTr("Total gas spent") + } } } diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 568fd7d0d..75c93034c 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -20,10 +20,7 @@ Rectangle { function toggleRightView() { if (!rightView.visible) - { rightView.show(); - debugModel.updateDebugPanel(); - } else rightView.hide(); } @@ -43,7 +40,6 @@ Rectangle { CodeEditorExtensionManager { headerView: headerPaneTabs; rightView: rightPaneTabs; - editor: codeEditor } GridLayout @@ -88,68 +84,24 @@ Rectangle { orientation: Qt.Horizontal; Layout.fillWidth: true Layout.preferredHeight: root.height - headerView.height; + + ProjectList { + width: parent.width * 0.2 + height: parent.height + Layout.minimumWidth: 200 + } + Rectangle { anchors.top: parent.top id: contentView width: parent.width - height: parent.height * 0.7 - - Item { - anchors.fill: parent - Rectangle { - id: lineColumn - property int rowHeight: codeEditor.font.pixelSize + 3 - color: "#202020" - width: 50 - height: parent.height - Column { - y: -codeEditor.flickableItem.contentY + 4 - width: parent.width - Repeater { - model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) - delegate: Text { - id: text - color: codeEditor.textColor - font: codeEditor.font - width: lineColumn.width - 4 - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - height: lineColumn.rowHeight - renderType: Text.NativeRendering - text: index + 1 - } - } - } - } - - TextArea { - id: codeEditor - textColor: "#EEE8D5" - style: TextAreaStyle { - backgroundColor: "#002B36" - } - - anchors.left: lineColumn.right - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - wrapMode: TextEdit.NoWrap - frameVisible: false - - height: parent.height - font.family: "Monospace" - font.pointSize: 12 - width: parent.width - tabChangesFocus: false - Keys.onPressed: { - if (event.key === Qt.Key_Tab) { - codeEditor.insert(codeEditor.cursorPosition, "\t"); - event.accepted = true; + height: parent.height + CodeEditorView { + height: parent.height + anchors.top: parent.top + width: parent.width } - } - } - } } Rectangle { diff --git a/mix/qml/NewProjectDialog.qml b/mix/qml/NewProjectDialog.qml new file mode 100644 index 000000000..406dc02b2 --- /dev/null +++ b/mix/qml/NewProjectDialog.qml @@ -0,0 +1,93 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.0 +import QtQuick.Dialogs 1.2 + +Window { + + modality: Qt.WindowModal + + width: 640 + height: 120 + + visible: false + + property alias projectTitle: titleField.text + readonly property string projectPath: "file://" + pathField.text + signal accepted + + function open() { + visible = true; + titleField.focus = true; + } + + function close() { + visible = false; + } + + GridLayout { + id: dialogContent + columns: 2 + anchors.fill: parent + anchors.margins: 10 + rowSpacing: 10 + columnSpacing: 10 + + Label { + text: qsTr("Title") + } + TextField { + id: titleField + focus: true + Layout.fillWidth: true + } + + Label { + text: qsTr("Path") + } + RowLayout { + TextField { + id: pathField + Layout.fillWidth: true + } + Button { + text: qsTr("Browse") + onClicked: createProjectFileDialog.open() + } + } + + RowLayout + { + anchors.bottom: parent.bottom + anchors.right: parent.right; + + Button { + enabled: titleField.text != "" && pathField.text != "" + text: qsTr("Ok"); + onClicked: { + close(); + accepted(); + } + } + Button { + text: qsTr("Cancel"); + onClicked: close(); + } + } + } + + FileDialog { + id: createProjectFileDialog + visible: false + title: qsTr("Please choose a path for the project") + selectFolder: true + onAccepted: { + var u = createProjectFileDialog.fileUrl.toString(); + if (u.indexOf("file://") == 0) + u = u.substring(7, u.length) + pathField.text = u; + } + } + Component.onCompleted: pathField.text = fileIo.homePath +} diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml new file mode 100644 index 000000000..077aae28e --- /dev/null +++ b/mix/qml/ProjectList.qml @@ -0,0 +1,131 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import org.ethereum.qml.ProjectModel 1.0 + +Item { + property bool renameMode: false; + ColumnLayout { + anchors.fill: parent + Text { + Layout.fillWidth: true + color: "blue" + text: ProjectModel.projectData ? ProjectModel.projectData.title : "" + horizontalAlignment: Text.AlignHCenter + visible: !ProjectModel.isEmpty; + } + ListView { + id: projectList + Layout.fillWidth: true + Layout.fillHeight: true + + model: ProjectModel.listModel + + delegate: renderDelegate + highlight: Rectangle { + color: "lightsteelblue"; + } + highlightFollowsCurrentItem: true + focus: true + clip: true + + onCurrentIndexChanged: { + if (currentIndex >= 0 && currentIndex < ProjectModel.listModel.count) + ProjectModel.openDocument(ProjectModel.listModel.get(currentIndex).documentId); + } + } + Menu { + id: contextMenu + MenuItem { + text: qsTr("Rename") + onTriggered: { + renameMode = true; + } + } + MenuItem { + text: qsTr("Delete") + onTriggered: { + ProjectModel.removeDocument(projectList.model.get(projectList.currentIndex).documentId); + } + } + } + } + Component { + id: renderDelegate + Item { + id: wrapperItem + height: 20 + width: parent.width + RowLayout { + anchors.fill: parent + visible: !(index === projectList.currentIndex) || !renameMode + Text { + id: nameText + Layout.fillWidth: true + Layout.fillHeight: true + text: name + font.pointSize: 12 + verticalAlignment: Text.AlignBottom + } + } + + TextInput { + id: textInput + text: nameText.text + visible: (index === projectList.currentIndex) && renameMode + MouseArea { + id: textMouseArea + anchors.fill: parent + hoverEnabled: true + z:2 + onClicked: { + console.log("clicked"); + textInput.forceActiveFocus(); + } + } + + onVisibleChanged: { + if (visible) { + selectAll(); + forceActiveFocus(); + } + } + + onAccepted: close(true); + onCursorVisibleChanged: { + if (!cursorVisible) + close(false); + } + onFocusChanged: { + if (!focus) + close(false); + } + function close(accept) { + renameMode = false; + if (accept) + ProjectModel.renameDocument(projectList.model.get(projectList.currentIndex).documentId, textInput.text); + } + } + MouseArea { + id: mouseArea + z: 1 + hoverEnabled: false + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked:{ + projectList.currentIndex = index; + if (mouse.button === Qt.RightButton && !projectList.model.get(index).isContract) + contextMenu.popup(); + } + } + Connections { + target: ProjectModel + onProjectLoaded: { + projectList.currentIndex = 0; + } + } + } + } +} + diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml new file mode 100644 index 000000000..5337e2780 --- /dev/null +++ b/mix/qml/ProjectModel.qml @@ -0,0 +1,109 @@ +pragma Singleton + +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtQuick.Dialogs 1.1 +import Qt.labs.settings 1.0 + +import "js/ProjectModel.js" as ProjectModelCode + +Item { + id: projectModel + + signal projectClosed + signal projectLoaded + signal documentOpened(var document) + signal documentRemoved(var documentId) + signal documentUpdated(var documentId) //renamed + signal projectSaving(var projectData) + + property bool isEmpty: (projectData === null) + readonly property string projectFileName: ".mix" + + property bool haveUnsavedChanges: false + property string projectPath: "" + property var projectData: null + property var listModel: projectListModel + + //interface + function saveAll() { ProjectModelCode.saveAll(); } + function createProject() { ProjectModelCode.createProject(); } + function browseProject() { ProjectModelCode.browseProject(); } + function closeProject() { ProjectModelCode.closeProject(); } + function saveProject() { ProjectModelCode.saveProject(); } + function loadProject(path) { ProjectModelCode.loadProject(path); } + function addExistingFile() { ProjectModelCode.addExistingFile(); } + function newHtmlFile() { ProjectModelCode.newHtmlFile(); } + function newJsFile() { ProjectModelCode.newJsFile(); } + //function newContract() { ProjectModelCode.newContract(); } + function openDocument(documentId) { ProjectModelCode.openDocument(documentId); } + function renameDocument(documentId, newName) { ProjectModelCode.renameDocument(documentId, newName); } + function removeDocument(documentId) { ProjectModelCode.removeDocument(documentId); } + + Connections { + target: appContext + onAppLoaded: { + if (projectSettings.lastProjectPath) + loadProject(projectSettings.lastProjectPath) + } + } + + NewProjectDialog { + id: newProjectDialog + visible: false + onAccepted: { + var title = newProjectDialog.projectTitle; + var path = newProjectDialog.projectPath; + ProjectModelCode.doCreateProject(title, path); + } + } + + MessageDialog { + id: saveMessageDialog + title: qsTr("Project") + text: qsTr("Do you want to save changes?") + standardButtons: StandardButton.Ok | StandardButton.Cancel + icon: StandardIcon.Question + onAccepted: { + projectModel.saveAll(); + ProjectModelCode.doCloseProject(); + } + onRejected: { + ProjectModelCode.doCloseProject(); + } + } + + ListModel { + id: projectListModel + } + + Settings { + id: projectSettings + property string lastProjectPath; + } + + FileDialog { + id: openProjectFileDialog + visible: false + title: qsTr("Open a project") + selectFolder: true + onAccepted: { + var path = openProjectFileDialog.fileUrl.toString(); + path += "/"; + loadProject(path); + } + } + + FileDialog { + id: addExistingFileDialog + visible: false + title: qsTr("Add a file") + selectFolder: false + onAccepted: { + var paths = addExistingFileDialog.fileUrls; + ProjectModelCode.doAddExistingFiles(paths); + } + } +} diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index 862e6f854..cde7cb3ef 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -11,8 +11,8 @@ Window { visible: false - property alias stateTitle : titleField.text - property alias stateBalance : balanceField.text + property alias stateTitle: titleField.text + property alias stateBalance: balanceField.text property int stateIndex property var stateTransactions: [] signal accepted @@ -122,7 +122,7 @@ Window { var item = { value: "0", functionId: "", - gas: "1000000000000", + gas: "125000", gasPrice: "100000" }; diff --git a/mix/qml/StateList.qml b/mix/qml/StateList.qml index 52a49b450..519e533d5 100644 --- a/mix/qml/StateList.qml +++ b/mix/qml/StateList.qml @@ -3,6 +3,7 @@ import QtQuick.Controls.Styles 1.1 import QtQuick.Controls 1.1 import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 +import org.ethereum.qml.ProjectModel 1.0 Rectangle { color: "#ededed" @@ -15,14 +16,22 @@ Rectangle { property var stateList: [] Connections { - target: appContext + target: ProjectModel + onProjectClosed: { + stateListModel.clear(); + } onProjectLoaded: { - var items = JSON.parse(_json); + if (!target.projectData.states) + target.projectData.states = []; + var items = target.projectData.states; for(var i = 0; i < items.length; i++) { stateListModel.append(items[i]); stateList.push(items[i]) } } + onProjectSaving: { + projectData.states = stateList; + } } ListView { @@ -72,7 +81,7 @@ Rectangle { function runState(index) { var item = stateList[index]; - debugModel.debugState(item); + clientModel.debugState(item); } function deleteState(index) { @@ -82,8 +91,7 @@ Rectangle { } function save() { - var json = JSON.stringify(stateList); - appContext.saveProject(json); + ProjectModel.saveProject(); } } @@ -127,8 +135,8 @@ Rectangle { Action { id: addStateAction text: "&Add State" - shortcut: "Ctrl+N" - enabled: codeModel.hasContract && !debugModel.running; + shortcut: "Ctrl+T" + enabled: codeModel.hasContract && !clientModel.running; onTriggered: stateListModel.addState(); } } diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 36820a6e6..e40c1101a 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -9,7 +9,7 @@ Rectangle { function updateStatus() { - if (statusPane.result.successfull) + if (statusPane.result.successful) { image.state = ""; status.state = ""; @@ -24,7 +24,7 @@ Rectangle { status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail; logslink.visible = true; } - debugRunActionIcon.enabled = statusPane.result.successfull; + debugRunActionIcon.enabled = statusPane.result.successful; } color: "transparent" @@ -90,7 +90,8 @@ Rectangle { anchors.fill: parent onClicked: { mainContent.ensureRightView(); - debugModel.updateDebugPanel(); + //clientModel.showDebugger(); + //debugModel.updateDebugPanel(); } } } @@ -123,16 +124,9 @@ Rectangle { id: debugRunActionIcon onTriggered: { mainContent.ensureRightView(); - debugModel.debugDeployment(); + clientModel.debugDeployment(); } enabled: false - onEnabledChanged: { - console.log(debugRunActionIcon.enabled) - if (debugRunActionIcon.enabled) - debugImg.iconSource = "qrc:/qml/img/bugiconactive.png" - else - debugImg.iconSource = "qrc:/qml/img/bugiconinactive.png" - } } } } diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 54528f97f..3a32e2af5 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -10,11 +10,11 @@ Window { visible: false property int transactionIndex - property alias transactionParams : paramsModel; - property alias gas : gasField.text; - property alias gasPrice : gasPriceField.text; - property alias transactionValue : valueField.text; - property alias functionId : functionComboBox.currentText; + property alias transactionParams: paramsModel; + property alias gas: gasField.text; + property alias gasPrice: gasPriceField.text; + property alias transactionValue: valueField.text; + property alias functionId: functionComboBox.currentText; property var itemParams; signal accepted; diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml new file mode 100644 index 000000000..77e60ee66 --- /dev/null +++ b/mix/qml/WebPreview.qml @@ -0,0 +1,81 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.1 + +Component { + Item { + signal editorTextChanged + + function setText(text) { + codeEditor.text = text; + } + + function getText() { + return codeEditor.text; + } + + anchors.fill: parent + id: contentView + width: parent.width + height: parent.height * 0.7 + Rectangle { + id: lineColumn + property int rowHeight: codeEditor.font.pixelSize + 3 + color: "#202020" + width: 50 + height: parent.height + Column { + y: -codeEditor.flickableItem.contentY + 4 + width: parent.width + Repeater { + model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) + delegate: Text { + id: text + color: codeEditor.textColor + font: codeEditor.font + width: lineColumn.width - 4 + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + height: lineColumn.rowHeight + renderType: Text.NativeRendering + text: index + 1 + } + } + } + } + + TextArea { + id: codeEditor + textColor: "#EEE8D5" + style: TextAreaStyle { + backgroundColor: "#002B36" + } + + anchors.left: lineColumn.right + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + wrapMode: TextEdit.NoWrap + frameVisible: false + + height: parent.height + font.family: "Monospace" + font.pointSize: 12 + width: parent.width + + tabChangesFocus: false + Keys.onPressed: { + if (event.key === Qt.Key_Tab) { + codeEditor.insert(codeEditor.cursorPosition, "\t"); + event.accepted = true; + } + } + onTextChanged: { + editorTextChanged(); + } + + } + } +} diff --git a/mix/qml/js/Debugger.js b/mix/qml/js/Debugger.js index ebdd77873..7c32c2426 100644 --- a/mix/qml/js/Debugger.js +++ b/mix/qml/js/Debugger.js @@ -7,6 +7,7 @@ var currentSelectedState = null; var jumpStartingPoint = null; function init() { + console.log('popopop'); if (debugStates === undefined) return; @@ -66,10 +67,10 @@ function highlightSelection(index) function completeCtxInformation(state) { - basicInfo.currentStep = state.step; - basicInfo.mem = state.newMemSize + " " + qsTr("words"); - basicInfo.stepCost = state.gasCost; - basicInfo.gasSpent = debugStates[0].gas - state.gas; + currentStep.update(state.step); + mem.update(state.newMemSize + " " + qsTr("words")); + stepCost.update(state.gasCost); + gasSpent.update(debugStates[0].gas - state.gas); stack.listModel = state.debugStack; storage.listModel = state.debugStorage; diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js new file mode 100644 index 000000000..bece4cc28 --- /dev/null +++ b/mix/qml/js/ProjectModel.js @@ -0,0 +1,204 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ProjectModel.js + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +function saveAll() { + saveProject(); +} + +function createProject() { + newProjectDialog.open(); +} + +function browseProject() { + openProjectFileDialog.open(); +} + +function closeProject() { + if (!isEmpty) { + if (haveUnsavedChanges) + saveMessageDialog.open(); + else + doCloseProject(); + } +} + +function saveProject() { + if (!isEmpty) { + projectSaving(projectData); + var json = JSON.stringify(projectData); + var projectFile = projectPath + projectFileName; + fileIo.writeFile(projectFile, json); + } +} + +function loadProject(path) { + closeProject(); + console.log("loading project at " + path); + var projectFile = path + projectFileName; + var json = fileIo.readFile(projectFile); + projectData = JSON.parse(json); + if (!projectData.title) { + var parts = path.split("/"); + projectData.title = parts[parts.length - 2]; + } + projectPath = path; + if (!projectData.files) + projectData.files = []; + + for(var i = 0; i < projectData.files.length; i++) { + addFile(projectData.files[i]); + } + projectSettings.lastProjectPath = path; + projectLoaded(); +} + +function addExistingFile() { + addExistingFileDialog.open(); +} + +function addProjectFiles(files) { + for(var i = 0; i < files.length; i++) + addFile(files[i]); +} + +function addFile(fileName) { + var p = projectPath + fileName; + var extension = fileName.substring(fileName.length - 4, fileName.length); + var isContract = extension === ".sol"; + var fileData = { + contract: false, + path: p, + name: isContract ? "Contract" : fileName, + documentId: fileName, + isText: isContract || extension === ".html" || extension === ".js", + isContract: isContract, + }; + + projectListModel.append(fileData); +} + +function findDocument(documentId) +{ + for (var i = 0; i < projectListModel.count; i++) + if (projectListModel.get(i).documentId === documentId) + return i; + console.error("Cant find document " + documentId); + return -1; +} + +function openDocument(documentId) { + documentOpened(projectListModel.get(findDocument(documentId))); +} + +function doCloseProject() { + console.log("closing project"); + projectListModel.clear(); + projectPath = ""; + projectData = null; + projectClosed(); +} + +function doCreateProject(title, path) { + closeProject(); + console.log("creating project " + title + " at " + path); + if (path[path.length - 1] !== "/") + path += "/"; + var dirPath = path + title + "/"; + fileIo.makeDir(dirPath); + var projectFile = dirPath + projectFileName; + + var indexFile = "index.html"; + var contractsFile = "contracts.sol"; + var projectData = { + title: title, + files: [ indexFile, contractsFile ] + }; + //TODO: copy from template + fileIo.writeFile(dirPath + indexFile, ""); + fileIo.writeFile(dirPath + contractsFile, "contract MyContract {\n }\n"); + var json = JSON.stringify(projectData); + fileIo.writeFile(projectFile, json); + loadProject(dirPath); +} + +function doAddExistingFiles(files) { + for(var i = 0; i < files.length; i++) { + var sourcePath = files[i]; + console.log(sourcePath); + var sourceFileName = sourcePath.substring(sourcePath.lastIndexOf("/") + 1, sourcePath.length); + console.log(sourceFileName); + var destPath = projectPath + sourceFileName; + console.log(destPath); + if (sourcePath !== destPath) + fileIo.copyFile(sourcePath, destPath); + addFile(sourceFileName); + } +} + +function renameDocument(documentId, newName) { + var i = findDocument(documentId); + var document = projectListModel.get(i); + if (!document.isContract) { + var sourcePath = document.path; + var destPath = projectPath + newName; + fileIo.moveFile(sourcePath, destPath); + document.path = destPath; + document.name = newName; + projectListModel.set(i, document); + documentUpdated(documentId); + } +} + +function removeDocument(documentId) { + var i = findDocument(documentId); + var document = projectListModel.get(i); + if (!document.isContract) { + projectListModel.remove(i); + documentUpdated(documentId); + } +} + +function newHtmlFile() { + createAndAddFile("page", "html", "\n"); +} + +function newJsFile() { + createAndAddFile("script", "js", "function foo() {\n}\n"); +} + +function createAndAddFile(name, extension, content) { + var fileName = generateFileName(name, extension); + var filePath = projectPath + fileName; + fileIo.writeFile(filePath, content); + addFile(fileName); +} + +function generateFileName(name, extension) { + var i = 1; + do { + var fileName = name + i + "." + extension; + var filePath = projectPath + fileName; + i++; + } while (fileIo.fileExists(filePath)); + return fileName +} + diff --git a/mix/qml/main.qml b/mix/qml/main.qml index 75c1166da..469a6d1f4 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.1 import CodeEditorExtensionManager 1.0 +import org.ethereum.qml.ProjectModel 1.0 ApplicationWindow { id: mainApplication @@ -18,10 +19,19 @@ ApplicationWindow { menuBar: MenuBar { Menu { title: qsTr("File") - MenuItem { - text: qsTr("Exit") - onTriggered: Qt.quit(); - } + MenuItem { action: createProjectAction } + MenuItem { action: openProjectAction } + MenuSeparator {} + MenuItem { action: saveAllFilesAction } + MenuSeparator {} + MenuItem { action: addExistingFileAction } + MenuItem { action: addNewJsFileAction } + MenuItem { action: addNewHtmlFileAction } + MenuSeparator {} + //MenuItem { action: addNewContractAction } + MenuItem { action: closeProjectAction } + MenuSeparator {} + MenuItem { action: exitAppAction } } Menu { title: qsTr("Debug") @@ -54,22 +64,29 @@ ApplicationWindow { id: messageDialog } + Action { + id: exitAppAction + text: qsTr("Exit") + shortcut: "Ctrl+Q" + onTriggered: Qt.quit(); + } + Action { id: debugRunAction text: "&Run" shortcut: "F5" onTriggered: { mainContent.ensureRightView(); - debugModel.debugDeployment(); + clientModel.debugDeployment(); } - enabled: codeModel.hasContract && !debugModel.running; + enabled: codeModel.hasContract && !clientModel.running; } Action { id: debugResetStateAction text: "Reset &State" shortcut: "F6" - onTriggered: debugModel.resetState(); + onTriggered: clientModel.resetState(); } Action { @@ -78,4 +95,68 @@ ApplicationWindow { shortcut: "F7" onTriggered: mainContent.toggleRightView(); } + + Action { + id: createProjectAction + text: qsTr("&New project") + shortcut: "Ctrl+N" + enabled: true; + onTriggered: ProjectModel.createProject(); + } + + Action { + id: openProjectAction + text: qsTr("&Open project") + shortcut: "Ctrl+O" + enabled: true; + onTriggered: ProjectModel.browseProject(); + } + + Action { + id: addNewJsFileAction + text: qsTr("New JavaScript file") + shortcut: "Ctrl+Alt+J" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.newJsFile(); + } + + Action { + id: addNewHtmlFileAction + text: qsTr("New HTML file") + shortcut: "Ctrl+Alt+H" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.newHtmlFile(); + } + + Action { + id: addNewContractAction + text: qsTr("New contract") + shortcut: "Ctrl+Alt+C" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.newContract(); + } + + Action { + id: addExistingFileAction + text: qsTr("Add existing file") + shortcut: "Ctrl+Alt+A" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.addExistingFile(); + } + + Action { + id: saveAllFilesAction + text: qsTr("Save all") + shortcut: "Ctrl+S" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.saveAll(); + } + + Action { + id: closeProjectAction + text: qsTr("Close project") + shortcut: "Ctrl+W" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.closeProject(); + } } diff --git a/neth/main.cpp b/neth/main.cpp index 8552177ce..1252b205b 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -63,54 +64,54 @@ void help() { cout << "Usage neth [OPTIONS] " << endl - << "Options:" << endl - << " -a,--address Set the coinbase (mining payout) address to addr (default: auto)." << endl - << " -c,--client-name Add a name to your client's version string (default: blank)." << endl - << " -d,--db-path Load database from path (default: ~/.ethereum " << endl - << " /Etherum or Library/Application Support/Ethereum)." << endl - << " -h,--help Show this help message and exit." << endl + << "Options:" << endl + << " -a,--address Set the coinbase (mining payout) address to addr (default: auto)." << endl + << " -c,--client-name Add a name to your client's version string (default: blank)." << endl + << " -d,--db-path Load database from path (default: ~/.ethereum " << endl + << " /Etherum or Library/Application Support/Ethereum)." << endl + << " -h,--help Show this help message and exit." << endl #if ETH_JSONRPC - << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl - << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl + << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl + << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl #endif - << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl - << " -m,--mining Enable mining (default: off)" << endl - << " -n,--upnp Use upnp for NAT (default: on)." << endl - << " -o,--mode Start a full node or a peer node (Default: full)." << endl - << " -p,--port Connect to remote port (default: 30303)." << endl - << " -r,--remote Connect to remote host (default: none)." << endl - << " -s,--secret Set the secret key for use with send command (default: auto)." << endl - << " -u,--public-ip Force public ip to given (default; auto)." << endl - << " -v,--verbosity <0..9> Set the log verbosity from 0 to 9 (tmp forced to 1)." << endl - << " -x,--peers Attempt to connect to given number of peers (default: 5)." << endl - << " -V,--version Show the version and exit." << endl; - exit(0); + << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl + << " -m,--mining Enable mining (default: off)" << endl + << " -n,--upnp Use upnp for NAT (default: on)." << endl + << " -o,--mode Start a full node or a peer node (Default: full)." << endl + << " -p,--port Connect to remote port (default: 30303)." << endl + << " -r,--remote Connect to remote host (default: none)." << endl + << " -s,--secret Set the secret key for use with send command (default: auto)." << endl + << " -u,--public-ip Force public ip to given (default; auto)." << endl + << " -v,--verbosity <0..9> Set the log verbosity from 0 to 9 (tmp forced to 1)." << endl + << " -x,--peers Attempt to connect to given number of peers (default: 5)." << endl + << " -V,--version Show the version and exit." << endl; + exit(0); } void interactiveHelp() { cout - << "Commands:" << endl - << " netstart Starts the network sybsystem on a specific port." << endl - << " netstop Stops the network subsystem." << endl + << "Commands:" << endl + << " netstart Starts the network sybsystem on a specific port." << endl + << " netstop Stops the network subsystem." << endl #if ETH_JSONRPC - << " jsonstart Starts the JSON-RPC server." << endl - << " jsonstop Stops the JSON-RPC server." << endl + << " jsonstart Starts the JSON-RPC server." << endl + << " jsonstop Stops the JSON-RPC server." << endl #endif - << " connect Connects to a specific peer." << endl - << " minestart Starts mining." << endl - << " minestop Stops mining." << endl - << " address Gives the current address." << endl - << " secret Gives the current secret" << endl - << " block Gives the current block height." << endl - << " balance Gives the current balance." << endl - << " peers List the peers that are connected" << endl - << " transact Execute a given transaction." << endl - << " send Execute a given transaction with current secret." << endl - << " contract Create a new contract with current secret." << endl - << " inspect Dumps a contract to /.evm." << endl - << " reset Resets ncurses windows" << endl - << " exit Exits the application." << endl; + << " connect Connects to a specific peer." << endl + << " minestart Starts mining." << endl + << " minestop Stops mining." << endl + << " address Gives the current address." << endl + << " secret Gives the current secret" << endl + << " block Gives the current block height." << endl + << " balance Gives the current balance." << endl + << " peers List the peers that are connected" << endl + << " transact Execute a given transaction." << endl + << " send Execute a given transaction with current secret." << endl + << " contract Create a new contract with current secret." << endl + << " inspect Dumps a contract to /.evm." << endl + << " reset Resets ncurses windows" << endl + << " exit Exits the application." << endl; } string credits() @@ -121,17 +122,8 @@ string credits() << " Code by Gav Wood & , (c) 2013, 2014." << endl << " Based on a design by Vitalik Buterin." << endl << endl; - string vs = toString(dev::Version); - vs = vs.substr(vs.find_first_of('.') + 1)[0]; - int pocnumber = stoi(vs); - string m_servers; - if (pocnumber == 5) - m_servers = "54.72.69.180"; - else - m_servers = "54.76.56.74"; - ccout << "Type 'netstart 30303' to start networking" << endl; - ccout << "Type 'connect " << m_servers << " 30303' to connect" << endl; + ccout << "Type 'connect " << Host::pocHost() << " 30303' to connect" << endl; ccout << "Type 'exit' to quit" << endl << endl; return ccout.str(); } @@ -139,12 +131,13 @@ string credits() void version() { cout << "neth version " << dev::Version << endl; + cout << "Network protocol version: " << dev::eth::c_protocolVersion << endl; + cout << "Client database version: " << dev::eth::c_databaseVersion << endl; cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; exit(0); } Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f"); - string pretty(h160 _a, dev::eth::State _st) { string ns; @@ -161,6 +154,13 @@ string pretty(h160 _a, dev::eth::State _st) return ns; } +bool g_exit = false; + +void sighandler(int) +{ + g_exit = true; +} + namespace nc { @@ -292,19 +292,30 @@ int nc_window_streambuf::sync() vector form_dialog(vector _sfields, vector _lfields, vector _bfields, int _cols, int _rows, string _post_form); +enum class NodeMode +{ + PeerServer, + Full +}; + int main(int argc, char** argv) { unsigned short listenPort = 30303; string remoteHost; unsigned short remotePort = 30303; string dbPath; - bool mining = false; + unsigned mining = ~(unsigned)0; + (void)mining; + NodeMode mode = NodeMode::Full; unsigned peers = 5; #if ETH_JSONRPC int jsonrpc = 8080; #endif string publicIP; + bool bootstrap = false; bool upnp = true; + bool useLocal = false; + bool forceMining = false; string clientName; // Init defaults @@ -365,14 +376,21 @@ int main(int argc, char** argv) { string m = argv[++i]; if (isTrue(m)) - mining = true; + mining = ~(unsigned)0; else if (isFalse(m)) - mining = false; + mining = 0; + else if (int i = stoi(m)) + mining = i; else { - cerr << "Unknown mining option: " << m << endl; + cerr << "Unknown -m/--mining option: " << m << endl; + return -1; } } + else if (arg == "-b" || arg == "--bootstrap") + bootstrap = true; + else if (arg == "-f" || arg == "--force-mining") + forceMining = true; #if ETH_JSONRPC else if ((arg == "-j" || arg == "--json-rpc")) jsonrpc = jsonrpc ? jsonrpc : 8080; @@ -394,12 +412,50 @@ int main(int argc, char** argv) if (!clientName.empty()) clientName += "/"; - WebThreeDirect web3("NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), dbPath); - Client& c = *web3.ethereum(); + cout << credits(); + + NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); + dev::WebThreeDirect web3( + "NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), + dbPath, + false, + mode == NodeMode::Full ? set{"eth", "shh"} : set(), + netPrefs + ); + web3.setIdealPeerCount(peers); + eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr; + + if (c) + { + c->setForceMining(forceMining); + c->setAddress(coinbase); + } - c.setForceMining(true); + auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/nodeState.rlp"); + web3.restoreNodes(&nodesState); - cout << credits(); + web3.startNetwork(); + + if (bootstrap) + web3.connect(Host::pocHost()); + if (remoteHost.size()) + web3.connect(remoteHost, remotePort); + +#if ETH_JSONRPC + shared_ptr jsonrpcServer; + unique_ptr jsonrpcConnector; + if (jsonrpc > -1) + { + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc)); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); + jsonrpcServer->setIdentities({us}); + jsonrpcServer->StartListening(); + } +#endif + + signal(SIGABRT, &sighandler); + signal(SIGTERM, &sighandler); + signal(SIGINT, &sighandler); std::ostringstream ccout; @@ -461,28 +517,6 @@ int main(int argc, char** argv) wmove(mainwin, 1, 4); - if (!remoteHost.empty()) - { - web3.setIdealPeerCount(peers); - web3.setNetworkPreferences(NetworkPreferences(listenPort, publicIP, upnp)); - web3.startNetwork(); - web3.connect(remoteHost, remotePort); - } - if (mining) - c.startMining(); - -#if ETH_JSONRPC - shared_ptr jsonrpcServer; - unique_ptr jsonrpcConnector; - if (jsonrpc > -1) - { - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); - jsonrpcServer->setIdentities({us}); - jsonrpcServer->StartListening(); - } -#endif - while (true) { wclrtobot(consolewin); @@ -532,13 +566,25 @@ int main(int argc, char** argv) { web3.stopNetwork(); } - else if (cmd == "minestart") + else if (c && cmd == "minestart") + { + c->startMining(); + } + else if (c && cmd == "minestop") { - c.startMining(); + c->stopMining(); } - else if (cmd == "minestop") + else if (c && cmd == "mineforce") { - c.stopMining(); + string enable; + iss >> enable; + c->setForceMining(isTrue(enable)); + } + else if (cmd == "verbosity") + { + if (iss.peek() != -1) + iss >> g_logVerbosity; + cout << "Verbosity: " << g_logVerbosity << endl; } #if ETH_JSONRPC else if (cmd == "jsonport") @@ -575,7 +621,7 @@ int main(int argc, char** argv) } else if (cmd == "block") { - unsigned n = c.blockChain().details().number; + unsigned n = c->blockChain().details().number; ccout << "Current block # "; ccout << toString(n) << endl; } @@ -588,13 +634,13 @@ int main(int argc, char** argv) } else if (cmd == "balance") { - u256 balance = c.balanceAt(us.address()); + u256 balance = c->balanceAt(us.address()); ccout << "Current balance:" << endl; ccout << toString(balance) << endl; } else if (cmd == "transact") { - auto const& bc = c.blockChain(); + auto const& bc = c->blockChain(); auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); @@ -663,7 +709,7 @@ int main(int argc, char** argv) { Secret secret = h256(fromHex(sechex)); Address dest = h160(fromHex(fields[0])); - c.transact(secret, amount, dest, data, gas, gasPrice); + c->transact(secret, amount, dest, data, gas, gasPrice); } } } @@ -696,19 +742,19 @@ int main(int argc, char** argv) } else { - auto const& bc = c.blockChain(); + auto const& bc = c->blockChain(); auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); u256 minGas = (u256)Client::txGas(bytes(), 0); Address dest = h160(fromHex(fields[0])); - c.transact(us.secret(), amount, dest, bytes(), minGas); + c->transact(us.secret(), amount, dest, bytes(), minGas); } } } else if (cmd == "contract") { - auto const& bc = c.blockChain(); + auto const& bc = c->blockChain(); auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); @@ -768,7 +814,7 @@ int main(int argc, char** argv) cwarn << "Minimum gas amount is" << minGas; else { - c.transact(us.secret(), endowment, init, gas); + c->transact(us.secret(), endowment, init, gas); } } } @@ -786,10 +832,10 @@ int main(int argc, char** argv) try { - auto storage = c.storageAt(address); + auto storage = c->storageAt(address); for (auto const& i: storage) s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl; - s << endl << disassemble(c.codeAt(address)) << endl; + s << endl << disassemble(c->codeAt(address)) << endl; string outFile = getDataDir() + "/" + rechex + ".evm"; ofstream ofs; @@ -824,7 +870,7 @@ int main(int argc, char** argv) // Lock to prevent corrupt block-chain errors - auto const& bc = c.blockChain(); + auto const& bc = c->blockChain(); ccout << "Genesis hash: " << bc.genesisHash() << endl; // Blocks @@ -838,11 +884,11 @@ int main(int argc, char** argv) auto b = bc.block(h); for (auto const& i: RLP(b)[1]) { - Transaction t(i[0].data()); + Transaction t(i.data()); auto s = t.receiveAddress() ? boost::format(" %1% %2%> %3%: %4% [%5%]") % toString(t.safeSender()) % - (c.codeAt(t.receiveAddress(), 0).size() ? '*' : '-') % + (c->codeAt(t.receiveAddress(), 0).size() ? '*' : '-') % toString(t.receiveAddress()) % toString(formatBalance(t.value())) % toString((unsigned)t.nonce()) : @@ -862,12 +908,12 @@ int main(int argc, char** argv) // Pending y = 1; - for (Transaction const& t: c.pending()) + for (Transaction const& t: c->pending()) { auto s = t.receiveAddress() ? boost::format("%1% %2%> %3%: %4% [%5%]") % toString(t.safeSender()) % - (c.codeAt(t.receiveAddress(), 0).size() ? '*' : '-') % + (c->codeAt(t.receiveAddress(), 0).size() ? '*' : '-') % toString(t.receiveAddress()) % toString(formatBalance(t.value())) % toString((unsigned)t.nonce()) : @@ -877,7 +923,7 @@ int main(int argc, char** argv) toString(formatBalance(t.value())) % toString((unsigned)t.nonce()); mvwaddnstr(pendingwin, y++, x, s.str().c_str(), qwidth); - if (y > height * 1 / 5 - 4) + if (y > height * 1 / 5 - 2) break; } @@ -885,27 +931,27 @@ int main(int argc, char** argv) // Contracts and addresses y = 1; int cc = 1; - auto acs = c.addresses(); + auto acs = c->addresses(); for (auto const& i: acs) - if (c.codeAt(i, 0).size()) + if (c->codeAt(i, 0).size()) { auto s = boost::format("%1%%2% : %3% [%4%]") % toString(i) % - pretty(i, c.postState()) % - toString(formatBalance(c.balanceAt(i))) % - toString((unsigned)c.countAt(i, 0)); + pretty(i, c->postState()) % + toString(formatBalance(c->balanceAt(i))) % + toString((unsigned)c->countAt(i, 0)); mvwaddnstr(contractswin, cc++, x, s.str().c_str(), qwidth); if (cc > qheight - 2) break; } for (auto const& i: acs) - if (c.codeAt(i, 0).empty()) + if (c->codeAt(i, 0).empty()) { auto s = boost::format("%1%%2% : %3% [%4%]") % toString(i) % - pretty(i, c.postState()) % - toString(formatBalance(c.balanceAt(i))) % - toString((unsigned)c.countAt(i, 0)); + pretty(i, c->postState()) % + toString(formatBalance(c->balanceAt(i))) % + toString((unsigned)c->countAt(i, 0)); mvwaddnstr(addswin, y++, x, s.str().c_str(), width / 2 - 4); if (y > height * 3 / 5 - 4) break; @@ -935,21 +981,18 @@ int main(int argc, char** argv) // Balance stringstream ssb; - u256 balance = c.balanceAt(us.address()); - Address coinsAddr = right160(c.stateAt(c_config, 1)); - Address gavCoin = right160(c.stateAt(coinsAddr, c.stateAt(coinsAddr, 1))); - u256 totalGavCoinBalance = c.stateAt(gavCoin, (u160)us.address()); - ssb << "Balance: " << formatBalance(balance) << " | " << totalGavCoinBalance << " GAV"; + u256 balance = c->balanceAt(us.address()); + ssb << "Balance: " << formatBalance(balance); mvwprintw(consolewin, 0, x, ssb.str().c_str()); // Block mvwprintw(blockswin, 0, x, "Block # "); - unsigned n = c.blockChain().details().number; + unsigned n = c->blockChain().details().number; mvwprintw(blockswin, 0, 10, toString(n).c_str()); // Pending string pc; - pc = "Pending: " + toString(c.pending().size()); + pc = "Pending: " + toString(c->pending().size()); mvwprintw(pendingwin, 0, x, pc.c_str()); // Contracts @@ -962,10 +1005,10 @@ int main(int argc, char** argv) mvwprintw(peerswin, 0, 9, toString(web3.peers().size()).c_str()); // Mining flag - if (c.isMining()) + if (c->isMining()) { mvwprintw(consolewin, qheight - 1, width / 4 - 11, "Mining ON"); - dev::eth::MineProgress p = c.miningProgress(); + dev::eth::MineProgress p = c->miningProgress(); auto speed = boost::format("%2% kH/s @ %1%s") % (p.ms / 1000) % (p.ms ? p.hashes / p.ms : 0); mvwprintw(consolewin, qheight - 2, width / 4 - speed.str().length() - 2, speed.str().c_str()); } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 2a79521ef..8c5192abe 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -16,6 +16,7 @@ */ /** * @author Lefteris + * @author Gav Wood * @date 2014 * Solidity command line interface. */ @@ -50,7 +51,8 @@ namespace solidity // LTODO: Maybe some argument class pairing names with // extensions and other attributes would be a better choice here? -static string const g_argAbiStr = "abi"; +static string const g_argAbiStr = "json-abi"; +static string const g_argSolAbiStr = "sol-abi"; static string const g_argAsmStr = "asm"; static string const g_argAstStr = "ast"; static string const g_argBinaryStr = "binary"; @@ -60,7 +62,7 @@ static string const g_argNatspecUserStr = "natspec-user"; static void version() { - cout << "solc, the solidity complier commandline interface " << dev::Version << endl + cout << "solc, the solidity compiler commandline interface " << dev::Version << endl << " by Christian and Lefteris , (c) 2014." << endl << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; exit(0); @@ -73,9 +75,11 @@ static inline bool argToStdout(po::variables_map const& _args, string const& _na static bool needStdout(po::variables_map const& _args) { - return argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argNatspecUserStr) || - argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) || - argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr); + return + argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argSolAbiStr) || + argToStdout(_args, g_argNatspecUserStr) || + argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) || + argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr); } static inline bool outputToFile(OutputType type) @@ -146,8 +150,7 @@ void CommandLineInterface::handleBytecode(string const& _contract) handleBinary(_contract); } -void CommandLineInterface::handleJson(DocumentationType _type, - string const& _contract) +void CommandLineInterface::handleMeta(DocumentationType _type, string const& _contract) { std::string argName; std::string suffix; @@ -157,10 +160,15 @@ void CommandLineInterface::handleJson(DocumentationType _type, case DocumentationType::ABI_INTERFACE: argName = g_argAbiStr; suffix = ".abi"; - title = "Contract ABI"; + title = "Contract JSON ABI"; + break; + case DocumentationType::ABI_SOLIDITY_INTERFACE: + argName = g_argSolAbiStr; + suffix = ".sol"; + title = "Contract Solidity ABI"; break; case DocumentationType::NATSPEC_USER: - argName = "g_argNatspecUserStr"; + argName = g_argNatspecUserStr; suffix = ".docuser"; title = "User Documentation"; break; @@ -180,13 +188,13 @@ void CommandLineInterface::handleJson(DocumentationType _type, if (outputToStdout(choice)) { cout << title << endl; - cout << m_compiler.getJsonDocumentation(_contract, _type); + cout << m_compiler.getMetadata(_contract, _type) << endl; } if (outputToFile(choice)) { ofstream outFile(_contract + suffix); - outFile << m_compiler.getJsonDocumentation(_contract, _type); + outFile << m_compiler.getMetadata(_contract, _type); outFile.close(); } } @@ -214,7 +222,9 @@ bool CommandLineInterface::parseArguments(int argc, char** argv) (g_argBinaryStr.c_str(), po::value(), "Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR) (g_argAbiStr.c_str(), po::value(), - "Request to output the contract's ABI interface. " OUTPUT_TYPE_STR) + "Request to output the contract's JSON ABI interface. " OUTPUT_TYPE_STR) + (g_argSolAbiStr.c_str(), po::value(), + "Request to output the contract's Solidity ABI interface. " OUTPUT_TYPE_STR) (g_argNatspecUserStr.c_str(), po::value(), "Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR) (g_argNatspecDevStr.c_str(), po::value(), @@ -384,9 +394,10 @@ void CommandLineInterface::actOnInput() } handleBytecode(contract); - handleJson(DocumentationType::ABI_INTERFACE, contract); - handleJson(DocumentationType::NATSPEC_DEV, contract); - handleJson(DocumentationType::NATSPEC_USER, contract); + handleMeta(DocumentationType::ABI_INTERFACE, contract); + handleMeta(DocumentationType::ABI_SOLIDITY_INTERFACE, contract); + handleMeta(DocumentationType::NATSPEC_DEV, contract); + handleMeta(DocumentationType::NATSPEC_USER, contract); } // end of contracts iteration } diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 7e3ad2502..9dfee7199 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -56,7 +56,7 @@ private: void handleBinary(std::string const& _contract); void handleOpcode(std::string const& _contract); void handleBytecode(std::string const& _contract); - void handleJson(DocumentationType _type, + void handleMeta(DocumentationType _type, std::string const& _contract); /// Compiler arguments variable map diff --git a/standard.js b/standard.js new file mode 100644 index 000000000..74ebc74ad --- /dev/null +++ b/standard.js @@ -0,0 +1,61 @@ +var compile = function(name) { return web3.eth.solidity(env.contents("../../dapp-bin/" + name + "/" + name + ".sol")); }; +var create = function(code) { return web3.eth.transact({ 'code': code }); }; +var send = function(from, val, to) { return web3.eth.transact({ 'from': from, 'value': val, 'to': to }); }; +var initService = function(name, dep) { return dep.then(function(){ return compile(name).then(create); }); }; + +var addrConfig = compile("config").then(create); +var addrNameReg = initService("namereg", addrConfig); + +var abiNameReg = [{"constant":true,"inputs":[{"name":"name","type":"string32"}],"name":"addressOf","outputs":[{"name":"addr","type":"address"}]},{"constant":false,"inputs":[],"name":"kill","outputs":[]},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"nameOf","outputs":[{"name":"name","type":"string32"}]},{"constant":false,"inputs":[{"name":"name","type":"string32"}],"name":"register","outputs":[]},{"constant":false,"inputs":[],"name":"unregister","outputs":[]}]; +var regName = function(account, name) { return web3.contract(addrNameReg, abiNameReg).register(name).transact({'from': account, 'gas': 10000}); }; +/* +var coins = initService("coins", 1, nameReg); +var coin = initService("coin", 2, coins); +var approve = function(account, approvedAddress) { web3.eth.transact({ 'from': account, 'to': coin, 'gas': '10000', 'data': [ web3.fromAscii('approve'), approvedAddress ] }); }; +var exchange = initService("exchange", 3, coin); +var offer = function(account, haveCoin, haveVal, wantCoin, wantVal) { web3.eth.transact({ 'from': account, 'to': exchange, 'gas': '10000', 'data': [web3.fromAscii('new'), haveCoin, haveVal, wantCoin, wantVal] }); }; +*/ +addrConfig.then(function() { + env.note("config ready"); + web3.eth.accounts.then(function(accounts) + { + env.note("accounts ready"); + var funded = send(accounts[0], '100000000000000000000', accounts[1]); + funded.then(function(){ + env.note("second account funded"); + regName(accounts[1], 'Gav Would'); + }); + regName(accounts[0], 'Gav'); + // TODO: once we have the exchange. +// approve(accounts[0], exchange).then(function(){ offer(accounts[0], coin, '5000', '0', '5000000000000000000'); }); +// funded.then(function(){ approve(accounts[1], exchange); }); + + // TODO: once we have a new implementation of DNSReg. + // env.note('Register gav.eth...') + // eth.transact({ 'to': dnsReg, 'data': [web3.fromAscii('register'), web3.fromAscii('gav'), web3.fromAscii('opensecrecy.com')] }); + }); +}); + +// TODO +/* +var nameRegJeff; + +env.note('Create NameRegJeff...') +eth.transact({ 'code': nameRegCode }, function(a) { nameRegJeff = a; }); + +env.note('Register NameRegJeff...') +eth.transact({ 'to': config, 'data': ['4', nameRegJeff] }); + +var dnsRegCode = '0x60006000546000600053602001546000600053604001546020604060206020600073661005d2720d855f1d9976f88bb10c1a3398c77f6103e8f17f7265676973746572000000000000000000000000000000000000000000000000600053606001600060200201547f446e735265670000000000000000000000000000000000000000000000000000600053606001600160200201546000600060006000604060606000600053604001536103e8f1327f6f776e65720000000000000000000000000000000000000000000000000000005761011663000000e46000396101166000f20060006000547f72656769737465720000000000000000000000000000000000000000000000006000602002350e0f630000006d596000600160200235560e0f630000006c59600032560e0f0f6300000057596000325657600260200235600160200235576001602002353257007f64657265676973746572000000000000000000000000000000000000000000006000602002350e0f63000000b95960016020023532560e0f63000000b959600032576000600160200235577f6b696c6c000000000000000000000000000000000000000000000000000000006000602002350e0f630000011559327f6f776e6572000000000000000000000000000000000000000000000000000000560e0f63000001155932ff00'; + +var dnsReg; +env.note('Create DnsReg...') +eth.transact({ 'code': dnsRegCode }, function(a) { dnsReg = a; }); + +env.note('DnsReg at address ' + dnsReg) + +env.note('Register DnsReg...') +eth.transact({ 'to': config, 'data': ['4', dnsReg] }); +*/ + +// env.load('/home/gav/Eth/cpp-ethereum/stdserv.js') diff --git a/test/SolidityABIJSON.cpp b/test/SolidityABIJSON.cpp index 714aa0f30..d553f576a 100644 --- a/test/SolidityABIJSON.cpp +++ b/test/SolidityABIJSON.cpp @@ -50,7 +50,7 @@ public: msg += *extra; BOOST_FAIL(msg); } - std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation("", DocumentationType::ABI_INTERFACE); + std::string generatedInterfaceString = m_compilerStack.getMetadata("", DocumentationType::ABI_INTERFACE); Json::Value generatedInterface; m_reader.parse(generatedInterfaceString, generatedInterface); Json::Value expectedInterface; @@ -236,20 +236,6 @@ BOOST_AUTO_TEST_CASE(const_function) "}\n"; char const* interface = R"([ - { - "name": "boo", - "constant": true, - "inputs": [{ - "name": "a", - "type": "uint32" - }], - "outputs": [ - { - "name": "b", - "type": "uint256" - } - ] - }, { "name": "foo", "constant": false, @@ -269,6 +255,20 @@ BOOST_AUTO_TEST_CASE(const_function) "type": "uint256" } ] + }, + { + "name": "boo", + "constant": true, + "inputs": [{ + "name": "a", + "type": "uint32" + }], + "outputs": [ + { + "name": "b", + "type": "uint256" + } + ] } ])"; diff --git a/test/SolidityCompiler.cpp b/test/SolidityCompiler.cpp index 385a3e577..b4874e195 100644 --- a/test/SolidityCompiler.cpp +++ b/test/SolidityCompiler.cpp @@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) "}\n"; bytes code = compileContract(sourceCode); - unsigned boilerplateSize = 40; + unsigned boilerplateSize = 73; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, // initialize local variable x byte(Instruction::PUSH1), 0x2, @@ -115,9 +115,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers) " function g() returns (uint e, uint h) { h = f(1, 2, 3); }\n" "}\n"; bytes code = compileContract(sourceCode); - - unsigned shift = 68; - unsigned boilerplateSize = 81; + unsigned shift = 103; + unsigned boilerplateSize = 116; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, // initialize return variable d byte(Instruction::DUP3), @@ -135,10 +134,10 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers) byte(Instruction::JUMPDEST), // beginning of g byte(Instruction::PUSH1), 0x0, byte(Instruction::PUSH1), 0x0, // initialized e and h - byte(Instruction::PUSH1), byte(0x2a + shift), // ret address - byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), - byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), - byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), + byte(Instruction::PUSH1), byte(0x21 + shift), // ret address + byte(Instruction::PUSH1), 0x1, + byte(Instruction::PUSH1), 0x2, + byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), byte(0x1 + shift), // stack here: ret e h 0x20 1 2 3 0x1 byte(Instruction::JUMP), @@ -166,9 +165,8 @@ BOOST_AUTO_TEST_CASE(ifStatement) " function f() { bool x; if (x) 77; else if (!x) 78; else 79; }" "}\n"; bytes code = compileContract(sourceCode); - - unsigned shift = 27; - unsigned boilerplateSize = 40; + unsigned shift = 60; + unsigned boilerplateSize = 73; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, byte(Instruction::DUP1), @@ -208,9 +206,8 @@ BOOST_AUTO_TEST_CASE(loops) " function f() { while(true){1;break;2;continue;3;return;4;} }" "}\n"; bytes code = compileContract(sourceCode); - - unsigned shift = 27; - unsigned boilerplateSize = 40; + unsigned shift = 60; + unsigned boilerplateSize = 73; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x1, diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 9559e3702..2afe875f2 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -17,6 +17,7 @@ */ /** * @author Christian + * @author Gav Wood * @date 2014 * Unit tests for the solidity expression compiler, testing the behaviour of the code. */ @@ -44,7 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) " function f(uint a) returns(uint d) { return a * 7; }\n" "}\n"; compileAndRun(sourceCode); - testSolidityAgainstCppOnRange(0, [](u256 const& a) -> u256 { return a * 7; }, 0, 100); + testSolidityAgainstCppOnRange("f(uint256)", [](u256 const& a) -> u256 { return a * 7; }, 0, 100); } BOOST_AUTO_TEST_CASE(empty_contract) @@ -52,7 +53,7 @@ BOOST_AUTO_TEST_CASE(empty_contract) char const* sourceCode = "contract test {\n" "}\n"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction(0, bytes()).empty()); + BOOST_CHECK(callContractFunction("i_am_not_there()", bytes()).empty()); } BOOST_AUTO_TEST_CASE(recursive_calls) @@ -72,7 +73,7 @@ BOOST_AUTO_TEST_CASE(recursive_calls) return n * recursive_calls_cpp(n - 1); }; - testSolidityAgainstCppOnRange(0, recursive_calls_cpp, 0, 5); + testSolidityAgainstCppOnRange("f(uint256)", recursive_calls_cpp, 0, 5); } BOOST_AUTO_TEST_CASE(multiple_functions) @@ -84,11 +85,11 @@ BOOST_AUTO_TEST_CASE(multiple_functions) " function f() returns(uint n) { return 3; }\n" "}\n"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction(0, bytes()) == toBigEndian(u256(0))); - BOOST_CHECK(callContractFunction(1, bytes()) == toBigEndian(u256(1))); - BOOST_CHECK(callContractFunction(2, bytes()) == toBigEndian(u256(2))); - BOOST_CHECK(callContractFunction(3, bytes()) == toBigEndian(u256(3))); - BOOST_CHECK(callContractFunction(4, bytes()) == bytes()); + BOOST_CHECK(callContractFunction("a()", bytes()) == toBigEndian(u256(0))); + BOOST_CHECK(callContractFunction("b()", bytes()) == toBigEndian(u256(1))); + BOOST_CHECK(callContractFunction("c()", bytes()) == toBigEndian(u256(2))); + BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(3))); + BOOST_CHECK(callContractFunction("i_am_not_there()", bytes()) == bytes()); } BOOST_AUTO_TEST_CASE(while_loop) @@ -112,7 +113,7 @@ BOOST_AUTO_TEST_CASE(while_loop) return nfac; }; - testSolidityAgainstCppOnRange(0, while_loop_cpp, 0, 5); + testSolidityAgainstCppOnRange("f(uint256)", while_loop_cpp, 0, 5); } BOOST_AUTO_TEST_CASE(break_outside_loop) @@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(break_outside_loop) " }\n" "}\n"; compileAndRun(sourceCode); - testSolidityAgainstCpp(0, [](u256 const&) -> u256 { return 2; }, u256(0)); + testSolidityAgainstCpp("f(uint256)", [](u256 const&) -> u256 { return 2; }, u256(0)); } BOOST_AUTO_TEST_CASE(nested_loops) @@ -173,7 +174,7 @@ BOOST_AUTO_TEST_CASE(nested_loops) return n; }; - testSolidityAgainstCppOnRange(0, nested_loops_cpp, 0, 12); + testSolidityAgainstCppOnRange("f(uint256)", nested_loops_cpp, 0, 12); } BOOST_AUTO_TEST_CASE(for_loop) @@ -195,7 +196,7 @@ BOOST_AUTO_TEST_CASE(for_loop) return nfac; }; - testSolidityAgainstCppOnRange(0, for_loop_cpp, 0, 5); + testSolidityAgainstCppOnRange("f(uint256)", for_loop_cpp, 0, 5); } BOOST_AUTO_TEST_CASE(for_loop_empty) @@ -223,7 +224,7 @@ BOOST_AUTO_TEST_CASE(for_loop_empty) return ret; }; - testSolidityAgainstCpp(0, for_loop_empty_cpp); + testSolidityAgainstCpp("f()", for_loop_empty_cpp); } BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr) @@ -247,7 +248,7 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr) return nfac; }; - testSolidityAgainstCppOnRange(0, for_loop_simple_init_expr_cpp, 0, 5); + testSolidityAgainstCppOnRange("f(uint256)", for_loop_simple_init_expr_cpp, 0, 5); } BOOST_AUTO_TEST_CASE(calling_other_functions) @@ -292,11 +293,11 @@ BOOST_AUTO_TEST_CASE(calling_other_functions) return y; }; - testSolidityAgainstCpp(2, collatz_cpp, u256(0)); - testSolidityAgainstCpp(2, collatz_cpp, u256(1)); - testSolidityAgainstCpp(2, collatz_cpp, u256(2)); - testSolidityAgainstCpp(2, collatz_cpp, u256(8)); - testSolidityAgainstCpp(2, collatz_cpp, u256(127)); + testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(0)); + testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(1)); + testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(2)); + testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(8)); + testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(127)); } BOOST_AUTO_TEST_CASE(many_local_variables) @@ -317,7 +318,7 @@ BOOST_AUTO_TEST_CASE(many_local_variables) u256 y = a + b + c + x1 + x2 + x3; return y + b + x2; }; - testSolidityAgainstCpp(0, f, u256(0x1000), u256(0x10000), u256(0x100000)); + testSolidityAgainstCpp("run(uint256,uint256,uint256)", f, u256(0x1000), u256(0x10000), u256(0x100000)); } BOOST_AUTO_TEST_CASE(packing_unpacking_types) @@ -330,10 +331,23 @@ BOOST_AUTO_TEST_CASE(packing_unpacking_types) " }\n" "}\n"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction(0, fromHex("01""0f0f0f0f""f0f0f0f0f0f0f0f0")) + BOOST_CHECK(callContractFunction("run(bool,uint32,uint64)", true, fromHex("0f0f0f0f"), fromHex("f0f0f0f0f0f0f0f0")) == fromHex("00000000000000000000000000000000000000""01""f0f0f0f0""0f0f0f0f0f0f0f0f")); } +BOOST_AUTO_TEST_CASE(packing_signed_types) +{ + char const* sourceCode = "contract test {\n" + " function run() returns(int8 y) {\n" + " uint8 x = 0xfa;\n" + " return int8(x);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("run()") + == fromHex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa")); +} + BOOST_AUTO_TEST_CASE(multiple_return_values) { char const* sourceCode = "contract test {\n" @@ -342,8 +356,7 @@ BOOST_AUTO_TEST_CASE(multiple_return_values) " }\n" "}\n"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction(0, bytes(1, 1) + toBigEndian(u256(0xcd))) - == toBigEndian(u256(0xcd)) + bytes(1, 1) + toBigEndian(u256(0))); + BOOST_CHECK(callContractFunction("run(bool,uint256)", true, 0xcd) == encodeArgs(0xcd, true, 0)); } BOOST_AUTO_TEST_CASE(short_circuiting) @@ -362,7 +375,7 @@ BOOST_AUTO_TEST_CASE(short_circuiting) return n; }; - testSolidityAgainstCppOnRange(0, short_circuiting_cpp, 0, 2); + testSolidityAgainstCppOnRange("run(uint256)", short_circuiting_cpp, 0, 2); } BOOST_AUTO_TEST_CASE(high_bits_cleaning) @@ -382,7 +395,7 @@ BOOST_AUTO_TEST_CASE(high_bits_cleaning) return 0; return x; }; - testSolidityAgainstCpp(0, high_bits_cleaning_cpp); + testSolidityAgainstCpp("run()", high_bits_cleaning_cpp); } BOOST_AUTO_TEST_CASE(sign_extension) @@ -402,7 +415,7 @@ BOOST_AUTO_TEST_CASE(sign_extension) return 0; return u256(x) * -1; }; - testSolidityAgainstCpp(0, sign_extension_cpp); + testSolidityAgainstCpp("run()", sign_extension_cpp); } BOOST_AUTO_TEST_CASE(small_unsigned_types) @@ -419,7 +432,7 @@ BOOST_AUTO_TEST_CASE(small_unsigned_types) uint32_t x = uint32_t(0xffffff) * 0xffffff; return x / 0x100; }; - testSolidityAgainstCpp(0, small_unsigned_types_cpp); + testSolidityAgainstCpp("run()", small_unsigned_types_cpp); } BOOST_AUTO_TEST_CASE(small_signed_types) @@ -434,7 +447,7 @@ BOOST_AUTO_TEST_CASE(small_signed_types) { return -int32_t(10) * -int64_t(20); }; - testSolidityAgainstCpp(0, small_signed_types_cpp); + testSolidityAgainstCpp("run()", small_signed_types_cpp); } BOOST_AUTO_TEST_CASE(strings) @@ -449,20 +462,8 @@ BOOST_AUTO_TEST_CASE(strings) " }\n" "}\n"; compileAndRun(sourceCode); - bytes expectation(32, 0); - expectation[0] = byte('a'); - expectation[1] = byte('b'); - expectation[2] = byte('c'); - expectation[3] = byte(0); - expectation[4] = byte(0xff); - expectation[5] = byte('_'); - expectation[6] = byte('_'); - BOOST_CHECK(callContractFunction(0, bytes()) == expectation); - expectation = bytes(17, 0); - expectation[0] = 0; - expectation[1] = 2; - expectation[16] = 1; - BOOST_CHECK(callContractFunction(1, bytes({0x00, 0x02, 0x01})) == expectation); + BOOST_CHECK(callContractFunction("fixed()") == encodeArgs(string("abc\0\xff__", 7))); + BOOST_CHECK(callContractFunction("pipeThrough(string2,bool)", string("\0\x02", 2), true) == encodeArgs(string("\0\x2", 2), true)); } BOOST_AUTO_TEST_CASE(empty_string_on_stack) @@ -476,7 +477,7 @@ BOOST_AUTO_TEST_CASE(empty_string_on_stack) " }\n" "}\n"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x02)) == bytes({0x00, 0x02, 0x61/*'a'*/, 0x62/*'b'*/, 0x63/*'c'*/, 0x00})); + BOOST_CHECK(callContractFunction("run(string0,uint8)", string(), byte(0x02)) == encodeArgs(0x2, string(""), string("abc\0"))); } BOOST_AUTO_TEST_CASE(state_smoke_test) @@ -494,14 +495,14 @@ BOOST_AUTO_TEST_CASE(state_smoke_test) " }\n" "}\n"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == toBigEndian(u256(0))); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == toBigEndian(u256(0))); - BOOST_CHECK(callContractFunction(1, bytes(1, 0x00) + toBigEndian(u256(0x1234))) == bytes()); - BOOST_CHECK(callContractFunction(1, bytes(1, 0x01) + toBigEndian(u256(0x8765))) == bytes()); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == toBigEndian(u256(0x1234))); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == toBigEndian(u256(0x8765))); - BOOST_CHECK(callContractFunction(1, bytes(1, 0x00) + toBigEndian(u256(0x3))) == bytes()); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == toBigEndian(u256(0x3))); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x00)) == encodeArgs(0)); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(0)); + BOOST_CHECK(callContractFunction("set(uint8,uint256)", byte(0x00), 0x1234) == encodeArgs()); + BOOST_CHECK(callContractFunction("set(uint8,uint256)", byte(0x01), 0x8765) == encodeArgs()); + BOOST_CHECK(callContractFunction("get(uint8)", byte( 0x00)) == encodeArgs(0x1234)); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(0x8765)); + BOOST_CHECK(callContractFunction("set(uint8,uint256)", byte(0x00), 0x3) == encodeArgs()); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x00)) == encodeArgs(0x3)); } BOOST_AUTO_TEST_CASE(compound_assign) @@ -529,14 +530,14 @@ BOOST_AUTO_TEST_CASE(compound_assign) value2 *= value3 + value1; return value2 += 7; }; - testSolidityAgainstCpp(0, f, u256(0), u256(6)); - testSolidityAgainstCpp(0, f, u256(1), u256(3)); - testSolidityAgainstCpp(0, f, u256(2), u256(25)); - testSolidityAgainstCpp(0, f, u256(3), u256(69)); - testSolidityAgainstCpp(0, f, u256(4), u256(84)); - testSolidityAgainstCpp(0, f, u256(5), u256(2)); - testSolidityAgainstCpp(0, f, u256(6), u256(51)); - testSolidityAgainstCpp(0, f, u256(7), u256(48)); + testSolidityAgainstCpp("f(uint256,uint256)", f, u256(0), u256(6)); + testSolidityAgainstCpp("f(uint256,uint256)", f, u256(1), u256(3)); + testSolidityAgainstCpp("f(uint256,uint256)", f, u256(2), u256(25)); + testSolidityAgainstCpp("f(uint256,uint256)", f, u256(3), u256(69)); + testSolidityAgainstCpp("f(uint256,uint256)", f, u256(4), u256(84)); + testSolidityAgainstCpp("f(uint256,uint256)", f, u256(5), u256(2)); + testSolidityAgainstCpp("f(uint256,uint256)", f, u256(6), u256(51)); + testSolidityAgainstCpp("f(uint256,uint256)", f, u256(7), u256(48)); } BOOST_AUTO_TEST_CASE(simple_mapping) @@ -552,22 +553,21 @@ BOOST_AUTO_TEST_CASE(simple_mapping) "}"; compileAndRun(sourceCode); - // msvc seems to have problems with initializer-list, when there is only 1 param in the list - BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == bytes(1, 0x00)); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == bytes(1, 0x00)); - BOOST_CHECK(callContractFunction(0, bytes(1, 0xa7)) == bytes(1, 0x00)); - callContractFunction(1, bytes({0x01, 0xa1})); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == bytes(1, 0x00)); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == bytes(1, 0xa1)); - BOOST_CHECK(callContractFunction(0, bytes(1, 0xa7)) == bytes(1, 0x00)); - callContractFunction(1, bytes({0x00, 0xef})); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == bytes(1, 0xef)); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == bytes(1, 0xa1)); - BOOST_CHECK(callContractFunction(0, bytes(1, 0xa7)) == bytes(1, 0x00)); - callContractFunction(1, bytes({0x01, 0x05})); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == bytes(1, 0xef)); - BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == bytes(1, 0x05)); - BOOST_CHECK(callContractFunction(0, bytes(1, 0xa7)) == bytes(1, 0x00)); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0)) == encodeArgs(byte(0x00))); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(byte(0x00))); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0xa7)) == encodeArgs(byte(0x00))); + callContractFunction("set(uint8,uint8)", byte(0x01), byte(0xa1)); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x00)) == encodeArgs(byte(0x00))); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(byte(0xa1))); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0xa7)) == encodeArgs(byte(0x00))); + callContractFunction("set(uint8,uint8)", byte(0x00), byte(0xef)); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x00)) == encodeArgs(byte(0xef))); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(byte(0xa1))); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0xa7)) == encodeArgs(byte(0x00))); + callContractFunction("set(uint8,uint8)", byte(0x01), byte(0x05)); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x00)) == encodeArgs(byte(0xef))); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(byte(0x05))); + BOOST_CHECK(callContractFunction("get(uint8)", byte(0xa7)) == encodeArgs(byte(0x00))); } BOOST_AUTO_TEST_CASE(mapping_state) @@ -611,38 +611,38 @@ BOOST_AUTO_TEST_CASE(mapping_state) auto getVoteCount = bind(&Ballot::getVoteCount, &ballot, _1); auto grantVoteRight = bind(&Ballot::grantVoteRight, &ballot, _1); auto vote = bind(&Ballot::vote, &ballot, _1, _2); - testSolidityAgainstCpp(0, getVoteCount, u160(0)); - testSolidityAgainstCpp(0, getVoteCount, u160(1)); - testSolidityAgainstCpp(0, getVoteCount, u160(2)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); // voting without vote right shourd be rejected - testSolidityAgainstCpp(2, vote, u160(0), u160(2)); - testSolidityAgainstCpp(0, getVoteCount, u160(0)); - testSolidityAgainstCpp(0, getVoteCount, u160(1)); - testSolidityAgainstCpp(0, getVoteCount, u160(2)); + testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); // grant vote rights - testSolidityAgainstCpp(1, grantVoteRight, u160(0)); - testSolidityAgainstCpp(1, grantVoteRight, u160(1)); + testSolidityAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(0)); + testSolidityAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(1)); // vote, should increase 2's vote count - testSolidityAgainstCpp(2, vote, u160(0), u160(2)); - testSolidityAgainstCpp(0, getVoteCount, u160(0)); - testSolidityAgainstCpp(0, getVoteCount, u160(1)); - testSolidityAgainstCpp(0, getVoteCount, u160(2)); + testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); // vote again, should be rejected - testSolidityAgainstCpp(2, vote, u160(0), u160(1)); - testSolidityAgainstCpp(0, getVoteCount, u160(0)); - testSolidityAgainstCpp(0, getVoteCount, u160(1)); - testSolidityAgainstCpp(0, getVoteCount, u160(2)); + testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(1)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); // vote without right to vote - testSolidityAgainstCpp(2, vote, u160(2), u160(1)); - testSolidityAgainstCpp(0, getVoteCount, u160(0)); - testSolidityAgainstCpp(0, getVoteCount, u160(1)); - testSolidityAgainstCpp(0, getVoteCount, u160(2)); + testSolidityAgainstCpp("vote(address,address)", vote, u160(2), u160(1)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); // grant vote right and now vote again - testSolidityAgainstCpp(1, grantVoteRight, u160(2)); - testSolidityAgainstCpp(2, vote, u160(2), u160(1)); - testSolidityAgainstCpp(0, getVoteCount, u160(0)); - testSolidityAgainstCpp(0, getVoteCount, u160(1)); - testSolidityAgainstCpp(0, getVoteCount, u160(2)); + testSolidityAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(2)); + testSolidityAgainstCpp("vote(address,address)", vote, u160(2), u160(1)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); + testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); } BOOST_AUTO_TEST_CASE(mapping_state_inc_dec) @@ -673,7 +673,7 @@ BOOST_AUTO_TEST_CASE(mapping_state_inc_dec) table[value]++; return --table[value++]; }; - testSolidityAgainstCppOnRange(0, f, 0, 5); + testSolidityAgainstCppOnRange("f(uint256)", f, 0, 5); } BOOST_AUTO_TEST_CASE(multi_level_mapping) @@ -693,14 +693,14 @@ BOOST_AUTO_TEST_CASE(multi_level_mapping) if (_z == 0) return table[_x][_y]; else return table[_x][_y] = _z; }; - testSolidityAgainstCpp(0, f, u256(4), u256(5), u256(0)); - testSolidityAgainstCpp(0, f, u256(5), u256(4), u256(0)); - testSolidityAgainstCpp(0, f, u256(4), u256(5), u256(9)); - testSolidityAgainstCpp(0, f, u256(4), u256(5), u256(0)); - testSolidityAgainstCpp(0, f, u256(5), u256(4), u256(0)); - testSolidityAgainstCpp(0, f, u256(5), u256(4), u256(7)); - testSolidityAgainstCpp(0, f, u256(4), u256(5), u256(0)); - testSolidityAgainstCpp(0, f, u256(5), u256(4), u256(0)); + testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(0)); + testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(0)); + testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(9)); + testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(0)); + testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(0)); + testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(7)); + testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(0)); + testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(0)); } BOOST_AUTO_TEST_CASE(structs) @@ -735,9 +735,9 @@ BOOST_AUTO_TEST_CASE(structs) " }\n" "}\n"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction(0) == bytes(1, 0x00)); - BOOST_CHECK(callContractFunction(1) == bytes()); - BOOST_CHECK(callContractFunction(0) == bytes(1, 0x01)); + BOOST_CHECK(callContractFunction("check()") == encodeArgs(false)); + BOOST_CHECK(callContractFunction("set()") == bytes()); + BOOST_CHECK(callContractFunction("check()") == encodeArgs(true)); } BOOST_AUTO_TEST_CASE(struct_reference) @@ -763,9 +763,9 @@ BOOST_AUTO_TEST_CASE(struct_reference) " }\n" "}\n"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction(0) == bytes(1, 0x00)); - BOOST_CHECK(callContractFunction(1) == bytes()); - BOOST_CHECK(callContractFunction(0) == bytes(1, 0x01)); + BOOST_CHECK(callContractFunction("check()") == encodeArgs(false)); + BOOST_CHECK(callContractFunction("set()") == bytes()); + BOOST_CHECK(callContractFunction("check()") == encodeArgs(true)); } BOOST_AUTO_TEST_CASE(constructor) @@ -786,8 +786,8 @@ BOOST_AUTO_TEST_CASE(constructor) { return data[_x]; }; - testSolidityAgainstCpp(0, get, u256(6)); - testSolidityAgainstCpp(0, get, u256(7)); + testSolidityAgainstCpp("get(uint256)", get, u256(6)); + testSolidityAgainstCpp("get(uint256)", get, u256(7)); } BOOST_AUTO_TEST_CASE(balance) @@ -798,7 +798,7 @@ BOOST_AUTO_TEST_CASE(balance) " }\n" "}\n"; compileAndRun(sourceCode, 23); - BOOST_CHECK(callContractFunction(0) == toBigEndian(u256(23))); + BOOST_CHECK(callContractFunction("getBalance()") == encodeArgs(23)); } BOOST_AUTO_TEST_CASE(blockchain) @@ -811,7 +811,7 @@ BOOST_AUTO_TEST_CASE(blockchain) " }\n" "}\n"; compileAndRun(sourceCode, 27); - BOOST_CHECK(callContractFunction(0, bytes{0}, u256(28)) == toBigEndian(u256(28)) + bytes(20, 0) + toBigEndian(u256(1))); + BOOST_CHECK(callContractFunctionWithValue("someInfo()", 28) == encodeArgs(28, 0, 1)); } BOOST_AUTO_TEST_CASE(function_types) @@ -830,10 +830,25 @@ BOOST_AUTO_TEST_CASE(function_types) " }\n" "}\n"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction(0, bytes{0}) == toBigEndian(u256(11))); - BOOST_CHECK(callContractFunction(0, bytes{1}) == toBigEndian(u256(12))); + BOOST_CHECK(callContractFunction("a(bool)", false) == encodeArgs(11)); + BOOST_CHECK(callContractFunction("a(bool)", true) == encodeArgs(12)); } +BOOST_AUTO_TEST_CASE(type_conversions_cleanup) +{ + // 22-byte integer converted to a contract (i.e. address, 20 bytes), converted to a 32 byte + // integer should drop the first two bytes + char const* sourceCode = R"( + contract Test { + function test() returns (uint ret) { return uint(address(Test(address(0x11223344556677889900112233445566778899001122)))); } + })"; + compileAndRun(sourceCode); + BOOST_REQUIRE(callContractFunction("test()") == bytes({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, + 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22})); +} + + BOOST_AUTO_TEST_CASE(send_ether) { char const* sourceCode = "contract test {\n" @@ -845,10 +860,107 @@ BOOST_AUTO_TEST_CASE(send_ether) u256 amount(130); compileAndRun(sourceCode, amount + 1); u160 address(23); - BOOST_CHECK(callContractFunction(0, address, amount) == toBigEndian(u256(1))); + BOOST_CHECK(callContractFunction("a(address,uint256)", address, amount) == encodeArgs(1)); BOOST_CHECK_EQUAL(m_state.balance(address), amount); } +BOOST_AUTO_TEST_CASE(log0) +{ + char const* sourceCode = "contract test {\n" + " function a() {\n" + " log0(1);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + callContractFunction("a()"); + BOOST_CHECK_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1))); + BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 0); +} + +BOOST_AUTO_TEST_CASE(log1) +{ + char const* sourceCode = "contract test {\n" + " function a() {\n" + " log1(1, 2);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + callContractFunction("a()"); + BOOST_CHECK_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1))); + BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], h256(u256(2))); +} + +BOOST_AUTO_TEST_CASE(log2) +{ + char const* sourceCode = "contract test {\n" + " function a() {\n" + " log2(1, 2, 3);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + callContractFunction("a()"); + BOOST_CHECK_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1))); + BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 2); + for (unsigned i = 0; i < 2; ++i) + BOOST_CHECK_EQUAL(m_logs[0].topics[i], h256(u256(i + 2))); +} + +BOOST_AUTO_TEST_CASE(log3) +{ + char const* sourceCode = "contract test {\n" + " function a() {\n" + " log3(1, 2, 3, 4);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + callContractFunction("a()"); + BOOST_CHECK_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1))); + BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 3); + for (unsigned i = 0; i < 3; ++i) + BOOST_CHECK_EQUAL(m_logs[0].topics[i], h256(u256(i + 2))); +} + +BOOST_AUTO_TEST_CASE(log4) +{ + char const* sourceCode = "contract test {\n" + " function a() {\n" + " log4(1, 2, 3, 4, 5);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + callContractFunction("a()"); + BOOST_CHECK_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1))); + BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 4); + for (unsigned i = 0; i < 4; ++i) + BOOST_CHECK_EQUAL(m_logs[0].topics[i], h256(u256(i + 2))); +} + +BOOST_AUTO_TEST_CASE(log_in_constructor) +{ + char const* sourceCode = "contract test {\n" + " function test() {\n" + " log1(1, 2);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + BOOST_CHECK_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1))); + BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], h256(u256(2))); +} + BOOST_AUTO_TEST_CASE(suicide) { char const* sourceCode = "contract test {\n" @@ -860,7 +972,7 @@ BOOST_AUTO_TEST_CASE(suicide) u256 amount(130); compileAndRun(sourceCode, amount); u160 address(23); - BOOST_CHECK(callContractFunction(0, address) == bytes()); + BOOST_CHECK(callContractFunction("a(address)", address) == bytes()); BOOST_CHECK(!m_state.addressHasCode(m_contractAddress)); BOOST_CHECK_EQUAL(m_state.balance(address), amount); } @@ -877,9 +989,9 @@ BOOST_AUTO_TEST_CASE(sha3) { return dev::sha3(toBigEndian(_x)); }; - testSolidityAgainstCpp(0, f, u256(4)); - testSolidityAgainstCpp(0, f, u256(5)); - testSolidityAgainstCpp(0, f, u256(-1)); + testSolidityAgainstCpp("a(hash256)", f, u256(4)); + testSolidityAgainstCpp("a(hash256)", f, u256(5)); + testSolidityAgainstCpp("a(hash256)", f, u256(-1)); } BOOST_AUTO_TEST_CASE(sha256) @@ -896,9 +1008,9 @@ BOOST_AUTO_TEST_CASE(sha256) dev::sha256(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32)); return ret; }; - testSolidityAgainstCpp(0, f, u256(4)); - testSolidityAgainstCpp(0, f, u256(5)); - testSolidityAgainstCpp(0, f, u256(-1)); + testSolidityAgainstCpp("a(hash256)", f, u256(4)); + testSolidityAgainstCpp("a(hash256)", f, u256(5)); + testSolidityAgainstCpp("a(hash256)", f, u256(-1)); } BOOST_AUTO_TEST_CASE(ripemd) @@ -915,9 +1027,9 @@ BOOST_AUTO_TEST_CASE(ripemd) dev::ripemd160(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32)); return u256(ret) >> (256 - 160); }; - testSolidityAgainstCpp(0, f, u256(4)); - testSolidityAgainstCpp(0, f, u256(5)); - testSolidityAgainstCpp(0, f, u256(-1)); + testSolidityAgainstCpp("a(hash256)", f, u256(4)); + testSolidityAgainstCpp("a(hash256)", f, u256(5)); + testSolidityAgainstCpp("a(hash256)", f, u256(-1)); } BOOST_AUTO_TEST_CASE(ecrecover) @@ -933,7 +1045,7 @@ BOOST_AUTO_TEST_CASE(ecrecover) u256 r("0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f"); u256 s("0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"); u160 addr("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"); - BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr)); + BOOST_CHECK(callContractFunction("a(hash256,uint8,hash256,hash256)", h, v, r, s) == encodeArgs(addr)); } BOOST_AUTO_TEST_CASE(inter_contract_calls) @@ -957,13 +1069,13 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls) } })"; compileAndRun(sourceCode, 0, "Helper"); - u160 const helperAddress = m_contractAddress; + u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); - BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); + BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); u256 a(3456789); u256 b("0x282837623374623234aa74"); - BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b)); + BOOST_REQUIRE(callContractFunction("callHelper(uint256,uint256)", a, b) == encodeArgs(a * b)); } BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters) @@ -987,14 +1099,14 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters) } })"; compileAndRun(sourceCode, 0, "Helper"); - u160 const helperAddress = m_contractAddress; + u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); - BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); + BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); u256 a(3456789); u256 b("0x282837623374623234aa74"); - BOOST_REQUIRE(callContractFunction(0, a, true, b) == toBigEndian(a * 3)); - BOOST_REQUIRE(callContractFunction(0, a, false, b) == toBigEndian(b * 3)); + BOOST_REQUIRE(callContractFunction("callHelper(uint256,bool,uint256)", a, true, b) == encodeArgs(a * 3)); + BOOST_REQUIRE(callContractFunction("callHelper(uint256,bool,uint256)", a, false, b) == encodeArgs(b * 3)); } BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this) @@ -1018,11 +1130,11 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this) } })"; compileAndRun(sourceCode, 0, "Helper"); - u160 const helperAddress = m_contractAddress; + u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); - BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); - BOOST_REQUIRE(callContractFunction(0) == toBigEndian(helperAddress)); + BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); + BOOST_REQUIRE(callContractFunction("callHelper()") == encodeArgs(c_helperAddress)); } BOOST_AUTO_TEST_CASE(calls_to_this) @@ -1049,13 +1161,13 @@ BOOST_AUTO_TEST_CASE(calls_to_this) } })"; compileAndRun(sourceCode, 0, "Helper"); - u160 const helperAddress = m_contractAddress; + u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); - BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); + BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); u256 a(3456789); u256 b("0x282837623374623234aa74"); - BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 10)); + BOOST_REQUIRE(callContractFunction("callHelper(uint256,uint256)", a, b) == encodeArgs(a * b + 10)); } BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars) @@ -1084,13 +1196,13 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars) } })"; compileAndRun(sourceCode, 0, "Helper"); - u160 const helperAddress = m_contractAddress; + u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); - BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); + BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); u256 a(3456789); u256 b("0x282837623374623234aa74"); - BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 9)); + BOOST_REQUIRE(callContractFunction("callHelper(uint256,uint256)", a, b) == encodeArgs(a * b + 9)); } BOOST_AUTO_TEST_CASE(strings_in_calls) @@ -1114,11 +1226,11 @@ BOOST_AUTO_TEST_CASE(strings_in_calls) } })"; compileAndRun(sourceCode, 0, "Helper"); - u160 const helperAddress = m_contractAddress; + u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); - BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); - BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0})); + BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); + BOOST_CHECK(callContractFunction("callHelper(string2,bool)", string("\0a", 2), true) == encodeArgs(string("\0a\0\0\0", 5))); } BOOST_AUTO_TEST_CASE(constructor_arguments) @@ -1143,8 +1255,8 @@ BOOST_AUTO_TEST_CASE(constructor_arguments) function getName() returns (string3 ret) { return h.getName(); } })"; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction(0) == bytes({byte(0x01)})); - BOOST_REQUIRE(callContractFunction(1) == bytes({'a', 'b', 'c'})); + BOOST_REQUIRE(callContractFunction("getFlag()") == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc")); } BOOST_AUTO_TEST_CASE(functions_called_by_constructor) @@ -1161,7 +1273,25 @@ BOOST_AUTO_TEST_CASE(functions_called_by_constructor) function setName(string3 _name) { name = _name; } })"; compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction(0) == bytes({'a', 'b', 'c'})); + BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc")); +} + +BOOST_AUTO_TEST_CASE(contracts_as_addresses) +{ + char const* sourceCode = R"( + contract helper { + } + contract test { + helper h; + function test() { h = new helper(); h.send(5); } + function getBalance() returns (uint256 myBalance, uint256 helperBalance) { + myBalance = this.balance; + helperBalance = h.balance; + } + } + )"; + compileAndRun(sourceCode, 20); + BOOST_REQUIRE(callContractFunction("getBalance()") == toBigEndian(u256(20 - 5)) + toBigEndian(u256(5))); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/SolidityExpressionCompiler.cpp b/test/SolidityExpressionCompiler.cpp index 9c375418e..579af5bb9 100644 --- a/test/SolidityExpressionCompiler.cpp +++ b/test/SolidityExpressionCompiler.cpp @@ -174,8 +174,8 @@ BOOST_AUTO_TEST_CASE(comparison) bytes code = compileFirstExpression(sourceCode); bytes expectation({byte(eth::Instruction::PUSH1), 0x1, - byte(eth::Instruction::PUSH2), 0x11, 0xaa, byte(eth::Instruction::PUSH2), 0xff, 0xff, byte(eth::Instruction::AND), - byte(eth::Instruction::PUSH2), 0x10, 0xaa, byte(eth::Instruction::PUSH2), 0xff, 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::PUSH2), 0x11, 0xaa, + byte(eth::Instruction::PUSH2), 0x10, 0xaa, byte(eth::Instruction::LT), byte(eth::Instruction::EQ), byte(eth::Instruction::ISZERO)}); @@ -189,20 +189,18 @@ BOOST_AUTO_TEST_CASE(short_circuiting) "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(eth::Instruction::PUSH1), 0xa, - byte(eth::Instruction::PUSH1), 0x8, - byte(eth::Instruction::ADD), byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), - byte(eth::Instruction::PUSH1), 0x4, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + bytes expectation({byte(eth::Instruction::PUSH1), 0x12, // 8 + 10 + byte(eth::Instruction::PUSH1), 0x4, byte(eth::Instruction::GT), - byte(eth::Instruction::ISZERO), // after this we have 10 + 8 >= 4 + byte(eth::Instruction::ISZERO), // after this we have 4 <= 8 + 10 byte(eth::Instruction::DUP1), - byte(eth::Instruction::PUSH1), 0x20, + byte(eth::Instruction::PUSH1), 0x11, byte(eth::Instruction::JUMPI), // short-circuit if it is true byte(eth::Instruction::POP), - byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), - byte(eth::Instruction::PUSH1), 0x9, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::PUSH1), 0x2, + byte(eth::Instruction::PUSH1), 0x9, byte(eth::Instruction::EQ), - byte(eth::Instruction::ISZERO), // after this we have 2 != 9 + byte(eth::Instruction::ISZERO), // after this we have 9 != 2 byte(eth::Instruction::JUMPDEST), byte(eth::Instruction::PUSH1), 0x1, byte(eth::Instruction::EQ), @@ -213,28 +211,24 @@ BOOST_AUTO_TEST_CASE(short_circuiting) BOOST_AUTO_TEST_CASE(arithmetics) { char const* sourceCode = "contract test {\n" - " function f() { var x = ((((((((9 ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }" + " function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }" "}\n"; - bytes code = compileFirstExpression(sourceCode); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); bytes expectation({byte(eth::Instruction::PUSH1), 0x1, byte(eth::Instruction::PUSH1), 0x2, - byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::PUSH1), 0x3, - byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::PUSH1), 0x4, byte(eth::Instruction::PUSH1), 0x5, byte(eth::Instruction::PUSH1), 0x6, byte(eth::Instruction::PUSH1), 0x7, byte(eth::Instruction::PUSH1), 0x8, - byte(eth::Instruction::PUSH1), 0x9, + byte(eth::Instruction::DUP10), byte(eth::Instruction::XOR), byte(eth::Instruction::AND), byte(eth::Instruction::OR), byte(eth::Instruction::SUB), byte(eth::Instruction::ADD), - byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::MOD), - byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::DIV), byte(eth::Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); @@ -243,15 +237,15 @@ BOOST_AUTO_TEST_CASE(arithmetics) BOOST_AUTO_TEST_CASE(unary_operators) { char const* sourceCode = "contract test {\n" - " function f() { var x = !(~+- 1 == 2); }" + " function f(int y) { var x = !(~+- y == 2); }" "}\n"; - bytes code = compileFirstExpression(sourceCode); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); - bytes expectation({byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), - byte(eth::Instruction::PUSH1), 0x1, + bytes expectation({byte(eth::Instruction::PUSH1), 0x2, + byte(eth::Instruction::DUP3), byte(eth::Instruction::PUSH1), 0x0, byte(eth::Instruction::SUB), - byte(eth::Instruction::NOT), byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::NOT), byte(eth::Instruction::EQ), byte(eth::Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); @@ -315,7 +309,7 @@ BOOST_AUTO_TEST_CASE(assignment) bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}}); // Stack: a, b - bytes expectation({byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + bytes expectation({byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::DUP2), byte(eth::Instruction::DUP4), byte(eth::Instruction::ADD), @@ -338,14 +332,14 @@ BOOST_AUTO_TEST_CASE(function_call) {{"test", "f", "a"}, {"test", "f", "b"}}); // Stack: a, b - bytes expectation({byte(eth::Instruction::PUSH1), 0x02, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), - byte(eth::Instruction::PUSH1), 0x12, - byte(eth::Instruction::PUSH1), 0x01, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + bytes expectation({byte(eth::Instruction::PUSH1), 0x02, + byte(eth::Instruction::PUSH1), 0x0c, + byte(eth::Instruction::PUSH1), 0x01, byte(eth::Instruction::DUP5), byte(eth::Instruction::ADD), // Stack here: a b 2 (a+1) byte(eth::Instruction::DUP4), - byte(eth::Instruction::PUSH1), 0x19, + byte(eth::Instruction::PUSH1), 0x13, byte(eth::Instruction::JUMP), byte(eth::Instruction::JUMPDEST), // Stack here: a b 2 g(a+1, b) @@ -363,40 +357,36 @@ BOOST_AUTO_TEST_CASE(function_call) BOOST_AUTO_TEST_CASE(negative_literals_8bits) { - // these all fit in 8 bits char const* sourceCode = "contract test {\n" - " function f() { int8 x = -0 + -1 + -0x01 + -127 + -128; }\n" + " function f() { int8 x = -0x80; }\n" "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80) + - bytes({byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x81) + - bytes({byte(eth::Instruction::PUSH32)}) + bytes(32, 0xff) + - bytes({byte(eth::Instruction::PUSH32)}) + bytes(32, 0xff) + - bytes({byte(eth::Instruction::PUSH1), 0x00, - byte(eth::Instruction::ADD), - byte(eth::Instruction::ADD), - byte(eth::Instruction::ADD), - byte(eth::Instruction::ADD)})); + bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80)); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } BOOST_AUTO_TEST_CASE(negative_literals_16bits) { - // -1 should need 8 bits, -129 should need 16 bits, how many bits are used is visible - // from the SIGNEXTEND opcodes char const* sourceCode = "contract test {\n" - " function f() { int64 x = int64(-1 + -129); }\n" + " function f() { int64 x = ~0xabc; }\n" + "}\n"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(30, 0xff) + bytes{0xf5, 0x43}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + +BOOST_AUTO_TEST_CASE(intermediately_overflowing_literals) +{ + // first literal itself is too large for 256 bits but it fits after all constant operations + // have been applied + char const* sourceCode = "contract test {\n" + " function f() { var x = (0xffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; }\n" "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x7f) + - bytes({byte(eth::Instruction::PUSH32)}) + bytes(32, 0xff) + - bytes({byte(eth::Instruction::PUSH1), 0x00, - byte(eth::Instruction::SIGNEXTEND), - byte(eth::Instruction::ADD), - byte(eth::Instruction::PUSH1), 0x01, - byte(eth::Instruction::SIGNEXTEND)})); + bytes expectation(bytes({byte(eth::Instruction::PUSH1), 0xbf})); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } diff --git a/test/SolidityNatspecJSON.cpp b/test/SolidityNatspecJSON.cpp index 5b48a67ce..7edb97a7b 100644 --- a/test/SolidityNatspecJSON.cpp +++ b/test/SolidityNatspecJSON.cpp @@ -56,9 +56,9 @@ public: } if (_userDocumentation) - generatedDocumentationString = m_compilerStack.getJsonDocumentation("", DocumentationType::NATSPEC_USER); + generatedDocumentationString = m_compilerStack.getMetadata("", DocumentationType::NATSPEC_USER); else - generatedDocumentationString = m_compilerStack.getJsonDocumentation("", DocumentationType::NATSPEC_DEV); + generatedDocumentationString = m_compilerStack.getMetadata("", DocumentationType::NATSPEC_DEV); Json::Value generatedDocumentation; m_reader.parse(generatedDocumentationString, generatedDocumentation); Json::Value expectedDocumentation; @@ -510,6 +510,19 @@ BOOST_AUTO_TEST_CASE(dev_title_at_function_error) BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError); } +// test for bug where having no tags in docstring would cause infinite loop +BOOST_AUTO_TEST_CASE(natspec_no_tags) +{ + char const* sourceCode = "contract test {\n" + " /// I do something awesome\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + char const* natspec = "{\"methods\": {}}"; + + checkNatspec(sourceCode, natspec, false); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index 7e06c2e30..41ec1f90c 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -55,12 +55,12 @@ public: } template - void compareVersions(byte _index, Args const&... _arguments) + void compareVersions(std::string _sig, Args const&... _arguments) { m_contractAddress = m_nonOptimizedContract; - bytes nonOptimizedOutput = callContractFunction(_index, _arguments...); + bytes nonOptimizedOutput = callContractFunction(_sig, _arguments...); m_contractAddress = m_optimizedContract; - bytes optimizedOutput = callContractFunction(_index, _arguments...); + bytes optimizedOutput = callContractFunction(_sig, _arguments...); BOOST_CHECK_MESSAGE(nonOptimizedOutput == optimizedOutput, "Computed values do not match." "\nNon-Optimized: " + toHex(nonOptimizedOutput) + "\nOptimized: " + toHex(optimizedOutput)); @@ -81,8 +81,8 @@ BOOST_AUTO_TEST_CASE(smoke_test) return a; } })"; - compileBothVersions(4, sourceCode); - compareVersions(0, u256(7)); + compileBothVersions(29, sourceCode); + compareVersions("f(uint256)", u256(7)); } BOOST_AUTO_TEST_CASE(large_integers) @@ -94,8 +94,8 @@ BOOST_AUTO_TEST_CASE(large_integers) b = 0x10000000000000000000000002; } })"; - compileBothVersions(33, sourceCode); - compareVersions(0); + compileBothVersions(36, sourceCode); + compareVersions("f()"); } BOOST_AUTO_TEST_CASE(invariants) @@ -106,8 +106,8 @@ BOOST_AUTO_TEST_CASE(invariants) return int(0) | (int(1) * (int(0) ^ (0 + a))); } })"; - compileBothVersions(28, sourceCode); - compareVersions(0, u256(0x12334664)); + compileBothVersions(41, sourceCode); + compareVersions("f(uint256)", u256(0x12334664)); } BOOST_AUTO_TEST_CASE(unused_expressions) @@ -120,8 +120,8 @@ BOOST_AUTO_TEST_CASE(unused_expressions) data; } })"; - compileBothVersions(11, sourceCode); - compareVersions(0); + compileBothVersions(33, sourceCode); + compareVersions("f()"); } BOOST_AUTO_TEST_CASE(constant_folding_both_sides) @@ -135,8 +135,8 @@ BOOST_AUTO_TEST_CASE(constant_folding_both_sides) return 98 ^ (7 * ((1 | (x | 1000)) * 40) ^ 102); } })"; - compileBothVersions(31, sourceCode); - compareVersions(0); + compileBothVersions(37, sourceCode); + compareVersions("f(uint256)"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/SolidityScanner.cpp b/test/SolidityScanner.cpp index b7942d293..7dc9ef482 100644 --- a/test/SolidityScanner.cpp +++ b/test/SolidityScanner.cpp @@ -103,14 +103,17 @@ BOOST_AUTO_TEST_CASE(negative_numbers) BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::VAR); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN); + BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "-.2"); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ".2"); BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); + BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "-0x78"); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "0x78"); BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); + BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "-7.3"); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "7.3"); BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "8.9"); @@ -130,8 +133,9 @@ BOOST_AUTO_TEST_CASE(locations) BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 24); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 25); + BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 26); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 27); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 32); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 45); diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index ea848c7ce..ff330d60e 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -489,4 +489,12 @@ void processCommandLineOptions() } } +LastHashes lastHashes(u256 _currentBlockNumber) +{ + LastHashes ret; + for (u256 i = 1; i <= 256 && i <= _currentBlockNumber; ++i) + ret.push_back(sha3(toString(_currentBlockNumber - i))); + return ret; +} + } } // namespaces diff --git a/test/TestHelper.h b/test/TestHelper.h index 20328c913..85017c842 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -77,6 +77,7 @@ void executeTests(const std::string& _name, const std::string& _testPathAppendix std::string getTestPath(); void userDefinedTest(std::string testTypeFlag, std::function doTests); void processCommandLineOptions(); +eth::LastHashes lastHashes(u256 _currentBlockNumber); template void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) diff --git a/test/jsonrpc.cpp b/test/jsonrpc.cpp index eed54ed8f..42b1a5ebb 100644 --- a/test/jsonrpc.cpp +++ b/test/jsonrpc.cpp @@ -74,6 +74,12 @@ struct Setup } }; +string fromAscii(string _s) +{ + bytes b = asBytes(_s); + return "0x" + toHex(b); +} + BOOST_FIXTURE_TEST_SUITE(environment, Setup) BOOST_AUTO_TEST_CASE(jsonrpc_defaultBlock) @@ -312,6 +318,18 @@ BOOST_AUTO_TEST_CASE(contract_storage) } } +BOOST_AUTO_TEST_CASE(sha3) +{ + cnote << "Testing jsonrpc sha3..."; + string testString = "multiply(uint256)"; + h256 expected = dev::sha3(testString); + + auto hexValue = fromAscii(testString); + string result = jsonrpcClient->web3_sha3(hexValue); + BOOST_CHECK_EQUAL(toJS(expected), result); + BOOST_CHECK_EQUAL("0xc6888fa159d67f77c2f3d7a402e199802766bd7e8d4d1ecd2274fc920265d56a", result); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/test/solidityExecutionFramework.h b/test/solidityExecutionFramework.h index 9f25b3725..8d3c7e77c 100644 --- a/test/solidityExecutionFramework.h +++ b/test/solidityExecutionFramework.h @@ -32,9 +32,6 @@ namespace dev { -/// Provides additional overloads for toBigEndian to encode arguments and return values. -inline bytes toBigEndian(byte _value) { return bytes({_value}); } -inline bytes toBigEndian(bool _value) { return bytes({byte(_value)}); } namespace solidity { @@ -56,50 +53,68 @@ public: return m_output; } - bytes const& callContractFunction(byte _index, bytes const& _data = bytes(), u256 const& _value = 0) + template + bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, + Args const&... _arguments) { - sendMessage(bytes(1, _index) + _data, false, _value); + + FixedHash<4> hash(dev::sha3(_sig)); + sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); return m_output; } template - bytes const& callContractFunction(byte _index, Args const&... _arguments) + bytes const& callContractFunction(std::string _sig, Args const&... _arguments) { - return callContractFunction(_index, argsToBigEndian(_arguments...)); + return callContractFunctionWithValue(_sig, 0, _arguments...); } template - void testSolidityAgainstCpp(byte _index, CppFunction const& _cppFunction, Args const&... _arguments) + void testSolidityAgainstCpp(std::string _sig, CppFunction const& _cppFunction, Args const&... _arguments) { - bytes solidityResult = callContractFunction(_index, _arguments...); + bytes solidityResult = callContractFunction(_sig, _arguments...); bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...); BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match." "\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult)); } template - void testSolidityAgainstCppOnRange(byte _index, CppFunction const& _cppFunction, + void testSolidityAgainstCppOnRange(std::string _sig, CppFunction const& _cppFunction, u256 const& _rangeStart, u256 const& _rangeEnd) { for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument) { - bytes solidityResult = callContractFunction(_index, argument); + bytes solidityResult = callContractFunction(_sig, argument); bytes cppResult = callCppAndEncodeResult(_cppFunction, argument); BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match." "\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult) + - "\nArgument: " + toHex(toBigEndian(argument))); + "\nArgument: " + toHex(encode(argument))); } } -private: - template - bytes argsToBigEndian(FirstArg const& _firstArg, Args const&... _followingArgs) const + static bytes encode(bool _value) { return encode(byte(_value)); } + static bytes encode(int _value) { return encode(u256(_value)); } + static bytes encode(char const* _value) { return encode(std::string(_value)); } + static bytes encode(byte _value) { return bytes(31, 0) + bytes{_value}; } + static bytes encode(u256 const& _value) { return toBigEndian(_value); } + static bytes encode(bytes const& _value, bool _padLeft = true) { - return toBigEndian(_firstArg) + argsToBigEndian(_followingArgs...); + bytes padding = bytes((32 - _value.size() % 32) % 32, 0); + return _padLeft ? padding + _value : _value + padding; } + static bytes encode(std::string const& _value) { return encode(asBytes(_value), false); } - bytes argsToBigEndian() const { return bytes(); } + template + static bytes encodeArgs(FirstArg const& _firstArg, Args const&... _followingArgs) + { + return encode(_firstArg) + encodeArgs(_followingArgs...); + } + static bytes encodeArgs() + { + return bytes(); + } +private: template auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments) -> typename std::enable_if::value, bytes>::type @@ -111,7 +126,7 @@ private: auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments) -> typename std::enable_if::value, bytes>::type { - return toBigEndian(_cppFunction(_arguments...)); + return encode(_cppFunction(_arguments...)); } void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0) @@ -143,6 +158,7 @@ private: m_state.noteSending(m_sender); executive.finalize(); m_output = executive.out().toVector(); + m_logs = executive.logs(); } protected: @@ -153,6 +169,7 @@ protected: u256 const m_gasPrice = 100 * eth::szabo; u256 const m_gas = 1000000; bytes m_output; + eth::LogEntries m_logs; }; } diff --git a/test/stSystemOperationsTestFiller.json b/test/stSystemOperationsTestFiller.json index 12f10fde5..253ba0a8a 100644 --- a/test/stSystemOperationsTestFiller.json +++ b/test/stSystemOperationsTestFiller.json @@ -529,12 +529,53 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (MSTORE 32 0xaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa ) [[ 0 ]] (CALL 1000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 64 0 2) }", + "code" : "{ [[ 0 ]] (CALL 1000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 0 31 1) [[ 1 ]] @0 }", "storage": {} }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x6001600155603760005360026000f3", + "code" : "0x6001600155602a601f536001601ff3", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "CallToReturn1ForDynamicJump0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6001601f60006000601773945304eb96065b2a98b57a48a06ae28d285a71b56103e8f1600055600051565b6023602355", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "0x6001600155602a601f536001601ff3", "nonce" : "0", "storage" : { } @@ -545,7 +586,47 @@ "code" : "", "storage": {} } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "CallToReturn1ForDynamicJump1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6001601f60006000601773945304eb96065b2a98b57a48a06ae28d285a71b56103e8f160005560005156605b6023602355", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "0x6001600155602b601f536001601ff3", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } }, "transaction" : { "nonce" : "0", diff --git a/test/state.cpp b/test/state.cpp index db8350e42..6da18e708 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -39,16 +39,6 @@ using namespace dev::eth; namespace dev { namespace test { -LastHashes lastHashes(u256 _currentBlockNumber) -{ - LastHashes ret; - for (u256 i = 1; i <= 256 && i <= _currentBlockNumber; ++i) - ret.push_back(sha3(toString(_currentBlockNumber - i))); - return ret; -} - - - void doStateTests(json_spirit::mValue& v, bool _fillin) { processCommandLineOptions(); diff --git a/test/vm.cpp b/test/vm.cpp index 18bf57977..6ae95f256 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -33,7 +33,7 @@ using namespace dev::eth; using namespace dev::test; FakeExtVM::FakeExtVM(eth::BlockInfo const& _previousBlock, eth::BlockInfo const& _currentBlock, unsigned _depth): /// TODO: XXX: remove the default argument & fix. - ExtVMFace(Address(), Address(), Address(), 0, 1, bytesConstRef(), bytes(), _previousBlock, _currentBlock, LastHashes(), _depth) {} + ExtVMFace(Address(), Address(), Address(), 0, 1, bytesConstRef(), bytes(), _previousBlock, _currentBlock, test::lastHashes(_currentBlock.number), _depth) {} h160 FakeExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _init, OnOpFunc const&) { @@ -117,6 +117,7 @@ void FakeExtVM::importEnv(mObject& _o) previousBlock.hash = h256(_o["previousHash"].get_str()); currentBlock.number = toInt(_o["currentNumber"]); + lastHashes = test::lastHashes(currentBlock.number); currentBlock.gasLimit = toInt(_o["currentGasLimit"]); currentBlock.difficulty = toInt(_o["currentDifficulty"]); currentBlock.timestamp = toInt(_o["currentTimestamp"]); diff --git a/test/vmArithmeticTestFiller.json b/test/vmArithmeticTestFiller.json index fb1fb8ddd..12574f51c 100644 --- a/test/vmArithmeticTestFiller.json +++ b/test/vmArithmeticTestFiller.json @@ -814,6 +814,62 @@ } }, + "sdiv4": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SDIV 5 (- 0 4) ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sdiv5": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SDIV (- 0 57896044618658097711785492504343953926634992332820282019728792003956564819967) (- 0 1) ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "mod0": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/test/vmBlockInfoTestFiller.json b/test/vmBlockInfoTestFiller.json index 15c270173..898629477 100644 --- a/test/vmBlockInfoTestFiller.json +++ b/test/vmBlockInfoTestFiller.json @@ -111,6 +111,62 @@ } }, + "blockhashInRange": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "257", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BLOCKHASH 1) [[ 1 ]] (BLOCKHASH 2) [[ 2 ]] (BLOCKHASH 256) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "blockhashOutOfRange": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "257", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BLOCKHASH 0) [[ 1 ]] (BLOCKHASH 257) [[ 2 ]] (BLOCKHASH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "coinbase": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/test/vmIOandFlowOperationsTestFiller.json b/test/vmIOandFlowOperationsTestFiller.json index efbd0d683..a6c221716 100644 --- a/test/vmIOandFlowOperationsTestFiller.json +++ b/test/vmIOandFlowOperationsTestFiller.json @@ -475,6 +475,34 @@ } }, + "jumpdestBigList": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6009565b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "100000000" + } + }, + "jumpAfterStop": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -1202,6 +1230,34 @@ } }, + "DynamicJumpStartWithJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x5b586000555960115758600052596000575b58600055", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "DynamicJump0_foreverOutOfGas": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -1537,6 +1593,1194 @@ } }, + "BlockNumberDynamicJumpiAfterStop": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x600160084301570060015b6002600355", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJumpInsidePushWithoutJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x600543015661eeff", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJumpInsidePushWithJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6004430156655b6001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJumpifInsidePushWithoutJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6001600743015761eeff", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJumpifInsidePushWithJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60016006430157655b6001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJump0_foreverOutOfGas": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x5b600060000156", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DyanmicJump0_outOfBoundary": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x602360074301566001600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "BlockNumberDynamicJump0_jumpdest0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600743015660015b600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJump0_withoutJumpdest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x602360074301566001600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJump0_AfterJumpdest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600843015660015b600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJump0_jumpdest2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600a60085043015660015b600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJump0_AfterJumpdest3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600b60085043015660015b600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJump1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x620fffff620fffff01430156", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJumpi0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600160094301576001600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJumpi1_jumpdest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60236001600a43015760015b600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJumpi1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600060094301576001600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "BlockNumberDynamicJumpiOutsideBoundary": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04301576002600355", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "DynamicJumpJD_DependsOnJumps0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "1", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "//" : "PUSH1 3 JUMP", + "code" : "0x6009436006575b566001", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpJD_DependsOnJumps1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "1", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x600a436006575b5660015b6001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpPathologicalTest0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "4", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x435660615b4343025660615b60615b5b5b6001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpPathologicalTest1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "4", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x435660615b4343025660615b60615b605b6001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpPathologicalTest2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "4", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x435631615b60615b60615b606001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpPathologicalTest3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "7", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x435631615b60615b60615b606001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpPathologicalTest3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "7", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x435631615b60615b60615b606001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJumpiAfterStop": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6001600860005401570060015b6002600355", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJumpInsidePushWithoutJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6005600054015661eeff", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJumpInsidePushWithJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60046000540156655b6001600155", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJumpifInsidePushWithoutJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60016007600054015761eeff", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJumpifInsidePushWithJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x600160066000540157655b6001600155", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJump0_foreverOutOfGas": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x5b600060000156", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DyanmicJump0_outOfBoundary": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600760005401566001600255", + "storage" : { + "0x00" : "0x04" + } + + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "JDfromStorageDynamicJump0_jumpdest0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60236007600054015660015b600255", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJump0_withoutJumpdest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600760005401566001600255", + "storage" : { + "0x00" : "0x04" + } + + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJump0_AfterJumpdest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60236008600054015660015b600255", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJump0_jumpdest2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600a600850600054015660015b600255", + "storage" : { + "0x00" : "0x04" + } + + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJump0_AfterJumpdest3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600b600850600054015660015b600255", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJump1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x620fffff620fffff016000540156", + "storage" : { + "0x00" : "0x04" + } + + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJumpi0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60236001600960005401576001600255", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJumpi1_jumpdest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60236001600a600054015760015b600255", + "storage" : { + "0x00" : "0x04" + } + + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJumpi1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60236000600960005401576001600255", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "JDfromStorageDynamicJumpiOutsideBoundary": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "2", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff060005401576002600355", + "storage" : { + "0x00" : "0x04" + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "pc0": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/test/webthreestubclient.h b/test/webthreestubclient.h index 9b36c3644..6a82263d0 100644 --- a/test/webthreestubclient.h +++ b/test/webthreestubclient.h @@ -12,6 +12,16 @@ class WebThreeStubClient : public jsonrpc::Client public: WebThreeStubClient(jsonrpc::IClientConnector &conn) : jsonrpc::Client(conn) {} + std::string web3_sha3(const std::string& param1) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + Json::Value result = this->CallMethod("web3_sha3",p); + if (result.isString()) + return result.asString(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } std::string eth_coinbase() throw (jsonrpc::JsonRpcException) { Json::Value p; diff --git a/third/CMakeLists.txt b/third/CMakeLists.txt index c9c95cf33..4b83b4fa5 100644 --- a/third/CMakeLists.txt +++ b/third/CMakeLists.txt @@ -33,7 +33,7 @@ add_dependencies(${EXECUTABLE} BuildInfo.h) target_link_libraries(${EXECUTABLE} Qt5::Core) target_link_libraries(${EXECUTABLE} webthree) -target_link_libraries(${EXECUTABLE} qethereum) +target_link_libraries(${EXECUTABLE} qwebthree) target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) diff --git a/third/MainWin.cpp b/third/MainWin.cpp index 894f1af0f..2e967e407 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -182,14 +182,14 @@ void Main::onKeysChanged() installBalancesWatch(); } -unsigned Main::installWatch(dev::eth::LogFilter const& _tf, std::function const& _f) +unsigned Main::installWatch(dev::eth::LogFilter const& _tf, WatchHandler const& _f) { auto ret = ethereum()->installWatch(_tf); m_handlers[ret] = _f; return ret; } -unsigned Main::installWatch(dev::h256 _tf, std::function const& _f) +unsigned Main::installWatch(dev::h256 _tf, WatchHandler const& _f) { auto ret = ethereum()->installWatch(_tf); m_handlers[ret] = _f; @@ -198,21 +198,21 @@ unsigned Main::installWatch(dev::h256 _tf, std::function const& _f) void Main::installWatches() { - installWatch(dev::eth::LogFilter().topic((u256)(u160)c_config).topic((u256)0), [=](){ installNameRegWatch(); }); - installWatch(dev::eth::LogFilter().topic((u256)(u160)c_config).topic((u256)1), [=](){ installCurrenciesWatch(); }); - installWatch(dev::eth::ChainChangedFilter, [=](){ onNewBlock(); }); + installWatch(dev::eth::LogFilter().topic((u256)(u160)c_config).topic((u256)0), [=](LocalisedLogEntries const&){ installNameRegWatch(); }); + installWatch(dev::eth::LogFilter().topic((u256)(u160)c_config).topic((u256)1), [=](LocalisedLogEntries const&){ installCurrenciesWatch(); }); + installWatch(dev::eth::ChainChangedFilter, [=](LocalisedLogEntries const&){ onNewBlock(); }); } void Main::installNameRegWatch() { ethereum()->uninstallWatch(m_nameRegFilter); - m_nameRegFilter = installWatch(dev::eth::LogFilter().topic(ethereum()->stateAt(c_config, 0)), [=](){ onNameRegChange(); }); + m_nameRegFilter = installWatch(dev::eth::LogFilter().topic(ethereum()->stateAt(c_config, 0)), [=](LocalisedLogEntries const&){ onNameRegChange(); }); } void Main::installCurrenciesWatch() { ethereum()->uninstallWatch(m_currenciesFilter); - m_currenciesFilter = installWatch(dev::eth::LogFilter().topic(ethereum()->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); }); + m_currenciesFilter = installWatch(dev::eth::LogFilter().topic(ethereum()->stateAt(c_config, 1)), [=](LocalisedLogEntries const&){ onCurrenciesChange(); }); } void Main::installBalancesWatch() @@ -228,7 +228,7 @@ void Main::installBalancesWatch() tf.address(c).topic((u256)(u160)i.address()); ethereum()->uninstallWatch(m_balancesFilter); - m_balancesFilter = installWatch(tf, [=](){ onBalancesChange(); }); + m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); }); } void Main::onNameRegChange() @@ -536,8 +536,11 @@ void Main::timerEvent(QTimerEvent*) m_qweb->poll(); for (auto const& i: m_handlers) - if (ethereum()->checkWatch(i.first)) - i.second(); + { + auto ls = ethereum()->checkWatch(i.first); + if (ls.size()) + i.second(ls); + } } void Main::ourAccountsRowsMoved() diff --git a/third/MainWin.h b/third/MainWin.h index 478fb6fb6..0bd75d5de 100644 --- a/third/MainWin.h +++ b/third/MainWin.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include namespace Ui { class Main; @@ -50,6 +50,8 @@ class WhisperHost; class QQuickView; class WebThreeStubServer; +using WatchHandler = std::function; + class Main : public QMainWindow { Q_OBJECT @@ -95,8 +97,8 @@ private: void readSettings(bool _skipGeometry = false); void writeSettings(); - unsigned installWatch(dev::eth::LogFilter const& _tf, std::function const& _f); - unsigned installWatch(dev::h256 _tf, std::function const& _f); + unsigned installWatch(dev::eth::LogFilter const& _tf, WatchHandler const& _f); + unsigned installWatch(dev::h256 _tf, WatchHandler const& _f); void onNewBlock(); void onNameRegChange(); @@ -124,7 +126,7 @@ private: QList m_myKeys; QList m_myIdentities; - std::map> m_handlers; + std::map m_handlers; unsigned m_nameRegFilter = (unsigned)-1; unsigned m_currenciesFilter = (unsigned)-1; unsigned m_balancesFilter = (unsigned)-1;