diff --git a/CMakeLists.txt b/CMakeLists.txt index a2b31d174..9ba214cce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ option(GUI "Build GUI components (AlethZero, Mix)" ON) option(TESTS "Build the tests." ON) option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF) option(ETHASHCL "Build in support for GPU mining via OpenCL" OFF) +option(JSCONSOLE "Build in javascript console" OFF) # propagates CMake configuration options to the compiler function(configureProject) @@ -193,9 +194,14 @@ eth_format_option(GUI) eth_format_option(TESTS) eth_format_option(TOOLS) eth_format_option(ETHASHCL) +eth_format_option(JSCONSOLE) eth_format_option_on_decent_platform(SERPENT) eth_format_option_on_decent_platform(NCURSES) +if (JSCONSOLE) + set(JSONRPC ON) +endif() + if (GUI) set(JSONRPC ON) endif() @@ -284,6 +290,7 @@ message("-- GUI Build GUI components ${GUI}") message("-- NCURSES Build NCurses components ${NCURSES}") message("-- TESTS Build tests ${TESTS}") message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}") +message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}") message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}") message("------------------------------------------------------------------------") message("") @@ -328,6 +335,11 @@ if (JSONRPC) add_subdirectory(libweb3jsonrpc) endif() +if (JSCONSOLE) + add_subdirectory(libjsengine) + add_subdirectory(libjsconsole) +endif() + add_subdirectory(secp256k1) add_subdirectory(libp2p) add_subdirectory(libdevcrypto) @@ -384,6 +396,7 @@ if (GUI) endif() + #unset(TARGET_PLATFORM CACHE) if (WIN32) diff --git a/alethzero/Debugger.h b/alethzero/Debugger.h index 76ef6a0d3..38ed973df 100644 --- a/alethzero/Debugger.h +++ b/alethzero/Debugger.h @@ -45,7 +45,7 @@ struct WorldState dev::u256s stack; dev::bytes memory; dev::bigint gasCost; - std::map storage; + std::unordered_map storage; std::vector levels; }; diff --git a/alethzero/ExportState.cpp b/alethzero/ExportState.cpp index a8e47ad6a..c11132768 100644 --- a/alethzero/ExportState.cpp +++ b/alethzero/ExportState.cpp @@ -91,7 +91,7 @@ void ExportStateDialog::fillBlocks() while (i > 0 && i >= m_recentBlocks) ui->block->removeItem(i--); - h256Set blocks; + h256Hash blocks; for (QString f: filters) { if (f.startsWith("#")) @@ -153,13 +153,17 @@ void ExportStateDialog::generateJSON() auto address = Address((byte const*)hba.data(), Address::ConstructFromPointer); json << prefix << "\t\"" << toHex(address.ref()) << "\":\n\t{\n\t\t\"wei\": \"" << ethereum()->balanceAt(address, m_block) << "\",\n"; json << "\t\t\"code\": \"" << toHex(ethereum()->codeAt(address, m_block)) << "\",\n"; - std::map storage = ethereum()->storageAt(address, m_block); + std::unordered_map storage = ethereum()->storageAt(address, m_block); if (!storage.empty()) { json << "\t\t\"storage\":\n\t\t{\n"; + std::string storagePrefix; for (auto s: storage) - json << "\t\t\t\"" << toHex(s.first) << "\": \"" << toHex(s.second) << "\"" << (s.first == storage.rbegin()->first ? "" : ",") <<"\n"; - json << "\t\t}\n"; + { + json << storagePrefix << "\t\t\t\"" << toHex(s.first) << "\": \"" << toHex(s.second) << "\""; + storagePrefix = ",\n"; + } + json << "\n\t\t}\n"; } json << "\t}"; prefix = ",\n"; diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index e5504cab2..437e75576 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1092,7 +1092,7 @@ void Main::refreshBlockChain() auto const& bc = ethereum()->blockChain(); QStringList filters = ui->blockChainFilter->text().toLower().split(QRegExp("\\s+"), QString::SkipEmptyParts); - h256Set blocks; + h256Hash blocks; for (QString f: filters) if (f.size() == 64) { diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 9e9ae687e..0d6fd7907 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -55,6 +55,7 @@ endif () if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}") + set(CMAKE_C_FLAGS "-g ${CMAKE_C_FLAGS}") add_definitions(-DETH_PROFILING_GPERF) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lprofiler") # set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -lprofiler") diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 2dfb80ac3..1e4651d3d 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -31,7 +31,8 @@ endif() # homebrew installs qts in opt if (APPLE) - set (CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "/usr/local/opt/qt5") + set (CMAKE_PREFIX_PATH "/usr/local/opt/qt5" ${CMAKE_PREFIX_PATH}) + set (CMAKE_PREFIX_PATH "/usr/local/opt/v8-315" ${CMAKE_PREFIX_PATH}) endif() find_program(CTEST_COMMAND ctest) @@ -47,6 +48,13 @@ find_package (LevelDB REQUIRED) message(" - LevelDB header: ${LEVELDB_INCLUDE_DIRS}") message(" - LevelDB lib: ${LEVELDB_LIBRARIES}") +if (JSCONSOLE) + find_package (v8 REQUIRED) + message(" - v8 header: ${V8_INCLUDE_DIRS}") + message(" - v8 lib : ${V8_LIBRARIES}") + add_definitions(-DETH_JSCONSOLE) +endif() + # TODO the Jsoncpp package does not yet check for correct version number find_package (Jsoncpp 0.60 REQUIRED) message(" - Jsoncpp header: ${JSONCPP_INCLUDE_DIRS}") diff --git a/cmake/Findv8.cmake b/cmake/Findv8.cmake new file mode 100644 index 000000000..2451c708f --- /dev/null +++ b/cmake/Findv8.cmake @@ -0,0 +1,49 @@ +# Find v8 +# +# Find the v8 includes and library +# +# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH +# +# This module defines +# V8_INCLUDE_DIRS, where to find header, etc. +# V8_LIBRARIES, the libraries needed to use v8. +# V8_FOUND, If false, do not try to use v8. + +# only look in default directories +find_path( + V8_INCLUDE_DIR + NAMES v8.h + DOC "v8 include dir" +) + +find_library( + V8_LIBRARY + NAMES v8 + DOC "v8 library" +) + +set(V8_INCLUDE_DIRS ${V8_INCLUDE_DIR}) +set(V8_LIBRARIES ${V8_LIBRARY}) + +# debug library on windows +# same naming convention as in qt (appending debug library with d) +# boost is using the same "hack" as us with "optimized" and "debug" +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + + find_library( + V8_LIBRARY_DEBUG + NAMES v8d + DOC "v8 debug library" + ) + + set(V8_LIBRARIES optimized ${V8_LIBRARIES} debug ${V8_LIBRARY_DEBUG}) + +endif() + +# handle the QUIETLY and REQUIRED arguments and set V8_FOUND to TRUE +# if all listed variables are TRUE, hide their existence from configuration view +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(v8 DEFAULT_MSG + V8_INCLUDE_DIR V8_LIBRARY) +mark_as_advanced (V8_INCLUDE_DIR V8_LIBRARY) + diff --git a/eth/CMakeLists.txt b/eth/CMakeLists.txt index 0e56eb9f5..d317be28a 100644 --- a/eth/CMakeLists.txt +++ b/eth/CMakeLists.txt @@ -7,6 +7,10 @@ include_directories(BEFORE ..) include_directories(${Boost_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) +if (JSCONSOLE) + include_directories(${V8_INCLUDE_DIRS}) +endif() + set(EXECUTABLE eth) file(GLOB HEADERS "*.h") @@ -33,6 +37,10 @@ endif() target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} ethash) +if (JSCONSOLE) + target_link_libraries(${EXECUTABLE} jsconsole) +endif() + if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) eth_copy_dlls("${EXECUTABLE}" MHD_DLLS) endif() diff --git a/eth/main.cpp b/eth/main.cpp index cb051aad1..a5db1eecb 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -38,6 +38,9 @@ #include #include #include +#if ETH_JSCONSOLE || !ETH_TRUE +#include +#endif #if ETH_READLINE || !ETH_TRUE #include #include @@ -124,6 +127,7 @@ void help() << " -R,--rebuild Rebuild the blockchain from the existing database." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl << " -S,--session-secret Set the secret key for use with send command, for this session only." << endl + << " --master Give the master password for the key store." << endl << "Client transacting:" << endl << " -B,--block-fees Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl << " -e,--ether-price Set the ether price in the reference unit e.g. ¢ (default: 30.679)." << endl @@ -179,6 +183,9 @@ void help() << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl << " -V,--version Show the version and exit." << endl << " -h,--help Show this help message and exit." << endl +#if ETH_JSCONSOLE || !ETH_TRUE + << " --console Use interactive javascript console" << endl +#endif ; exit(0); } @@ -406,6 +413,13 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod) exit(0); } +void stopMiningAfterXBlocks(eth::Client* _c, unsigned _start, unsigned _mining) +{ + if (_c->isMining() && _c->blockChain().details().number - _start == _mining) + _c->stopMining(); + this_thread::sleep_for(chrono::milliseconds(100)); +} + int main(int argc, char** argv) { #if 0 @@ -542,10 +556,16 @@ int main(int argc, char** argv) unsigned benchmarkTrial = 3; unsigned benchmarkTrials = 5; + // javascript console + bool useConsole = false; + /// Farm params string farmURL = "http://127.0.0.1:8080"; unsigned farmRecheckPeriod = 500; + /// Wallet password stuff + string masterPassword; + string configFile = getDataDir() + "/config.rlp"; bytes b = contents(configFile); if (b.size()) @@ -580,6 +600,8 @@ int main(int argc, char** argv) cerr << "-p is DEPRECATED. It will be removed for the Frontier. Use --port instead (or place directly as host:port)." << endl; remotePort = (short)atoi(argv[++i]); } + else if (arg == "--master" && i + 1 < argc) + masterPassword = argv[++i]; else if ((arg == "-I" || arg == "--import") && i + 1 < argc) { mode = OperationMode::Import; @@ -888,6 +910,10 @@ int main(int argc, char** argv) jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc; else if (arg == "--json-rpc-port" && i + 1 < argc) jsonrpc = atoi(argv[++i]); +#endif +#if ETH_JSCONSOLE + else if (arg == "--console") + useConsole = true; #endif else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc) g_logVerbosity = atoi(argv[++i]); @@ -1625,12 +1651,20 @@ int main(int argc, char** argv) unsigned n =c->blockChain().details().number; if (mining) c->startMining(); - while (!g_exit) + if (useConsole) { - if ( c->isMining() &&c->blockChain().details().number - n == mining) - c->stopMining(); - this_thread::sleep_for(chrono::milliseconds(100)); +#if ETH_JSCONSOLE + JSConsole console(web3, vector({sigKey})); + while (!g_exit) + { + console.repl(); + stopMiningAfterXBlocks(c, n, mining); + } +#endif } + else + while (!g_exit) + stopMiningAfterXBlocks(c, n, mining); } else while (!g_exit) diff --git a/exp/main.cpp b/exp/main.cpp index 4fff5b63a..5fee5cbee 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -42,10 +42,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -64,135 +66,26 @@ namespace fs = boost::filesystem; #if 1 -inline h128 fromUUID(std::string const& _uuid) { return h128(boost::replace_all_copy(_uuid, "-", "")); } - -class KeyManager: public Worker +int main() { -public: - KeyManager() { readKeys(); } - ~KeyManager() {} - - Secret secret(h128 const& _uuid, function const& _pass) - { - auto rit = m_ready.find(_uuid); - if (rit != m_ready.end()) - return rit->second; - auto it = m_keys.find(_uuid); - if (it == m_keys.end()) - return Secret(); - Secret ret(decrypt(it->second, _pass())); - if (ret) - m_ready[_uuid] = ret; - return ret; - } - - h128 create(std::string const& _pass) - { - auto s = Secret::random(); - h128 r(sha3(s)); - m_ready[r] = s; - m_keys[r] = encrypt(s.asBytes(), _pass); - return r; - } - -private: - void writeKeys(std::string const& _keysPath = getDataDir("web3") + "/keys") - { - (void)_keysPath; - } - - void readKeys(std::string const& _keysPath = getDataDir("web3") + "/keys") - { - fs::path p(_keysPath); - js::mValue v; - for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) - if (is_regular_file(it->path())) - { - cdebug << "Reading" << it->path(); - js::read_string(contentsString(it->path().string()), v); - if (v.type() == js::obj_type) - { - js::mObject o = v.get_obj(); - int version = o.count("Version") ? stoi(o["Version"].get_str()) : o.count("version") ? o["version"].get_int() : 0; - if (version == 2) - m_keys[fromUUID(o["id"].get_str())] = o["crypto"]; - else - cwarn << "Cannot read key version" << version; - } - else - cwarn << "Invalid JSON in key file" << it->path().string(); - } - } - - static js::mValue encrypt(bytes const& _v, std::string const& _pass) - { - (void)_v; - (void)_pass; - return js::mValue(); - } - - static bytes decrypt(js::mValue const& _v, std::string const& _pass) - { - js::mObject o = _v.get_obj(); + KeyManager keyman; + if (keyman.exists()) + keyman.load("foo"); + else + keyman.create("foo"); - // derive key - bytes derivedKey; - if (o["kdf"].get_str() == "pbkdf2") - { - auto params = o["kdfparams"].get_obj(); - if (params["prf"].get_str() != "hmac-sha256") - { - cwarn << "Unknown PRF for PBKDF2" << params["prf"].get_str() << "not supported."; - return bytes(); - } - unsigned iterations = params["c"].get_int(); - bytes salt = fromHex(params["salt"].get_str()); - derivedKey = pbkdf2(_pass, salt, iterations, params["dklen"].get_int()); - } - else - { - cwarn << "Unknown KDF" << o["kdf"].get_str() << "not supported."; - return bytes(); - } + Address a("9cab1cc4e8fe528267c6c3af664a1adbce810b5f"); - bytes cipherText = fromHex(o["ciphertext"].get_str()); +// keyman.importExisting(fromUUID("441193ae-a767-f1c3-48ba-dd6610db5ed0"), "{\"name\":\"Gavin Wood - Main identity\"}", "bar", "{\"hint\":\"Not foo.\"}"); +// Address a2 = keyman.address(keyman.import(Secret::random(), "Key with no additional security.")); +// cdebug << toString(a2); + Address a2("19c486071651b2650449ba3c6a807f316a73e8fe"); - // check MAC - h256 mac(o["mac"].get_str()); - h256 macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); - if (mac != macExp) - { - cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); - return bytes(); - } + cdebug << keyman.keys(); - // decrypt - bytes ret; - if (o["cipher"].get_str() == "aes-128-cbc") - { - auto params = o["cipherparams"].get_obj(); - h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight); - h128 iv(params["iv"].get_str()); - decryptSymNoAuth(key, iv, &cipherText, ret); - } - else - { - cwarn << "Unknown cipher" << o["cipher"].get_str() << "not supported."; - return bytes(); - } + cdebug << "Secret key for " << a << "is" << keyman.secret(a, [](){ return "bar"; }); + cdebug << "Secret key for " << a2 << "is" << keyman.secret(a2); - return ret; - } - - mutable std::map m_ready; - std::map m_keys; -}; - -int main() -{ - cdebug << toHex(pbkdf2("password", asBytes("salt"), 1, 20)); - KeyManager keyman; - cdebug << "Secret key for 0498f19a-59db-4d54-ac95-33901b4f1870 is " << keyman.secret(fromUUID("0498f19a-59db-4d54-ac95-33901b4f1870"), [](){ return "foo"; }); } #elif 0 diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 8afd6082e..5f3658030 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -28,7 +28,7 @@ using namespace dev; namespace dev { -char const* Version = "0.9.15o"; +char const* Version = "0.9.17"; void HasInvariants::checkInvariants() const { diff --git a/libdevcore/Common.h b/libdevcore/Common.h index e0872b5d4..36e57c44f 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -34,10 +34,13 @@ #endif #include +#include #include #include +#include #include #include +#include #pragma warning(push) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -84,6 +87,10 @@ using StringMap = std::map; using u256Map = std::map; using HexMap = std::map; +// Hash types. +using StringHashMap = std::unordered_map; +using u256HashMap = std::unordered_map; + // String types. using strings = std::vector; @@ -215,4 +222,14 @@ inline dev::WithExisting max(dev::WithExisting _a, dev::WithExisting _b) return static_cast(max(static_cast(_a), static_cast(_b))); } +template <> struct hash +{ + size_t operator()(dev::u256 const& _a) const + { + unsigned size = _a.backend().size(); + auto limbs = _a.backend().limbs(); + return boost::hash_range(limbs, limbs + size); + } +}; + } diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index a0ea01a1b..6c1f34667 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -258,6 +258,14 @@ template std::set& operator+=(std::set& _a, U const& _b return _a; } +/// Insert the contents of a container into an unordered_st +template std::unordered_set& operator+=(std::unordered_set& _a, U const& _b) +{ + for (auto const& i: _b) + _a.insert(i); + return _a; +} + /// Concatenate the contents of a container onto a vector template std::vector& operator+=(std::vector& _a, U const& _b) { diff --git a/libdevcore/FixedHash.cpp b/libdevcore/FixedHash.cpp index ae2d77c85..6c2a9a57e 100644 --- a/libdevcore/FixedHash.cpp +++ b/libdevcore/FixedHash.cpp @@ -19,10 +19,25 @@ * @date 2014 */ -#include #include "FixedHash.h" +#include +#include using namespace std; using namespace dev; std::random_device dev::s_fixedHashEngine; + +h128 dev::fromUUID(std::string const& _uuid) +{ + return h128(boost::replace_all_copy(_uuid, "-", "")); +} + +std::string dev::toUUID(h128 const& _uuid) +{ + std::string ret = toHex(_uuid.ref()); + for (unsigned i: {20, 16, 12, 8}) + ret.insert(ret.begin() + i, '-'); + return ret; +} + diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 2cf81cb77..14bc500e6 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -113,6 +113,9 @@ public: /// @returns an abridged version of the hash as a user-readable hex string. std::string abridged() const { return toHex(ref().cropped(0, 4)) + "\342\200\246"; } + /// @returns an abridged version of the hash as a user-readable hex string. + std::string hex() const { return toHex(ref()); } + /// @returns a mutable byte vector_ref to the object's data. bytesRef ref() { return bytesRef(m_data.data(), N); } @@ -147,17 +150,10 @@ public: /// @returns a random valued object. static FixedHash random() { return random(s_fixedHashEngine); } - /// A generic std::hash compatible function object. struct hash { /// Make a hash of the object's data. - size_t operator()(FixedHash const& value) const - { - size_t h = 0; - for (auto i: value.m_data) - h = (h << (5 - h)) + i; - return h; - } + size_t operator()(FixedHash const& _value) const { return boost::hash_range(_value.m_data.cbegin(), _value.m_data.cend()); } }; template inline FixedHash& shiftBloom(FixedHash const& _h) @@ -218,12 +214,8 @@ template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) co /// Fast std::hash compatible hash function object for h256. template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& value) const { - const uint64_t*data = (const uint64_t*)value.data(); - uint64_t hash = data[0]; - hash ^= data[1]; - hash ^= data[2]; - hash ^= data[3]; - return (size_t)hash; + uint64_t const* data = reinterpret_cast(value.data()); + return boost::hash_range(data, data + 4); } /// Stream I/O for the FixedHash class. @@ -251,6 +243,8 @@ using h256s = std::vector; using h160s = std::vector; using h256Set = std::set; using h160Set = std::set; +using h256Hash = std::unordered_set; +using h160Hash = std::unordered_set; /// Convert the given value into h160 (160-bit unsigned integer) using the right 20 bytes. inline h160 right160(h256 const& _t) @@ -268,6 +262,10 @@ inline h160 left160(h256 const& _t) return ret; } +h128 fromUUID(std::string const& _uuid); + +std::string toUUID(h128 const& _uuid); + inline std::string toString(h256s const& _bs) { std::ostringstream out; @@ -282,6 +280,9 @@ inline std::string toString(h256s const& _bs) namespace std { - /// Forward std::hash to dev::h256::hash. + /// Forward std::hash to dev::FixedHash::hash. + template<> struct hash: dev::h64::hash {}; + template<> struct hash: dev::h160::hash {}; template<> struct hash: dev::h256::hash {}; + template<> struct hash: dev::h512::hash {}; } diff --git a/libdevcore/Log.h b/libdevcore/Log.h index f42e9cbdd..20ad9fd20 100644 --- a/libdevcore/Log.h +++ b/libdevcore/Log.h @@ -164,6 +164,30 @@ public: } m_sstr << EthLime "}" EthReset; } + template void append(std::unordered_set const& _t) + { + m_sstr << EthYellow "{" EthReset; + int n = 0; + for (auto const& i: _t) + { + m_sstr << (n++ ? EthYellow ", " EthReset : ""); + append(i); + } + m_sstr << EthYellow "}" EthReset; + } + template void append(std::unordered_map const& _t) + { + m_sstr << EthLime "{" EthReset; + int n = 0; + for (auto const& i: _t) + { + m_sstr << (n++ ? EthLime ", " EthReset : ""); + append(i.first); + m_sstr << (n++ ? EthLime ": " EthReset : ""); + append(i.second); + } + m_sstr << EthLime "}" EthReset; + } template void append(std::pair const& _t) { m_sstr << EthPurple "(" EthReset; diff --git a/libdevcore/RLP.h b/libdevcore/RLP.h index ac5e2ef1e..6e46807ab 100644 --- a/libdevcore/RLP.h +++ b/libdevcore/RLP.h @@ -362,6 +362,7 @@ public: template RLPStream& appendVector(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } template RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } template RLPStream& append(std::set<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } + template RLPStream& append(std::unordered_set<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } template RLPStream& append(std::pair const& _s) { appendList(2); append(_s.first); append(_s.second); return *this; } /// Appends a list. diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index c3133cca7..a5c176fe6 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -20,6 +20,7 @@ * @date 2014 */ +#include "Common.h" #include #include #include @@ -28,7 +29,6 @@ #include "SHA3.h" #include "FileSystem.h" #include "CryptoPP.h" -#include "Common.h" using namespace std; using namespace dev; using namespace dev::crypto; @@ -112,51 +112,47 @@ bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain) return decrypt(_k, _cipher, o_plain); } -h128 dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plain, bytes& o_cipher) +std::pair dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plain) { h128 iv(Nonce::get()); - return encryptSymNoAuth(_k, _plain, o_cipher, iv); + return make_pair(encryptSymNoAuth(_k, iv, _plain), iv); } -h128 dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv) +bytes dev::encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain) { - o_cipher.resize(_plain.size()); - const int c_aesKeyLen = 16; SecByteBlock key(_k.data(), c_aesKeyLen); try { CTR_Mode::Encryption e; e.SetKeyWithIV(key, key.size(), _iv.data()); - e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); - return _iv; + bytes ret(_plain.size()); + e.ProcessData(ret.data(), _plain.data(), _plain.size()); + return ret; } catch (CryptoPP::Exception& _e) { cerr << _e.what() << endl; - o_cipher.resize(0); - return h128(); + return bytes(); } } -bool dev::decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext) +bytes dev::decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher) { - o_plaintext.resize(_cipher.size()); - const size_t c_aesKeyLen = 16; SecByteBlock key(_k.data(), c_aesKeyLen); try { CTR_Mode::Decryption d; d.SetKeyWithIV(key, key.size(), _iv.data()); - d.ProcessData(o_plaintext.data(), _cipher.data(), _cipher.size()); - return true; + bytes ret(_cipher.size()); + d.ProcessData(ret.data(), _cipher.data(), _cipher.size()); + return ret; } catch (CryptoPP::Exception& _e) { cerr << _e.what() << endl; - o_plaintext.resize(0); - return false; + return bytes(); } } diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 3b243d319..6464c7ede 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -68,8 +68,8 @@ extern Address ZeroAddress; /// A vector of Ethereum addresses. using Addresses = h160s; -/// A set of Ethereum addresses. -using AddressSet = std::set; +/// A hash set of Ethereum addresses. +using AddressHash = std::unordered_set; /// A vector of secrets. using Secrets = h256s; @@ -103,13 +103,13 @@ void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher); bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); /// Encrypts payload with random IV/ctr using AES128-CTR. -h128 encryptSymNoAuth(h128 const& _k, bytesConstRef _plain, bytes& o_cipher); +std::pair encryptSymNoAuth(h128 const& _k, bytesConstRef _plain); /// Encrypts payload with specified IV/ctr using AES128-CTR. -h128 encryptSymNoAuth(h128 const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv); - +bytes encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain); + /// Decrypts payload with specified IV/ctr using AES128-CTR. -bool decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); +bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher); /// Recovers Public key from signed message hash. Public recover(Signature const& _sig, h256 const& _hash); diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index e89857502..b701fed8d 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -78,8 +78,7 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) bytes mKey(32); ctx.Final(mKey.data()); - bytes cipherText; - encryptSymNoAuth(h128(eKey), bytesConstRef(&io_cipher), cipherText, h128()); + bytes cipherText = encryptSymNoAuth(h128(eKey), h128(), bytesConstRef(&io_cipher)); if (cipherText.empty()) return; @@ -139,7 +138,7 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) if (mac[i] != msgMac[i]) return false; - decryptSymNoAuth(h128(eKey), iv, cipherNoIV, plain); + plain = decryptSymNoAuth(h128(eKey), iv, cipherNoIV); io_text.resize(plain.size()); io_text.swap(plain); diff --git a/libdevcrypto/MemoryDB.cpp b/libdevcrypto/MemoryDB.cpp index 907e6abe6..4b08db083 100644 --- a/libdevcrypto/MemoryDB.cpp +++ b/libdevcrypto/MemoryDB.cpp @@ -30,9 +30,9 @@ namespace dev const char* DBChannel::name() { return "TDB"; } const char* DBWarn::name() { return "TDB"; } -std::map MemoryDB::get() const +std::unordered_map MemoryDB::get() const { - std::map ret; + std::unordered_map ret; for (auto const& i: m_main) if (!m_enforceRefs || i.second.second > 0) ret.insert(make_pair(i.first, i.second.first)); @@ -112,9 +112,9 @@ void MemoryDB::purge() it = m_main.erase(it); } -set MemoryDB::keys() const +h256Hash MemoryDB::keys() const { - set ret; + h256Hash ret; for (auto const& i: m_main) if (i.second.second) ret.insert(i.first); diff --git a/libdevcrypto/MemoryDB.h b/libdevcrypto/MemoryDB.h index 71428ecdb..3169d8fbc 100644 --- a/libdevcrypto/MemoryDB.h +++ b/libdevcrypto/MemoryDB.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include #include @@ -45,7 +45,7 @@ public: MemoryDB() {} void clear() { m_main.clear(); } // WARNING !!!! didn't originally clear m_refCount!!! - std::map get() const; + std::unordered_map get() const; std::string lookup(h256 const& _h) const; bool exists(h256 const& _h) const; @@ -57,11 +57,11 @@ public: void removeAux(h256 const& _h) { m_aux[_h].second = false; } void insertAux(h256 const& _h, bytesConstRef _v) { m_aux[_h] = make_pair(_v.toBytes(), true); } - std::set keys() const; + h256Hash keys() const; protected: - std::map> m_main; - std::map> m_aux; + std::unordered_map> m_main; + std::unordered_map> m_aux; mutable bool m_enforceRefs = false; }; diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp new file mode 100644 index 000000000..858f2a09f --- /dev/null +++ b/libdevcrypto/SecretStore.cpp @@ -0,0 +1,220 @@ +/* + 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 SecretStore.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "SecretStore.h" +#include +#include +#include +#include +#include +#include +#include "SHA3.h" +#include "FileSystem.h" +using namespace std; +using namespace dev; +namespace js = json_spirit; +namespace fs = boost::filesystem; + +SecretStore::SecretStore() +{ + load(); +} + +SecretStore::~SecretStore() +{ +} + +bytes SecretStore::secret(h128 const& _uuid, function const& _pass) const +{ + auto rit = m_cached.find(_uuid); + if (rit != m_cached.end()) + return rit->second; + auto it = m_keys.find(_uuid); + if (it == m_keys.end()) + return bytes(); + bytes key = decrypt(it->second.first, _pass()); + if (!key.empty()) + m_cached[_uuid] = key; + return key; +} + +h128 SecretStore::importSecret(bytes const& _s, std::string const& _pass) +{ + h128 r = h128::random(); + m_cached[r] = _s; + m_keys[r] = make_pair(encrypt(_s, _pass), std::string()); + save(); + return r; +} + +void SecretStore::kill(h128 const& _uuid) +{ + m_cached.erase(_uuid); + if (m_keys.count(_uuid)) + { + boost::filesystem::remove(m_keys[_uuid].second); + m_keys.erase(_uuid); + } +} + +void SecretStore::clearCache() const +{ + m_cached.clear(); +} + +void SecretStore::save(std::string const& _keysPath) +{ + fs::path p(_keysPath); + boost::filesystem::create_directories(p); + for (auto& k: m_keys) + { + std::string uuid = toUUID(k.first); + std::string filename = (p / uuid).string() + ".json"; + js::mObject v; + js::mValue crypto; + js::read_string(k.second.first, crypto); + v["crypto"] = crypto; + v["id"] = uuid; + v["version"] = 2; + writeFile(filename, js::write_string(js::mValue(v), true)); + if (!k.second.second.empty() && k.second.second != filename) + boost::filesystem::remove(k.second.second); + k.second.second = filename; + } +} + +void SecretStore::load(std::string const& _keysPath) +{ + fs::path p(_keysPath); + js::mValue v; + for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) + if (is_regular_file(it->path())) + { + cdebug << "Reading" << it->path(); + js::read_string(contentsString(it->path().string()), v); + if (v.type() == js::obj_type) + { + js::mObject o = v.get_obj(); + int version = o.count("Version") ? stoi(o["Version"].get_str()) : o.count("version") ? o["version"].get_int() : 0; + if (version == 2) + m_keys[fromUUID(o["id"].get_str())] = make_pair(js::write_string(o["crypto"], false), it->path().string()); + else + cwarn << "Cannot read key version" << version; + } +// else +// cwarn << "Invalid JSON in key file" << it->path().string(); + } +} + +std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass) +{ + js::mObject ret; + + // KDF info + unsigned dklen = 16; + unsigned iterations = 262144; + bytes salt = h256::random().asBytes(); + ret["kdf"] = "pbkdf2"; + { + js::mObject params; + params["prf"] = "hmac-sha256"; + params["c"] = (int)iterations; + params["salt"] = toHex(salt); + params["dklen"] = (int)dklen; + ret["kdfparams"] = params; + } + bytes derivedKey = pbkdf2(_pass, salt, iterations, dklen); + + // cipher info + ret["cipher"] = "aes-128-cbc"; + h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight); + h128 iv = h128::random(); + { + js::mObject params; + params["iv"] = toHex(iv.ref()); + ret["cipherparams"] = params; + } + + // cipher text + bytes cipherText = encryptSymNoAuth(key, iv, &_v); + ret["ciphertext"] = toHex(cipherText); + + // and mac. + h256 mac = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + ret["mac"] = toHex(mac.ref()); + + return js::write_string((js::mValue)ret, true); +} + +bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) +{ + js::mObject o; + { + js::mValue ov; + js::read_string(_v, ov); + o = ov.get_obj(); + } + + // derive key + bytes derivedKey; + if (o["kdf"].get_str() == "pbkdf2") + { + auto params = o["kdfparams"].get_obj(); + if (params["prf"].get_str() != "hmac-sha256") + { + cwarn << "Unknown PRF for PBKDF2" << params["prf"].get_str() << "not supported."; + return bytes(); + } + unsigned iterations = params["c"].get_int(); + bytes salt = fromHex(params["salt"].get_str()); + derivedKey = pbkdf2(_pass, salt, iterations, params["dklen"].get_int()); + } + else + { + cwarn << "Unknown KDF" << o["kdf"].get_str() << "not supported."; + return bytes(); + } + + bytes cipherText = fromHex(o["ciphertext"].get_str()); + + // check MAC + h256 mac(o["mac"].get_str()); + h256 macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + if (mac != macExp) + { + cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); + return bytes(); + } + + // decrypt + if (o["cipher"].get_str() == "aes-128-cbc") + { + auto params = o["cipherparams"].get_obj(); + h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight); + h128 iv(params["iv"].get_str()); + return decryptSymNoAuth(key, iv, &cipherText); + } + else + { + cwarn << "Unknown cipher" << o["cipher"].get_str() << "not supported."; + return bytes(); + } +} diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h new file mode 100644 index 000000000..7f4ae08f1 --- /dev/null +++ b/libdevcrypto/SecretStore.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 SecretStore.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include "Common.h" +#include "FileSystem.h" + +namespace dev +{ + +class SecretStore +{ +public: + SecretStore(); + ~SecretStore(); + + bytes secret(h128 const& _uuid, std::function const& _pass) const; + h128 importSecret(bytes const& _s, std::string const& _pass); + void kill(h128 const& _uuid); + + // Clear any cached keys. + void clearCache() const; + +private: + void save(std::string const& _keysPath = getDataDir("web3") + "/keys"); + void load(std::string const& _keysPath = getDataDir("web3") + "/keys"); + static std::string encrypt(bytes const& _v, std::string const& _pass); + static bytes decrypt(std::string const& _v, std::string const& _pass); + + mutable std::map m_cached; + std::map> m_keys; +}; + +} + diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h index ef628d20b..ff2bcc589 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -26,7 +26,6 @@ #include #pragma warning(pop) -#include #include #include #include @@ -105,7 +104,7 @@ public: void debugPrint() {} - void descendKey(h256 _k, std::set& _keyMask, bool _wasExt, std::ostream* _out, int _indent = 0) const + void descendKey(h256 _k, h256Hash& _keyMask, bool _wasExt, std::ostream* _out, int _indent = 0) const { _keyMask.erase(_k); if (_k == m_root && _k == c_shaNull) // root allowed to be empty @@ -113,7 +112,7 @@ public: descendList(RLP(node(_k)), _keyMask, _wasExt, _out, _indent); // if not, it must be a list } - void descendEntry(RLP const& _r, std::set& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const + void descendEntry(RLP const& _r, h256Hash& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const { if (_r.isData() && _r.size() == 32) descendKey(_r.toHash(), _keyMask, _wasExt, _out, _indent); @@ -123,7 +122,7 @@ public: BOOST_THROW_EXCEPTION(InvalidTrie()); } - void descendList(RLP const& _r, std::set& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const + void descendList(RLP const& _r, h256Hash& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const { if (_r.isList() && _r.itemCount() == 2 && (!_wasExt || _out)) { @@ -144,9 +143,9 @@ public: BOOST_THROW_EXCEPTION(InvalidTrie()); } - std::set leftOvers(std::ostream* _out = nullptr) const + h256Hash leftOvers(std::ostream* _out = nullptr) const { - std::set k = m_db->keys(); + h256Hash k = m_db->keys(); descendKey(m_root, k, false, _out); return k; } @@ -431,7 +430,7 @@ public: void insert(bytesConstRef _key, bytesConstRef _value) { Super::insert(_key, _value); m_secure.insert(_key, _value); syncRoot(); } void remove(bytesConstRef _key) { Super::remove(_key); m_secure.remove(_key); syncRoot(); } - std::set leftOvers(std::ostream* = nullptr) const { return std::set{}; } + h256Hash leftOvers(std::ostream* = nullptr) const { return h256Hash{}; } bool check(bool) const { return m_secure.check(false) && Super::check(false); } private: diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 24fc1cb65..e20e88a91 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -195,26 +195,18 @@ template h256 trieRootOver(unsigned _itemCount, T const& _get return t.root(); } +struct BlockInfoDiagnosticsChannel: public LogChannel { static const char* name() { return EthBlue "▧" EthWhite " ◌"; } static const int verbosity = 9; }; + void BlockInfo::verifyInternals(bytesConstRef _block) const { RLP root(_block); - /*OverlayDB db; - GenericTrieDB t(&db); - t.init(); - unsigned i = 0; - for (auto const& tr: root[1]) - { - bytes k = rlp(i); - t.insert(&k, tr.data()); - ++i; - } - if (transactionsRoot != t.root())*/ auto txList = root[1]; auto expectedRoot = trieRootOver(txList.itemCount(), [&](unsigned i){ return rlp(i); }, [&](unsigned i){ return txList[i].data(); }); + clog(BlockInfoDiagnosticsChannel) << "Expected trie root:" << toString(expectedRoot); if (transactionsRoot != expectedRoot) BOOST_THROW_EXCEPTION(InvalidTransactionsHash() << HashMismatchError(expectedRoot, transactionsRoot)); - + clog(BlockInfoDiagnosticsChannel) << "Expected uncle hash:" << toString(sha3(root[2].data())); if (sha3Uncles != sha3(root[2].data())) BOOST_THROW_EXCEPTION(InvalidUnclesHash()); } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index 0389697f5..c90ee048e 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -75,12 +75,12 @@ private: static EthashAux* s_this; RecursiveMutex x_this; - std::map> m_lights; - std::map> m_fulls; + std::unordered_map> m_lights; + std::unordered_map> m_fulls; FullType m_lastUsedFull; Mutex x_epochs; - std::map m_epochs; + std::unordered_map m_epochs; h256s m_seedHashes; }; diff --git a/libethereum/Account.h b/libethereum/Account.h index 2cc962baa..660dc0a4c 100644 --- a/libethereum/Account.h +++ b/libethereum/Account.h @@ -134,8 +134,8 @@ public: /// which encodes the base-state of the account's storage (upon which the storage is overlaid). h256 baseRoot() const { assert(m_storageRoot); return m_storageRoot; } - /// @returns the storage overlay as a simple map. - std::map const& storageOverlay() const { return m_storageOverlay; } + /// @returns the storage overlay as a simple hash map. + std::unordered_map const& storageOverlay() const { return m_storageOverlay; } /// Set a key/value pair in the account's storage. This actually goes into the overlay, for committing /// to the trie later. @@ -194,7 +194,7 @@ private: h256 m_codeHash = EmptySHA3; /// The map with is overlaid onto whatever storage is implied by the m_storageRoot in the trie. - std::map m_storageOverlay; + std::unordered_map m_storageOverlay; /// The associated code for this account. The SHA3 of this should be equal to m_codeHash unless m_codeHash /// equals c_contractConceptionCodeHash. diff --git a/libethereum/AccountDiff.h b/libethereum/AccountDiff.h index dd494c0a5..22107b958 100644 --- a/libethereum/AccountDiff.h +++ b/libethereum/AccountDiff.h @@ -62,7 +62,7 @@ struct AccountDiff Diff exist; ///< The account's existance; was it created/deleted or not? Diff balance; ///< The account's balance; did it alter? Diff nonce; ///< The account's nonce; did it alter? - std::map> storage; ///< The account's storage addresses; each has its own Diff. + std::unordered_map> storage; ///< The account's storage addresses; each has its own Diff. Diff code; ///< The account's code; in general this should only have changed if exist also changed. }; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 192cff34c..607fc586c 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -961,10 +961,10 @@ vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earlie return ret; } -h256Set BlockChain::allUnclesFrom(h256 const& _parent) const +h256Hash BlockChain::allUnclesFrom(h256 const& _parent) const { // Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). - h256Set ret; + h256Hash ret; h256 p = _parent; for (unsigned i = 0; i < 6 && p != m_genesisHash; ++i, p = details(p).parent) { diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 12e1fc785..e77369534 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -72,7 +72,7 @@ struct BlockChainWarn: public LogChannel { static const char* name(); static con struct BlockChainDebug: public LogChannel { static const char* name(); static const int verbosity = 0; }; // TODO: Move all this Genesis stuff into Genesis.h/.cpp -std::map const& genesisState(); +std::unordered_map const& genesisState(); ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); @@ -206,7 +206,7 @@ public: /// Get all blocks not allowed as uncles given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). /// @returns set including the header-hash of every parent (including @a _parent) up to and including generation +5 /// togther with all their quoted uncles. - h256Set allUnclesFrom(h256 const& _parent) const; + h256Hash allUnclesFrom(h256 const& _parent) const; /// Run through database and verify all blocks by reevaluating. /// Will call _progress with the progress in this operation first param done, second total. diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index d9cb0ed53..a4e44b390 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -106,15 +106,15 @@ private: bool invariants() const override; - mutable boost::shared_mutex m_lock; ///< General lock. - std::set m_drainingSet; ///< All blocks being imported. - std::set m_readySet; ///< All blocks ready for chain-import. - std::vector> m_ready; ///< List of blocks, in correct order, ready for chain-import. - std::set m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain. - std::multimap> m_unknown; ///< For blocks that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears. - std::set m_knownBad; ///< Set of blocks that we know will never be valid. - std::multimap> m_future;///< Set of blocks that are not yet valid. - Signal m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast. + mutable boost::shared_mutex m_lock; ///< General lock. + h256Hash m_drainingSet; ///< All blocks being imported. + h256Hash m_readySet; ///< All blocks ready for chain-import. + std::vector> m_ready; ///< List of blocks, in correct order, ready for chain-import. + h256Hash m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain. + std::unordered_multimap> m_unknown; ///< For blocks that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears. + h256Hash m_knownBad; ///< Set of blocks that we know will never be valid. + std::multimap> m_future; ///< Set of blocks that are not yet valid. Ordered by timestamp + Signal m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast. }; } diff --git a/libethereum/CachedAddressState.cpp b/libethereum/CachedAddressState.cpp index e2fadc8b5..a25017793 100644 --- a/libethereum/CachedAddressState.cpp +++ b/libethereum/CachedAddressState.cpp @@ -51,9 +51,9 @@ bytes CachedAddressState::code() const return h == EmptySHA3 ? bytes() : asBytes(m_o->lookup(h)); } -std::map CachedAddressState::storage() const +std::unordered_map CachedAddressState::storage() const { - std::map ret; + std::unordered_map ret; if (m_r) { SecureTrieDB memdb(const_cast(m_o), m_r[2].toHash()); // promise we won't alter the overlay! :) diff --git a/libethereum/CachedAddressState.h b/libethereum/CachedAddressState.h index 8a3c3a607..2a34c1b34 100644 --- a/libethereum/CachedAddressState.h +++ b/libethereum/CachedAddressState.h @@ -47,7 +47,7 @@ public: bytes code() const; // TODO: DEPRECATE. - std::map storage() const; + std::unordered_map storage() const; AccountDiff diff(CachedAddressState const& _c); diff --git a/libethereum/CanonBlockChain.cpp b/libethereum/CanonBlockChain.cpp index 2cc1d24dc..f1de7292b 100644 --- a/libethereum/CanonBlockChain.cpp +++ b/libethereum/CanonBlockChain.cpp @@ -41,9 +41,9 @@ namespace js = json_spirit; #define ETH_CATCH 1 -std::map const& dev::eth::genesisState() +std::unordered_map const& dev::eth::genesisState() { - static std::map s_ret; + static std::unordered_map s_ret; if (s_ret.empty()) { diff --git a/libethereum/CanonBlockChain.h b/libethereum/CanonBlockChain.h index 619af87eb..df4ac2d88 100644 --- a/libethereum/CanonBlockChain.h +++ b/libethereum/CanonBlockChain.h @@ -45,7 +45,7 @@ namespace eth { // TODO: Move all this Genesis stuff into Genesis.h/.cpp -std::map const& genesisState(); +std::unordered_map const& genesisState(); /** * @brief Implements the blockchain database. All data this gives is disk-backed. diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 01426527f..a43c98aa2 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -308,7 +308,7 @@ void Client::killChain() void Client::clearPending() { - h256Set changeds; + h256Hash changeds; DEV_WRITE_GUARDED(x_postMine) { if (!m_postMine.pending().size()) @@ -345,7 +345,7 @@ static S& filtersStreamOut(S& _out, T const& _fs) return _out; } -void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash) +void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _transactionHash) { Guard l(x_filtersWatches); for (pair& i: m_filters) @@ -363,7 +363,7 @@ void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& i } } -void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed) +void Client::appendFromNewBlock(h256 const& _block, h256Hash& io_changed) { // TODO: more precise check on whether the txs match. auto d = m_bc.info(_block); @@ -496,7 +496,7 @@ void Client::syncTransactionQueue() // returns TransactionReceipts, once for each transaction. cwork << "postSTATE <== TQ"; - h256Set changeds; + h256Hash changeds; TransactionReceipts newPendingReceipts; DEV_TIMED(working) DEV_WRITE_GUARDED(x_working) @@ -552,7 +552,7 @@ void Client::onChainChanged(ImportRoute const& _ir) if (auto h = m_host.lock()) h->noteNewBlocks(); - h256Set changeds; + h256Hash changeds; for (auto const& h: _ir.first) appendFromNewBlock(h, changeds); changeds.insert(ChainChangedFilter); @@ -631,7 +631,7 @@ void Client::startMining() onPostStateChanged(); } -void Client::noteChanged(h256Set const& _filters) +void Client::noteChanged(h256Hash const& _filters) { Guard l(x_filtersWatches); if (_filters.size()) diff --git a/libethereum/Client.h b/libethereum/Client.h index 946825828..674556a6e 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -228,15 +228,15 @@ protected: /// 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(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _sha3); + void appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _sha3); /// Collate the changed filters for the hash of the given block. /// Insert any filters that are activated into @a o_changed. - void appendFromNewBlock(h256 const& _blockHash, h256Set& io_changed); + void appendFromNewBlock(h256 const& _blockHash, h256Hash& 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. - void noteChanged(h256Set const& _filters); + void noteChanged(h256Hash const& _filters); private: /// Called when Worker is starting. @@ -309,7 +309,6 @@ private: ActivityReport m_report; - // TODO!!!!!! REPLACE WITH A PROPER X-THREAD ASIO SIGNAL SYSTEM (could just be condition variables) std::condition_variable m_signalled; Mutex x_signalled; std::atomic m_syncTransactionQueue = {false}; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 136239cf3..cfa271cf6 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -75,17 +75,17 @@ Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes con } // TODO: remove try/catch, allow exceptions -ExecutionResult ClientBase::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff) +ExecutionResult ClientBase::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff) { ExecutionResult ret; try { State temp = asOf(_blockNumber); - Address a = toAddress(_secret); - u256 n = temp.transactionsFrom(a); - Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + u256 n = temp.transactionsFrom(_from); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n); + t.forceSender(_from); if (_ff == FudgeFactor::Lenient) - temp.addBalance(a, (u256)(t.gas() * t.gasPrice() + t.value())); + temp.addBalance(_from, (u256)(t.gas() * t.gasPrice() + t.value())); ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted); } catch (...) @@ -95,19 +95,19 @@ ExecutionResult ClientBase::call(Secret _secret, u256 _value, Address _dest, byt return ret; } -ExecutionResult ClientBase::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff) +ExecutionResult ClientBase::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff) { ExecutionResult ret; try { State temp = asOf(_blockNumber); - Address a = toAddress(_secret); - u256 n = temp.transactionsFrom(a); + u256 n = temp.transactionsFrom(_from); // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); - Transaction t(_value, _gasPrice, _gas, _data, n, _secret); + Transaction t(_value, _gasPrice, _gas, _data, n); + t.forceSender(_from); if (_ff == FudgeFactor::Lenient) - temp.addBalance(a, (u256)(t.gasRequired() * t.gasPrice() + t.value())); + temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted); } catch (...) @@ -147,7 +147,7 @@ h256 ClientBase::codeHashAt(Address _a, BlockNumber _block) const return asOf(_block).codeHash(_a); } -map ClientBase::storageAt(Address _a, BlockNumber _block) const +unordered_map ClientBase::storageAt(Address _a, BlockNumber _block) const { return asOf(_block).storage(_a); } diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index fc0b301ad..32fd36cce 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -83,11 +83,13 @@ public: virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; /// Makes the given call. Nothing is recorded into the state. - virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override; + virtual ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override; + using Interface::call; /// Makes the given create. Nothing is recorded into the state. - virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override; - + virtual ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override; + using Interface::create; + using Interface::balanceAt; using Interface::countAt; using Interface::stateAt; @@ -100,7 +102,7 @@ public: virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const override; virtual bytes codeAt(Address _a, BlockNumber _block) const override; virtual h256 codeHashAt(Address _a, BlockNumber _block) const override; - virtual std::map storageAt(Address _a, BlockNumber _block) const override; + virtual std::unordered_map storageAt(Address _a, BlockNumber _block) const override; virtual LocalisedLogEntries logs(unsigned _watchId) const override; virtual LocalisedLogEntries logs(LogFilter const& _filter) const override; @@ -172,9 +174,9 @@ protected: TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. // filters - mutable Mutex x_filtersWatches; ///< Our lock. - std::map m_filters; ///< The dictionary of filters that are active. - std::map m_watches; ///< Each and every watch - these reference a filter. + mutable Mutex x_filtersWatches; ///< Our lock. + std::unordered_map m_filters; ///< The dictionary of filters that are active. + std::map m_watches; ///< Each and every watch - these reference a filter. }; }} diff --git a/libethereum/DownloadMan.cpp b/libethereum/DownloadMan.cpp index 1b73dca5b..05d0a533e 100644 --- a/libethereum/DownloadMan.cpp +++ b/libethereum/DownloadMan.cpp @@ -39,7 +39,7 @@ DownloadSub::~DownloadSub() } } -h256Set DownloadSub::nextFetch(unsigned _n) +h256Hash DownloadSub::nextFetch(unsigned _n) { Guard l(m_fetch); @@ -51,7 +51,7 @@ h256Set DownloadSub::nextFetch(unsigned _n) m_remaining.clear(); if (!m_man || m_man->chainEmpty()) - return h256Set(); + return h256Hash(); m_asked = (~(m_man->taken() + m_attempted)).lowest(_n); if (m_asked.empty()) diff --git a/libethereum/DownloadMan.h b/libethereum/DownloadMan.h index 82e0f09e2..2b41e660b 100644 --- a/libethereum/DownloadMan.h +++ b/libethereum/DownloadMan.h @@ -21,9 +21,9 @@ #pragma once -#include #include -#include +#include +#include #include #include #include @@ -46,7 +46,7 @@ public: ~DownloadSub(); /// Finished last fetch - grab the next bunch of block hashes to download. - h256Set nextFetch(unsigned _n); + h256Hash nextFetch(unsigned _n); /// Note that we've received a particular block. @returns true if we had asked for it but haven't received it yet. bool noteBlock(h256 _hash); @@ -71,8 +71,8 @@ private: DownloadMan* m_man = nullptr; mutable Mutex m_fetch; - h256Set m_remaining; - std::map m_indices; + h256Hash m_remaining; + std::unordered_map m_indices; RangeMask m_asked; RangeMask m_attempted; }; @@ -155,7 +155,7 @@ private: RangeMask m_blocksGot; mutable SharedMutex x_subs; - std::set m_subs; + std::unordered_set m_subs; }; } diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 299984a16..72ee1854d 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -21,7 +21,6 @@ #include "EthereumHost.h" -#include #include #include #include @@ -184,7 +183,7 @@ void EthereumHost::doWork() void EthereumHost::maintainTransactions() { // Send any new transactions. - map, h256s> peerTransactions; + unordered_map, h256s> peerTransactions; auto ts = m_tq.transactions(); for (auto const& i: ts) { diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index baa850b5c..d53c3cc79 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -22,9 +22,9 @@ #pragma once #include -#include +#include #include -#include +#include #include #include #include @@ -97,7 +97,7 @@ private: /// Get a bunch of needed blocks. /// Removes them from our list of needed blocks. /// @returns empty if there's no more blocks left to fetch, otherwise the blocks to fetch. - h256Set neededBlocks(h256Set const& _exclude); + h256Hash neededBlocks(h256Hash const& _exclude); /// Check to see if the network peer-state initialisation has happened. bool isInitialised() const { return (bool)m_latestBlockSent; } @@ -121,9 +121,9 @@ private: DownloadMan m_man; h256 m_latestBlockSent; - h256Set m_transactionsSent; + h256Hash m_transactionsSent; - std::set m_banned; + std::unordered_set m_banned; bool m_newTransactions = false; bool m_newBlocks = false; diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index 75ebab02f..54f7ad829 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -140,9 +139,9 @@ private: bool m_requireTransactions = false; Mutex x_knownBlocks; - h256Set m_knownBlocks; ///< Blocks that the peer already knows about (that don't need to be sent to them). + h256Hash m_knownBlocks; ///< Blocks that the peer already knows about (that don't need to be sent to them). Mutex x_knownTransactions; - h256Set m_knownTransactions; ///< Transactions that the peer already knows of. + h256Hash m_knownTransactions; ///< Transactions that the peer already knows of. }; diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index 8807bcd58..1a2d180dd 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -91,8 +91,8 @@ public: State& state() const { return m_s; } private: - State& m_s; ///< A reference to the base state. - std::map m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution. + State& m_s; ///< A reference to the base state. + std::unordered_map m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution. }; } diff --git a/libethereum/Interface.h b/libethereum/Interface.h index ba68e976e..a82595e2d 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -76,13 +76,17 @@ public: virtual void flushTransactions() = 0; /// Makes the given call. Nothing is recorded into the state. - virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) = 0; - ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, FudgeFactor _ff = FudgeFactor::Strict) { return call(_secret, _value, _dest, _data, _gas, _gasPrice, m_default, _ff); } + virtual ExecutionResult call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) = 0; + ExecutionResult call(Address const& _from, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, FudgeFactor _ff = FudgeFactor::Strict) { return call(_from, _value, _dest, _data, _gas, _gasPrice, m_default, _ff); } + ExecutionResult call(Secret const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) { return call(toAddress(_secret), _value, _dest, _data, _gas, _gasPrice, _blockNumber, _ff); } + ExecutionResult call(Secret const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, FudgeFactor _ff = FudgeFactor::Strict) { return call(toAddress(_secret), _value, _dest, _data, _gas, _gasPrice, _ff); } /// Does the given creation. Nothing is recorded into the state. /// @returns the pair of the Address of the created contract together with its code. - virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) = 0; - ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, FudgeFactor _ff = FudgeFactor::Strict) { return create(_secret, _value, _data, _gas, _gasPrice, m_default, _ff); } + virtual ExecutionResult create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) = 0; + ExecutionResult create(Address const& _from, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, FudgeFactor _ff = FudgeFactor::Strict) { return create(_from, _value, _data, _gas, _gasPrice, m_default, _ff); } + ExecutionResult create(Secret const& _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) { return create(toAddress(_secret), _value, _data, _gas, _gasPrice, _blockNumber, _ff); } + ExecutionResult create(Secret const& _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, FudgeFactor _ff = FudgeFactor::Strict) { return create(toAddress(_secret), _value, _data, _gas, _gasPrice, _ff); } /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. virtual ImportResult injectTransaction(bytes const& _rlp) = 0; @@ -100,14 +104,14 @@ public: u256 stateAt(Address _a, u256 _l) const { return stateAt(_a, _l, m_default); } bytes codeAt(Address _a) const { return codeAt(_a, m_default); } h256 codeHashAt(Address _a) const { return codeHashAt(_a, m_default); } - std::map storageAt(Address _a) const { return storageAt(_a, m_default); } + std::unordered_map storageAt(Address _a) const { return storageAt(_a, m_default); } virtual u256 balanceAt(Address _a, BlockNumber _block) const = 0; virtual u256 countAt(Address _a, BlockNumber _block) const = 0; virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const = 0; virtual bytes codeAt(Address _a, BlockNumber _block) const = 0; virtual h256 codeHashAt(Address _a, BlockNumber _block) const = 0; - virtual std::map storageAt(Address _a, BlockNumber _block) const = 0; + virtual std::unordered_map storageAt(Address _a, BlockNumber _block) const = 0; // [LOGS API] diff --git a/libethereum/KeyManager.cpp b/libethereum/KeyManager.cpp new file mode 100644 index 000000000..15f767c88 --- /dev/null +++ b/libethereum/KeyManager.cpp @@ -0,0 +1,203 @@ +/* + 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 KeyManager.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "KeyManager.h" +#include +#include +#include +#include +#include +#include +using namespace std; +using namespace dev; +namespace fs = boost::filesystem; + +KeyManager::KeyManager(std::string const& _keysFile): + m_keysFile(_keysFile) +{} + +KeyManager::~KeyManager() +{} + +bool KeyManager::exists() const +{ + return !contents(m_keysFile + ".salt").empty() && !contents(m_keysFile).empty(); +} + +void KeyManager::create(std::string const& _pass) +{ + m_password = asString(h256::random().asBytes()); + write(_pass, m_keysFile); +} + +bool KeyManager::load(std::string const& _pass) +{ + try { + bytes salt = contents(m_keysFile + ".salt"); + bytes encKeys = contents(m_keysFile); + m_key = h128(pbkdf2(_pass, salt, 262144, 16)); + bytes bs = decryptSymNoAuth(m_key, h128(), &encKeys); + RLP s(bs); + unsigned version = (unsigned)s[0]; + if (version == 1) + { + for (auto const& i: s[1]) + m_keyInfo[m_addrLookup[(Address)i[0]] = (h128)i[1]] = KeyInfo((h256)i[2], (std::string)i[3]); + for (auto const& i: s[2]) + m_passwordInfo[(h256)i[0]] = (std::string)i[1]; + m_password = (string)s[3]; + } + m_cachedPasswords[hashPassword(m_password)] = m_password; + return true; + } + catch (...) { + return false; + } +} + +Secret KeyManager::secret(Address const& _address, function const& _pass) const +{ + auto it = m_addrLookup.find(_address); + if (it == m_addrLookup.end()) + return Secret(); + return secret(it->second, _pass); +} + +Secret KeyManager::secret(h128 const& _uuid, function const& _pass) const +{ + return Secret(m_store.secret(_uuid, [&](){ + auto kit = m_keyInfo.find(_uuid); + if (kit != m_keyInfo.end()) + { + auto it = m_cachedPasswords.find(kit->second.passHash); + if (it != m_cachedPasswords.end()) + return it->second; + } + std::string p = _pass(); + m_cachedPasswords[hashPassword(p)] = p; + return p; + })); +} + +h128 KeyManager::uuid(Address const& _a) const +{ + auto it = m_addrLookup.find(_a); + if (it == m_addrLookup.end()) + return h128(); + return it->second; +} + +Address KeyManager::address(h128 const& _uuid) const +{ + for (auto const& i: m_addrLookup) + if (i.second == _uuid) + return i.first; + return Address(); +} + +h128 KeyManager::import(Secret const& _s, string const& _info, std::string const& _pass, string const& _passInfo) +{ + Address addr = KeyPair(_s).address(); + auto passHash = hashPassword(_pass); + m_cachedPasswords[passHash] = _pass; + m_passwordInfo[passHash] = _passInfo; + auto uuid = m_store.importSecret(_s.asBytes(), _pass); + m_keyInfo[uuid] = KeyInfo{passHash, _info}; + m_addrLookup[addr] = uuid; + write(m_keysFile); + return uuid; +} + +void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo) +{ + bytes key = m_store.secret(_uuid, [&](){ return _pass; }); + if (key.empty()) + return; + Address a = KeyPair(Secret(key)).address(); + auto passHash = hashPassword(_pass); + if (!m_passwordInfo.count(passHash)) + m_passwordInfo[passHash] = _passInfo; + if (!m_cachedPasswords.count(passHash)) + m_cachedPasswords[passHash] = _pass; + m_addrLookup[a] = _uuid; + m_keyInfo[_uuid].passHash = passHash; + m_keyInfo[_uuid].info = _info; + write(m_keysFile); +} + +void KeyManager::kill(Address const& _a) +{ + auto id = m_addrLookup[_a]; + m_addrLookup.erase(_a); + m_keyInfo.erase(id); + m_store.kill(id); +} + +std::map> KeyManager::keys() const +{ + std::map> ret; + for (auto const& i: m_addrLookup) + if (m_keyInfo.count(i.second) > 0) + ret[i.first] = make_pair(m_keyInfo.at(i.second).info, m_passwordInfo.at(m_keyInfo.at(i.second).passHash)); + return ret; +} + +h256 KeyManager::hashPassword(std::string const& _pass) const +{ + // TODO SECURITY: store this a bit more securely; Scrypt perhaps? + return h256(pbkdf2(_pass, asBytes(m_password), 262144, 32)); +} + +bool KeyManager::write(std::string const& _keysFile) const +{ + if (!m_key) + return false; + write(m_key, _keysFile); + return true; +} + +void KeyManager::write(std::string const& _pass, std::string const& _keysFile) const +{ + bytes salt = h256::random().asBytes(); + writeFile(_keysFile + ".salt", salt); + auto key = h128(pbkdf2(_pass, salt, 262144, 16)); + write(key, _keysFile); +} + +void KeyManager::write(h128 const& _key, std::string const& _keysFile) const +{ + RLPStream s(4); + s << 1; + s.appendList(m_addrLookup.size()); + for (auto const& i: m_addrLookup) + if (m_keyInfo.count(i.second)) + { + auto ki = m_keyInfo.at(i.second); + s.appendList(4) << i.first << i.second << ki.passHash << ki.info; + } + s.appendList(m_passwordInfo.size()); + for (auto const& i: m_passwordInfo) + s.appendList(2) << i.first << i.second; + s.append(m_password); + + writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out())); + m_key = _key; +} diff --git a/libethereum/KeyManager.h b/libethereum/KeyManager.h new file mode 100644 index 000000000..9cb22e5b3 --- /dev/null +++ b/libethereum/KeyManager.h @@ -0,0 +1,110 @@ +/* + 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 KeyManager.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include + +namespace dev +{ + +class UnknownPassword: public Exception {}; + +struct KeyInfo +{ + KeyInfo() = default; + KeyInfo(h256 const& _passHash, std::string const& _info): passHash(_passHash), info(_info) {} + h256 passHash; + std::string info; +}; + +static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::string(); }; + +// TODO: This one is specifically for Ethereum, but we can make it generic in due course. +// TODO: hidden-partition style key-store. +/** + * @brief High-level manager of keys for Ethereum. + * Usage: + * + * Call exists() to check whether there is already a database. If so, get the master password from + * the user and call load() with it. If not, get a new master password from the user (get them to type + * it twice and keep some hint around!) and call create() with it. + */ +class KeyManager +{ +public: + KeyManager(std::string const& _keysFile = getDataDir("ethereum") + "/keys.info"); + ~KeyManager(); + + void setKeysFile(std::string const& _keysFile) { m_keysFile = _keysFile; } + std::string const& keysFile() const { return m_keysFile; } + + bool exists() const; + void create(std::string const& _pass); + bool load(std::string const& _pass); + void save(std::string const& _pass) const { write(_pass, m_keysFile); } + + std::map> keys() const; + + h128 uuid(Address const& _a) const; + Address address(h128 const& _uuid) const; + + h128 import(Secret const& _s, std::string const& _info, std::string const& _pass, std::string const& _passInfo); + h128 import(Secret const& _s, std::string const& _info) { return import(_s, _info, m_password, std::string()); } + + SecretStore& store() { return m_store; } + void importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo); + + Secret secret(Address const& _address, std::function const& _pass = DontKnowThrow) const; + Secret secret(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; + + void kill(h128 const& _id) { kill(address(_id)); } + void kill(Address const& _a); + +private: + h256 hashPassword(std::string const& _pass) const; + + // Only use if previously loaded ok. + // @returns false if wasn't previously loaded ok. + bool write(std::string const& _keysFile) const; + void write(std::string const& _pass, std::string const& _keysFile) const; + void write(h128 const& _key, std::string const& _keysFile) const; + + // Ethereum keys. + std::map m_addrLookup; + std::map m_keyInfo; + std::map m_passwordInfo; + + // Passwords that we're storing. + mutable std::map m_cachedPasswords; + + // The default password for keys in the keystore - protected by the master password. + std::string m_password; + + SecretStore m_store; + mutable h128 m_key; + mutable std::string m_keysFile; +}; + +} diff --git a/libethereum/LogFilter.h b/libethereum/LogFilter.h index 304fab317..97ff5a3b1 100644 --- a/libethereum/LogFilter.h +++ b/libethereum/LogFilter.h @@ -67,8 +67,8 @@ public: friend std::ostream& dev::eth::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s); private: - AddressSet m_addresses; - std::array m_topics; + AddressHash m_addresses; + std::array m_topics; unsigned m_earliest = 0; unsigned m_latest = LatestBlock; }; diff --git a/libethereum/Precompiled.cpp b/libethereum/Precompiled.cpp index 0fd5cb45b..cdcb4a46a 100644 --- a/libethereum/Precompiled.cpp +++ b/libethereum/Precompiled.cpp @@ -81,7 +81,7 @@ static bytes identityCode(bytesConstRef _in) return _in.toBytes(); } -static const std::map c_precompiled = +static const std::unordered_map c_precompiled = { { 1, { [](bytesConstRef) -> bigint { return c_ecrecoverGas; }, ecrecoverCode }}, { 2, { [](bytesConstRef i) -> bigint { return c_sha256Gas + (i.size() + 31) / 32 * c_sha256WordGas; }, sha256Code }}, @@ -89,7 +89,7 @@ static const std::map c_precompiled = { 4, { [](bytesConstRef i) -> bigint { return c_identityGas + (i.size() + 31) / 32 * c_identityWordGas; }, identityCode }} }; -std::map const& dev::eth::precompiled() +std::unordered_map const& dev::eth::precompiled() { return c_precompiled; } diff --git a/libethereum/Precompiled.h b/libethereum/Precompiled.h index c65cd9a63..bded27386 100644 --- a/libethereum/Precompiled.h +++ b/libethereum/Precompiled.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include @@ -38,7 +38,7 @@ struct PrecompiledAddress }; /// Info on precompiled contract accounts baked into the protocol. -std::map const& precompiled(); +std::unordered_map const& precompiled(); } } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 83a78e1e8..2e4fe5749 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -208,9 +208,9 @@ StateDiff State::diff(State const& _c) const { StateDiff ret; - std::set
ads; - std::set
trieAds; - std::set
trieAdsD; + std::unordered_set
ads; + std::unordered_set
trieAds; + std::unordered_set
trieAdsD; auto trie = SecureTrieDB(const_cast(&m_db), rootHash()); auto trieD = SecureTrieDB(const_cast(&_c.m_db), _c.rootHash()); @@ -246,7 +246,7 @@ void State::ensureCached(Address _a, bool _requireCode, bool _forceCreate) const ensureCached(m_cache, _a, _requireCode, _forceCreate); } -void State::ensureCached(std::map& _cache, Address _a, bool _requireCode, bool _forceCreate) const +void State::ensureCached(std::unordered_map& _cache, Address _a, bool _requireCode, bool _forceCreate) const { auto it = _cache.find(_a); if (it == _cache.end()) @@ -426,10 +426,10 @@ u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const return ret; } -map State::addresses() const +unordered_map State::addresses() const { #if ETH_FATDB - map ret; + unordered_map ret; for (auto i: m_cache) if (i.second.isAlive()) ret[i.first] = i.second.balance(); @@ -610,6 +610,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement if (receiptsTrie.root() != m_currentBlock.receiptsRoot) { cwarn << "Bad receipts state root."; + cwarn << "Expected: " << toString(receiptsTrie.root()) << " received: " << toString(m_currentBlock.receiptsRoot); cwarn << "Block:" << toHex(_block); cwarn << "Block RLP:" << rlp; cwarn << "Calculated: " << receiptsTrie.root(); @@ -649,9 +650,9 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement if (rlp[2].itemCount() > 2) BOOST_THROW_EXCEPTION(TooManyUncles()); - set nonces = { m_currentBlock.nonce }; + unordered_set nonces = { m_currentBlock.nonce }; vector rewarded; - set knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); + h256Hash knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); for (auto const& i: rlp[2]) { @@ -805,7 +806,7 @@ void State::commitToMine(BlockChain const& _bc) { // Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations. // cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl; - set knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); + h256Hash knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); auto p = m_previousBlock.parentHash; for (unsigned gen = 0; gen < 6 && p != _bc.genesisHash() && unclesCount < 2; ++gen, p = _bc.details(p).parent) { @@ -1016,9 +1017,9 @@ u256 State::storage(Address _id, u256 _memory) const return ret; } -map State::storage(Address _id) const +unordered_map State::storage(Address _id) const { - map ret; + unordered_map ret; ensureCached(_id, false, false); auto it = m_cache.find(_id); diff --git a/libethereum/State.h b/libethereum/State.h index e3468c24c..74d75685c 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -22,7 +22,6 @@ #pragma once #include -#include #include #include #include @@ -142,7 +141,7 @@ public: /// @returns the set containing all addresses currently in use in Ethereum. /// @throws InterfaceNotSupported if compiled without ETH_FATDB. - std::map addresses() const; + std::unordered_map addresses() const; /// Get the header information on the present block. BlockInfo const& info() const { return m_currentBlock; } @@ -238,8 +237,8 @@ public: /// Get the storage of an account. /// @note This is expensive. Don't use it unless you need to. - /// @returns std::map if no account exists at that address. - std::map storage(Address _contract) const; + /// @returns std::unordered_map if no account exists at that address. + std::unordered_map storage(Address _contract) const; /// Get the code of an account. /// @returns bytes() if no account exists at that address. @@ -263,7 +262,7 @@ public: Transactions const& pending() const { return m_transactions; } /// Get the list of hashes of pending transactions. - h256Set const& pendingHashes() const { return m_transactionSet; } + h256Hash const& pendingHashes() const { return m_transactionSet; } /// Get the transaction receipt for the transaction of the given index. TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; } @@ -334,7 +333,7 @@ private: void ensureCached(Address _a, bool _requireCode, bool _forceCreate) const; /// Retrieve all information about a given address into a cache. - void ensureCached(std::map& _cache, Address _a, bool _requireCode, bool _forceCreate) const; + void ensureCached(std::unordered_map& _cache, Address _a, bool _requireCode, bool _forceCreate) const; /// Execute the given block, assuming it corresponds to m_currentBlock. /// Throws on failure. @@ -355,10 +354,10 @@ private: SecureTrieDB m_state; ///< Our state tree, as an OverlayDB DB. Transactions m_transactions; ///< The current list of transactions that we've included in the state. TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts. - std::set m_transactionSet; ///< The set of transaction hashes that we've included in the state. + h256Hash m_transactionSet; ///< The set of transaction hashes that we've included in the state. OverlayDB m_lastTx; - mutable std::map m_cache; ///< Our address cache. This stores the states of each address that has (or at least might have) been changed. + mutable std::unordered_map m_cache; ///< Our address cache. This stores the states of each address that has (or at least might have) been changed. BlockInfo m_previousBlock; ///< The previous block's information. BlockInfo m_currentBlock; ///< The current block's information. @@ -380,7 +379,7 @@ private: std::ostream& operator<<(std::ostream& _out, State const& _s); template -void commit(std::map const& _cache, DB& _db, SecureTrieDB& _state) +void commit(std::unordered_map const& _cache, DB& _db, SecureTrieDB& _state) { for (auto const& i: _cache) if (i.second.isDirty()) diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 2b11c52d6..78852b7b8 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -111,10 +111,10 @@ public: Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } /// Constructs an unsigned message-call transaction. - Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data): m_type(MessageCall), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} + Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} /// Constructs an unsigned contract-creation transaction. - Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} + Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} /// Constructs a transaction from the given RLP. explicit Transaction(bytesConstRef _rlp, CheckTransaction _checkSig); @@ -132,6 +132,8 @@ public: Address const& sender() const; /// Like sender() but will never throw. @returns a null Address if the signature is invalid. Address const& safeSender() const noexcept; + /// Force the sender to a particular value. This will result in an invalid transaction RLP. + void forceSender(Address const& _a) { m_sender = _a; } /// @returns true if transaction is non-null. explicit operator bool() const { return m_type != NullTransaction; } diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index 69e1c935f..50fcea574 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -55,7 +55,7 @@ public: void drop(h256 const& _txHash); - std::map transactions() const { ReadGuard l(m_lock); return m_current; } + std::unordered_map transactions() const { ReadGuard l(m_lock); return m_current; } std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_unknown.size()); } u256 maxNonce(Address const& _a) const; @@ -72,14 +72,14 @@ private: void insertCurrent_WITH_LOCK(std::pair const& _p); bool removeCurrent_WITH_LOCK(h256 const& _txHash); - mutable SharedMutex m_lock; ///< General lock. - std::set m_known; ///< Hashes of transactions in both sets. - std::map m_current; ///< Map of SHA3(tx) to tx. - std::multimap> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. - std::map> m_callbacks; ///< Called once. - std::set m_dropped; ///< Transactions that have previously been dropped. - std::multimap m_senders; ///< Mapping from the sender address to the transaction hash; useful for determining the nonce of a given sender. - Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast. + mutable SharedMutex m_lock; ///< General lock. + h256Hash m_known; ///< Hashes of transactions in both sets. + std::unordered_map m_current; ///< Map of SHA3(tx) to tx. + std::unordered_multimap> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. + std::unordered_map> m_callbacks; ///< Called once. + h256Hash m_dropped; ///< Transactions that have previously been dropped. + std::multimap m_senders; ///< Mapping from the sender address to the transaction hash; useful for determining the nonce of a given sender. + Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast. }; } diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index 1e0663054..0a0b154f4 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -22,7 +22,6 @@ #pragma once #include -#include #include #include #include diff --git a/libethereumx/Ethereum.cpp b/libethereumx/Ethereum.cpp index 54b698646..8ab4243a1 100644 --- a/libethereumx/Ethereum.cpp +++ b/libethereumx/Ethereum.cpp @@ -87,7 +87,7 @@ void Ethereum::submitTransaction(Secret _secret, u256 _value, Address _dest, byt { } -bytes Ethereum::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +bytes Ethereum::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { return bytes(); } diff --git a/libethereumx/Ethereum.h b/libethereumx/Ethereum.h index 15f00f4ae..0e81b8e0c 100644 --- a/libethereumx/Ethereum.h +++ b/libethereumx/Ethereum.h @@ -75,7 +75,7 @@ public: void flushTransactions(); /// Makes the given call. Nothing is recorded into the state. - bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo); + bytes call(Address const& _from, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo); // Informational stuff diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 6cc09a4bc..9530ded49 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -304,9 +304,6 @@ Assembly& Assembly::optimise(bool _enable) { if (!_enable) return *this; - std::vector>> rules; - // jump to next instruction - rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data() == m[2].data()) return {m[2]}; else return m.toVector(); }}); unsigned total = 0; for (unsigned count = 1; count > 0; total += count) @@ -314,10 +311,17 @@ Assembly& Assembly::optimise(bool _enable) copt << toString(*this); count = 0; + //@todo CFG interface should be a generator, that returns an item and a pointer to a + // knownstate, which has to replace the current state if it is not null. + // Feed these items to the CSE, but also store them and replace the stored version + // if the items generated by the CSE are shorter. (or even use less gas?) copt << "Performing control flow analysis..."; { ControlFlowGraph cfg(m_items); - AssemblyItems optItems = cfg.optimisedItems(); + AssemblyItems optItems; + for (BasicBlock const& block: cfg.optimisedBlocks()) + copy(m_items.begin() + block.begin, m_items.begin() + block.end, + back_inserter(optItems)); if (optItems.size() < m_items.size()) { copt << "Old size: " << m_items.size() << ", new size: " << optItems.size(); @@ -329,7 +333,9 @@ Assembly& Assembly::optimise(bool _enable) copt << "Performing common subexpression elimination..."; for (auto iter = m_items.begin(); iter != m_items.end();) { - CommonSubexpressionEliminator eliminator; + //@todo use only a single state / expression classes instance. + KnownState state(make_shared()); + CommonSubexpressionEliminator eliminator(state); auto orig = iter; iter = eliminator.feedItems(iter, m_items.end()); AssemblyItems optItems; diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 63524d6f3..4b85eba40 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -37,18 +37,19 @@ vector CommonSubexpressionEliminator::getOptimizedItems() map initialStackContents; map targetStackContents; - int minHeight = m_stackHeight + 1; - if (!m_stackElements.empty()) - minHeight = min(minHeight, m_stackElements.begin()->first); - for (int height = minHeight; height <= 0; ++height) - initialStackContents[height] = initialStackElement(height, SourceLocation()); - for (int height = minHeight; height <= m_stackHeight; ++height) - targetStackContents[height] = stackElement(height, SourceLocation()); + int minHeight = m_state.stackHeight() + 1; + if (!m_state.stackElements().empty()) + minHeight = min(minHeight, m_state.stackElements().begin()->first); + for (int height = minHeight; height <= m_initialState.stackHeight(); ++height) + initialStackContents[height] = m_initialState.stackElement(height, SourceLocation()); + for (int height = minHeight; height <= m_state.stackHeight(); ++height) + targetStackContents[height] = m_state.stackElement(height, SourceLocation()); // Debug info: //stream(cout, initialStackContents, targetStackContents); - AssemblyItems items = CSECodeGenerator(m_expressionClasses, m_storeOperations).generateCode( + AssemblyItems items = CSECodeGenerator(m_state.expressionClasses(), m_storeOperations).generateCode( + m_initialState.stackHeight(), initialStackContents, targetStackContents ); @@ -57,103 +58,11 @@ vector CommonSubexpressionEliminator::getOptimizedItems() return items; } -ostream& CommonSubexpressionEliminator::stream( - ostream& _out, - map _initialStack, - map _targetStack -) const -{ - auto streamExpressionClass = [this](ostream& _out, Id _id) - { - auto const& expr = m_expressionClasses.representative(_id); - _out << " " << dec << _id << ": " << *expr.item; - if (expr.sequenceNumber) - _out << "@" << dec << expr.sequenceNumber; - _out << "("; - for (Id arg: expr.arguments) - _out << dec << arg << ","; - _out << ")" << endl; - }; - - _out << "Optimizer analysis:" << endl; - _out << "Final stack height: " << dec << m_stackHeight << endl; - _out << "Equivalence classes: " << endl; - for (Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass) - streamExpressionClass(_out, eqClass); - - _out << "Initial stack: " << endl; - for (auto const& it: _initialStack) - { - _out << " " << dec << it.first << ": "; - streamExpressionClass(_out, it.second); - } - _out << "Target stack: " << endl; - for (auto const& it: _targetStack) - { - _out << " " << dec << it.first << ": "; - streamExpressionClass(_out, it.second); - } - - return _out; -} - void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _copyItem) { - if (_item.type() != Operation) - { - assertThrow(_item.deposit() == 1, InvalidDeposit, ""); - setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, _copyItem)); - } - else - { - Instruction instruction = _item.instruction(); - InstructionInfo info = instructionInfo(instruction); - if (SemanticInformation::isDupInstruction(_item)) - setStackElement( - m_stackHeight + 1, - stackElement( - m_stackHeight - int(instruction) + int(Instruction::DUP1), - _item.getLocation() - ) - ); - else if (SemanticInformation::isSwapInstruction(_item)) - swapStackElements( - m_stackHeight, - m_stackHeight - 1 - int(instruction) + int(Instruction::SWAP1), - _item.getLocation() - ); - else if (instruction != Instruction::POP) - { - vector arguments(info.args); - for (int i = 0; i < info.args; ++i) - arguments[i] = stackElement(m_stackHeight - i, _item.getLocation()); - if (_item.instruction() == Instruction::SSTORE) - storeInStorage(arguments[0], arguments[1], _item.getLocation()); - else if (_item.instruction() == Instruction::SLOAD) - setStackElement( - m_stackHeight + _item.deposit(), - loadFromStorage(arguments[0], _item.getLocation()) - ); - else if (_item.instruction() == Instruction::MSTORE) - storeInMemory(arguments[0], arguments[1], _item.getLocation()); - else if (_item.instruction() == Instruction::MLOAD) - setStackElement( - m_stackHeight + _item.deposit(), - loadFromMemory(arguments[0], _item.getLocation()) - ); - else if (_item.instruction() == Instruction::SHA3) - setStackElement( - m_stackHeight + _item.deposit(), - applySha3(arguments.at(0), arguments.at(1), _item.getLocation()) - ); - else - setStackElement( - m_stackHeight + _item.deposit(), - m_expressionClasses.find(_item, arguments, _copyItem) - ); - } - m_stackHeight += _item.deposit(); - } + StoreOperation op = m_state.feedItem(_item, _copyItem); + if (op.isValid()) + m_storeOperations.push_back(op); } void CommonSubexpressionEliminator::optimizeBreakingItem() @@ -164,20 +73,20 @@ void CommonSubexpressionEliminator::optimizeBreakingItem() SourceLocation const& location = m_breakingItem->getLocation(); AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType(); - Id condition = stackElement(m_stackHeight - 1, location); - Id zero = m_expressionClasses.find(u256(0)); - if (m_expressionClasses.knownToBeDifferent(condition, zero)) + Id condition = m_state.stackElement(m_state.stackHeight() - 1, location); + Id zero = m_state.expressionClasses().find(u256(0)); + if (m_state.expressionClasses().knownToBeDifferent(condition, zero)) { feedItem(AssemblyItem(Instruction::SWAP1, location), true); feedItem(AssemblyItem(Instruction::POP, location), true); AssemblyItem item(Instruction::JUMP, location); item.setJumpType(jumpType); - m_breakingItem = m_expressionClasses.storeItem(item); + m_breakingItem = m_state.expressionClasses().storeItem(item); return; } - Id negatedCondition = m_expressionClasses.find(Instruction::ISZERO, {condition}); - if (m_expressionClasses.knownToBeDifferent(negatedCondition, zero)) + Id negatedCondition = m_state.expressionClasses().find(Instruction::ISZERO, {condition}); + if (m_state.expressionClasses().knownToBeDifferent(negatedCondition, zero)) { AssemblyItem it(Instruction::POP, location); feedItem(it, true); @@ -186,148 +95,6 @@ void CommonSubexpressionEliminator::optimizeBreakingItem() } } -void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Id _class) -{ - m_stackElements[_stackHeight] = _class; -} - -void CommonSubexpressionEliminator::swapStackElements( - int _stackHeightA, - int _stackHeightB, - SourceLocation const& _location -) -{ - assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements."); - // ensure they are created - stackElement(_stackHeightA, _location); - stackElement(_stackHeightB, _location); - - swap(m_stackElements[_stackHeightA], m_stackElements[_stackHeightB]); -} - -ExpressionClasses::Id CommonSubexpressionEliminator::stackElement( - int _stackHeight, - SourceLocation const& _location -) -{ - if (m_stackElements.count(_stackHeight)) - return m_stackElements.at(_stackHeight); - // Stack element not found (not assigned yet), create new equivalence class. - return m_stackElements[_stackHeight] = initialStackElement(_stackHeight, _location); -} - -ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement( - int _stackHeight, - SourceLocation const& _location -) -{ - assertThrow(_stackHeight <= 0, OptimizerException, "Initial stack element of positive height requested."); - assertThrow(_stackHeight > -16, StackTooDeepException, ""); - // This is a special assembly item that refers to elements pre-existing on the initial stack. - return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight), _location)); -} - -void CommonSubexpressionEliminator::storeInStorage(Id _slot, Id _value, SourceLocation const& _location) -{ - if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value) - // do not execute the storage if we know that the value is already there - return; - m_sequenceNumber++; - decltype(m_storageContent) storageContents; - // Copy over all values (i.e. retain knowledge about them) where we know that this store - // operation will not destroy the knowledge. Specifically, we copy storage locations we know - // are different from _slot or locations where we know that the stored value is equal to _value. - for (auto const& storageItem: m_storageContent) - if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot) || storageItem.second == _value) - storageContents.insert(storageItem); - m_storageContent = move(storageContents); - - AssemblyItem item(Instruction::SSTORE, _location); - Id id = m_expressionClasses.find(item, {_slot, _value}, true, m_sequenceNumber); - m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id)); - m_storageContent[_slot] = _value; - // increment a second time so that we get unique sequence numbers for writes - m_sequenceNumber++; -} - -ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(Id _slot, SourceLocation const& _location) -{ - if (m_storageContent.count(_slot)) - return m_storageContent.at(_slot); - - AssemblyItem item(Instruction::SLOAD, _location); - return m_storageContent[_slot] = m_expressionClasses.find(item, {_slot}, true, m_sequenceNumber); -} - -void CommonSubexpressionEliminator::storeInMemory(Id _slot, Id _value, SourceLocation const& _location) -{ - if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value) - // do not execute the store if we know that the value is already there - return; - m_sequenceNumber++; - decltype(m_memoryContent) memoryContents; - // copy over values at points where we know that they are different from _slot by at least 32 - for (auto const& memoryItem: m_memoryContent) - if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot)) - memoryContents.insert(memoryItem); - m_memoryContent = move(memoryContents); - - AssemblyItem item(Instruction::MSTORE, _location); - Id id = m_expressionClasses.find(item, {_slot, _value}, true, m_sequenceNumber); - m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id)); - m_memoryContent[_slot] = _value; - // increment a second time so that we get unique sequence numbers for writes - m_sequenceNumber++; -} - -ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(Id _slot, SourceLocation const& _location) -{ - if (m_memoryContent.count(_slot)) - return m_memoryContent.at(_slot); - - AssemblyItem item(Instruction::MLOAD, _location); - return m_memoryContent[_slot] = m_expressionClasses.find(item, {_slot}, true, m_sequenceNumber); -} - -CommonSubexpressionEliminator::Id CommonSubexpressionEliminator::applySha3( - Id _start, - Id _length, - SourceLocation const& _location -) -{ - AssemblyItem sha3Item(Instruction::SHA3, _location); - // Special logic if length is a short constant, otherwise we cannot tell. - u256 const* l = m_expressionClasses.knownConstant(_length); - // unknown or too large length - if (!l || *l > 128) - return m_expressionClasses.find(sha3Item, {_start, _length}, true, m_sequenceNumber); - - vector arguments; - for (u256 i = 0; i < *l; i += 32) - { - Id slot = m_expressionClasses.find( - AssemblyItem(Instruction::ADD, _location), - {_start, m_expressionClasses.find(i)} - ); - arguments.push_back(loadFromMemory(slot, _location)); - } - if (m_knownSha3Hashes.count(arguments)) - return m_knownSha3Hashes.at(arguments); - Id v; - // If all arguments are known constants, compute the sha3 here - if (all_of(arguments.begin(), arguments.end(), [this](Id _a) { return !!m_expressionClasses.knownConstant(_a); })) - { - bytes data; - for (Id a: arguments) - data += toBigEndian(*m_expressionClasses.knownConstant(a)); - data.resize(size_t(*l)); - v = m_expressionClasses.find(AssemblyItem(u256(sha3(data)), _location)); - } - else - v = m_expressionClasses.find(sha3Item, {_start, _length}, true, m_sequenceNumber); - return m_knownSha3Hashes[arguments] = v; -} - CSECodeGenerator::CSECodeGenerator( ExpressionClasses& _expressionClasses, vector const& _storeOperations @@ -339,10 +106,12 @@ CSECodeGenerator::CSECodeGenerator( } AssemblyItems CSECodeGenerator::generateCode( + int _initialStackHeight, map const& _initialStack, map const& _targetStackContents ) { + m_stackHeight = _initialStackHeight; m_stack = _initialStack; for (auto const& item: m_stack) if (!m_classPositions.count(item.second)) diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index 6156bc81a..6e1ba40b3 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -32,6 +32,7 @@ #include #include #include +#include namespace dev { @@ -58,20 +59,9 @@ class CommonSubexpressionEliminator { public: using Id = ExpressionClasses::Id; - struct StoreOperation - { - enum Target { Memory, Storage }; - StoreOperation( - Target _target, - Id _slot, - unsigned _sequenceNumber, - Id _expression - ): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {} - Target target; - Id slot; - unsigned sequenceNumber; - Id expression; - }; + using StoreOperation = KnownState::StoreOperation; + + CommonSubexpressionEliminator(KnownState const& _state): m_initialState(_state), m_state(_state) {} /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first /// item that must be fed into a new instance of the eliminator. @@ -95,49 +85,11 @@ private: /// Tries to optimize the item that breaks the basic block at the end. void optimizeBreakingItem(); - /// Simplifies the given item using - /// Assigns a new equivalence class to the next sequence number of the given stack element. - void setStackElement(int _stackHeight, Id _class); - /// Swaps the given stack elements in their next sequence number. - void swapStackElements(int _stackHeightA, int _stackHeightB, SourceLocation const& _location); - /// Retrieves the current equivalence class fo the given stack element (or generates a new - /// one if it does not exist yet). - Id stackElement(int _stackHeight, SourceLocation const& _location); - /// @returns the equivalence class id of the special initial stack element at the given height - /// (must not be positive). - Id initialStackElement(int _stackHeight, SourceLocation const& _location); - - /// Increments the sequence number, deletes all storage information that might be overwritten - /// and stores the new value at the given slot. - void storeInStorage(Id _slot, Id _value, SourceLocation const& _location); - /// Retrieves the current value at the given slot in storage or creates a new special sload class. - Id loadFromStorage(Id _slot, SourceLocation const& _location); - /// Increments the sequence number, deletes all memory information that might be overwritten - /// and stores the new value at the given slot. - void storeInMemory(Id _slot, Id _value, SourceLocation const& _location); - /// Retrieves the current value at the given slot in memory or creates a new special mload class. - Id loadFromMemory(Id _slot, SourceLocation const& _location); - /// Finds or creates a new expression that applies the sha3 hash function to the contents in memory. - Id applySha3(Id _start, Id _length, SourceLocation const& _location); - - /// Current stack height, can be negative. - int m_stackHeight = 0; - /// Current stack layout, mapping stack height -> equivalence class - std::map m_stackElements; - /// Current sequence number, this is incremented with each modification to storage or memory. - unsigned m_sequenceNumber = 1; - /// Knowledge about storage content. - std::map m_storageContent; - /// Knowledge about memory content. Keys are memory addresses, note that the values overlap - /// and are not contained here if they are not completely known. - std::map m_memoryContent; - /// Keeps record of all sha3 hashes that are computed. - std::map, Id> m_knownSha3Hashes; + KnownState m_initialState; + KnownState m_state; /// Keeps information about which storage or memory slots were written to at which sequence /// number with what instruction. std::vector m_storeOperations; - /// Structure containing the classes of equivalent expressions. - ExpressionClasses m_expressionClasses; /// The item that breaks the basic block, can be nullptr. /// It is usually appended to the block but can be optimized in some cases. @@ -164,6 +116,7 @@ public: /// @param _targetStackContents final contents of the stack, by stack height relative to initial /// @note should only be called once on each object. AssemblyItems generateCode( + int _initialStackHeight, std::map const& _initialStack, std::map const& _targetStackContents ); @@ -199,7 +152,7 @@ private: AssemblyItems m_generatedItems; /// Current height of the stack relative to the start. - int m_stackHeight = 0; + int m_stackHeight; /// If (b, a) is in m_requests then b is needed to compute a. std::multimap m_neededBy; /// Current content of the stack. diff --git a/libevmasm/ControlFlowGraph.cpp b/libevmasm/ControlFlowGraph.cpp index cc4367e64..2e28317a3 100644 --- a/libevmasm/ControlFlowGraph.cpp +++ b/libevmasm/ControlFlowGraph.cpp @@ -23,9 +23,11 @@ #include #include +#include #include #include #include +#include using namespace std; using namespace dev; @@ -36,16 +38,17 @@ BlockId::BlockId(u256 const& _id): m_id(_id) assertThrow( _id < initial().m_id, OptimizerException, "Tag number too large."); } -AssemblyItems ControlFlowGraph::optimisedItems() +BasicBlocks ControlFlowGraph::optimisedBlocks() { if (m_items.empty()) - return m_items; + return BasicBlocks(); findLargestTag(); splitBlocks(); resolveNextLinks(); removeUnusedBlocks(); setPrevLinks(); + gatherKnowledge(); return rebuildCode(); } @@ -209,7 +212,78 @@ void ControlFlowGraph::setPrevLinks() } } -AssemblyItems ControlFlowGraph::rebuildCode() +void ControlFlowGraph::gatherKnowledge() +{ + // @todo actually we know that memory is filled with zeros at the beginning, + // we could make use of that. + KnownStatePointer emptyState = make_shared(); + ExpressionClasses& expr = emptyState->expressionClasses(); + bool unknownJumpEncountered = false; + + vector> workQueue({make_pair(BlockId::initial(), emptyState->copy())}); + while (!workQueue.empty()) + { + //@todo we might have to do something like incrementing the sequence number for each JUMPDEST + assertThrow(!!workQueue.back().first, OptimizerException, ""); + BasicBlock& block = m_blocks.at(workQueue.back().first); + KnownStatePointer state = workQueue.back().second; + workQueue.pop_back(); + if (block.startState) + { + state->reduceToCommonKnowledge(*block.startState); + if (*state == *block.startState) + continue; + } + + block.startState = state->copy(); + //@todo we might know the return address for the first pass, but not anymore for the second, + // -> store knowledge about tags as a union. + + // Feed all items except for the final jump yet because it will erase the target tag. + unsigned pc = block.begin; + while (pc < block.end && !SemanticInformation::altersControlFlow(m_items.at(pc))) + state->feedItem(m_items.at(pc++)); + + if ( + block.endType == BasicBlock::EndType::JUMP || + block.endType == BasicBlock::EndType::JUMPI + ) + { + assertThrow(block.begin <= pc && pc == block.end - 1, OptimizerException, ""); + //@todo in the case of JUMPI, add knowledge about the condition to the state + // (for both values of the condition) + BlockId nextBlock = expressionClassToBlockId( + state->stackElement(state->stackHeight(), SourceLocation()), + expr + ); + state->feedItem(m_items.at(pc++)); + if (nextBlock) + workQueue.push_back(make_pair(nextBlock, state->copy())); + else if (!unknownJumpEncountered) + { + // We do not know where this jump goes, so we have to reset the states of all + // JUMPDESTs. + unknownJumpEncountered = true; + for (auto const& it: m_blocks) + if (it.second.begin < it.second.end && m_items[it.second.begin].type() == Tag) + workQueue.push_back(make_pair(it.first, emptyState->copy())); + } + } + else if (block.begin <= pc && pc < block.end) + state->feedItem(m_items.at(pc++)); + assertThrow(block.end <= block.begin || pc == block.end, OptimizerException, ""); + + block.endState = state; + + if ( + block.endType == BasicBlock::EndType::HANDOVER || + block.endType == BasicBlock::EndType::JUMPI + ) + workQueue.push_back(make_pair(block.next, state->copy())); + } +} + +BasicBlocks ControlFlowGraph::rebuildCode() { map pushes; for (auto& idAndBlock: m_blocks) @@ -220,7 +294,7 @@ AssemblyItems ControlFlowGraph::rebuildCode() for (auto it: m_blocks) blocksToAdd.insert(it.first); set blocksAdded; - AssemblyItems code; + BasicBlocks blocks; for ( BlockId blockId = BlockId::initial(); @@ -233,23 +307,34 @@ AssemblyItems ControlFlowGraph::rebuildCode() blockId = m_blocks.at(blockId).prev; for (; blockId; blockId = m_blocks.at(blockId).next) { - BasicBlock const& block = m_blocks.at(blockId); + BasicBlock& block = m_blocks.at(blockId); blocksToAdd.erase(blockId); blocksAdded.insert(blockId); - auto begin = m_items.begin() + block.begin; - auto end = m_items.begin() + block.end; - if (begin == end) + if (block.begin == block.end) continue; // If block starts with unused tag, skip it. - if (previousHandedOver && !pushes[blockId] && begin->type() == Tag) - ++begin; + if (previousHandedOver && !pushes[blockId] && m_items[block.begin].type() == Tag) + ++block.begin; + if (block.begin < block.end) + blocks.push_back(block); previousHandedOver = (block.endType == BasicBlock::EndType::HANDOVER); - copy(begin, end, back_inserter(code)); } } - return code; + return blocks; +} + +BlockId ControlFlowGraph::expressionClassToBlockId( + ExpressionClasses::Id _id, + ExpressionClasses& _exprClasses +) +{ + ExpressionClasses::Expression expr = _exprClasses.representative(_id); + if (expr.item && expr.item->type() == PushTag) + return BlockId(expr.item->data()); + else + return BlockId::invalid(); } BlockId ControlFlowGraph::generateNewId() diff --git a/libevmasm/ControlFlowGraph.h b/libevmasm/ControlFlowGraph.h index 5d16df327..3366dc45f 100644 --- a/libevmasm/ControlFlowGraph.h +++ b/libevmasm/ControlFlowGraph.h @@ -24,16 +24,18 @@ #pragma once #include +#include #include #include +#include namespace dev { namespace eth { -class AssemblyItem; -using AssemblyItems = std::vector; +class KnownState; +using KnownStatePointer = std::shared_ptr; /** * Identifier for a block, coincides with the tag number of an AssemblyItem but adds a special @@ -69,32 +71,46 @@ struct BasicBlock unsigned end = 0; /// Tags pushed inside this block, with multiplicity. std::vector pushedTags; - /// ID of the block that always follows this one (either JUMP or flow into new block), - /// or BlockId::invalid() otherwise + /// ID of the block that always follows this one (either non-branching part of JUMPI or flow + /// into new block), or BlockId::invalid() otherwise BlockId next = BlockId::invalid(); - /// ID of the block that has to precede this one. + /// ID of the block that has to precede this one (because control flows into it). BlockId prev = BlockId::invalid(); enum class EndType { JUMP, JUMPI, STOP, HANDOVER }; EndType endType = EndType::HANDOVER; + + /// Knowledge about the state when this block is entered. Intersection of all possible ways + /// to enter this block. + KnownStatePointer startState; + /// Knowledge about the state at the end of this block. + KnownStatePointer endState; }; +using BasicBlocks = std::vector; + class ControlFlowGraph { public: /// Initializes the control flow graph. /// @a _items has to persist across the usage of this class. ControlFlowGraph(AssemblyItems const& _items): m_items(_items) {} - /// @returns the collection of optimised items, should be called only once. - AssemblyItems optimisedItems(); + /// @returns vector of basic blocks in the order they should be used in the final code. + /// Should be called only once. + BasicBlocks optimisedBlocks(); private: void findLargestTag(); void splitBlocks(); void resolveNextLinks(); void removeUnusedBlocks(); + void gatherKnowledge(); void setPrevLinks(); - AssemblyItems rebuildCode(); + BasicBlocks rebuildCode(); + + /// @returns the corresponding BlockId if _id is a pushed jump tag, + /// and an invalid BlockId otherwise. + BlockId expressionClassToBlockId(ExpressionClasses::Id _id, ExpressionClasses& _exprClasses); BlockId generateNewId(); diff --git a/libevmasm/ExpressionClasses.cpp b/libevmasm/ExpressionClasses.cpp index 1e60a7fe8..cfbeba7fa 100644 --- a/libevmasm/ExpressionClasses.cpp +++ b/libevmasm/ExpressionClasses.cpp @@ -37,6 +37,7 @@ using namespace dev::eth; bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression const& _other) const { + assertThrow(!!item && !!_other.item, OptimizerException, ""); auto type = item->type(); auto otherType = _other.item->type(); return std::tie(type, item->data(), arguments, sequenceNumber) < @@ -56,12 +57,15 @@ ExpressionClasses::Id ExpressionClasses::find( exp.arguments = _arguments; exp.sequenceNumber = _sequenceNumber; - if (SemanticInformation::isCommutativeOperation(_item)) - sort(exp.arguments.begin(), exp.arguments.end()); + if (SemanticInformation::isDeterministic(_item)) + { + if (SemanticInformation::isCommutativeOperation(_item)) + sort(exp.arguments.begin(), exp.arguments.end()); - auto it = m_expressions.find(exp); - if (it != m_expressions.end()) - return it->id; + auto it = m_expressions.find(exp); + if (it != m_expressions.end()) + return it->id; + } if (_copyItem) exp.item = storeItem(_item); @@ -122,10 +126,16 @@ string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const { Expression const& expr = representative(_id); stringstream str; - str << dec << expr.id << ":" << *expr.item << "("; - for (Id arg: expr.arguments) - str << fullDAGToString(arg) << ","; - str << ")"; + str << dec << expr.id << ":"; + if (expr.item) + { + str << *expr.item << "("; + for (Id arg: expr.arguments) + str << fullDAGToString(arg) << ","; + str << ")"; + } + else + str << " UNIQUE"; return str.str(); } @@ -279,7 +289,11 @@ ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, { static Rules rules; - if (_expr.item->type() != Operation) + if ( + !_expr.item || + _expr.item->type() != Operation || + !SemanticInformation::isDeterministic(*_expr.item) + ) return -1; for (auto const& rule: rules.rules()) @@ -337,7 +351,7 @@ void Pattern::setMatchGroup(unsigned _group, map& _ bool Pattern::matches(Expression const& _expr, ExpressionClasses const& _classes) const { - if (!matchesBaseItem(*_expr.item)) + if (!matchesBaseItem(_expr.item)) return false; if (m_matchGroup) { @@ -387,13 +401,15 @@ string Pattern::toString() const return s.str(); } -bool Pattern::matchesBaseItem(AssemblyItem const& _item) const +bool Pattern::matchesBaseItem(AssemblyItem const* _item) const { if (m_type == UndefinedItem) return true; - if (m_type != _item.type()) + if (!_item) + return false; + if (m_type != _item->type()) return false; - if (m_requireDataMatch && m_data != _item.data()) + if (m_requireDataMatch && m_data != _item->data()) return false; return true; } diff --git a/libevmasm/ExpressionClasses.h b/libevmasm/ExpressionClasses.h index 2f720f606..c83520300 100644 --- a/libevmasm/ExpressionClasses.h +++ b/libevmasm/ExpressionClasses.h @@ -50,7 +50,7 @@ public: struct Expression { Id id; - AssemblyItem const* item; + AssemblyItem const* item = nullptr; Ids arguments; unsigned sequenceNumber; ///< Storage modification sequence, only used for SLOAD/SSTORE instructions. /// Behaves as if this was a tuple of (item->type(), item->data(), arguments, sequenceNumber). @@ -149,7 +149,7 @@ public: std::string toString() const; private: - bool matchesBaseItem(AssemblyItem const& _item) const; + bool matchesBaseItem(AssemblyItem const* _item) const; Expression const& matchGroupValue() const; AssemblyItemType m_type; diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp new file mode 100644 index 000000000..41ac4802b --- /dev/null +++ b/libevmasm/KnownState.cpp @@ -0,0 +1,326 @@ +/* + 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 KnownState.cpp + * @author Christian + * @date 2015 + * Contains knowledge about the state of the virtual machine at a specific instruction. + */ + +#include "KnownState.h" +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::eth; + +ostream& KnownState::stream(ostream& _out) const +{ + auto streamExpressionClass = [this](ostream& _out, Id _id) + { + auto const& expr = m_expressionClasses->representative(_id); + _out << " " << dec << _id << ": "; + if (!expr.item) + _out << " no item"; + else if (expr.item->type() == UndefinedItem) + _out << " unknown " << int(expr.item->data()); + else + _out << *expr.item; + if (expr.sequenceNumber) + _out << "@" << dec << expr.sequenceNumber; + _out << "("; + for (Id arg: expr.arguments) + _out << dec << arg << ","; + _out << ")" << endl; + }; + + _out << "=== State ===" << endl; + _out << "Stack height: " << dec << m_stackHeight << endl; + _out << "Equivalence classes: " << endl; + for (Id eqClass = 0; eqClass < m_expressionClasses->size(); ++eqClass) + streamExpressionClass(_out, eqClass); + + _out << "Stack: " << endl; + for (auto const& it: m_stackElements) + { + _out << " " << dec << it.first << ": "; + streamExpressionClass(_out, it.second); + } + _out << "Storage: " << endl; + for (auto const& it: m_storageContent) + { + _out << " "; + streamExpressionClass(_out, it.first); + _out << ": "; + streamExpressionClass(_out, it.second); + } + _out << "Memory: " << endl; + for (auto const& it: m_memoryContent) + { + _out << " "; + streamExpressionClass(_out, it.first); + _out << ": "; + streamExpressionClass(_out, it.second); + } + + return _out; +} + +KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool _copyItem) +{ + StoreOperation op; + if (_item.type() == Tag) + { + // can be ignored + } + else if (_item.type() != Operation) + { + assertThrow(_item.deposit() == 1, InvalidDeposit, ""); + setStackElement(++m_stackHeight, m_expressionClasses->find(_item, {}, _copyItem)); + } + else + { + Instruction instruction = _item.instruction(); + InstructionInfo info = instructionInfo(instruction); + if (SemanticInformation::isDupInstruction(_item)) + setStackElement( + m_stackHeight + 1, + stackElement( + m_stackHeight - int(instruction) + int(Instruction::DUP1), + _item.getLocation() + ) + ); + else if (SemanticInformation::isSwapInstruction(_item)) + swapStackElements( + m_stackHeight, + m_stackHeight - 1 - int(instruction) + int(Instruction::SWAP1), + _item.getLocation() + ); + else if (instruction != Instruction::POP) + { + vector arguments(info.args); + for (int i = 0; i < info.args; ++i) + arguments[i] = stackElement(m_stackHeight - i, _item.getLocation()); + + if (_item.instruction() == Instruction::SSTORE) + op = storeInStorage(arguments[0], arguments[1], _item.getLocation()); + else if (_item.instruction() == Instruction::SLOAD) + setStackElement( + m_stackHeight + _item.deposit(), + loadFromStorage(arguments[0], _item.getLocation()) + ); + else if (_item.instruction() == Instruction::MSTORE) + op = storeInMemory(arguments[0], arguments[1], _item.getLocation()); + else if (_item.instruction() == Instruction::MLOAD) + setStackElement( + m_stackHeight + _item.deposit(), + loadFromMemory(arguments[0], _item.getLocation()) + ); + else if (_item.instruction() == Instruction::SHA3) + setStackElement( + m_stackHeight + _item.deposit(), + applySha3(arguments.at(0), arguments.at(1), _item.getLocation()) + ); + else + { + if (SemanticInformation::invalidatesMemory(_item.instruction())) + resetMemory(); + if (SemanticInformation::invalidatesStorage(_item.instruction())) + resetStorage(); + assertThrow(info.ret <= 1, InvalidDeposit, ""); + if (info.ret == 1) + setStackElement( + m_stackHeight + _item.deposit(), + m_expressionClasses->find(_item, arguments, _copyItem) + ); + } + } + m_stackElements.erase( + m_stackElements.upper_bound(m_stackHeight + _item.deposit()), + m_stackElements.end() + ); + m_stackHeight += _item.deposit(); + } + return op; +} + +void KnownState::reduceToCommonKnowledge(KnownState const& /*_other*/) +{ + //@todo + *this = KnownState(m_expressionClasses); +} + +bool KnownState::operator==(const KnownState& _other) const +{ + //@todo + return ( + m_stackElements.empty() && + _other.m_stackElements.empty() && + m_storageContent.empty() && + _other.m_storageContent.empty() && + m_memoryContent.empty() && + _other.m_memoryContent.empty() + ); +} + +ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation const& _location) +{ + if (m_stackElements.count(_stackHeight)) + return m_stackElements.at(_stackHeight); + // Stack element not found (not assigned yet), create new unknown equivalence class. + //@todo check that we do not infer incorrect equivalences when the stack is cleared partially + //in between. + return m_stackElements[_stackHeight] = initialStackElement(_stackHeight, _location); +} + +ExpressionClasses::Id KnownState::initialStackElement( + int _stackHeight, + SourceLocation const& _location +) +{ + // This is a special assembly item that refers to elements pre-existing on the initial stack. + return m_expressionClasses->find(AssemblyItem(UndefinedItem, u256(_stackHeight), _location)); +} + +void KnownState::setStackElement(int _stackHeight, Id _class) +{ + m_stackElements[_stackHeight] = _class; +} + +void KnownState::swapStackElements( + int _stackHeightA, + int _stackHeightB, + SourceLocation const& _location +) +{ + assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements."); + // ensure they are created + stackElement(_stackHeightA, _location); + stackElement(_stackHeightB, _location); + + swap(m_stackElements[_stackHeightA], m_stackElements[_stackHeightB]); +} + +KnownState::StoreOperation KnownState::storeInStorage( + Id _slot, + Id _value, + SourceLocation const& _location) +{ + if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value) + // do not execute the storage if we know that the value is already there + return StoreOperation(); + m_sequenceNumber++; + decltype(m_storageContent) storageContents; + // Copy over all values (i.e. retain knowledge about them) where we know that this store + // operation will not destroy the knowledge. Specifically, we copy storage locations we know + // are different from _slot or locations where we know that the stored value is equal to _value. + for (auto const& storageItem: m_storageContent) + if (m_expressionClasses->knownToBeDifferent(storageItem.first, _slot) || storageItem.second == _value) + storageContents.insert(storageItem); + m_storageContent = move(storageContents); + + AssemblyItem item(Instruction::SSTORE, _location); + Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber); + StoreOperation operation(StoreOperation::Storage, _slot, m_sequenceNumber, id); + m_storageContent[_slot] = _value; + // increment a second time so that we get unique sequence numbers for writes + m_sequenceNumber++; + + return operation; +} + +ExpressionClasses::Id KnownState::loadFromStorage(Id _slot, SourceLocation const& _location) +{ + if (m_storageContent.count(_slot)) + return m_storageContent.at(_slot); + + AssemblyItem item(Instruction::SLOAD, _location); + return m_storageContent[_slot] = m_expressionClasses->find(item, {_slot}, true, m_sequenceNumber); +} + +KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, SourceLocation const& _location) +{ + if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value) + // do not execute the store if we know that the value is already there + return StoreOperation(); + m_sequenceNumber++; + decltype(m_memoryContent) memoryContents; + // copy over values at points where we know that they are different from _slot by at least 32 + for (auto const& memoryItem: m_memoryContent) + if (m_expressionClasses->knownToBeDifferentBy32(memoryItem.first, _slot)) + memoryContents.insert(memoryItem); + m_memoryContent = move(memoryContents); + + AssemblyItem item(Instruction::MSTORE, _location); + Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber); + StoreOperation operation(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id)); + m_memoryContent[_slot] = _value; + // increment a second time so that we get unique sequence numbers for writes + m_sequenceNumber++; + return operation; +} + +ExpressionClasses::Id KnownState::loadFromMemory(Id _slot, SourceLocation const& _location) +{ + if (m_memoryContent.count(_slot)) + return m_memoryContent.at(_slot); + + AssemblyItem item(Instruction::MLOAD, _location); + return m_memoryContent[_slot] = m_expressionClasses->find(item, {_slot}, true, m_sequenceNumber); +} + +KnownState::Id KnownState::applySha3( + Id _start, + Id _length, + SourceLocation const& _location +) +{ + AssemblyItem sha3Item(Instruction::SHA3, _location); + // Special logic if length is a short constant, otherwise we cannot tell. + u256 const* l = m_expressionClasses->knownConstant(_length); + // unknown or too large length + if (!l || *l > 128) + return m_expressionClasses->find(sha3Item, {_start, _length}, true, m_sequenceNumber); + + vector arguments; + for (u256 i = 0; i < *l; i += 32) + { + Id slot = m_expressionClasses->find( + AssemblyItem(Instruction::ADD, _location), + {_start, m_expressionClasses->find(i)} + ); + arguments.push_back(loadFromMemory(slot, _location)); + } + if (m_knownSha3Hashes.count(arguments)) + return m_knownSha3Hashes.at(arguments); + Id v; + // If all arguments are known constants, compute the sha3 here + if (all_of(arguments.begin(), arguments.end(), [this](Id _a) { return !!m_expressionClasses->knownConstant(_a); })) + { + bytes data; + for (Id a: arguments) + data += toBigEndian(*m_expressionClasses->knownConstant(a)); + data.resize(size_t(*l)); + v = m_expressionClasses->find(AssemblyItem(u256(sha3(data)), _location)); + } + else + v = m_expressionClasses->find(sha3Item, {_start, _length}, true, m_sequenceNumber); + return m_knownSha3Hashes[arguments] = v; +} + diff --git a/libevmasm/KnownState.h b/libevmasm/KnownState.h new file mode 100644 index 000000000..f7a3dd675 --- /dev/null +++ b/libevmasm/KnownState.h @@ -0,0 +1,163 @@ +/* + 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 KnownState.h + * @author Christian + * @date 2015 + * Contains knowledge about the state of the virtual machine at a specific instruction. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dev +{ +namespace eth +{ + +class AssemblyItem; +using AssemblyItems = std::vector; + +/** + * Class to infer and store knowledge about the state of the virtual machine at a specific + * instruction. + * + * The general workings are that for each assembly item that is fed, an equivalence class is + * derived from the operation and the equivalence class of its arguments. DUPi, SWAPi and some + * arithmetic instructions are used to infer equivalences while these classes are determined. + */ +class KnownState +{ +public: + using Id = ExpressionClasses::Id; + struct StoreOperation + { + enum Target { Invalid, Memory, Storage }; + StoreOperation(): target(Invalid), sequenceNumber(-1) {} + StoreOperation( + Target _target, + Id _slot, + unsigned _sequenceNumber, + Id _expression + ): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {} + bool isValid() const { return target != Invalid; } + Target target; + Id slot; + unsigned sequenceNumber; + Id expression; + }; + + explicit KnownState( + std::shared_ptr _expressionClasses = std::make_shared() + ): m_expressionClasses(_expressionClasses) + { + } + + /// Streams debugging information to @a _out. + std::ostream& stream(std::ostream& _out) const; + + /// Feeds the item into the system for analysis. + /// @returns a possible store operation + StoreOperation feedItem(AssemblyItem const& _item, bool _copyItem = false); + + /// Resets any knowledge about storage. + void resetStorage() { m_storageContent.clear(); } + /// Resets any knowledge about storage. + void resetMemory() { m_memoryContent.clear(); } + /// Resets any knowledge about the current stack. + void resetStack() { m_stackElements.clear(); m_stackHeight = 0; } + /// Resets any knowledge. + void reset() { resetStorage(); resetMemory(); resetStack(); } + + /// Manually increments the storage and memory sequence number. + void incrementSequenceNumber() { m_sequenceNumber += 2; } + + /// Replaces the state by the intersection with _other, i.e. only equal knowledge is retained. + /// If the stack heighht is different, the smaller one is used and the stack is compared + /// relatively. + void reduceToCommonKnowledge(KnownState const& _other); + + /// @returns a shared pointer to a copy of this state. + std::shared_ptr copy() const { return std::make_shared(*this); } + + /// @returns true if the knowledge about the state of both objects is (known to be) equal. + bool operator==(KnownState const& _other) const; + + ///@todo the sequence numbers in two copies of this class should never be the same. + /// might be doable using two-dimensional sequence numbers, where the first value is incremented + /// for each copy + + /// Retrieves the current equivalence class fo the given stack element (or generates a new + /// one if it does not exist yet). + Id stackElement(int _stackHeight, SourceLocation const& _location); + /// @returns the equivalence class id of the special initial stack element at the given height. + Id initialStackElement(int _stackHeight, SourceLocation const& _location); + + int stackHeight() const { return m_stackHeight; } + std::map const& stackElements() const { return m_stackElements; } + ExpressionClasses& expressionClasses() const { return *m_expressionClasses; } + +private: + /// Assigns a new equivalence class to the next sequence number of the given stack element. + void setStackElement(int _stackHeight, Id _class); + /// Swaps the given stack elements in their next sequence number. + void swapStackElements(int _stackHeightA, int _stackHeightB, SourceLocation const& _location); + + /// Increments the sequence number, deletes all storage information that might be overwritten + /// and stores the new value at the given slot. + /// @returns the store operation, which might be invalid if storage was not modified + StoreOperation storeInStorage(Id _slot, Id _value, SourceLocation const& _location); + /// Retrieves the current value at the given slot in storage or creates a new special sload class. + Id loadFromStorage(Id _slot, SourceLocation const& _location); + /// Increments the sequence number, deletes all memory information that might be overwritten + /// and stores the new value at the given slot. + /// @returns the store operation, which might be invalid if memory was not modified + StoreOperation storeInMemory(Id _slot, Id _value, SourceLocation const& _location); + /// Retrieves the current value at the given slot in memory or creates a new special mload class. + Id loadFromMemory(Id _slot, SourceLocation const& _location); + /// Finds or creates a new expression that applies the sha3 hash function to the contents in memory. + Id applySha3(Id _start, Id _length, SourceLocation const& _location); + + /// Current stack height, can be negative. + int m_stackHeight = 0; + /// Current stack layout, mapping stack height -> equivalence class + std::map m_stackElements; + /// Current sequence number, this is incremented with each modification to storage or memory. + unsigned m_sequenceNumber = 1; + /// Knowledge about storage content. + std::map m_storageContent; + /// Knowledge about memory content. Keys are memory addresses, note that the values overlap + /// and are not contained here if they are not completely known. + std::map m_memoryContent; + /// Keeps record of all sha3 hashes that are computed. + std::map, Id> m_knownSha3Hashes; + /// Structure containing the classes of equivalent expressions. + std::shared_ptr m_expressionClasses; +}; + +} +} diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 83d59efc7..056162b5f 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -122,3 +122,56 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item) return false; } } + + +bool SemanticInformation::isDeterministic(AssemblyItem const& _item) +{ + if (_item.type() != Operation) + return true; + + switch (_item.instruction()) + { + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::CREATE: + case Instruction::GAS: + case Instruction::PC: + case Instruction::MSIZE: // depends on previous writes and reads, not only on content + case Instruction::BALANCE: // depends on previous calls + case Instruction::EXTCODESIZE: + return false; + default: + return true; + } +} + +bool SemanticInformation::invalidatesMemory(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::CALLDATACOPY: + case Instruction::CODECOPY: + case Instruction::EXTCODECOPY: + case Instruction::MSTORE: + case Instruction::MSTORE8: + case Instruction::CALL: + case Instruction::CALLCODE: + return true; + default: + return false; + } +} + +bool SemanticInformation::invalidatesStorage(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::CREATE: + case Instruction::SSTORE: + return true; + default: + return false; + } +} diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index 27aa6f1a4..094f45912 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -23,6 +23,7 @@ #pragma once +#include namespace dev { @@ -45,6 +46,13 @@ struct SemanticInformation static bool isSwapInstruction(AssemblyItem const& _item); static bool isJumpInstruction(AssemblyItem const& _item); static bool altersControlFlow(AssemblyItem const& _item); + /// @returns false if the value put on the stack by _item depends on anything else than + /// the information in the current block header, memory, storage or stack. + static bool isDeterministic(AssemblyItem const& _item); + /// @returns true if the given instruction modifies memory. + static bool invalidatesMemory(Instruction _instruction); + /// @returns true if the given instruction modifies storage (even indirectly). + static bool invalidatesStorage(Instruction _instruction); }; } diff --git a/libjsconsole/CMakeLists.txt b/libjsconsole/CMakeLists.txt new file mode 100644 index 000000000..e8f98de88 --- /dev/null +++ b/libjsconsole/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_policy(SET CMP0015 NEW) +# this policy was introduced in cmake 3.0 +# remove if, once 3.0 will be used on unix +if (${CMAKE_MAJOR_VERSION} GREATER 2) + # old policy do not use MACOSX_RPATH + cmake_policy(SET CMP0042 OLD) +endif() + +set(CMAKE_AUTOMOC OFF) + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ${V8_INCLUDE_DIRS}) +include_directories(BEFORE ..) +include_directories(${READLINE_INCLUDE_DIRS}) +include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) + +set(EXECUTABLE jsconsole) + +file(GLOB HEADERS "*.h") + +add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) + +target_link_libraries(${EXECUTABLE} jsengine) +target_link_libraries(${EXECUTABLE} devcore) +target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES}) +target_link_libraries(${EXECUTABLE} web3jsonrpc) + +install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) diff --git a/libjsconsole/JSConsole.cpp b/libjsconsole/JSConsole.cpp new file mode 100644 index 000000000..791df2de4 --- /dev/null +++ b/libjsconsole/JSConsole.cpp @@ -0,0 +1,86 @@ +/* + 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 JSConsole.cpp + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#include +#include +#include +#include "JSConsole.h" +#include "JSV8Connector.h" +#include "libjsconsole/JSConsoleResources.hpp" + +// TODO! make readline optional! +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::eth; + +JSConsole::JSConsole(WebThreeDirect& _web3, std::vector const& _accounts): + m_engine(), + m_printer(m_engine) +{ + m_jsonrpcConnector.reset(new JSV8Connector(m_engine)); + m_jsonrpcServer.reset(new WebThreeStubServer(*m_jsonrpcConnector.get(), _web3, _accounts)); +} + +JSConsole::~JSConsole() {} + +void JSConsole::repl() const +{ + string cmd = ""; + g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << flush; rl_forced_update_display(); }; + + bool isEmpty = true; + int openBrackets = 0; + do { + char* buff = readline(promptForIndentionLevel(openBrackets).c_str()); + isEmpty = !(buff && *buff); + if (!isEmpty) + { + cmd += string(buff); + cmd += " "; + free(buff); + int open = count(cmd.begin(), cmd.end(), '{'); + open += count(cmd.begin(), cmd.end(), '('); + int closed = count(cmd.begin(), cmd.end(), '}'); + closed += count(cmd.begin(), cmd.end(), ')'); + openBrackets = open - closed; + } + } while (openBrackets > 0); + + if (!isEmpty) + { + add_history(cmd.c_str()); + auto value = m_engine.eval(cmd.c_str()); + string result = m_printer.prettyPrint(value).cstr(); + cout << result << endl; + } +} + +std::string JSConsole::promptForIndentionLevel(int _i) const +{ + if (_i == 0) + return "> "; + + return string((_i + 1) * 2, ' '); +} diff --git a/libjsconsole/JSConsole.h b/libjsconsole/JSConsole.h new file mode 100644 index 000000000..3b65691f6 --- /dev/null +++ b/libjsconsole/JSConsole.h @@ -0,0 +1,53 @@ +/* + 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 JSConsole.h + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#pragma once + +#include +#include + +class WebThreeStubServer; +namespace jsonrpc { class AbstractServerConnector; } + +namespace dev +{ +namespace eth +{ + +class JSConsole +{ +public: + JSConsole(WebThreeDirect& _web3, std::vector const& _accounts); + ~JSConsole(); + void repl() const; + +private: + std::string promptForIndentionLevel(int _i) const; + + JSV8Engine m_engine; + JSV8Printer m_printer; + std::unique_ptr m_jsonrpcServer; + std::unique_ptr m_jsonrpcConnector; +}; + +} +} diff --git a/libjsconsole/JSV8Connector.cpp b/libjsconsole/JSV8Connector.cpp new file mode 100644 index 000000000..ed560a368 --- /dev/null +++ b/libjsconsole/JSV8Connector.cpp @@ -0,0 +1,54 @@ +/* + 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 JSV8Connector.cpp + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#include "JSV8Connector.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +bool JSV8Connector::StartListening() +{ + return true; +} + +bool JSV8Connector::StopListening() +{ + return true; +} + +bool JSV8Connector::SendResponse(std::string const& _response, void* _addInfo) +{ + (void)_addInfo; + m_lastResponse = _response.c_str(); + return true; +} + +void JSV8Connector::onSend(char const* payload) +{ + OnRequest(payload, NULL); +} + +JSV8Connector::~JSV8Connector() +{ + StopListening(); +} diff --git a/libjsconsole/JSV8Connector.h b/libjsconsole/JSV8Connector.h new file mode 100644 index 000000000..98cef4c2c --- /dev/null +++ b/libjsconsole/JSV8Connector.h @@ -0,0 +1,50 @@ +/* + 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 JSV8Connector.h + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#pragma once + +#include +#include + +namespace dev +{ +namespace eth +{ + +class JSV8Connector: public jsonrpc::AbstractServerConnector, public JSV8RPC +{ + +public: + JSV8Connector(JSV8Engine const& _engine): JSV8RPC(_engine) {} + virtual ~JSV8Connector(); + + // implement AbstractServerConnector interface + bool StartListening(); + bool StopListening(); + bool SendResponse(std::string const& _response, void* _addInfo = nullptr); + + // implement JSV8RPC interface + void onSend(char const* payload); +}; + +} +} diff --git a/libjsengine/CMakeLists.txt b/libjsengine/CMakeLists.txt new file mode 100644 index 000000000..0023494c6 --- /dev/null +++ b/libjsengine/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_policy(SET CMP0015 NEW) +# this policy was introduced in cmake 3.0 +# remove if, once 3.0 will be used on unix +if (${CMAKE_MAJOR_VERSION} GREATER 2) + # old policy do not use MACOSX_RPATH + cmake_policy(SET CMP0042 OLD) +endif() + +set(CMAKE_AUTOMOC OFF) + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ${V8_INCLUDE_DIRS}) +include_directories(BEFORE ..) + +set(EXECUTABLE jsengine) + +file(GLOB HEADERS "*.h") + +include(EthUtils) +eth_add_resources("${CMAKE_CURRENT_SOURCE_DIR}/JSResources.cmake" "JSRES") +message(STATUS "HERE!!! ${JSRES}") +add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS} ${JSRES}) + +# macos brew version of v8 needs to be compiled with libstdc++ +# it also needs to be dynamic library +# xcode needs libstdc++ to be explicitly set as it's attribute +if (APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++") + set_property(TARGET ${EXECUTABLE} PROPERTY XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libstdc++") +endif() + +target_link_libraries(${EXECUTABLE} ${V8_LIBRARIES}) + +install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) diff --git a/libjsengine/Common.js b/libjsengine/Common.js new file mode 100644 index 000000000..3911409a7 --- /dev/null +++ b/libjsengine/Common.js @@ -0,0 +1,11 @@ +console = {}; +console.log = function () { +}; +console.warn = function () { +}; +console.error = function () { +}; + +setTimeout = function () { +}; + diff --git a/libjsengine/JSEngine.cpp b/libjsengine/JSEngine.cpp new file mode 100644 index 000000000..b2bad4859 --- /dev/null +++ b/libjsengine/JSEngine.cpp @@ -0,0 +1,36 @@ +/* + 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 JSEngine.cpp + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#include +#include +#include "JSEngine.h" + +using namespace dev; +using namespace dev::eth; + +JSString::JSString(char const* _cstr): m_cstr(strdup(_cstr)) {} + +JSString::~JSString() +{ + if (m_cstr) + free(m_cstr); +} diff --git a/libjsengine/JSEngine.h b/libjsengine/JSEngine.h new file mode 100644 index 000000000..ce54379a1 --- /dev/null +++ b/libjsengine/JSEngine.h @@ -0,0 +1,60 @@ +/* + 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 JSEngine.h + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#pragma once +#include + +namespace dev +{ +namespace eth +{ + +class JSException: public std::exception {}; +class JSPrintException: public JSException { char const* what() const noexcept { return "Cannot print expression!"; } }; + +class JSString +{ +public: + JSString(char const* _cstr); + ~JSString(); + char const* cstr() const { return m_cstr; } + +private: + char* m_cstr; +}; + +class JSValue +{ +public: + virtual JSString toString() const = 0; +}; + +template +class JSEngine +{ +public: + // should be used to evalute javascript expression + virtual T eval(char const* _cstr) const = 0; +}; + +} +} diff --git a/libjsengine/JSPrinter.cpp b/libjsengine/JSPrinter.cpp new file mode 100644 index 000000000..35e315a78 --- /dev/null +++ b/libjsengine/JSPrinter.cpp @@ -0,0 +1,23 @@ +/* + 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 JSPrinter.cpp + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#include "JSPrinter.h" diff --git a/libjsengine/JSPrinter.h b/libjsengine/JSPrinter.h new file mode 100644 index 000000000..bf13fcea7 --- /dev/null +++ b/libjsengine/JSPrinter.h @@ -0,0 +1,41 @@ +/* + 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 JSPrinter.h + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#pragma once + +#include "JSEngine.h" + +namespace dev +{ +namespace eth +{ + +template +class JSPrinter +{ +public: + virtual JSString print(T const& _value) const { return _value.toString(); } + virtual JSString prettyPrint(T const& _value) const { return print(_value); } +}; + +} +} diff --git a/libjsengine/JSResources.cmake b/libjsengine/JSResources.cmake new file mode 100644 index 000000000..d4370a8da --- /dev/null +++ b/libjsengine/JSResources.cmake @@ -0,0 +1,8 @@ + +set(web3 "${CMAKE_CURRENT_LIST_DIR}/../libjsqrc/ethereumjs/dist/web3.js") +set(pretty_print "${CMAKE_CURRENT_LIST_DIR}/PrettyPrint.js") +set(common "${CMAKE_CURRENT_LIST_DIR}/Common.js") + +set(ETH_RESOURCE_NAME "JSEngineResources") +set(ETH_RESOURCE_LOCATION "${CMAKE_CURRENT_BINARY_DIR}") +set(ETH_RESOURCES "web3" "pretty_print" "common") diff --git a/libjsengine/JSV8Engine.cpp b/libjsengine/JSV8Engine.cpp new file mode 100644 index 000000000..4e06f0f65 --- /dev/null +++ b/libjsengine/JSV8Engine.cpp @@ -0,0 +1,187 @@ +/* + 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 JSV8Engine.cpp + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#include +#include "JSV8Engine.h" +#include "libjsengine/JSEngineResources.hpp" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +namespace dev +{ +namespace eth +{ + +static char const* toCString(v8::String::Utf8Value const& _value) +{ + if (*_value) + return *_value; + throw JSPrintException(); +} + +// from: https://github.com/v8/v8-git-mirror/blob/master/samples/shell.cc +// v3.15 from: https://chromium.googlesource.com/v8/v8.git/+/3.14.5.9/samples/shell.cc +void reportException(v8::TryCatch* _tryCatch) +{ + v8::HandleScope handle_scope; + v8::String::Utf8Value exception(_tryCatch->Exception()); + char const* exceptionString = toCString(exception); + v8::Handle message = _tryCatch->Message(); + + // V8 didn't provide any extra information about this error; just + // print the exception. + if (message.IsEmpty()) + printf("%s\n", exceptionString); + else + { + // Print (filename):(line number): (message). + v8::String::Utf8Value filename(message->GetScriptResourceName()); + char const* filenameString = toCString(filename); + int linenum = message->GetLineNumber(); + printf("%s:%i: %s\n", filenameString, linenum, exceptionString); + + // Print line of source code. + v8::String::Utf8Value sourceline(message->GetSourceLine()); + char const* sourcelineString = toCString(sourceline); + printf("%s\n", sourcelineString); + + // Print wavy underline (GetUnderline is deprecated). + int start = message->GetStartColumn(); + for (int i = 0; i < start; i++) + printf(" "); + + int end = message->GetEndColumn(); + + for (int i = start; i < end; i++) + printf("^"); + + printf("\n"); + + v8::String::Utf8Value stackTrace(_tryCatch->StackTrace()); + if (stackTrace.length() > 0) + { + char const* stackTraceString = toCString(stackTrace); + printf("%s\n", stackTraceString); + } + } +} + +class JSV8Env +{ +public: + ~JSV8Env() + { + v8::V8::Dispose(); + } +}; + +class JSV8Scope +{ +public: + JSV8Scope(): + m_handleScope(), + m_context(v8::Context::New(NULL, v8::ObjectTemplate::New())), + m_contextScope(m_context) + { + m_context->Enter(); + } + + ~JSV8Scope() + { + m_context->Exit(); + m_context.Dispose(); + } + + v8::Persistent const& context() const { return m_context; } + +private: + v8::HandleScope m_handleScope; + v8::Persistent m_context; + v8::Context::Scope m_contextScope; +}; + +} +} + +JSV8Env JSV8Engine::s_env = JSV8Env(); + +JSString JSV8Value::toString() const +{ + if (m_value.IsEmpty()) + return ""; + + else if (m_value->IsUndefined()) + return "undefined"; + + v8::String::Utf8Value str(m_value); + return toCString(str); +} + +JSV8Engine::JSV8Engine(): m_scope(new JSV8Scope()) +{ + JSEngineResources resources; + string common = resources.loadResourceAsString("common"); + string web3 = resources.loadResourceAsString("web3"); + eval(common.c_str()); + eval(web3.c_str()); + eval("web3 = require('web3');"); +} + +JSV8Engine::~JSV8Engine() +{ + delete m_scope; +} + +JSV8Value JSV8Engine::eval(char const* _cstr) const +{ + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Local source = v8::String::New(_cstr); + v8::Local name(v8::String::New("(shell)")); + v8::ScriptOrigin origin(name); + v8::Handle script = v8::Script::Compile(source, &origin); + + // Make sure to wrap the exception in a new handle because + // the handle returned from the TryCatch is destroyed + if (script.IsEmpty()) + { + reportException(&tryCatch); + return v8::Exception::Error(v8::Local::New(tryCatch.Message()->Get())); + } + + auto result = script->Run(); + + if (result.IsEmpty()) + { + reportException(&tryCatch); + return v8::Exception::Error(v8::Local::New(tryCatch.Message()->Get())); + } + + return result; +} + +v8::Handle const& JSV8Engine::context() const +{ + return m_scope->context(); +} diff --git a/libjsengine/JSV8Engine.h b/libjsengine/JSV8Engine.h new file mode 100644 index 000000000..56459c5d0 --- /dev/null +++ b/libjsengine/JSV8Engine.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 JSV8Engine.h + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#pragma once + +#include +#include "JSEngine.h" + +namespace dev +{ +namespace eth +{ + +class JSV8Env; +class JSV8Scope; + +class JSV8Value: public JSValue +{ +public: + JSV8Value(v8::Handle _value): m_value(_value) {} + JSString toString() const; + v8::Handle const& value() const { return m_value; } + +private: + v8::Handle m_value; +}; + +class JSV8Engine: public JSEngine +{ +public: + JSV8Engine(); + virtual ~JSV8Engine(); + JSV8Value eval(char const* _cstr) const; + v8::Handle const& context() const; + +private: + static JSV8Env s_env; + JSV8Scope* m_scope; +}; + +} +} diff --git a/libjsengine/JSV8Printer.cpp b/libjsengine/JSV8Printer.cpp new file mode 100644 index 000000000..93c235859 --- /dev/null +++ b/libjsengine/JSV8Printer.cpp @@ -0,0 +1,49 @@ +/* + 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 JSV8Printer.cpp + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#include +#include "JSV8Printer.h" +#include "libjsengine/JSEngineResources.hpp" + +using namespace std; +using namespace dev; +using namespace eth; + +JSV8Printer::JSV8Printer(JSV8Engine const& _engine): m_engine(_engine) +{ + JSEngineResources resources; + string prettyPrint = resources.loadResourceAsString("pretty_print"); + m_engine.eval(prettyPrint.c_str()); +} + +JSString JSV8Printer::prettyPrint(JSV8Value const& _value) const +{ + v8::HandleScope handleScope; + v8::Local pp = v8::String::New("prettyPrint"); + v8::Handle func = v8::Handle::Cast(m_engine.context()->Global()->Get(pp)); + v8::Local values[1] = {v8::Local::New(_value.value())}; + v8::Local res = func->Call(func, 1, values); + v8::String::Utf8Value str(res); + if (*str) + return *str; + throw JSPrintException(); +} diff --git a/libjsengine/JSV8Printer.h b/libjsengine/JSV8Printer.h new file mode 100644 index 000000000..2ec9c78b6 --- /dev/null +++ b/libjsengine/JSV8Printer.h @@ -0,0 +1,43 @@ +/* + 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 JSV8Printer.h + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#pragma once + +#include "JSPrinter.h" +#include "JSV8Engine.h" + +namespace dev +{ +namespace eth +{ + +class JSV8Printer: public JSPrinter +{ +public: + JSV8Printer(JSV8Engine const& _engine); + JSString prettyPrint(JSV8Value const& _value) const; +private: + JSV8Engine const& m_engine; +}; + +} +} diff --git a/libjsengine/JSV8RPC.cpp b/libjsengine/JSV8RPC.cpp new file mode 100644 index 000000000..994cfbbf2 --- /dev/null +++ b/libjsengine/JSV8RPC.cpp @@ -0,0 +1,84 @@ +/* + 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 JSV8RPC.cpp + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#include "JSV8RPC.h" + +using namespace dev; +using namespace dev::eth; + +namespace dev +{ +namespace eth +{ + +v8::Handle JSV8RPCSend(v8::Arguments const& _args) +{ + v8::Local JSON = v8::String::New("JSON"); + v8::Local parse = v8::String::New("parse"); + v8::Local stringify = v8::String::New("stringify"); + v8::Handle jsonObject = v8::Handle::Cast(v8::Context::GetCurrent()->Global()->Get(JSON)); + v8::Handle parseFunc = v8::Handle::Cast(jsonObject->Get(parse)); + v8::Handle stringifyFunc = v8::Handle::Cast(jsonObject->Get(stringify)); + + v8::Local self = _args.Holder(); + v8::Local wrap = v8::Local::Cast(self->GetInternalField(0)); + JSV8RPC* that = static_cast(wrap->Value()); + v8::Local vals[1] = {_args[0]->ToObject()}; + v8::Local stringifiedArg = stringifyFunc->Call(stringifyFunc, 1, vals); + v8::String::Utf8Value str(stringifiedArg); + that->onSend(*str); + + v8::Local values[1] = {v8::String::New(that->lastResponse())}; + return parseFunc->Call(parseFunc, 1, values); +} + +} +} + +JSV8RPC::JSV8RPC(JSV8Engine const& _engine): m_engine(_engine) +{ + v8::HandleScope scope; + v8::Local rpcTemplate = v8::ObjectTemplate::New(); + rpcTemplate->SetInternalFieldCount(1); + rpcTemplate->Set(v8::String::New("send"), + v8::FunctionTemplate::New(JSV8RPCSend)); + rpcTemplate->Set(v8::String::New("sendAsync"), + v8::FunctionTemplate::New(JSV8RPCSend)); + + v8::Local obj = rpcTemplate->NewInstance(); + obj->SetInternalField(0, v8::External::New(this)); + + v8::Local web3 = v8::String::New("web3"); + v8::Local setProvider = v8::String::New("setProvider"); + v8::Handle web3object = v8::Handle::Cast(m_engine.context()->Global()->Get(web3)); + v8::Handle func = v8::Handle::Cast(web3object->Get(setProvider)); + v8::Local values[1] = {obj}; + func->Call(func, 1, values); + + m_lastResponse = R"( + { + "id": 1, + "jsonrpc": "2.0", + "error": "Uninitalized JSV8RPC!" + } + )"; +} diff --git a/libjsengine/JSV8RPC.h b/libjsengine/JSV8RPC.h new file mode 100644 index 000000000..a2180ef51 --- /dev/null +++ b/libjsengine/JSV8RPC.h @@ -0,0 +1,47 @@ +/* + 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 JSV8RPC.h + * @author Marek Kotewicz + * @date 2015 + * Ethereum client. + */ + +#pragma once + +#include + +namespace dev +{ +namespace eth +{ + +class JSV8RPC +{ +public: + JSV8RPC(JSV8Engine const& _engine); + virtual void onSend(char const* _payload) = 0; + char const* lastResponse() const { return m_lastResponse; } + +private: + JSV8Engine const& m_engine; + +protected: + char const* m_lastResponse; +}; + +} +} diff --git a/libjsengine/PrettyPrint.js b/libjsengine/PrettyPrint.js new file mode 100644 index 000000000..f617cf29c --- /dev/null +++ b/libjsengine/PrettyPrint.js @@ -0,0 +1,91 @@ +var prettyPrint = (function () { + function pp(object, indent) { + try { + JSON.stringify(object) + } catch(e) { + return pp(e, indent); + } + var str = ""; + if(object instanceof Array) { + str += "["; + for(var i = 0, l = object.length; i < l; i++) { + str += pp(object[i], indent); + if(i < l-1) { + str += ", "; + } + } + str += " ]"; + } else if (object instanceof Error) { + str += "\033[31m" + "Error:\033[0m " + object.message; + } else if (object === null) { + str += "\033[1m\033[30m" + "null"; + } else if(typeof(object) === "undefined") { + str += "\033[1m\033[30m" + object; + } else if (isBigNumber(object)) { + str += "\033[32m'" + object.toString(10) + "'"; + } else if(typeof(object) === "object") { + str += "{\n"; + indent += " "; + var last = getFields(object).pop() + getFields(object).forEach(function (k) { + str += indent + k + ": "; + try { + str += pp(object[k], indent); + } catch (e) { + str += pp(e, indent); + } + if(k !== last) { + str += ","; + } + str += "\n"; + }); + str += indent.substr(2, indent.length) + "}"; + } else if(typeof(object) === "string") { + str += "\033[32m'" + object + "'"; + } else if(typeof(object) === "number") { + str += "\033[31m" + object; + } else if(typeof(object) === "function") { + str += "\033[35m[Function]"; + } else { + str += object; + } + str += "\033[0m"; + return str; + } + var redundantFields = [ + 'valueOf', + 'toString', + 'toLocaleString', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor', + '__defineGetter__', + '__defineSetter__', + '__lookupGetter__', + '__lookupSetter__', + '__proto__' + ]; + var getFields = function (object) { + var result = Object.getOwnPropertyNames(object); + if (object.constructor && object.constructor.prototype) { + result = result.concat(Object.getOwnPropertyNames(object.constructor.prototype)); + } + return result.filter(function (field) { + return redundantFields.indexOf(field) === -1; + }); + }; + var isBigNumber = function (object) { + return (!!object.constructor && object.constructor.name === 'BigNumber') || + (typeof BigNumber !== 'undefined' && object instanceof BigNumber) + }; + function prettyPrintInner(/* */) { + var args = arguments; + var ret = ""; + for(var i = 0, l = args.length; i < l; i++) { + ret += pp(args[i], "") + "\n"; + } + return ret; + }; + return prettyPrintInner; +})(); diff --git a/libp2p/Common.h b/libp2p/Common.h index 15ee981bc..da7558ecb 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -214,3 +214,26 @@ struct Node /// Simple stream output for a NodeIPEndpoint. std::ostream& operator<<(std::ostream& _out, dev::p2p::NodeIPEndpoint const& _ep); } + +/// std::hash for asio::adress +namespace std +{ + +template <> struct hash +{ + size_t operator()(bi::address const& _a) const + { + if (_a.is_v4()) + return std::hash()(_a.to_v4().to_ulong()); + if (_a.is_v6()) + { + auto const& range = _a.to_v6().to_bytes(); + return boost::hash_range(range.begin(), range.end()); + } + if (_a.is_unspecified()) + return static_cast(0x3487194039229152ul); // Chosen by fair dice roll, guaranteed to be random + return std::hash()(_a.to_string()); + } +}; + +} diff --git a/libp2p/Host.h b/libp2p/Host.h index 41f4d1e72..b9af0e8b0 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -227,7 +227,7 @@ private: std::shared_ptr m_nodeTable; ///< Node table (uses kademlia-like discovery). /// Shared storage of Peer objects. Peers are created or destroyed on demand by the Host. Active sessions maintain a shared_ptr to a Peer; - std::map> m_peers; + std::unordered_map> m_peers; /// Peers we try to connect regardless of p2p network. std::set m_requiredPeers; @@ -235,7 +235,7 @@ private: /// The nodes to which we are currently connected. Used by host to service peer requests and keepAlivePeers and for shutdown. (see run()) /// Mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. - mutable std::map> m_sessions; + mutable std::unordered_map> m_sessions; mutable RecursiveMutex x_sessions; std::list> m_connecting; ///< Pending connections. diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index 847e6a42d..d8ab90a20 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -154,12 +154,12 @@ int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, NetworkPreferences const& _acceptor.bind(endpoint); _acceptor.listen(); retport = _acceptor.local_endpoint().port(); + assert(retport == _netPrefs.listenPort); } catch (...) { clog(NetWarn) << "Couldn't start accepting connections on host. Failed to accept socket.\n" << boost::current_exception_diagnostic_information(); } - assert(retport == _netPrefs.listenPort); return retport; } return retport; diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index c3215da71..e926356b7 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -81,7 +81,8 @@ shared_ptr NodeTable::addNode(Node const& _node, NodeRelation _relati { shared_ptr ret(new NodeEntry(m_node, _node.id, _node.endpoint)); ret->pending = false; - m_nodes[_node.id] = ret; + DEV_GUARDED(x_nodes) + m_nodes[_node.id] = ret; noteActiveNode(_node.id, _node.endpoint); return ret; } @@ -101,14 +102,13 @@ shared_ptr NodeTable::addNode(Node const& _node, NodeRelation _relati return move(shared_ptr()); } - { - Guard ln(x_nodes); + DEV_GUARDED(x_nodes) if (m_nodes.count(_node.id)) return m_nodes[_node.id]; - } shared_ptr ret(new NodeEntry(m_node, _node.id, _node.endpoint)); - m_nodes[_node.id] = ret; + DEV_GUARDED(x_nodes) + m_nodes[_node.id] = ret; clog(NodeTableConnect) << "addNode pending for" << _node.endpoint; ping(_node.endpoint); return ret; @@ -186,7 +186,8 @@ void NodeTable::discover(NodeId _node, unsigned _round, shared_ptrendpoint, _node); p.sign(m_secret); - m_findNodeTimeout.push_back(make_pair(r->id, chrono::steady_clock::now())); + DEV_GUARDED(x_findNodeTimeout) + m_findNodeTimeout.push_back(make_pair(r->id, chrono::steady_clock::now())); m_socketPointer->send(p); } @@ -447,17 +448,17 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes { if (auto n = nodeEntry(nodeid)) n->pending = false; - else if (m_pubkDiscoverPings.count(_from.address())) + else { + DEV_GUARDED(x_pubkDiscoverPings) { - Guard l(x_pubkDiscoverPings); + if (!m_pubkDiscoverPings.count(_from.address())) + return; // unsolicited pong; don't note node as active m_pubkDiscoverPings.erase(_from.address()); } if (!haveNode(nodeid)) addNode(Node(nodeid, NodeIPEndpoint(_from.address(), _from.port(), _from.port()))); } - else - return; // unsolicited pong; don't note node as active } // update our endpoint address and UDP port @@ -473,14 +474,15 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes { bool expected = false; auto now = chrono::steady_clock::now(); - m_findNodeTimeout.remove_if([&](NodeIdTimePoint const& t) - { - if (t.first == nodeid && now - t.second < c_reqTimeout) - expected = true; - else if (t.first == nodeid) - return true; - return false; - }); + DEV_GUARDED(x_findNodeTimeout) + m_findNodeTimeout.remove_if([&](NodeIdTimePoint const& t) + { + if (t.first == nodeid && now - t.second < c_reqTimeout) + expected = true; + else if (t.first == nodeid) + return true; + return false; + }); if (!expected) { diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 458044997..92bf17f79 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -80,7 +80,7 @@ protected: Mutex x_events; std::list m_nodeEventHandler; - std::map m_events; + std::unordered_map m_events; }; class NodeTable; @@ -243,32 +243,32 @@ private: /// Purges and pings nodes for any buckets which haven't been touched for c_bucketRefresh seconds. void doRefreshBuckets(boost::system::error_code const& _ec); - std::unique_ptr m_nodeEventHandler; ///< Event handler for node events. + std::unique_ptr m_nodeEventHandler; ///< Event handler for node events. - Node m_node; ///< This node. - Secret m_secret; ///< This nodes secret key. + Node m_node; ///< This node. + Secret m_secret; ///< This nodes secret key. - mutable Mutex x_nodes; ///< LOCK x_state first if both locks are required. Mutable for thread-safe copy in nodes() const. - std::map> m_nodes; ///< Nodes + mutable Mutex x_nodes; ///< LOCK x_state first if both locks are required. Mutable for thread-safe copy in nodes() const. + std::unordered_map> m_nodes; ///< Nodes - mutable Mutex x_state; ///< LOCK x_state first if both x_nodes and x_state locks are required. - std::array m_state; ///< State of p2p node network. + mutable Mutex x_state; ///< LOCK x_state first if both x_nodes and x_state locks are required. + std::array m_state; ///< State of p2p node network. - Mutex x_evictions; ///< LOCK x_evictions first if both x_nodes and x_evictions locks are required. - std::deque m_evictions; ///< Eviction timeouts. + Mutex x_evictions; ///< LOCK x_evictions first if both x_nodes and x_evictions locks are required. + std::deque m_evictions; ///< Eviction timeouts. - Mutex x_pubkDiscoverPings; ///< LOCK x_nodes first if both x_nodes and x_pubkDiscoverPings locks are required. - std::map m_pubkDiscoverPings; ///< List of pending pings where node entry wasn't created due to unkown pubk. + Mutex x_pubkDiscoverPings; ///< LOCK x_nodes first if both x_nodes and x_pubkDiscoverPings locks are required. + std::unordered_map m_pubkDiscoverPings; ///< List of pending pings where node entry wasn't created due to unkown pubk. Mutex x_findNodeTimeout; - std::list m_findNodeTimeout; ///< Timeouts for pending Ping and FindNode requests. + std::list m_findNodeTimeout; ///< Timeouts for pending Ping and FindNode requests. - ba::io_service& m_io; ///< Used by bucket refresh timer. - std::shared_ptr m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr. - NodeSocket* m_socketPointer; ///< Set to m_socket.get(). Socket is created in constructor and disconnected in destructor to ensure access to pointer is safe. + ba::io_service& m_io; ///< Used by bucket refresh timer. + std::shared_ptr m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr. + NodeSocket* m_socketPointer; ///< Set to m_socket.get(). Socket is created in constructor and disconnected in destructor to ensure access to pointer is safe. - boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. - boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. + boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. + boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. }; inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) diff --git a/libp2p/UDP.cpp b/libp2p/UDP.cpp index eeb3a0b1a..9f89d9ad0 100644 --- a/libp2p/UDP.cpp +++ b/libp2p/UDP.cpp @@ -20,9 +20,13 @@ */ #include "UDP.h" +using namespace std; using namespace dev; using namespace dev::p2p; +const char* RLPXWarn::name() { return "!X!"; } +const char* RLPXNote::name() { return "-X-"; } + h256 RLPXDatagramFace::sign(Secret const& _k) { assert(packetType()); diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 97136ee43..b09d556e7 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "Common.h" namespace ba = boost::asio; @@ -40,6 +41,9 @@ namespace dev namespace p2p { +struct RLPXWarn: public LogChannel { static const char* name(); static const int verbosity = 0; }; +struct RLPXNote: public LogChannel { static const char* name(); static const int verbosity = 1; }; + /** * UDP Datagram * @todo make data protected/functional @@ -203,14 +207,14 @@ void UDPSocket::doRead() auto self(UDPSocket::shared_from_this()); m_socket.async_receive_from(boost::asio::buffer(m_recvData), m_recvEndpoint, [this, self](boost::system::error_code _ec, size_t _len) { - // ASIO Safety: It is possible that ASIO will call lambda w/o an error - // and after the socket has been disconnected. Checking m_closed - // guarantees that m_host will not be called after disconnect(). - if (_ec || m_closed) + if (m_closed) return disconnectWithError(_ec); + + if (_ec != boost::system::errc::success) + clog(NetWarn) << "Receiving UDP message failed. " << _ec.value() << ":" << _ec.message(); - assert(_len); - m_host.onReceived(this, m_recvEndpoint, bytesConstRef(m_recvData.data(), _len)); + if (_len) + m_host.onReceived(this, m_recvEndpoint, bytesConstRef(m_recvData.data(), _len)); doRead(); }); } @@ -223,17 +227,19 @@ void UDPSocket::doWrite() const UDPDatagram& datagram = m_sendQ[0]; auto self(UDPSocket::shared_from_this()); - m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.endpoint(), [this, self](boost::system::error_code _ec, std::size_t) + bi::udp::endpoint endpoint(datagram.endpoint()); + m_socket.async_send_to(boost::asio::buffer(datagram.data), endpoint, [this, self, endpoint](boost::system::error_code _ec, std::size_t) { - if (_ec || m_closed) + if (m_closed) return disconnectWithError(_ec); - else - { - Guard l(x_sendQ); - m_sendQ.pop_front(); - if (m_sendQ.empty()) - return; - } + + if (_ec != boost::system::errc::success) + clog(NetWarn) << "Failed delivering UDP message. " << _ec.value() << ":" << _ec.message(); + + Guard l(x_sendQ); + m_sendQ.pop_front(); + if (m_sendQ.empty()) + return; doWrite(); }); } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index fde0b71b0..be5c9a6cd 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -1220,7 +1220,7 @@ public: /// Stores a set of possible declarations referenced by this identifier. Has to be resolved /// providing argument types using overloadResolution before the referenced declaration /// is accessed. - void setOverloadedDeclarations(std::set const& _declarations) + void setOverloadedDeclarations(std::vector const& _declarations) { m_overloadedDeclarations = _declarations; } @@ -1237,8 +1237,8 @@ private: /// Stores a reference to the current contract. This is needed because types of base contracts /// change depending on the context. ContractDefinition const* m_currentContract = nullptr; - /// A set of overloaded declarations, right now only FunctionDefinition has overloaded declarations. - std::set m_overloadedDeclarations; + /// A vector of overloaded declarations, right now only FunctionDefinition has overloaded declarations. + std::vector m_overloadedDeclarations; }; /** diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index dabf6470a..25678c176 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -42,7 +42,7 @@ public: ASTPrinter( ASTNode const& _ast, std::string const& _source = std::string(), - StructuralGasEstimator::ASTGasConsumption const& _gasCosts = {} + StructuralGasEstimator::ASTGasConsumption const& _gasCosts = StructuralGasEstimator::ASTGasConsumption() ); /// Output the string representation of the AST to _stream. void print(std::ostream& _stream); diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index fb2ab3156..bcd4f9d68 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -206,16 +206,9 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) { - // We do not check the calldata size, everything is zero-padded. - unsigned offset(CompilerUtils::dataStartOffset); + // We do not check the calldata size, everything is zero-paddedd - bigint parameterHeadEnd = offset; - for (TypePointer const& type: _typeParameters) - parameterHeadEnd += type->isDynamicallySized() ? 32 : type->getCalldataEncodedSize(); - solAssert(parameterHeadEnd <= numeric_limits::max(), "Arguments too large."); - - unsigned stackHeightOfPreviousDynamicArgument = 0; - ArrayType const* previousDynamicType = nullptr; + m_context << u256(CompilerUtils::dataStartOffset); for (TypePointer const& type: _typeParameters) { switch (type->getCategory()) @@ -223,34 +216,31 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool case Type::Category::Array: if (type->isDynamicallySized()) { - // put on stack: data_offset length - unsigned newStackHeight = m_context.getStackHeight(); - if (previousDynamicType) - { - // Retrieve data start offset by adding length to start offset of previous dynamic type - unsigned stackDepth = m_context.getStackHeight() - stackHeightOfPreviousDynamicArgument; - solAssert(stackDepth <= 16, "Stack too deep."); - m_context << eth::dupInstruction(stackDepth) << eth::dupInstruction(stackDepth); - ArrayUtils(m_context).convertLengthToSize(*previousDynamicType, true); - m_context << eth::Instruction::ADD; - } - else - m_context << u256(parameterHeadEnd); - stackHeightOfPreviousDynamicArgument = newStackHeight; - previousDynamicType = &dynamic_cast(*type); - offset += CompilerUtils(m_context).loadFromMemory(offset, IntegerType(256), !_fromMemory); + // put on stack: data_pointer length + CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); + // stack: data_offset next_pointer + //@todo once we support nested arrays, this offset needs to be dynamic. + m_context << eth::Instruction::SWAP1 << u256(CompilerUtils::dataStartOffset); + m_context << eth::Instruction::ADD; + // stack: next_pointer data_pointer + // retrieve length + CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); + // stack: next_pointer length data_pointer + m_context << eth::Instruction::SWAP2; } else { - m_context << u256(offset); - offset += type->getCalldataEncodedSize(); + // leave the pointer on the stack + m_context << eth::Instruction::DUP1; + m_context << u256(type->getCalldataEncodedSize()) << eth::Instruction::ADD; } break; default: solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); - offset += CompilerUtils(m_context).loadFromMemory(offset, *type, !_fromMemory, true); + CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true); } } + m_context << eth::Instruction::POP; } void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters) diff --git a/libsolidity/DeclarationContainer.cpp b/libsolidity/DeclarationContainer.cpp index c836663c7..3e23d93b8 100644 --- a/libsolidity/DeclarationContainer.cpp +++ b/libsolidity/DeclarationContainer.cpp @@ -37,6 +37,7 @@ Declaration const* DeclarationContainer::conflictingDeclaration(Declaration cons declarations += m_declarations.at(name); if (m_invisibleDeclarations.count(name)) declarations += m_invisibleDeclarations.at(name); + if (dynamic_cast(&_declaration)) { // check that all other declarations with the same name are functions @@ -66,14 +67,13 @@ bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, return false; if (_invisible) - m_invisibleDeclarations[name].insert(&_declaration); + m_invisibleDeclarations[name].push_back(&_declaration); else - m_declarations[name].insert(&_declaration); - + m_declarations[name].push_back(&_declaration); return true; } -set DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const +std::vector DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const { solAssert(!_name.empty(), "Attempt to resolve empty name."); auto result = m_declarations.find(_name); @@ -81,5 +81,5 @@ set DeclarationContainer::resolveName(ASTString const& _name return result->second; if (_recursive && m_enclosingContainer) return m_enclosingContainer->resolveName(_name, true); - return set({}); + return vector({}); } diff --git a/libsolidity/DeclarationContainer.h b/libsolidity/DeclarationContainer.h index 94545eefb..0f0b57179 100644 --- a/libsolidity/DeclarationContainer.h +++ b/libsolidity/DeclarationContainer.h @@ -48,17 +48,17 @@ public: /// @param _update if true, replaces a potential declaration that is already present /// @returns false if the name was already declared. bool registerDeclaration(Declaration const& _declaration, bool _invisible = false, bool _update = false); - std::set resolveName(ASTString const& _name, bool _recursive = false) const; + std::vector resolveName(ASTString const& _name, bool _recursive = false) const; Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; } - std::map> const& getDeclarations() const { return m_declarations; } + std::map> const& getDeclarations() const { return m_declarations; } /// @returns whether declaration is valid, and if not also returns previous declaration. Declaration const* conflictingDeclaration(Declaration const& _declaration) const; private: Declaration const* m_enclosingDeclaration; DeclarationContainer const* m_enclosingContainer; - std::map> m_declarations; - std::map> m_invisibleDeclarations; + std::map> m_declarations; + std::map> m_invisibleDeclarations; }; } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 9aebbf054..5ef14f60b 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -53,9 +53,13 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) m_currentScope = &m_scopes[&_contract]; linearizeBaseContracts(_contract); - // we first import non-functions only as we do not yet know the argument types - for (ContractDefinition const* base: _contract.getLinearizedBaseContracts()) - importInheritedScope(*base, false); // import non-functions + std::vector properBases( + ++_contract.getLinearizedBaseContracts().begin(), + _contract.getLinearizedBaseContracts().end() + ); + + for (ContractDefinition const* base: properBases) + importInheritedScope(*base); for (ASTPointer const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, &_contract, nullptr); @@ -80,8 +84,6 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) } m_currentScope = &m_scopes[&_contract]; - for (ContractDefinition const* base: _contract.getLinearizedBaseContracts()) - importInheritedScope(*base, true); // import functions // now resolve references inside the code for (ASTPointer const& modifier: _contract.getFunctionModifiers()) @@ -115,20 +117,55 @@ void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope."); } -set NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const +vector NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const { auto iterator = m_scopes.find(_scope); if (iterator == end(m_scopes)) - return set({}); + return vector({}); return iterator->second.resolveName(_name, false); } -set NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) +vector NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) { return m_currentScope->resolveName(_name, _recursive); } -void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base, bool _importFunctions) +vector NameAndTypeResolver::cleanedDeclarations( + Identifier const& _identifier, + vector const& _declarations +) +{ + solAssert(_declarations.size() > 1, ""); + vector uniqueFunctions; + + for (auto it = _declarations.begin(); it != _declarations.end(); ++it) + { + solAssert(*it, ""); + // the declaration is functionDefinition while declarations > 1 + FunctionDefinition const& functionDefinition = dynamic_cast(**it); + FunctionType functionType(functionDefinition); + for (auto parameter: functionType.getParameterTypes() + functionType.getReturnParameterTypes()) + if (!parameter) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(_identifier.getLocation()) << + errinfo_comment("Function type can not be used in this context") + ); + if (uniqueFunctions.end() == find_if( + uniqueFunctions.begin(), + uniqueFunctions.end(), + [&](Declaration const* d) + { + FunctionType newFunctionType(dynamic_cast(*d)); + return functionType.hasEqualArgumentTypes(newFunctionType); + } + )) + uniqueFunctions.push_back(*it); + } + return uniqueFunctions; +} + +void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base) { auto iterator = m_scopes.find(&_base); solAssert(iterator != end(m_scopes), ""); @@ -136,30 +173,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base, for (auto const& declaration: nameAndDeclaration.second) // Import if it was declared in the base, is not the constructor and is visible in derived classes if (declaration->getScope() == &_base && declaration->isVisibleInDerivedContracts()) - { - auto function = dynamic_cast(declaration); - if ((function == nullptr) == _importFunctions) - continue; - if (!!function) - { - FunctionType functionType(*function); - // only import if a function with the same arguments does not exist yet - bool functionWithEqualArgumentsFound = false; - for (auto knownDeclaration: m_currentScope->resolveName(nameAndDeclaration.first)) - { - auto knownFunction = dynamic_cast(knownDeclaration); - if (!knownFunction) - continue; // this is not legal, but will be caught later - if (!FunctionType(*knownFunction).hasEqualArgumentTypes(functionType)) - continue; - functionWithEqualArgumentsFound = true; - break; - } - if (functionWithEqualArgumentsFound) - continue; - } m_currentScope->registerDeclaration(*declaration); - } } void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const @@ -465,10 +479,9 @@ bool ReferencesResolver::visit(Identifier& _identifier) errinfo_comment("Undeclared identifier.") ); else if (declarations.size() == 1) - _identifier.setReferencedDeclaration(**declarations.begin(), m_currentContract); + _identifier.setReferencedDeclaration(*declarations.front(), m_currentContract); else - // Duplicate declaration will be checked in checkTypeRequirements() - _identifier.setOverloadedDeclarations(declarations); + _identifier.setOverloadedDeclarations(m_resolver.cleanedDeclarations(_identifier, declarations)); return false; } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 6528bbef2..d7a0a3b2f 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -56,19 +56,24 @@ public: /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, /// the global scope is used (i.e. the one containing only the contract). /// @returns a pointer to the declaration on success or nullptr on failure. - std::set resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const; + std::vector resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const; /// Resolves a name in the "current" scope. Should only be called during the initial /// resolving phase. - std::set getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); + std::vector getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); + + /// returns the vector of declarations without repetitions + static std::vector cleanedDeclarations( + Identifier const& _identifier, + std::vector const& _declarations + ); private: void reset(); - /// Either imports all non-function members or all function members declared directly in the - /// given contract (i.e. does not import inherited members) into the current scope if they are - ///not present already. - void importInheritedScope(ContractDefinition const& _base, bool _importFunctions); + /// Imports all members declared directly in the given contract (i.e. does not import inherited members) + /// into the current scope if they are not present already. + void importInheritedScope(ContractDefinition const& _base); /// Computes "C3-Linearization" of base contracts and stores it inside the contract. void linearizeBaseContracts(ContractDefinition& _contract) const; diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 65a6867d6..da2fcdb89 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -617,6 +617,7 @@ public: /// @returns true if this function can take the given argument types (possibly /// after implicit conversion). bool canTakeArguments(TypePointers const& _arguments) const; + /// @returns true if the types of parameters are equal (does't check return parameter types) bool hasEqualArgumentTypes(FunctionType const& _other) const; Location const& getLocation() const { return m_location; } diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 94a43871f..12486d529 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -298,16 +298,16 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message } WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, vector const& _accounts): - AbstractWebThreeStubServer(_conn), m_accounts(make_shared(bind(&WebThreeStubServerBase::client, this))) + AbstractWebThreeStubServer(_conn), m_ethAccounts(make_shared(bind(&WebThreeStubServerBase::client, this))) { - m_accounts->setAccounts(_accounts); + m_ethAccounts->setAccounts(_accounts); } void WebThreeStubServerBase::setIdentities(vector const& _ids) { - m_ids.clear(); + m_shhIds.clear(); for (auto i: _ids) - m_ids[i.pub()] = i.secret(); + m_shhIds[i.pub()] = i.secret(); } string WebThreeStubServerBase::web3_sha3(string const& _param1) @@ -353,7 +353,7 @@ string WebThreeStubServerBase::eth_gasPrice() Json::Value WebThreeStubServerBase::eth_accounts() { Json::Value ret(Json::arrayValue); - for (auto const& i: m_accounts->getAllAccounts()) + for (auto const& i: m_ethAccounts->getAllAccounts()) ret.append(toJS(i)); return ret; } @@ -499,7 +499,7 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json) TransactionSkeleton t = toTransaction(_json); if (!t.from) - t.from = m_accounts->getDefaultTransactAccount(); + t.from = m_ethAccounts->getDefaultTransactAccount(); if (t.creation) ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));; if (t.gasPrice == UndefinedU256) @@ -507,9 +507,9 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json) if (t.gas == UndefinedU256) t.gas = min(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice); - if (m_accounts->isRealAccount(t.from)) + if (m_ethAccounts->isRealAccount(t.from)) authenticate(t, false); - else if (m_accounts->isProxyAccount(t.from)) + else if (m_ethAccounts->isProxyAccount(t.from)) authenticate(t, true); return ret; @@ -528,17 +528,17 @@ string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json) TransactionSkeleton t = toTransaction(_json); if (!t.from) - t.from = m_accounts->getDefaultTransactAccount(); + t.from = m_ethAccounts->getDefaultTransactAccount(); if (t.creation) ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));; - if (!t.gasPrice) + if (t.gasPrice == UndefinedU256) t.gasPrice = 10 * dev::eth::szabo; // TODO: should be determined by user somehow. - if (!t.gas) + if (t.gas == UndefinedU256) t.gas = min(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice); - if (m_accounts->isRealAccount(t.from)) + if (m_ethAccounts->isRealAccount(t.from)) authenticate(t, false); - else if (m_accounts->isProxyAccount(t.from)) + else if (m_ethAccounts->isProxyAccount(t.from)) authenticate(t, true); return toJS((t.creation ? Transaction(t.value, t.gasPrice, t.gas, t.data) : Transaction(t.value, t.gasPrice, t.gas, t.to, t.data)).sha3(WithoutSignature)); @@ -579,15 +579,15 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const& { TransactionSkeleton t = toTransaction(_json); if (!t.from) - t.from = m_accounts->getDefaultTransactAccount(); + t.from = m_ethAccounts->getDefaultTransactAccount(); // if (!m_accounts->isRealAccount(t.from)) // return ret; - if (!t.gasPrice) + if (t.gasPrice == UndefinedU256) t.gasPrice = 10 * dev::eth::szabo; - if (!t.gas) + if (t.gas == UndefinedU256) t.gas = client()->gasLimitRemaining(); - return toJS(client()->call(m_accounts->secretKey(t.from), t.value, t.to, t.data, t.gas, t.gasPrice, jsToBlockNumber(_blockNumber), FudgeFactor::Lenient).output); + return toJS(client()->call(t.from, t.value, t.to, t.data, t.gas, t.gasPrice, jsToBlockNumber(_blockNumber), FudgeFactor::Lenient).output); } catch (...) { @@ -879,7 +879,7 @@ string WebThreeStubServerBase::eth_register(string const& _address) { try { - return toJS(m_accounts->addProxyAccount(jsToAddress(_address))); + return toJS(m_ethAccounts->addProxyAccount(jsToAddress(_address))); } catch (...) { @@ -891,7 +891,7 @@ bool WebThreeStubServerBase::eth_unregister(string const& _accountId) { try { - return m_accounts->removeProxyAccount(jsToInt(_accountId)); + return m_ethAccounts->removeProxyAccount(jsToInt(_accountId)); } catch (...) { @@ -906,9 +906,9 @@ Json::Value WebThreeStubServerBase::eth_fetchQueuedTransactions(string const& _a auto id = jsToInt(_accountId); Json::Value ret(Json::arrayValue); // TODO: throw an error on no account with given id - for (TransactionSkeleton const& t: m_accounts->getQueuedTransactions(id)) + for (TransactionSkeleton const& t: m_ethAccounts->getQueuedTransactions(id)) ret.append(toJson(t)); - m_accounts->clearQueue(id); + m_ethAccounts->clearQueue(id); return ret; } catch (...) @@ -934,11 +934,11 @@ bool WebThreeStubServerBase::shh_post(Json::Value const& _json) { shh::Message m = toMessage(_json); Secret from; - if (m.from() && m_ids.count(m.from())) + if (m.from() && m_shhIds.count(m.from())) { cwarn << "Silently signing message from identity" << m.from() << ": User validation hook goes here."; // TODO: insert validification hook here. - from = m_ids[m.from()]; + from = m_shhIds[m.from()]; } face()->inject(toSealed(_json, m, from)); @@ -953,7 +953,7 @@ bool WebThreeStubServerBase::shh_post(Json::Value const& _json) string WebThreeStubServerBase::shh_newIdentity() { KeyPair kp = KeyPair::create(); - m_ids[kp.pub()] = kp.secret(); + m_shhIds[kp.pub()] = kp.secret(); return toJS(kp.pub()); } @@ -961,7 +961,7 @@ bool WebThreeStubServerBase::shh_hasIdentity(string const& _identity) { try { - return m_ids.count(jsToPublic(_identity)) > 0; + return m_shhIds.count(jsToPublic(_identity)) > 0; } catch (...) { @@ -1021,7 +1021,7 @@ Json::Value WebThreeStubServerBase::shh_getFilterChanges(string const& _filterId int id = jsToInt(_filterId); auto pub = m_shhWatches[id]; - if (!pub || m_ids.count(pub)) + if (!pub || m_shhIds.count(pub)) for (h256 const& h: face()->checkWatch(id)) { auto e = face()->envelope(h); @@ -1029,7 +1029,7 @@ Json::Value WebThreeStubServerBase::shh_getFilterChanges(string const& _filterId if (pub) { cwarn << "Silently decrypting message from identity" << pub << ": User validation hook goes here."; - m = e.open(face()->fullTopic(id), m_ids[pub]); + m = e.open(face()->fullTopic(id), m_shhIds[pub]); } else m = e.open(face()->fullTopic(id)); @@ -1054,7 +1054,7 @@ Json::Value WebThreeStubServerBase::shh_getMessages(string const& _filterId) int id = jsToInt(_filterId); auto pub = m_shhWatches[id]; - if (!pub || m_ids.count(pub)) + if (!pub || m_shhIds.count(pub)) for (h256 const& h: face()->watchMessages(id)) { auto e = face()->envelope(h); @@ -1062,7 +1062,7 @@ Json::Value WebThreeStubServerBase::shh_getMessages(string const& _filterId) if (pub) { cwarn << "Silently decrypting message from identity" << pub << ": User validation hook goes here."; - m = e.open(face()->fullTopic(id), m_ids[pub]); + m = e.open(face()->fullTopic(id), m_shhIds[pub]); } else m = e.open(face()->fullTopic(id)); @@ -1081,14 +1081,14 @@ Json::Value WebThreeStubServerBase::shh_getMessages(string const& _filterId) void WebThreeStubServerBase::authenticate(TransactionSkeleton const& _t, bool _toProxy) { if (_toProxy) - m_accounts->queueTransaction(_t); + m_ethAccounts->queueTransaction(_t); else if (_t.to) - client()->submitTransaction(m_accounts->secretKey(_t.from), _t.value, _t.to, _t.data, _t.gas, _t.gasPrice); + client()->submitTransaction(m_ethAccounts->secretKey(_t.from), _t.value, _t.to, _t.data, _t.gas, _t.gasPrice); else - client()->submitTransaction(m_accounts->secretKey(_t.from), _t.value, _t.data, _t.gas, _t.gasPrice); + client()->submitTransaction(m_ethAccounts->secretKey(_t.from), _t.value, _t.data, _t.gas, _t.gasPrice); } void WebThreeStubServerBase::setAccounts(const vector& _accounts) { - m_accounts->setAccounts(_accounts); + m_ethAccounts->setAccounts(_accounts); } diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 3166eefad..425e6567e 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -136,7 +136,7 @@ public: void setAccounts(std::vector const& _accounts); void setIdentities(std::vector const& _ids); - std::map const& ids() const { return m_ids; } + std::map const& ids() const { return m_shhIds; } protected: virtual void authenticate(dev::eth::TransactionSkeleton const& _t, bool _toProxy); @@ -147,9 +147,10 @@ protected: virtual dev::WebThreeNetworkFace* network() = 0; virtual dev::WebThreeStubDatabaseFace* db() = 0; - std::map m_ids; + std::shared_ptr m_ethAccounts; + + std::map m_shhIds; std::map m_shhWatches; - std::shared_ptr m_accounts; }; } //namespace dev diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index fa5ec1c27..c558c71f3 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -211,7 +211,7 @@ void ClientModel::setupState(QVariantMap _state) QVariantList stateContracts = _state.value("contracts").toList(); QVariantList transactions = _state.value("transactions").toList(); - map accounts; + unordered_map accounts; std::vector userAccounts; for (auto const& b: stateAccounts) @@ -284,7 +284,7 @@ void ClientModel::setupState(QVariantMap _state) executeSequence(transactionSequence, accounts, Secret(_state.value("miner").toMap().value("secret").toString().toStdString())); } -void ClientModel::executeSequence(vector const& _sequence, std::map const& _accounts, Secret const& _miner) +void ClientModel::executeSequence(vector const& _sequence, std::unordered_map const& _accounts, Secret const& _miner) { if (m_running) { @@ -551,7 +551,7 @@ QVariant ClientModel::formatValue(SolidityType const& _type, u256 const& _value) return res; } -QVariant ClientModel::formatStorageValue(SolidityType const& _type, map const& _storage, unsigned _offset, u256 const& _slot) +QVariant ClientModel::formatStorageValue(SolidityType const& _type, unordered_map const& _storage, unsigned _offset, u256 const& _slot) { u256 slot = _slot; QVariantList values; diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 91b66c76c..b88ae8511 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -220,14 +220,14 @@ private: RecordLogEntry* lastBlock() const; QVariantMap contractAddresses() const; QVariantList gasCosts() const; - void executeSequence(std::vector const& _sequence, std::map const& _accounts, Secret const& _miner); + void executeSequence(std::vector const& _sequence, std::unordered_map const& _accounts, Secret const& _miner); dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings()); void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); void onNewTransaction(); void onStateReset(); void showDebuggerForTransaction(ExecutionResult const& _t); QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); - QVariant formatStorageValue(SolidityType const& _type, std::map const& _storage, unsigned _offset, dev::u256 const& _slot); + QVariant formatStorageValue(SolidityType const& _type, std::unordered_map const& _storage, unsigned _offset, dev::u256 const& _slot); std::atomic m_running; std::atomic m_mining; diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 6b29d8b98..07ab8dd73 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -36,6 +36,14 @@ using namespace dev::mix; bytes ContractCallDataEncoder::encodedData() { bytes r(m_encodedData); + size_t headerSize = m_encodedData.size() & ~0x1fUL; //ignore any prefix that is not 32-byte aligned + //apply offsets + for (auto const& p: m_offsetMap) + { + vector_ref offsetRef(r.data() + p.first, 32); + toBigEndian>(p.second + headerSize, offsetRef); //add header size minus signature hash + } + r.insert(r.end(), m_dynamicData.begin(), m_dynamicData.end()); return r; } @@ -64,6 +72,9 @@ void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& if (_type.dynamicSize) { + bytes empty(32); + size_t sizePos = m_dynamicData.size(); + m_dynamicData += empty; //reserve space for count if (_type.type == SolidityType::Type::Bytes) count = encodeSingleItem(_data.toString(), _type, m_dynamicData); else @@ -72,9 +83,10 @@ void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& for (auto const& item: strList) encodeSingleItem(item, _type, m_dynamicData); } - bytes sizeEnc(32); - toBigEndian(count, sizeEnc); - m_encodedData.insert(m_encodedData.end(), sizeEnc.begin(), sizeEnc.end()); + vector_ref sizeRef(m_dynamicData.data() + sizePos, 32); + toBigEndian(count, sizeRef); + m_offsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos)); + m_encodedData += empty; //reserve space for offset } else { diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index cab00dd93..2707845ae 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -73,6 +73,7 @@ private: private: bytes m_encodedData; bytes m_dynamicData; + std::vector> m_offsetMap; }; } diff --git a/mix/MachineStates.h b/mix/MachineStates.h index afd2b990a..2a88d83bf 100644 --- a/mix/MachineStates.h +++ b/mix/MachineStates.h @@ -50,7 +50,7 @@ namespace mix dev::u256s stack; dev::bytes memory; dev::bigint gasCost; - std::map storage; + std::unordered_map storage; std::vector levels; unsigned codeIndex; unsigned dataIndex; diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index b1d8f889e..cac208ba4 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -69,14 +69,14 @@ bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) MixClient::MixClient(std::string const& _dbPath): m_dbPath(_dbPath) { - resetState(std::map()); + resetState(std::unordered_map()); } MixClient::~MixClient() { } -void MixClient::resetState(std::map const& _accounts, Secret const& _miner) +void MixClient::resetState(std::unordered_map const& _accounts, Secret const& _miner) { WriteGuard l(x_state); Guard fl(x_filtersWatches); @@ -98,19 +98,30 @@ void MixClient::resetState(std::map const& _accounts, Secret m_executions.clear(); } -Transaction MixClient::replaceGas(Transaction const& _t, Secret const& _secret, u256 const& _gas) +Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secret const& _secret) { - if (_t.isCreation()) - return Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret); + Transaction ret; + if (_secret) + { + if (_t.isCreation()) + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret); + else + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret); + } else - return Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret); + { + if (_t.isCreation()) + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce()); + else + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce()); + ret.forceSender(_t.safeSender()); + } + return ret; } void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret) { - Transaction t = _gasAuto ? replaceGas(_t, _secret, m_state.gasLimitRemaining()) : _t; - bytes rlp = t.rlp(); - + Transaction t = _gasAuto ? replaceGas(_t, m_state.gasLimitRemaining()) : _t; // do debugging run first LastHashes lastHashes(256); lastHashes[0] = bc().numberHash(bc().number()); @@ -120,7 +131,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c State execState = _state; execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation Executive execution(execState, lastHashes, 0); - execution.initialize(&rlp); + execution.initialize(t); execution.execute(); std::vector machineStates; std::vector levels; @@ -219,7 +230,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c // execute on a state if (!_call) { - t = _gasAuto ? replaceGas(_t, _secret, d.gasUsed) : _t; + t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t; er =_state.execute(lastHashes, t); if (t.isCreation() && _state.code(d.contractAddress).empty()) BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); @@ -295,18 +306,17 @@ Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes cons return address; } -dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto, FudgeFactor _ff) +dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto, FudgeFactor _ff) { (void)_blockNumber; - Address a = toAddress(_secret); State temp = asOf(eth::PendingBlock); - u256 n = temp.transactionsFrom(a); - Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + u256 n = temp.transactionsFrom(_from); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n); + t.forceSender(_from); if (_ff == FudgeFactor::Lenient) - temp.addBalance(a, (u256)(t.gasRequired() * t.gasPrice() + t.value())); - bytes rlp = t.rlp(); + temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); WriteGuard lw(x_state); //TODO: lock is required only for last execution state - executeTransaction(t, temp, true, _gasAuto, _secret); + executeTransaction(t, temp, true, _gasAuto); return lastExecution().result; } @@ -320,28 +330,27 @@ Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes cons return submitTransaction(_secret, _endowment, _init, _gas, _gasPrice, false); } -dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff) +dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff) { - return call(_secret, _value, _dest, _data, _gas, _gasPrice, _blockNumber, false, _ff); + return call(_from, _value, _dest, _data, _gas, _gasPrice, _blockNumber, false, _ff); } -dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff) +dev::eth::ExecutionResult MixClient::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff) { (void)_blockNumber; u256 n; - Address a = toAddress(_secret); State temp; { ReadGuard lr(x_state); temp = asOf(eth::PendingBlock); - n = temp.transactionsFrom(a); + n = temp.transactionsFrom(_from); } - Transaction t(_value, _gasPrice, _gas, _data, n, _secret); + Transaction t(_value, _gasPrice, _gas, _data, n); + t.forceSender(_from); if (_ff == FudgeFactor::Lenient) - temp.addBalance(a, (u256)(t.gasRequired() * t.gasPrice() + t.value())); - bytes rlp = t.rlp(); + temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); WriteGuard lw(x_state); //TODO: lock is required only for last execution state - executeTransaction(t, temp, true, false, _secret); + executeTransaction(t, temp, true, false); return lastExecution().result; } diff --git a/mix/MixClient.h b/mix/MixClient.h index 182e333c2..2c6734234 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -48,19 +48,19 @@ public: MixClient(std::string const& _dbPath); virtual ~MixClient(); /// Reset state to the empty state with given balance. - void resetState(std::map const& _accounts, Secret const& _miner = Secret()); + void resetState(std::unordered_map const& _accounts, Secret const& _miner = Secret()); void mine(); ExecutionResult lastExecution() const; ExecutionResult execution(unsigned _index) const; void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override; - dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override; - dev::eth::ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override; + dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override; + dev::eth::ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override; void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto); Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto); - dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict); + dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict); void setAddress(Address _us) override; void startMining() override; @@ -87,9 +87,9 @@ protected: virtual void prepareForTransaction() override {} private: - void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret); + void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret()); void noteChanged(h256Set const& _filters); - dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::Secret const& _secret, dev::u256 const& _gas); + dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret()); eth::State m_state; eth::State m_startState; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a97bb86fc..39a235c58 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,6 +25,11 @@ add_subdirectory(libethereum) add_subdirectory(libevm) add_subdirectory(libnatspec) add_subdirectory(libp2p) + +if (JSCONSOLE) + add_subdirectory(libjsengine) +endif() + if (SOLIDITY) add_subdirectory(libsolidity) endif () @@ -41,6 +46,10 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) +if (JSCONSOLE) + include_directories(${V8_INCLUDE_DIRS}) +endif() + # search for test names and create ctest tests enable_testing() foreach(file ${SRC_LIST}) @@ -65,14 +74,22 @@ target_link_libraries(testeth ${CURL_LIBRARIES}) target_link_libraries(testeth ethereum) target_link_libraries(testeth ethcore) target_link_libraries(testeth secp256k1) + +if (JSCONSOLE) + target_link_libraries(testeth jsengine) +endif() + if (SOLIDITY) target_link_libraries(testeth solidity) endif () + target_link_libraries(testeth testutils) + if (GUI AND NOT JUSTTESTS) target_link_libraries(testeth webthree) target_link_libraries(testeth natspec) endif() + if (JSONRPC) target_link_libraries(testeth web3jsonrpc) target_link_libraries(testeth ${JSON_RPC_CPP_CLIENT_LIBRARIES}) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 144a1a286..96e11e02c 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -305,7 +305,7 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta if (addressOptions.m_bHasStorage) { - map stateStorage = _statePost.storage(a.first); + unordered_map stateStorage = _statePost.storage(a.first); for (auto const& s: _stateExpect.storage(a.first)) CHECK(stateStorage[s.first] == s.second, "Check State: " << a.first << ": incorrect storage [" << s.first << "] = " << toHex(stateStorage[s.first]) << ", expected [" << s.first << "] = " << toHex(s.second)); diff --git a/test/fuzzTesting/checkRandomStateTest.cpp b/test/fuzzTesting/checkRandomStateTest.cpp index 719798620..01366cd4d 100644 --- a/test/fuzzTesting/checkRandomStateTest.cpp +++ b/test/fuzzTesting/checkRandomStateTest.cpp @@ -173,7 +173,7 @@ bool doStateTest(mValue& _v) } //checkStorage(importer.m_statePost.storage(expectedAddr), theState.storage(expectedAddr), expectedAddr); - map _resultStore = theState.storage(expectedAddr); + unordered_map _resultStore = theState.storage(expectedAddr); for (auto&& expectedStorePair : importer.m_statePost.storage(expectedAddr)) { diff --git a/test/libdevcrypto/crypto.cpp b/test/libdevcrypto/crypto.cpp index 96826bdf9..88ff98965 100644 --- a/test/libdevcrypto/crypto.cpp +++ b/test/libdevcrypto/crypto.cpp @@ -593,15 +593,14 @@ BOOST_AUTO_TEST_CASE(ecies_aes128_ctr_unaligned) // TESTING: send encrypt magic sequence bytes magic {0x22,0x40,0x08,0x91}; bytes magicCipherAndMac; - encryptSymNoAuth(encryptK, &magic, magicCipherAndMac, h128()); + magicCipherAndMac = encryptSymNoAuth(encryptK, h128(), &magic); magicCipherAndMac.resize(magicCipherAndMac.size() + 32); sha3mac(egressMac.ref(), &magic, egressMac.ref()); egressMac.ref().copyTo(bytesRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32)); - bytes plaintext; bytesConstRef cipher(&magicCipherAndMac[0], magicCipherAndMac.size() - 32); - decryptSymNoAuth(encryptK, h128(), cipher, plaintext); + bytes plaintext = decryptSymNoAuth(encryptK, h128(), cipher); plaintext.resize(magic.size()); BOOST_REQUIRE(plaintext.size() > 0); @@ -615,10 +614,10 @@ BOOST_AUTO_TEST_CASE(ecies_aes128_ctr) bytesConstRef msg((byte*)m.data(), m.size()); bytes ciphertext; - auto iv = encryptSymNoAuth(k, msg, ciphertext); + h128 iv; + tie(ciphertext, iv) = encryptSymNoAuth(k, msg); - bytes plaintext; - decryptSymNoAuth(k, iv, &ciphertext, plaintext); + bytes plaintext = decryptSymNoAuth(k, iv, &ciphertext); BOOST_REQUIRE_EQUAL(asString(plaintext), m); } diff --git a/test/libethereum/BlockTestsFiller/bcTotalDifficultyTestFiller.json b/test/libethereum/BlockTestsFiller/bcTotalDifficultyTestFiller.json new file mode 100644 index 000000000..1acaa23e9 --- /dev/null +++ b/test/libethereum/BlockTestsFiller/bcTotalDifficultyTestFiller.json @@ -0,0 +1,1596 @@ +{ + "sideChainWithMoreTransactions" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "40" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "nonce" : "4" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "blocknumber" : "1", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "2", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "31059", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "300" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "0x3453454", + "gasLimit" : "31509", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "300" + }, + { + "data" : "", + "gasLimit" : "31509", + "gasPrice" : "1", + "nonce" : "4", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "195e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "300" + } + ], + "uncleHeaders" : [ + ] + } + ] + }, + + "lotsOfBranchesOverrideAtTheEnd" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "820" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "nonce" : "4" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "blocknumber" : "1", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "2", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314134359", + "gasPrice" : "1343", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "0x34235435346", + "gasLimit" : "314143359", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "3331", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "200" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "0x44634634", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "200" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "31059", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "300" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "0x3453454", + "gasLimit" : "31509", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "300" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "795e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "1000" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "31400", + "gasPrice" : "100000000", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "400" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "10", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "400" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "5", + "transactions" : [ + { + "data" : "0x44634634", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "400" + } + ], + "uncleHeaders" : [ + ] + } + ] + }, + + "lotsOfBranchesOverrideAtTheMiddle" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "420" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "nonce" : "4" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "blocknumber" : "1", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "2", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314134359", + "gasPrice" : "1343", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "0x34235435346", + "gasLimit" : "314143359", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "3331", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "200" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "0x44634634", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "200" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "5", + "transactions" : [ + { + "data" : "0x44634634", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "200" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "31059", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "300" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "0x3453454", + "gasLimit" : "31509", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "300" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "795e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "1000" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "31400", + "gasPrice" : "100000000", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "400" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "10", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "400" + } + ], + "uncleHeaders" : [ + ] + } + ] + }, + + "lotsOfBranches" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "40" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "nonce" : "4" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "blocknumber" : "1", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "2", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314134359", + "gasPrice" : "1343", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "0x34235435346", + "gasLimit" : "314143359", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "3331", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "200" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "0x44634634", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "200" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "31059", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "300" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "0x3453454", + "gasLimit" : "31509", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "300" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "795e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "1000" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "31400", + "gasPrice" : "100000000", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "400" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "10", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "400" + } + ], + "uncleHeaders" : [ + ] + } + ] + }, + + "lotsOfLeafs" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "nonce" : "3" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "blocknumber" : "1", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "0" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "2", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "0" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "1" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314134359", + "gasPrice" : "1343", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "2" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "0x34235435346", + "gasLimit" : "314143359", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "3" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "3331", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "4" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "0x44634634", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "5" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "31059", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "6" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "0x3453454", + "gasLimit" : "31509", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "7" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "795e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "9" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "31400", + "gasPrice" : "100000000000", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "10", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "11" + } + ], + "uncleHeaders" : [ + ] + } + ] + }, + + "sideChainWithNewMaxDifficultyStartingFromBlock3AfterBlock4" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "45" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "nonce" : "5" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "blocknumber" : "1", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "1" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "2", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "3" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "5" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "7" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "11" + } + ], + "uncleHeaders" : [ + { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "0x020040", + "extraData" : "0x", + "gasLimit" : "0x2fefd8", + "gasUsed" : "0x5208", + "hash" : "9168fbfb2e3fbedfdb9eac6f73b6d7c9323c957fc3406f27a2f44983f0e5dd43", + "mixHash" : "d56ea25bfefc68484759c3dd934030624308b7ab02bb20aee17852aff72f1397", + "nonce" : "6e7970f59660cbd7", + "number" : "0x02", + "parentHash" : "b7afc3043361f44fd15c0f8df8ebab8284d01c8f79fb47b6fc75eec98b4b4683", + "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "a2a5e3d96e902272adb58e90c364f7b92684c539e0ded77356cf33d966f917fe", + "timestamp" : "0x553e25de", + "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "13" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "5", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "4", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "17" + } + ], + "uncleHeaders" : [ + ] + } + ] + }, + + "uncleBlockAtBlock3afterBlock4" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "16" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "nonce" : "4" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "blocknumber" : "1", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "1" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "2", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "3" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "5" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "4", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "3", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "7" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "11" + } + ], + "uncleHeaders" : [ + { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "0x020040", + "extraData" : "0x", + "gasLimit" : "0x2fefd8", + "gasUsed" : "0x5208", + "hash" : "9168fbfb2e3fbedfdb9eac6f73b6d7c9323c957fc3406f27a2f44983f0e5dd43", + "mixHash" : "d56ea25bfefc68484759c3dd934030624308b7ab02bb20aee17852aff72f1397", + "nonce" : "6e7970f59660cbd7", + "number" : "0x02", + "parentHash" : "b7afc3043361f44fd15c0f8df8ebab8284d01c8f79fb47b6fc75eec98b4b4683", + "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "a2a5e3d96e902272adb58e90c364f7b92684c539e0ded77356cf33d966f917fe", + "timestamp" : "0x553e25de", + "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + } + ] + }, + + "uncleBlockAtBlock3AfterBlock3" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "9" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "nonce" : "3" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "blocknumber" : "1", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "1" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "2", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "3" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "5" + } + ], + "uncleHeaders" : [ + ] + }, + { + "blocknumber" : "3", + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "7" + } + ], + "uncleHeaders" : [ + { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "0x020040", + "extraData" : "0x", + "gasLimit" : "0x2fefd8", + "gasUsed" : "0x5208", + "hash" : "9168fbfb2e3fbedfdb9eac6f73b6d7c9323c957fc3406f27a2f44983f0e5dd43", + "mixHash" : "d56ea25bfefc68484759c3dd934030624308b7ab02bb20aee17852aff72f1397", + "nonce" : "6e7970f59660cbd7", + "number" : "0x02", + "parentHash" : "b7afc3043361f44fd15c0f8df8ebab8284d01c8f79fb47b6fc75eec98b4b4683", + "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "a2a5e3d96e902272adb58e90c364f7b92684c539e0ded77356cf33d966f917fe", + "timestamp" : "0x553e25de", + "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + } + ] + } +} diff --git a/test/libethereum/blockchain.cpp b/test/libethereum/blockchain.cpp index 954e65c8a..c5ac936fe 100644 --- a/test/libethereum/blockchain.cpp +++ b/test/libethereum/blockchain.cpp @@ -48,26 +48,35 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) { for (auto& i: _v.get_obj()) { - cerr << i.first << endl; mObject& o = i.second.get_obj(); + if (test::Options::get().singleTest && test::Options::get().singleTestName != i.first) + { + o.clear(); + continue; + } + cerr << i.first << endl; BOOST_REQUIRE(o.count("genesisBlockHeader")); BlockInfo biGenesisBlock = constructBlock(o["genesisBlockHeader"].get_obj()); BOOST_REQUIRE(o.count("pre")); ImportTest importer(o["pre"].get_obj()); - TransientDirectory td_stateDB; TransientDirectory td_stateDB_tmp; - State state(State::openDB(td_stateDB.path()), BaseState::Empty, biGenesisBlock.coinbaseAddress); - State stateTemp(State::openDB(td_stateDB_tmp.path()), BaseState::Empty, biGenesisBlock.coinbaseAddress); - importer.importState(o["pre"].get_obj(), state); - o["pre"] = fillJsonWithState(state); - state.commit(); + State trueState(OverlayDB(State::openDB(td_stateDB_tmp.path())), BaseState::Empty, biGenesisBlock.coinbaseAddress); + + //Imported blocks from the start + typedef std::vector uncleList; + typedef std::pair blockSet; + std::vector blockSets; + + importer.importState(o["pre"].get_obj(), trueState); + o["pre"] = fillJsonWithState(trueState); + trueState.commit(); if (_fillin) - biGenesisBlock.stateRoot = state.rootHash(); + biGenesisBlock.stateRoot = trueState.rootHash(); else - BOOST_CHECK_MESSAGE(biGenesisBlock.stateRoot == state.rootHash(), "root hash does not match"); + BOOST_CHECK_MESSAGE(biGenesisBlock.stateRoot == trueState.rootHash(), "root hash does not match"); if (_fillin) { @@ -83,20 +92,55 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) biGenesisBlock.verifyInternals(&rlpGenesisBlock.out()); o["genesisRLP"] = toHex(rlpGenesisBlock.out(), 2, HexPrefix::Add); - // construct blockchain + // construct true blockchain TransientDirectory td; - BlockChain bc(rlpGenesisBlock.out(), td.path(), WithExisting::Kill); + BlockChain trueBc(rlpGenesisBlock.out(), td.path(), WithExisting::Kill); if (_fillin) { BOOST_REQUIRE(o.count("blocks")); mArray blArray; + + blockSet genesis; + genesis.first = rlpGenesisBlock.out(); + genesis.second = uncleList(); + blockSets.push_back(genesis); vector vBiBlocks; vBiBlocks.push_back(biGenesisBlock); + + size_t importBlockNumber = 0; for (auto const& bl: o["blocks"].get_array()) { - stateTemp = state; mObject blObj = bl.get_obj(); + if (blObj.count("blocknumber") > 0) + importBlockNumber = std::max((int)toInt(blObj["blocknumber"]), 1); + else + importBlockNumber++; + + //each time construct a new blockchain up to importBlockNumber (to generate next block header) + vBiBlocks.clear(); + vBiBlocks.push_back(biGenesisBlock); + + TransientDirectory td_stateDB, td_bc; + BlockChain bc(rlpGenesisBlock.out(), td_bc.path(), WithExisting::Kill); + State state(OverlayDB(State::openDB(td_stateDB.path())), BaseState::Empty, biGenesisBlock.coinbaseAddress); + importer.importState(o["pre"].get_obj(), state); + state.commit(); + + for (size_t i = 1; i < importBlockNumber; i++) //0 block is genesis + { + BlockQueue uncleQueue; + uncleList uncles = blockSets.at(i).second; + for (size_t j = 0; j < uncles.size(); j++) + uncleQueue.import(&uncles.at(j), bc); + + const bytes block = blockSets.at(i).first; + bc.sync(uncleQueue, state.db(), 4); + bc.attemptImport(block, state.db()); + vBiBlocks.push_back(BlockInfo(block)); + + state.sync(bc); + } // get txs TransactionQueue txs; @@ -115,6 +159,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) blObj["uncleHeaders"] = importUncles(blObj, vBiUncles, vBiBlocks); BlockQueue uncleBlockQueue; + uncleList uncleBlockQueueList; cnote << "import uncle in blockQueue"; for (size_t i = 0; i < vBiUncles.size(); i++) { @@ -122,6 +167,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) try { uncleBlockQueue.import(&uncle.out(), bc); + uncleBlockQueueList.push_back(uncle.out()); } catch(...) { @@ -205,6 +251,25 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) bc.import(block2.out(), state.db()); state.sync(bc); state.commit(); + + //there we get new blockchain status in state which could have more difficulty than we have in trueState + //attempt to import new block to the true blockchain + trueBc.sync(uncleBlockQueue, trueState.db(), 4); + trueBc.attemptImport(block2.out(), trueState.db()); + trueState.sync(trueBc); + + blockSet newBlock; + newBlock.first = block2.out(); + newBlock.second = uncleBlockQueueList; + if (importBlockNumber < blockSets.size()) + { + //make new correct history of imported blocks + blockSets[importBlockNumber] = newBlock; + for (size_t i = importBlockNumber + 1; i < blockSets.size(); i++) + blockSets.pop_back(); + } + else + blockSets.push_back(newBlock); } // if exception is thrown, RLP is invalid and no blockHeader, Transaction list, or Uncle list should be given catch (...) @@ -213,7 +278,6 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) blObj.erase(blObj.find("blockHeader")); blObj.erase(blObj.find("uncleHeaders")); blObj.erase(blObj.find("transactions")); - state = stateTemp; //revert state as if it was before executing this block } blArray.push_back(blObj); this_thread::sleep_for(chrono::seconds(1)); @@ -224,14 +288,15 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) stateOptionsMap expectStateMap; State stateExpect(OverlayDB(), BaseState::Empty, biGenesisBlock.coinbaseAddress); importer.importState(o["expect"].get_obj(), stateExpect, expectStateMap); - ImportTest::checkExpectedState(stateExpect, state, expectStateMap, Options::get().checkState ? WhenError::Throw : WhenError::DontThrow); + ImportTest::checkExpectedState(stateExpect, trueState, expectStateMap, Options::get().checkState ? WhenError::Throw : WhenError::DontThrow); o.erase(o.find("expect")); } o["blocks"] = blArray; - o["postState"] = fillJsonWithState(state); + o["postState"] = fillJsonWithState(trueState); + o["lastblockhash"] = toString(trueBc.info().hash()); - //make all values hex + //make all values hex in pre section State prestate(OverlayDB(), BaseState::Empty, biGenesisBlock.coinbaseAddress); importer.importState(o["pre"].get_obj(), prestate); o["pre"] = fillJsonWithState(prestate); @@ -241,14 +306,17 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) { for (auto const& bl: o["blocks"].get_array()) { + bool importedAndBest = true; mObject blObj = bl.get_obj(); bytes blockRLP; try { - state.sync(bc); blockRLP = importByteArray(blObj["rlp"].get_str()); - bc.import(blockRLP, state.db()); - state.sync(bc); + trueState.sync(trueBc); + trueBc.import(blockRLP, trueState.db()); + if (trueBc.info() != BlockInfo(blockRLP)) + importedAndBest = false; + trueState.sync(trueBc); } // if exception is thrown, RLP is invalid and no blockHeader, Transaction list, or Uncle list should be given catch (Exception const& _e) @@ -284,124 +352,131 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) const RLP c_blockHeaderRLP(c_rlpBytesBlockHeader); blockHeaderFromFields.populateFromHeader(c_blockHeaderRLP, IgnoreNonce); - BlockInfo blockFromRlp = bc.info(); - - //Check the fields restored from RLP to original fields - BOOST_CHECK_MESSAGE(blockHeaderFromFields.headerHash(WithNonce) == blockFromRlp.headerHash(WithNonce), "hash in given RLP not matching the block hash!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.parentHash == blockFromRlp.parentHash, "parentHash in given RLP not matching the block parentHash!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.sha3Uncles == blockFromRlp.sha3Uncles, "sha3Uncles in given RLP not matching the block sha3Uncles!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.coinbaseAddress == blockFromRlp.coinbaseAddress,"coinbaseAddress in given RLP not matching the block coinbaseAddress!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.stateRoot == blockFromRlp.stateRoot, "stateRoot in given RLP not matching the block stateRoot!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.transactionsRoot == blockFromRlp.transactionsRoot, "transactionsRoot in given RLP not matching the block transactionsRoot!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.receiptsRoot == blockFromRlp.receiptsRoot, "receiptsRoot in given RLP not matching the block receiptsRoot!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.logBloom == blockFromRlp.logBloom, "logBloom in given RLP not matching the block logBloom!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.difficulty == blockFromRlp.difficulty, "difficulty in given RLP not matching the block difficulty!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.number == blockFromRlp.number, "number in given RLP not matching the block number!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.gasLimit == blockFromRlp.gasLimit,"gasLimit in given RLP not matching the block gasLimit!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.gasUsed == blockFromRlp.gasUsed, "gasUsed in given RLP not matching the block gasUsed!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.timestamp == blockFromRlp.timestamp, "timestamp in given RLP not matching the block timestamp!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.extraData == blockFromRlp.extraData, "extraData in given RLP not matching the block extraData!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.mixHash == blockFromRlp.mixHash, "mixHash in given RLP not matching the block mixHash!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.nonce == blockFromRlp.nonce, "nonce in given RLP not matching the block nonce!"); - - BOOST_CHECK_MESSAGE(blockHeaderFromFields == blockFromRlp, "However, blockHeaderFromFields != blockFromRlp!"); - - //Check transaction list + BlockInfo blockFromRlp = trueBc.info(); - Transactions txsFromField; - - for (auto const& txObj: blObj["transactions"].get_array()) + if (importedAndBest) { - mObject tx = txObj.get_obj(); - - BOOST_REQUIRE(tx.count("nonce")); - BOOST_REQUIRE(tx.count("gasPrice")); - BOOST_REQUIRE(tx.count("gasLimit")); - BOOST_REQUIRE(tx.count("to")); - BOOST_REQUIRE(tx.count("value")); - BOOST_REQUIRE(tx.count("v")); - BOOST_REQUIRE(tx.count("r")); - BOOST_REQUIRE(tx.count("s")); - BOOST_REQUIRE(tx.count("data")); - - try - { - Transaction t(createRLPStreamFromTransactionFields(tx).out(), CheckTransaction::Everything); - txsFromField.push_back(t); - } - catch (Exception const& _e) + //Check the fields restored from RLP to original fields + BOOST_CHECK_MESSAGE(blockHeaderFromFields.headerHash(WithNonce) == blockFromRlp.headerHash(WithNonce), "hash in given RLP not matching the block hash!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.parentHash == blockFromRlp.parentHash, "parentHash in given RLP not matching the block parentHash!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.sha3Uncles == blockFromRlp.sha3Uncles, "sha3Uncles in given RLP not matching the block sha3Uncles!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.coinbaseAddress == blockFromRlp.coinbaseAddress,"coinbaseAddress in given RLP not matching the block coinbaseAddress!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.stateRoot == blockFromRlp.stateRoot, "stateRoot in given RLP not matching the block stateRoot!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.transactionsRoot == blockFromRlp.transactionsRoot, "transactionsRoot in given RLP not matching the block transactionsRoot!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.receiptsRoot == blockFromRlp.receiptsRoot, "receiptsRoot in given RLP not matching the block receiptsRoot!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.logBloom == blockFromRlp.logBloom, "logBloom in given RLP not matching the block logBloom!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.difficulty == blockFromRlp.difficulty, "difficulty in given RLP not matching the block difficulty!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.number == blockFromRlp.number, "number in given RLP not matching the block number!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.gasLimit == blockFromRlp.gasLimit,"gasLimit in given RLP not matching the block gasLimit!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.gasUsed == blockFromRlp.gasUsed, "gasUsed in given RLP not matching the block gasUsed!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.timestamp == blockFromRlp.timestamp, "timestamp in given RLP not matching the block timestamp!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.extraData == blockFromRlp.extraData, "extraData in given RLP not matching the block extraData!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.mixHash == blockFromRlp.mixHash, "mixHash in given RLP not matching the block mixHash!"); + BOOST_CHECK_MESSAGE(blockHeaderFromFields.nonce == blockFromRlp.nonce, "nonce in given RLP not matching the block nonce!"); + + BOOST_CHECK_MESSAGE(blockHeaderFromFields == blockFromRlp, "However, blockHeaderFromFields != blockFromRlp!"); + + //Check transaction list + + Transactions txsFromField; + + for (auto const& txObj: blObj["transactions"].get_array()) { - BOOST_ERROR("Failed transaction constructor with Exception: " << diagnostic_information(_e)); + mObject tx = txObj.get_obj(); + + BOOST_REQUIRE(tx.count("nonce")); + BOOST_REQUIRE(tx.count("gasPrice")); + BOOST_REQUIRE(tx.count("gasLimit")); + BOOST_REQUIRE(tx.count("to")); + BOOST_REQUIRE(tx.count("value")); + BOOST_REQUIRE(tx.count("v")); + BOOST_REQUIRE(tx.count("r")); + BOOST_REQUIRE(tx.count("s")); + BOOST_REQUIRE(tx.count("data")); + + try + { + Transaction t(createRLPStreamFromTransactionFields(tx).out(), CheckTransaction::Everything); + txsFromField.push_back(t); + } + catch (Exception const& _e) + { + BOOST_ERROR("Failed transaction constructor with Exception: " << diagnostic_information(_e)); + } + catch (exception const& _e) + { + cnote << _e.what(); + } } - catch (exception const& _e) + + Transactions txsFromRlp; + RLP root(blockRLP); + for (auto const& tr: root[1]) { - cnote << _e.what(); + Transaction tx(tr.data(), CheckTransaction::Everything); + txsFromRlp.push_back(tx); } - } - Transactions txsFromRlp; - RLP root(blockRLP); - for (auto const& tr: root[1]) - { - Transaction tx(tr.data(), CheckTransaction::Everything); - txsFromRlp.push_back(tx); - } - - BOOST_CHECK_MESSAGE(txsFromRlp.size() == txsFromField.size(), "transaction list size does not match"); + BOOST_CHECK_MESSAGE(txsFromRlp.size() == txsFromField.size(), "transaction list size does not match"); - for (size_t i = 0; i < txsFromField.size(); ++i) - { - BOOST_CHECK_MESSAGE(txsFromField[i].data() == txsFromRlp[i].data(), "transaction data in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].gas() == txsFromRlp[i].gas(), "transaction gasLimit in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].gasPrice() == txsFromRlp[i].gasPrice(), "transaction gasPrice in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].nonce() == txsFromRlp[i].nonce(), "transaction nonce in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].signature().r == txsFromRlp[i].signature().r, "transaction r in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].signature().s == txsFromRlp[i].signature().s, "transaction s in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].signature().v == txsFromRlp[i].signature().v, "transaction v in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].receiveAddress() == txsFromRlp[i].receiveAddress(), "transaction receiveAddress in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].value() == txsFromRlp[i].value(), "transaction receiveAddress in rlp and in field do not match"); - - BOOST_CHECK_MESSAGE(txsFromField[i] == txsFromRlp[i], "transactions from rlp and transaction from field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].rlp() == txsFromRlp[i].rlp(), "transactions rlp do not match"); - } + for (size_t i = 0; i < txsFromField.size(); ++i) + { + BOOST_CHECK_MESSAGE(txsFromField[i].data() == txsFromRlp[i].data(), "transaction data in rlp and in field do not match"); + BOOST_CHECK_MESSAGE(txsFromField[i].gas() == txsFromRlp[i].gas(), "transaction gasLimit in rlp and in field do not match"); + BOOST_CHECK_MESSAGE(txsFromField[i].gasPrice() == txsFromRlp[i].gasPrice(), "transaction gasPrice in rlp and in field do not match"); + BOOST_CHECK_MESSAGE(txsFromField[i].nonce() == txsFromRlp[i].nonce(), "transaction nonce in rlp and in field do not match"); + BOOST_CHECK_MESSAGE(txsFromField[i].signature().r == txsFromRlp[i].signature().r, "transaction r in rlp and in field do not match"); + BOOST_CHECK_MESSAGE(txsFromField[i].signature().s == txsFromRlp[i].signature().s, "transaction s in rlp and in field do not match"); + BOOST_CHECK_MESSAGE(txsFromField[i].signature().v == txsFromRlp[i].signature().v, "transaction v in rlp and in field do not match"); + BOOST_CHECK_MESSAGE(txsFromField[i].receiveAddress() == txsFromRlp[i].receiveAddress(), "transaction receiveAddress in rlp and in field do not match"); + BOOST_CHECK_MESSAGE(txsFromField[i].value() == txsFromRlp[i].value(), "transaction receiveAddress in rlp and in field do not match"); + + BOOST_CHECK_MESSAGE(txsFromField[i] == txsFromRlp[i], "transactions from rlp and transaction from field do not match"); + BOOST_CHECK_MESSAGE(txsFromField[i].rlp() == txsFromRlp[i].rlp(), "transactions rlp do not match"); + } - // check uncle list + // check uncle list - // uncles from uncle list field - vector uBlHsFromField; - if (blObj["uncleHeaders"].type() != json_spirit::null_type) - for (auto const& uBlHeaderObj: blObj["uncleHeaders"].get_array()) - { - mObject uBlH = uBlHeaderObj.get_obj(); - BOOST_REQUIRE(uBlH.size() == 16); - bytes uncleRLP = createBlockRLPFromFields(uBlH); - const RLP c_uRLP(uncleRLP); - BlockInfo uncleBlockHeader; - try - { - uncleBlockHeader.populateFromHeader(c_uRLP); - } - catch(...) + // uncles from uncle list field + vector uBlHsFromField; + if (blObj["uncleHeaders"].type() != json_spirit::null_type) + for (auto const& uBlHeaderObj: blObj["uncleHeaders"].get_array()) { - BOOST_ERROR("invalid uncle header"); + mObject uBlH = uBlHeaderObj.get_obj(); + BOOST_REQUIRE(uBlH.size() == 16); + bytes uncleRLP = createBlockRLPFromFields(uBlH); + const RLP c_uRLP(uncleRLP); + BlockInfo uncleBlockHeader; + try + { + uncleBlockHeader.populateFromHeader(c_uRLP); + } + catch(...) + { + BOOST_ERROR("invalid uncle header"); + } + uBlHsFromField.push_back(uncleBlockHeader); } - uBlHsFromField.push_back(uncleBlockHeader); + + // uncles from block RLP + vector uBlHsFromRlp; + for (auto const& uRLP: root[2]) + { + BlockInfo uBl; + uBl.populateFromHeader(uRLP); + uBlHsFromRlp.push_back(uBl); } - // uncles from block RLP - vector uBlHsFromRlp; - for (auto const& uRLP: root[2]) - { - BlockInfo uBl; - uBl.populateFromHeader(uRLP); - uBlHsFromRlp.push_back(uBl); - } + BOOST_REQUIRE_EQUAL(uBlHsFromField.size(), uBlHsFromRlp.size()); - BOOST_REQUIRE_EQUAL(uBlHsFromField.size(), uBlHsFromRlp.size()); + for (size_t i = 0; i < uBlHsFromField.size(); ++i) + BOOST_CHECK_MESSAGE(uBlHsFromField[i] == uBlHsFromRlp[i], "block header in rlp and in field do not match"); + }//importedAndBest + }//all blocks - for (size_t i = 0; i < uBlHsFromField.size(); ++i) - BOOST_CHECK_MESSAGE(uBlHsFromField[i] == uBlHsFromRlp[i], "block header in rlp and in field do not match"); - } + BOOST_REQUIRE(o.count("lastblockhash") > 0); + BOOST_CHECK_MESSAGE(toString(trueBc.info().hash()) == o["lastblockhash"].get_str(), + "Boost check: " + i.first + " lastblockhash does not match " + toString(trueBc.info().hash()) + " expected: " + o["lastblockhash"].get_str()); } } } @@ -443,6 +518,7 @@ mArray importUncles(mObject const& blObj, vector& vBiUncles, vector >(expectedAddrs, resultAddrs); + checkAddresses(expectedAddrs, resultAddrs); #endif BOOST_CHECK_MESSAGE(theState.rootHash() == h256(o["postStateRoot"].get_str()), "wrong post state root"); } diff --git a/test/libjsengine/CMakeLists.txt b/test/libjsengine/CMakeLists.txt new file mode 100644 index 000000000..3ceda13b0 --- /dev/null +++ b/test/libjsengine/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_policy(SET CMP0015 NEW) + +aux_source_directory(. SRCS) + +add_sources(${SRCS}) diff --git a/test/libjsengine/JSV8Engine.cpp b/test/libjsengine/JSV8Engine.cpp new file mode 100644 index 000000000..da6a0da0e --- /dev/null +++ b/test/libjsengine/JSV8Engine.cpp @@ -0,0 +1,71 @@ +// +// Created by Marek Kotewicz on 27/04/15. +// + +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::eth; + +BOOST_AUTO_TEST_SUITE(jsv8engine) + +BOOST_AUTO_TEST_CASE(evalInteger) +{ + JSV8Engine engine; + JSV8Printer printer(engine); + auto value = engine.eval("1 + 1"); + string result = printer.print(value).cstr(); + BOOST_CHECK_EQUAL(result, "2"); +} + +BOOST_AUTO_TEST_CASE(evalString) +{ + JSV8Engine engine; + JSV8Printer printer(engine); + auto value = engine.eval("'hello ' + 'world'"); + string result = printer.print(value).cstr(); + BOOST_CHECK_EQUAL(result, "hello world"); +} + +BOOST_AUTO_TEST_CASE(evalEmpty) +{ + JSV8Engine engine; + JSV8Printer printer(engine); + auto value = engine.eval(""); + string result = printer.print(value).cstr(); + BOOST_CHECK_EQUAL(result, "undefined"); +} + +BOOST_AUTO_TEST_CASE(evalAssignment) +{ + JSV8Engine engine; + JSV8Printer printer(engine); + auto value = engine.eval("x = 5"); + string result = printer.print(value).cstr(); + BOOST_CHECK_EQUAL(result, "5"); +} + +BOOST_AUTO_TEST_CASE(evalIncorrectExpression) +{ + JSV8Engine engine; + JSV8Printer printer(engine); + auto value = engine.eval("["); + string result = printer.print(value).cstr(); + BOOST_CHECK_EQUAL(result, "Error: Uncaught SyntaxError: Unexpected end of input"); +} + +BOOST_AUTO_TEST_CASE(evalNull) +{ + JSV8Engine engine; + JSV8Printer printer(engine); + auto value = engine.eval("null"); + string result = printer.print(value).cstr(); + string prettyResult = printer.prettyPrint(value).cstr(); + BOOST_CHECK_EQUAL(result, "null"); + BOOST_CHECK_EQUAL(prettyResult.find("null") != std::string::npos, true); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp index 7b0ceedb6..aa83c4650 100644 --- a/test/libsolidity/SolidityCompiler.cpp +++ b/test/libsolidity/SolidityCompiler.cpp @@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) "}\n"; bytes code = compileContract(sourceCode); - unsigned boilerplateSize = 70; + unsigned boilerplateSize = 73; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, // initialize local variable x byte(Instruction::PUSH1), 0x2, @@ -114,8 +114,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 = 57; - unsigned boilerplateSize = 70; + unsigned shift = 60; + unsigned boilerplateSize = 73; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, byte(Instruction::DUP1), @@ -155,8 +155,8 @@ BOOST_AUTO_TEST_CASE(loops) " function f() { while(true){1;break;2;continue;3;return;4;} }" "}\n"; bytes code = compileContract(sourceCode); - unsigned shift = 57; - unsigned boilerplateSize = 70; + unsigned shift = 60; + unsigned boilerplateSize = 73; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x1, diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f168ad454..ed5f1acdf 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2962,12 +2962,13 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments) } )"; compileAndRun(sourceCode); + string innercalldata1 = asString(FixedHash<4>(dev::sha3("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9)); - bytes calldata1 = encodeArgs(u256(innercalldata1.length()), 12, innercalldata1, 13); string innercalldata2 = asString(FixedHash<4>(dev::sha3("g(uint256)")).asBytes() + encodeArgs(3)); bytes calldata = encodeArgs( - 12, u256(innercalldata1.length()), u256(innercalldata2.length()), 13, - innercalldata1, innercalldata2); + 12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13, + u256(innercalldata1.length()), innercalldata1, + u256(innercalldata2.length()), innercalldata2); BOOST_CHECK(callContractFunction("test(uint256,bytes,bytes,uint256)", calldata) == encodeArgs(12, (8 + 9) * 3, 13, u256(innercalldata1.length()))); } @@ -3383,9 +3384,10 @@ BOOST_AUTO_TEST_CASE(external_array_args) compileAndRun(sourceCode); bytes params = encodeArgs( 1, 2, 3, 4, 5, 6, 7, 8, // a - 3, // b.length + 32 * (8 + 1 + 5 + 1 + 1 + 1), // offset to b 21, 22, 23, 24, 25, // c 0, 1, 2, // (a,b,c)_index + 3, // b.length 11, 12, 13 // b ); BOOST_CHECK(callContractFunction("test(uint256[8],uint256[],uint256[5],uint256,uint256,uint256)", params) == encodeArgs(1, 12, 23)); @@ -3422,8 +3424,8 @@ BOOST_AUTO_TEST_CASE(bytes_index_access) 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33}; - BOOST_CHECK(callContractFunction("direct(bytes,uint256)", u256(array.length()), 32, array) == encodeArgs(32)); - BOOST_CHECK(callContractFunction("storageCopyRead(bytes,uint256)", u256(array.length()), 32, array) == encodeArgs(32)); + BOOST_CHECK(callContractFunction("direct(bytes,uint256)", 64, 33, u256(array.length()), array) == encodeArgs(33)); + BOOST_CHECK(callContractFunction("storageCopyRead(bytes,uint256)", 64, 33, u256(array.length()), array) == encodeArgs(33)); BOOST_CHECK(callContractFunction("storageWrite()") == encodeArgs(0x193)); } @@ -3474,6 +3476,7 @@ BOOST_AUTO_TEST_CASE(array_copy_calldata_storage) compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("store(uint256[9],uint8[3][])", encodeArgs( 21, 22, 23, 24, 25, 26, 27, 28, 29, // a + u256(32 * (9 + 1)), 4, // size of b 1, 2, 3, // b[0] 11, 12, 13, // b[1] @@ -3502,7 +3505,7 @@ BOOST_AUTO_TEST_CASE(array_copy_nested_array) )"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("test(uint256[2][])", encodeArgs( - 3, + 32, 3, 7, 8, 9, 10, 11, 12 @@ -3910,6 +3913,90 @@ BOOST_AUTO_TEST_CASE(external_types_in_calls) BOOST_CHECK(callContractFunction("nonexisting") == encodeArgs(u256(9))); } +BOOST_AUTO_TEST_CASE(proper_order_of_overwriting_of_attributes) +{ + // bug #1798 + char const* sourceCode = R"( + contract init { + function isOk() returns (bool) { return false; } + bool public ok = false; + } + contract fix { + function isOk() returns (bool) { return true; } + bool public ok = true; + } + + contract init_fix is init, fix { + function checkOk() returns (bool) { return ok; } + } + contract fix_init is fix, init { + function checkOk() returns (bool) { return ok; } + } + )"; + compileAndRun(sourceCode, 0, "init_fix"); + BOOST_CHECK(callContractFunction("isOk()") == encodeArgs(true)); + BOOST_CHECK(callContractFunction("ok()") == encodeArgs(true)); + + compileAndRun(sourceCode, 0, "fix_init"); + BOOST_CHECK(callContractFunction("isOk()") == encodeArgs(false)); + BOOST_CHECK(callContractFunction("ok()") == encodeArgs(false)); +} + +BOOST_AUTO_TEST_CASE(proper_overwriting_accessor_by_function) +{ + // bug #1798 + char const* sourceCode = R"( + contract attribute { + bool ok = false; + } + contract func { + function ok() returns (bool) { return true; } + } + + contract attr_func is attribute, func { + function checkOk() returns (bool) { return ok(); } + } + contract func_attr is func, attribute { + function checkOk() returns (bool) { return ok; } + } + )"; + compileAndRun(sourceCode, 0, "attr_func"); + BOOST_CHECK(callContractFunction("ok()") == encodeArgs(true)); + compileAndRun(sourceCode, 0, "func_attr"); + BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(false)); +} + + +BOOST_AUTO_TEST_CASE(overwriting_inheritance) +{ + // bug #1798 + char const* sourceCode = R"( + contract A { + function ok() returns (uint) { return 1; } + } + contract B { + function ok() returns (uint) { return 2; } + } + contract C { + uint ok = 6; + } + contract AB is A, B { + function ok() returns (uint) { return 4; } + } + contract reversedE is C, AB { + function checkOk() returns (uint) { return ok(); } + } + contract E is AB, C { + function checkOk() returns (uint) { return ok; } + } + )"; + compileAndRun(sourceCode, 0, "reversedE"); + BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(4)); + compileAndRun(sourceCode, 0, "E"); + BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(6)); +} + + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 9cdaa5886..3cb6a536a 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -83,14 +83,28 @@ public: "\nOptimized: " + toHex(optimizedOutput)); } - AssemblyItems getCSE(AssemblyItems const& _input) + AssemblyItems addDummyLocations(AssemblyItems const& _input) { // add dummy locations to each item so that we can check that they are not deleted AssemblyItems input = _input; for (AssemblyItem& item: input) item.setLocation(SourceLocation(1, 3, make_shared(""))); + return input; + } + + eth::KnownState createInitialState(AssemblyItems const& _input) + { + eth::KnownState state; + for (auto const& item: addDummyLocations(_input)) + state.feedItem(item); + return state; + } + + AssemblyItems getCSE(AssemblyItems const& _input, eth::KnownState const& _state = eth::KnownState()) + { + AssemblyItems input = addDummyLocations(_input); - eth::CommonSubexpressionEliminator cse; + eth::CommonSubexpressionEliminator cse(_state); BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); AssemblyItems output = cse.getOptimizedItems(); @@ -101,9 +115,13 @@ public: return output; } - void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation) + void checkCSE( + AssemblyItems const& _input, + AssemblyItems const& _expectation, + KnownState const& _state = eth::KnownState() + ) { - AssemblyItems output = getCSE(_input); + AssemblyItems output = getCSE(_input, _state); BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); } @@ -113,8 +131,12 @@ public: // Running it four times should be enough for these tests. for (unsigned i = 0; i < 4; ++i) { - eth::ControlFlowGraph cfg(output); - output = cfg.optimisedItems(); + ControlFlowGraph cfg(output); + AssemblyItems optItems; + for (BasicBlock const& block: cfg.optimisedBlocks()) + copy(output.begin() + block.begin, output.begin() + block.end, + back_inserter(optItems)); + output = move(optItems); } BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); } @@ -231,7 +253,8 @@ BOOST_AUTO_TEST_CASE(function_calls) BOOST_AUTO_TEST_CASE(cse_intermediate_swap) { - eth::CommonSubexpressionEliminator cse; + eth::KnownState state; + eth::CommonSubexpressionEliminator cse(state); AssemblyItems input{ Instruction::SWAP1, Instruction::POP, Instruction::ADD, u256(0), Instruction::SWAP1, Instruction::SLOAD, Instruction::SWAP1, u256(100), Instruction::EXP, Instruction::SWAP1, @@ -754,6 +777,30 @@ BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_noninterfering_store_in_between BOOST_CHECK_EQUAL(1, count(output.begin(), output.end(), AssemblyItem(Instruction::SHA3))); } +BOOST_AUTO_TEST_CASE(cse_with_initially_known_stack) +{ + eth::KnownState state = createInitialState(AssemblyItems{ + u256(0x12), + u256(0x20), + Instruction::ADD + }); + AssemblyItems input{ + u256(0x12 + 0x20) + }; + checkCSE(input, AssemblyItems{Instruction::DUP1}, state); +} + +BOOST_AUTO_TEST_CASE(cse_equality_on_initially_known_stack) +{ + eth::KnownState state = createInitialState(AssemblyItems{Instruction::DUP1}); + AssemblyItems input{ + Instruction::EQ + }; + AssemblyItems output = getCSE(input, state); + // check that it directly pushes 1 (true) + BOOST_CHECK(find(output.begin(), output.end(), AssemblyItem(u256(1))) != output.end()); +} + BOOST_AUTO_TEST_CASE(control_flow_graph_remove_unused) { // remove parts of the code that are unused