Browse Source

Merge branch 'develop' into whisper

cl-refactor
subtly 10 years ago
parent
commit
f3f450b2f3
  1. 13
      CMakeLists.txt
  2. 2
      alethzero/Debugger.h
  3. 12
      alethzero/ExportState.cpp
  4. 2
      alethzero/MainWin.cpp
  5. 1
      cmake/EthCompilerSettings.cmake
  6. 10
      cmake/EthDependencies.cmake
  7. 49
      cmake/Findv8.cmake
  8. 8
      eth/CMakeLists.txt
  9. 40
      eth/main.cpp
  10. 137
      exp/main.cpp
  11. 2
      libdevcore/Common.cpp
  12. 17
      libdevcore/Common.h
  13. 8
      libdevcore/CommonData.h
  14. 17
      libdevcore/FixedHash.cpp
  15. 31
      libdevcore/FixedHash.h
  16. 24
      libdevcore/Log.h
  17. 1
      libdevcore/RLP.h
  18. 30
      libdevcrypto/Common.cpp
  19. 10
      libdevcrypto/Common.h
  20. 5
      libdevcrypto/CryptoPP.cpp
  21. 8
      libdevcrypto/MemoryDB.cpp
  22. 10
      libdevcrypto/MemoryDB.h
  23. 220
      libdevcrypto/SecretStore.cpp
  24. 56
      libdevcrypto/SecretStore.h
  25. 13
      libdevcrypto/TrieDB.h
  26. 16
      libethcore/BlockInfo.cpp
  27. 6
      libethcore/EthashAux.h
  28. 6
      libethereum/Account.h
  29. 2
      libethereum/AccountDiff.h
  30. 4
      libethereum/BlockChain.cpp
  31. 4
      libethereum/BlockChain.h
  32. 12
      libethereum/BlockQueue.h
  33. 4
      libethereum/CachedAddressState.cpp
  34. 2
      libethereum/CachedAddressState.h
  35. 4
      libethereum/CanonBlockChain.cpp
  36. 2
      libethereum/CanonBlockChain.h
  37. 12
      libethereum/Client.cpp
  38. 7
      libethereum/Client.h
  39. 22
      libethereum/ClientBase.cpp
  40. 10
      libethereum/ClientBase.h
  41. 4
      libethereum/DownloadMan.cpp
  42. 12
      libethereum/DownloadMan.h
  43. 3
      libethereum/EthereumHost.cpp
  44. 10
      libethereum/EthereumHost.h
  45. 5
      libethereum/EthereumPeer.h
  46. 2
      libethereum/ExtVM.h
  47. 16
      libethereum/Interface.h
  48. 203
      libethereum/KeyManager.cpp
  49. 110
      libethereum/KeyManager.h
  50. 4
      libethereum/LogFilter.h
  51. 4
      libethereum/Precompiled.cpp
  52. 4
      libethereum/Precompiled.h
  53. 23
      libethereum/State.cpp
  54. 17
      libethereum/State.h
  55. 6
      libethereum/Transaction.h
  56. 12
      libethereum/TransactionQueue.h
  57. 1
      libethereum/TransactionReceipt.h
  58. 2
      libethereumx/Ethereum.cpp
  59. 2
      libethereumx/Ethereum.h
  60. 16
      libevmasm/Assembly.cpp
  61. 271
      libevmasm/CommonSubexpressionEliminator.cpp
  62. 63
      libevmasm/CommonSubexpressionEliminator.h
  63. 109
      libevmasm/ControlFlowGraph.cpp
  64. 32
      libevmasm/ControlFlowGraph.h
  65. 28
      libevmasm/ExpressionClasses.cpp
  66. 4
      libevmasm/ExpressionClasses.h
  67. 326
      libevmasm/KnownState.cpp
  68. 163
      libevmasm/KnownState.h
  69. 53
      libevmasm/SemanticInformation.cpp
  70. 8
      libevmasm/SemanticInformation.h
  71. 30
      libjsconsole/CMakeLists.txt
  72. 86
      libjsconsole/JSConsole.cpp
  73. 53
      libjsconsole/JSConsole.h
  74. 54
      libjsconsole/JSV8Connector.cpp
  75. 50
      libjsconsole/JSV8Connector.h
  76. 36
      libjsengine/CMakeLists.txt
  77. 11
      libjsengine/Common.js
  78. 36
      libjsengine/JSEngine.cpp
  79. 60
      libjsengine/JSEngine.h
  80. 23
      libjsengine/JSPrinter.cpp
  81. 41
      libjsengine/JSPrinter.h
  82. 8
      libjsengine/JSResources.cmake
  83. 187
      libjsengine/JSV8Engine.cpp
  84. 61
      libjsengine/JSV8Engine.h
  85. 49
      libjsengine/JSV8Printer.cpp
  86. 43
      libjsengine/JSV8Printer.h
  87. 84
      libjsengine/JSV8RPC.cpp
  88. 47
      libjsengine/JSV8RPC.h
  89. 91
      libjsengine/PrettyPrint.js
  90. 23
      libp2p/Common.h
  91. 4
      libp2p/Host.h
  92. 2
      libp2p/Network.cpp
  93. 16
      libp2p/NodeTable.cpp
  94. 6
      libp2p/NodeTable.h
  95. 4
      libp2p/UDP.cpp
  96. 26
      libp2p/UDP.h
  97. 6
      libsolidity/AST.h
  98. 2
      libsolidity/ASTPrinter.h
  99. 44
      libsolidity/Compiler.cpp
  100. 10
      libsolidity/DeclarationContainer.cpp

13
CMakeLists.txt

@ -40,6 +40,7 @@ option(GUI "Build GUI components (AlethZero, Mix)" ON)
option(TESTS "Build the tests." ON) option(TESTS "Build the tests." ON)
option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF) 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(ETHASHCL "Build in support for GPU mining via OpenCL" OFF)
option(JSCONSOLE "Build in javascript console" OFF)
# propagates CMake configuration options to the compiler # propagates CMake configuration options to the compiler
function(configureProject) function(configureProject)
@ -193,9 +194,14 @@ eth_format_option(GUI)
eth_format_option(TESTS) eth_format_option(TESTS)
eth_format_option(TOOLS) eth_format_option(TOOLS)
eth_format_option(ETHASHCL) eth_format_option(ETHASHCL)
eth_format_option(JSCONSOLE)
eth_format_option_on_decent_platform(SERPENT) eth_format_option_on_decent_platform(SERPENT)
eth_format_option_on_decent_platform(NCURSES) eth_format_option_on_decent_platform(NCURSES)
if (JSCONSOLE)
set(JSONRPC ON)
endif()
if (GUI) if (GUI)
set(JSONRPC ON) set(JSONRPC ON)
endif() endif()
@ -284,6 +290,7 @@ message("-- GUI Build GUI components ${GUI}")
message("-- NCURSES Build NCurses components ${NCURSES}") message("-- NCURSES Build NCurses components ${NCURSES}")
message("-- TESTS Build tests ${TESTS}") message("-- TESTS Build tests ${TESTS}")
message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}") message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}")
message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}")
message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}") message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}")
message("------------------------------------------------------------------------") message("------------------------------------------------------------------------")
message("") message("")
@ -328,6 +335,11 @@ if (JSONRPC)
add_subdirectory(libweb3jsonrpc) add_subdirectory(libweb3jsonrpc)
endif() endif()
if (JSCONSOLE)
add_subdirectory(libjsengine)
add_subdirectory(libjsconsole)
endif()
add_subdirectory(secp256k1) add_subdirectory(secp256k1)
add_subdirectory(libp2p) add_subdirectory(libp2p)
add_subdirectory(libdevcrypto) add_subdirectory(libdevcrypto)
@ -384,6 +396,7 @@ if (GUI)
endif() endif()
#unset(TARGET_PLATFORM CACHE) #unset(TARGET_PLATFORM CACHE)
if (WIN32) if (WIN32)

2
alethzero/Debugger.h

@ -45,7 +45,7 @@ struct WorldState
dev::u256s stack; dev::u256s stack;
dev::bytes memory; dev::bytes memory;
dev::bigint gasCost; dev::bigint gasCost;
std::map<dev::u256, dev::u256> storage; std::unordered_map<dev::u256, dev::u256> storage;
std::vector<WorldState const*> levels; std::vector<WorldState const*> levels;
}; };

12
alethzero/ExportState.cpp

@ -91,7 +91,7 @@ void ExportStateDialog::fillBlocks()
while (i > 0 && i >= m_recentBlocks) while (i > 0 && i >= m_recentBlocks)
ui->block->removeItem(i--); ui->block->removeItem(i--);
h256Set blocks; h256Hash blocks;
for (QString f: filters) for (QString f: filters)
{ {
if (f.startsWith("#")) if (f.startsWith("#"))
@ -153,13 +153,17 @@ void ExportStateDialog::generateJSON()
auto address = Address((byte const*)hba.data(), Address::ConstructFromPointer); 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 << 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"; json << "\t\t\"code\": \"" << toHex(ethereum()->codeAt(address, m_block)) << "\",\n";
std::map<u256, u256> storage = ethereum()->storageAt(address, m_block); std::unordered_map<u256, u256> storage = ethereum()->storageAt(address, m_block);
if (!storage.empty()) if (!storage.empty())
{ {
json << "\t\t\"storage\":\n\t\t{\n"; json << "\t\t\"storage\":\n\t\t{\n";
std::string storagePrefix;
for (auto s: storage) 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}"; json << "\t}";
prefix = ",\n"; prefix = ",\n";

2
alethzero/MainWin.cpp

@ -1092,7 +1092,7 @@ void Main::refreshBlockChain()
auto const& bc = ethereum()->blockChain(); auto const& bc = ethereum()->blockChain();
QStringList filters = ui->blockChainFilter->text().toLower().split(QRegExp("\\s+"), QString::SkipEmptyParts); QStringList filters = ui->blockChainFilter->text().toLower().split(QRegExp("\\s+"), QString::SkipEmptyParts);
h256Set blocks; h256Hash blocks;
for (QString f: filters) for (QString f: filters)
if (f.size() == 64) if (f.size() == 64)
{ {

1
cmake/EthCompilerSettings.cmake

@ -55,6 +55,7 @@ endif ()
if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) 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_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}")
set(CMAKE_C_FLAGS "-g ${CMAKE_C_FLAGS}")
add_definitions(-DETH_PROFILING_GPERF) add_definitions(-DETH_PROFILING_GPERF)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lprofiler") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lprofiler")
# set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -lprofiler") # set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -lprofiler")

10
cmake/EthDependencies.cmake

@ -31,7 +31,8 @@ endif()
# homebrew installs qts in opt # homebrew installs qts in opt
if (APPLE) 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() endif()
find_program(CTEST_COMMAND ctest) find_program(CTEST_COMMAND ctest)
@ -47,6 +48,13 @@ find_package (LevelDB REQUIRED)
message(" - LevelDB header: ${LEVELDB_INCLUDE_DIRS}") message(" - LevelDB header: ${LEVELDB_INCLUDE_DIRS}")
message(" - LevelDB lib: ${LEVELDB_LIBRARIES}") 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 # TODO the Jsoncpp package does not yet check for correct version number
find_package (Jsoncpp 0.60 REQUIRED) find_package (Jsoncpp 0.60 REQUIRED)
message(" - Jsoncpp header: ${JSONCPP_INCLUDE_DIRS}") message(" - Jsoncpp header: ${JSONCPP_INCLUDE_DIRS}")

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

8
eth/CMakeLists.txt

@ -7,6 +7,10 @@ include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
if (JSCONSOLE)
include_directories(${V8_INCLUDE_DIRS})
endif()
set(EXECUTABLE eth) set(EXECUTABLE eth)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
@ -33,6 +37,10 @@ endif()
target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} ethash) target_link_libraries(${EXECUTABLE} ethash)
if (JSCONSOLE)
target_link_libraries(${EXECUTABLE} jsconsole)
endif()
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS) eth_copy_dlls("${EXECUTABLE}" MHD_DLLS)
endif() endif()

40
eth/main.cpp

@ -38,6 +38,9 @@
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
#include <libethereum/All.h> #include <libethereum/All.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#if ETH_JSCONSOLE || !ETH_TRUE
#include <libjsconsole/JSConsole.h>
#endif
#if ETH_READLINE || !ETH_TRUE #if ETH_READLINE || !ETH_TRUE
#include <readline/readline.h> #include <readline/readline.h>
#include <readline/history.h> #include <readline/history.h>
@ -124,6 +127,7 @@ void help()
<< " -R,--rebuild Rebuild the blockchain from the existing database." << endl << " -R,--rebuild Rebuild the blockchain from the existing database." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl << " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -S,--session-secret <secretkeyhex> Set the secret key for use with send command, for this session only." << endl << " -S,--session-secret <secretkeyhex> Set the secret key for use with send command, for this session only." << endl
<< " --master <password> Give the master password for the key store." << endl
<< "Client transacting:" << endl << "Client transacting:" << endl
<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl << " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (default: 30.679)." << endl << " -e,--ether-price <n> 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,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl
<< " -V,--version Show the version and exit." << endl << " -V,--version Show the version and exit." << endl
<< " -h,--help Show this help message 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); exit(0);
} }
@ -406,6 +413,13 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
exit(0); 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) int main(int argc, char** argv)
{ {
#if 0 #if 0
@ -542,10 +556,16 @@ int main(int argc, char** argv)
unsigned benchmarkTrial = 3; unsigned benchmarkTrial = 3;
unsigned benchmarkTrials = 5; unsigned benchmarkTrials = 5;
// javascript console
bool useConsole = false;
/// Farm params /// Farm params
string farmURL = "http://127.0.0.1:8080"; string farmURL = "http://127.0.0.1:8080";
unsigned farmRecheckPeriod = 500; unsigned farmRecheckPeriod = 500;
/// Wallet password stuff
string masterPassword;
string configFile = getDataDir() + "/config.rlp"; string configFile = getDataDir() + "/config.rlp";
bytes b = contents(configFile); bytes b = contents(configFile);
if (b.size()) 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; 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]); remotePort = (short)atoi(argv[++i]);
} }
else if (arg == "--master" && i + 1 < argc)
masterPassword = argv[++i];
else if ((arg == "-I" || arg == "--import") && i + 1 < argc) else if ((arg == "-I" || arg == "--import") && i + 1 < argc)
{ {
mode = OperationMode::Import; mode = OperationMode::Import;
@ -888,6 +910,10 @@ int main(int argc, char** argv)
jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc; jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc;
else if (arg == "--json-rpc-port" && i + 1 < argc) else if (arg == "--json-rpc-port" && i + 1 < argc)
jsonrpc = atoi(argv[++i]); jsonrpc = atoi(argv[++i]);
#endif
#if ETH_JSCONSOLE
else if (arg == "--console")
useConsole = true;
#endif #endif
else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc) else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc)
g_logVerbosity = atoi(argv[++i]); g_logVerbosity = atoi(argv[++i]);
@ -1625,12 +1651,20 @@ int main(int argc, char** argv)
unsigned n =c->blockChain().details().number; unsigned n =c->blockChain().details().number;
if (mining) if (mining)
c->startMining(); c->startMining();
if (useConsole)
{
#if ETH_JSCONSOLE
JSConsole console(web3, vector<KeyPair>({sigKey}));
while (!g_exit) while (!g_exit)
{ {
if ( c->isMining() &&c->blockChain().details().number - n == mining) console.repl();
c->stopMining(); stopMiningAfterXBlocks(c, n, mining);
this_thread::sleep_for(chrono::milliseconds(100));
} }
#endif
}
else
while (!g_exit)
stopMiningAfterXBlocks(c, n, mining);
} }
else else
while (!g_exit) while (!g_exit)

137
exp/main.cpp

@ -42,10 +42,12 @@
#include <libdevcore/TransientDirectory.h> #include <libdevcore/TransientDirectory.h>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcrypto/TrieDB.h> #include <libdevcrypto/TrieDB.h>
#include <libdevcrypto/SecretStore.h>
#include <libp2p/All.h> #include <libp2p/All.h>
#include <libethcore/ProofOfWork.h> #include <libethcore/ProofOfWork.h>
#include <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
#include <libethereum/All.h> #include <libethereum/All.h>
#include <libethereum/KeyManager.h>
#include <libethereum/Farm.h> #include <libethereum/Farm.h>
#include <libethereum/AccountDiff.h> #include <libethereum/AccountDiff.h>
#include <libethereum/DownloadMan.h> #include <libethereum/DownloadMan.h>
@ -64,135 +66,26 @@ namespace fs = boost::filesystem;
#if 1 #if 1
inline h128 fromUUID(std::string const& _uuid) { return h128(boost::replace_all_copy(_uuid, "-", "")); } int main()
class KeyManager: public Worker
{ {
public: KeyManager keyman;
KeyManager() { readKeys(); } if (keyman.exists())
~KeyManager() {} keyman.load("foo");
Secret secret(h128 const& _uuid, function<std::string()> 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();
// 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 else
{ keyman.create("foo");
cwarn << "Unknown KDF" << o["kdf"].get_str() << "not supported.";
return bytes();
}
bytes cipherText = fromHex(o["ciphertext"].get_str());
// check MAC Address a("9cab1cc4e8fe528267c6c3af664a1adbce810b5f");
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 // keyman.importExisting(fromUUID("441193ae-a767-f1c3-48ba-dd6610db5ed0"), "{\"name\":\"Gavin Wood - Main identity\"}", "bar", "{\"hint\":\"Not foo.\"}");
bytes ret; // Address a2 = keyman.address(keyman.import(Secret::random(), "Key with no additional security."));
if (o["cipher"].get_str() == "aes-128-cbc") // cdebug << toString(a2);
{ Address a2("19c486071651b2650449ba3c6a807f316a73e8fe");
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();
}
return ret; cdebug << keyman.keys();
}
mutable std::map<h128, Secret> m_ready; cdebug << "Secret key for " << a << "is" << keyman.secret(a, [](){ return "bar"; });
std::map<h128, js::mValue> m_keys; cdebug << "Secret key for " << a2 << "is" << keyman.secret(a2);
};
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 #elif 0

2
libdevcore/Common.cpp

@ -28,7 +28,7 @@ using namespace dev;
namespace dev namespace dev
{ {
char const* Version = "0.9.15o"; char const* Version = "0.9.17";
void HasInvariants::checkInvariants() const void HasInvariants::checkInvariants() const
{ {

17
libdevcore/Common.h

@ -34,10 +34,13 @@
#endif #endif
#include <map> #include <map>
#include <unordered_map>
#include <vector> #include <vector>
#include <set> #include <set>
#include <unordered_set>
#include <functional> #include <functional>
#include <boost/timer.hpp> #include <boost/timer.hpp>
#include <boost/functional/hash.hpp>
#pragma warning(push) #pragma warning(push)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"
@ -84,6 +87,10 @@ using StringMap = std::map<std::string, std::string>;
using u256Map = std::map<u256, u256>; using u256Map = std::map<u256, u256>;
using HexMap = std::map<bytes, std::string>; using HexMap = std::map<bytes, std::string>;
// Hash types.
using StringHashMap = std::unordered_map<std::string, std::string>;
using u256HashMap = std::unordered_map<u256, u256>;
// String types. // String types.
using strings = std::vector<std::string>; using strings = std::vector<std::string>;
@ -215,4 +222,14 @@ inline dev::WithExisting max(dev::WithExisting _a, dev::WithExisting _b)
return static_cast<dev::WithExisting>(max(static_cast<int>(_a), static_cast<int>(_b))); return static_cast<dev::WithExisting>(max(static_cast<int>(_a), static_cast<int>(_b)));
} }
template <> struct hash<dev::u256>
{
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);
}
};
} }

8
libdevcore/CommonData.h

@ -258,6 +258,14 @@ template <class T, class U> std::set<T>& operator+=(std::set<T>& _a, U const& _b
return _a; return _a;
} }
/// Insert the contents of a container into an unordered_st
template <class T, class U> std::unordered_set<T>& operator+=(std::unordered_set<T>& _a, U const& _b)
{
for (auto const& i: _b)
_a.insert(i);
return _a;
}
/// Concatenate the contents of a container onto a vector /// Concatenate the contents of a container onto a vector
template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U const& _b) template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U const& _b)
{ {

17
libdevcore/FixedHash.cpp

@ -19,10 +19,25 @@
* @date 2014 * @date 2014
*/ */
#include <ctime>
#include "FixedHash.h" #include "FixedHash.h"
#include <ctime>
#include <boost/algorithm/string.hpp>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
std::random_device dev::s_fixedHashEngine; 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;
}

31
libdevcore/FixedHash.h

@ -113,6 +113,9 @@ public:
/// @returns an abridged version of the hash as a user-readable hex string. /// @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"; } 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. /// @returns a mutable byte vector_ref to the object's data.
bytesRef ref() { return bytesRef(m_data.data(), N); } bytesRef ref() { return bytesRef(m_data.data(), N); }
@ -147,17 +150,10 @@ public:
/// @returns a random valued object. /// @returns a random valued object.
static FixedHash random() { return random(s_fixedHashEngine); } static FixedHash random() { return random(s_fixedHashEngine); }
/// A generic std::hash compatible function object.
struct hash struct hash
{ {
/// Make a hash of the object's data. /// Make a hash of the object's data.
size_t operator()(FixedHash const& value) const size_t operator()(FixedHash const& _value) const { return boost::hash_range(_value.m_data.cbegin(), _value.m_data.cend()); }
{
size_t h = 0;
for (auto i: value.m_data)
h = (h << (5 - h)) + i;
return h;
}
}; };
template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> const& _h) template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> 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. /// Fast std::hash compatible hash function object for h256.
template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& value) const template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& value) const
{ {
const uint64_t*data = (const uint64_t*)value.data(); uint64_t const* data = reinterpret_cast<uint64_t const*>(value.data());
uint64_t hash = data[0]; return boost::hash_range(data, data + 4);
hash ^= data[1];
hash ^= data[2];
hash ^= data[3];
return (size_t)hash;
} }
/// Stream I/O for the FixedHash class. /// Stream I/O for the FixedHash class.
@ -251,6 +243,8 @@ using h256s = std::vector<h256>;
using h160s = std::vector<h160>; using h160s = std::vector<h160>;
using h256Set = std::set<h256>; using h256Set = std::set<h256>;
using h160Set = std::set<h160>; using h160Set = std::set<h160>;
using h256Hash = std::unordered_set<h256>;
using h160Hash = std::unordered_set<h160>;
/// Convert the given value into h160 (160-bit unsigned integer) using the right 20 bytes. /// Convert the given value into h160 (160-bit unsigned integer) using the right 20 bytes.
inline h160 right160(h256 const& _t) inline h160 right160(h256 const& _t)
@ -268,6 +262,10 @@ inline h160 left160(h256 const& _t)
return ret; return ret;
} }
h128 fromUUID(std::string const& _uuid);
std::string toUUID(h128 const& _uuid);
inline std::string toString(h256s const& _bs) inline std::string toString(h256s const& _bs)
{ {
std::ostringstream out; std::ostringstream out;
@ -282,6 +280,9 @@ inline std::string toString(h256s const& _bs)
namespace std namespace std
{ {
/// Forward std::hash<dev::h256> to dev::h256::hash. /// Forward std::hash<dev::FixedHash> to dev::FixedHash::hash.
template<> struct hash<dev::h64>: dev::h64::hash {};
template<> struct hash<dev::h160>: dev::h160::hash {};
template<> struct hash<dev::h256>: dev::h256::hash {}; template<> struct hash<dev::h256>: dev::h256::hash {};
template<> struct hash<dev::h512>: dev::h512::hash {};
} }

24
libdevcore/Log.h

@ -164,6 +164,30 @@ public:
} }
m_sstr << EthLime "}" EthReset; m_sstr << EthLime "}" EthReset;
} }
template <class T> void append(std::unordered_set<T> 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 <class T, class U> void append(std::unordered_map<T, U> 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 <class T, class U> void append(std::pair<T, U> const& _t) template <class T, class U> void append(std::pair<T, U> const& _t)
{ {
m_sstr << EthPurple "(" EthReset; m_sstr << EthPurple "(" EthReset;

1
libdevcore/RLP.h

@ -362,6 +362,7 @@ public:
template <class _T> RLPStream& appendVector(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } template <class _T> RLPStream& appendVector(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T, size_t S> RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } template <class _T, size_t S> RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T> RLPStream& append(std::set<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } template <class _T> RLPStream& append(std::set<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T> RLPStream& append(std::unordered_set<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class T, class U> RLPStream& append(std::pair<T, U> const& _s) { appendList(2); append(_s.first); append(_s.second); return *this; } template <class T, class U> RLPStream& append(std::pair<T, U> const& _s) { appendList(2); append(_s.first); append(_s.second); return *this; }
/// Appends a list. /// Appends a list.

30
libdevcrypto/Common.cpp

@ -20,6 +20,7 @@
* @date 2014 * @date 2014
*/ */
#include "Common.h"
#include <random> #include <random>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
@ -28,7 +29,6 @@
#include "SHA3.h" #include "SHA3.h"
#include "FileSystem.h" #include "FileSystem.h"
#include "CryptoPP.h" #include "CryptoPP.h"
#include "Common.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::crypto; 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); return decrypt(_k, _cipher, o_plain);
} }
h128 dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plain, bytes& o_cipher) std::pair<bytes, h128> dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plain)
{ {
h128 iv(Nonce::get()); 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; const int c_aesKeyLen = 16;
SecByteBlock key(_k.data(), c_aesKeyLen); SecByteBlock key(_k.data(), c_aesKeyLen);
try try
{ {
CTR_Mode<AES>::Encryption e; CTR_Mode<AES>::Encryption e;
e.SetKeyWithIV(key, key.size(), _iv.data()); e.SetKeyWithIV(key, key.size(), _iv.data());
e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); bytes ret(_plain.size());
return _iv; e.ProcessData(ret.data(), _plain.data(), _plain.size());
return ret;
} }
catch (CryptoPP::Exception& _e) catch (CryptoPP::Exception& _e)
{ {
cerr << _e.what() << endl; cerr << _e.what() << endl;
o_cipher.resize(0); return bytes();
return h128();
} }
} }
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; const size_t c_aesKeyLen = 16;
SecByteBlock key(_k.data(), c_aesKeyLen); SecByteBlock key(_k.data(), c_aesKeyLen);
try try
{ {
CTR_Mode<AES>::Decryption d; CTR_Mode<AES>::Decryption d;
d.SetKeyWithIV(key, key.size(), _iv.data()); d.SetKeyWithIV(key, key.size(), _iv.data());
d.ProcessData(o_plaintext.data(), _cipher.data(), _cipher.size()); bytes ret(_cipher.size());
return true; d.ProcessData(ret.data(), _cipher.data(), _cipher.size());
return ret;
} }
catch (CryptoPP::Exception& _e) catch (CryptoPP::Exception& _e)
{ {
cerr << _e.what() << endl; cerr << _e.what() << endl;
o_plaintext.resize(0); return bytes();
return false;
} }
} }

10
libdevcrypto/Common.h

@ -68,8 +68,8 @@ extern Address ZeroAddress;
/// A vector of Ethereum addresses. /// A vector of Ethereum addresses.
using Addresses = h160s; using Addresses = h160s;
/// A set of Ethereum addresses. /// A hash set of Ethereum addresses.
using AddressSet = std::set<h160>; using AddressHash = std::unordered_set<h160>;
/// A vector of secrets. /// A vector of secrets.
using Secrets = h256s; 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); bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Encrypts payload with random IV/ctr using AES128-CTR. /// Encrypts payload with random IV/ctr using AES128-CTR.
h128 encryptSymNoAuth(h128 const& _k, bytesConstRef _plain, bytes& o_cipher); std::pair<bytes, h128> encryptSymNoAuth(h128 const& _k, bytesConstRef _plain);
/// Encrypts payload with specified IV/ctr using AES128-CTR. /// 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. /// 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. /// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _hash); Public recover(Signature const& _sig, h256 const& _hash);

5
libdevcrypto/CryptoPP.cpp

@ -78,8 +78,7 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher)
bytes mKey(32); bytes mKey(32);
ctx.Final(mKey.data()); ctx.Final(mKey.data());
bytes cipherText; bytes cipherText = encryptSymNoAuth(h128(eKey), h128(), bytesConstRef(&io_cipher));
encryptSymNoAuth(h128(eKey), bytesConstRef(&io_cipher), cipherText, h128());
if (cipherText.empty()) if (cipherText.empty())
return; return;
@ -139,7 +138,7 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text)
if (mac[i] != msgMac[i]) if (mac[i] != msgMac[i])
return false; return false;
decryptSymNoAuth(h128(eKey), iv, cipherNoIV, plain); plain = decryptSymNoAuth(h128(eKey), iv, cipherNoIV);
io_text.resize(plain.size()); io_text.resize(plain.size());
io_text.swap(plain); io_text.swap(plain);

8
libdevcrypto/MemoryDB.cpp

@ -30,9 +30,9 @@ namespace dev
const char* DBChannel::name() { return "TDB"; } const char* DBChannel::name() { return "TDB"; }
const char* DBWarn::name() { return "TDB"; } const char* DBWarn::name() { return "TDB"; }
std::map<h256, std::string> MemoryDB::get() const std::unordered_map<h256, std::string> MemoryDB::get() const
{ {
std::map<h256, std::string> ret; std::unordered_map<h256, std::string> ret;
for (auto const& i: m_main) for (auto const& i: m_main)
if (!m_enforceRefs || i.second.second > 0) if (!m_enforceRefs || i.second.second > 0)
ret.insert(make_pair(i.first, i.second.first)); ret.insert(make_pair(i.first, i.second.first));
@ -112,9 +112,9 @@ void MemoryDB::purge()
it = m_main.erase(it); it = m_main.erase(it);
} }
set<h256> MemoryDB::keys() const h256Hash MemoryDB::keys() const
{ {
set<h256> ret; h256Hash ret;
for (auto const& i: m_main) for (auto const& i: m_main)
if (i.second.second) if (i.second.second)
ret.insert(i.first); ret.insert(i.first);

10
libdevcrypto/MemoryDB.h

@ -21,7 +21,7 @@
#pragma once #pragma once
#include <map> #include <unordered_map>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h> #include <libdevcore/FixedHash.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
@ -45,7 +45,7 @@ public:
MemoryDB() {} MemoryDB() {}
void clear() { m_main.clear(); } // WARNING !!!! didn't originally clear m_refCount!!! void clear() { m_main.clear(); } // WARNING !!!! didn't originally clear m_refCount!!!
std::map<h256, std::string> get() const; std::unordered_map<h256, std::string> get() const;
std::string lookup(h256 const& _h) const; std::string lookup(h256 const& _h) const;
bool exists(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 removeAux(h256 const& _h) { m_aux[_h].second = false; }
void insertAux(h256 const& _h, bytesConstRef _v) { m_aux[_h] = make_pair(_v.toBytes(), true); } void insertAux(h256 const& _h, bytesConstRef _v) { m_aux[_h] = make_pair(_v.toBytes(), true); }
std::set<h256> keys() const; h256Hash keys() const;
protected: protected:
std::map<h256, std::pair<std::string, unsigned>> m_main; std::unordered_map<h256, std::pair<std::string, unsigned>> m_main;
std::map<h256, std::pair<bytes, bool>> m_aux; std::unordered_map<h256, std::pair<bytes, bool>> m_aux;
mutable bool m_enforceRefs = false; mutable bool m_enforceRefs = false;
}; };

220
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 <http://www.gnu.org/licenses/>.
*/
/** @file SecretStore.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "SecretStore.h"
#include <thread>
#include <mutex>
#include <boost/filesystem.hpp>
#include <libdevcore/Log.h>
#include <libdevcore/Guards.h>
#include <test/JsonSpiritHeaders.h>
#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<std::string()> 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();
}
}

56
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 <http://www.gnu.org/licenses/>.
*/
/** @file SecretStore.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <functional>
#include <mutex>
#include "Common.h"
#include "FileSystem.h"
namespace dev
{
class SecretStore
{
public:
SecretStore();
~SecretStore();
bytes secret(h128 const& _uuid, std::function<std::string()> 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<h128, bytes> m_cached;
std::map<h128, std::pair<std::string, std::string>> m_keys;
};
}

13
libdevcrypto/TrieDB.h

@ -26,7 +26,6 @@
#include <leveldb/db.h> #include <leveldb/db.h>
#pragma warning(pop) #pragma warning(pop)
#include <map>
#include <memory> #include <memory>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
@ -105,7 +104,7 @@ public:
void debugPrint() {} void debugPrint() {}
void descendKey(h256 _k, std::set<h256>& _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); _keyMask.erase(_k);
if (_k == m_root && _k == c_shaNull) // root allowed to be empty 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 descendList(RLP(node(_k)), _keyMask, _wasExt, _out, _indent); // if not, it must be a list
} }
void descendEntry(RLP const& _r, std::set<h256>& _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) if (_r.isData() && _r.size() == 32)
descendKey(_r.toHash<h256>(), _keyMask, _wasExt, _out, _indent); descendKey(_r.toHash<h256>(), _keyMask, _wasExt, _out, _indent);
@ -123,7 +122,7 @@ public:
BOOST_THROW_EXCEPTION(InvalidTrie()); BOOST_THROW_EXCEPTION(InvalidTrie());
} }
void descendList(RLP const& _r, std::set<h256>& _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)) if (_r.isList() && _r.itemCount() == 2 && (!_wasExt || _out))
{ {
@ -144,9 +143,9 @@ public:
BOOST_THROW_EXCEPTION(InvalidTrie()); BOOST_THROW_EXCEPTION(InvalidTrie());
} }
std::set<h256> leftOvers(std::ostream* _out = nullptr) const h256Hash leftOvers(std::ostream* _out = nullptr) const
{ {
std::set<h256> k = m_db->keys(); h256Hash k = m_db->keys();
descendKey(m_root, k, false, _out); descendKey(m_root, k, false, _out);
return k; return k;
} }
@ -431,7 +430,7 @@ public:
void insert(bytesConstRef _key, bytesConstRef _value) { Super::insert(_key, _value); m_secure.insert(_key, _value); syncRoot(); } 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(); } void remove(bytesConstRef _key) { Super::remove(_key); m_secure.remove(_key); syncRoot(); }
std::set<h256> leftOvers(std::ostream* = nullptr) const { return std::set<h256>{}; } h256Hash leftOvers(std::ostream* = nullptr) const { return h256Hash{}; }
bool check(bool) const { return m_secure.check(false) && Super::check(false); } bool check(bool) const { return m_secure.check(false) && Super::check(false); }
private: private:

16
libethcore/BlockInfo.cpp

@ -195,26 +195,18 @@ template <class T, class U> h256 trieRootOver(unsigned _itemCount, T const& _get
return t.root(); 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 void BlockInfo::verifyInternals(bytesConstRef _block) const
{ {
RLP root(_block); RLP root(_block);
/*OverlayDB db;
GenericTrieDB<OverlayDB> 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 txList = root[1];
auto expectedRoot = trieRootOver(txList.itemCount(), [&](unsigned i){ return rlp(i); }, [&](unsigned i){ return txList[i].data(); }); 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) if (transactionsRoot != expectedRoot)
BOOST_THROW_EXCEPTION(InvalidTransactionsHash() << HashMismatchError(expectedRoot, transactionsRoot)); BOOST_THROW_EXCEPTION(InvalidTransactionsHash() << HashMismatchError(expectedRoot, transactionsRoot));
clog(BlockInfoDiagnosticsChannel) << "Expected uncle hash:" << toString(sha3(root[2].data()));
if (sha3Uncles != sha3(root[2].data())) if (sha3Uncles != sha3(root[2].data()))
BOOST_THROW_EXCEPTION(InvalidUnclesHash()); BOOST_THROW_EXCEPTION(InvalidUnclesHash());
} }

6
libethcore/EthashAux.h

@ -75,12 +75,12 @@ private:
static EthashAux* s_this; static EthashAux* s_this;
RecursiveMutex x_this; RecursiveMutex x_this;
std::map<h256, std::shared_ptr<LightAllocation>> m_lights; std::unordered_map<h256, std::shared_ptr<LightAllocation>> m_lights;
std::map<h256, std::weak_ptr<FullAllocation>> m_fulls; std::unordered_map<h256, std::weak_ptr<FullAllocation>> m_fulls;
FullType m_lastUsedFull; FullType m_lastUsedFull;
Mutex x_epochs; Mutex x_epochs;
std::map<h256, unsigned> m_epochs; std::unordered_map<h256, unsigned> m_epochs;
h256s m_seedHashes; h256s m_seedHashes;
}; };

6
libethereum/Account.h

@ -134,8 +134,8 @@ public:
/// which encodes the base-state of the account's storage (upon which the storage is overlaid). /// 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; } h256 baseRoot() const { assert(m_storageRoot); return m_storageRoot; }
/// @returns the storage overlay as a simple map. /// @returns the storage overlay as a simple hash map.
std::map<u256, u256> const& storageOverlay() const { return m_storageOverlay; } std::unordered_map<u256, u256> const& storageOverlay() const { return m_storageOverlay; }
/// Set a key/value pair in the account's storage. This actually goes into the overlay, for committing /// Set a key/value pair in the account's storage. This actually goes into the overlay, for committing
/// to the trie later. /// to the trie later.
@ -194,7 +194,7 @@ private:
h256 m_codeHash = EmptySHA3; h256 m_codeHash = EmptySHA3;
/// The map with is overlaid onto whatever storage is implied by the m_storageRoot in the trie. /// The map with is overlaid onto whatever storage is implied by the m_storageRoot in the trie.
std::map<u256, u256> m_storageOverlay; std::unordered_map<u256, u256> m_storageOverlay;
/// The associated code for this account. The SHA3 of this should be equal to m_codeHash unless m_codeHash /// The associated code for this account. The SHA3 of this should be equal to m_codeHash unless m_codeHash
/// equals c_contractConceptionCodeHash. /// equals c_contractConceptionCodeHash.

2
libethereum/AccountDiff.h

@ -62,7 +62,7 @@ struct AccountDiff
Diff<bool> exist; ///< The account's existance; was it created/deleted or not? Diff<bool> exist; ///< The account's existance; was it created/deleted or not?
Diff<u256> balance; ///< The account's balance; did it alter? Diff<u256> balance; ///< The account's balance; did it alter?
Diff<u256> nonce; ///< The account's nonce; did it alter? Diff<u256> nonce; ///< The account's nonce; did it alter?
std::map<u256, Diff<u256>> storage; ///< The account's storage addresses; each has its own Diff. std::unordered_map<u256, Diff<u256>> storage; ///< The account's storage addresses; each has its own Diff.
Diff<bytes> code; ///< The account's code; in general this should only have changed if exist also changed. Diff<bytes> code; ///< The account's code; in general this should only have changed if exist also changed.
}; };

4
libethereum/BlockChain.cpp

@ -961,10 +961,10 @@ vector<unsigned> BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earlie
return ret; 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). // 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; h256 p = _parent;
for (unsigned i = 0; i < 6 && p != m_genesisHash; ++i, p = details(p).parent) for (unsigned i = 0; i < 6 && p != m_genesisHash; ++i, p = details(p).parent)
{ {

4
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; }; struct BlockChainDebug: public LogChannel { static const char* name(); static const int verbosity = 0; };
// TODO: Move all this Genesis stuff into Genesis.h/.cpp // TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::map<Address, Account> const& genesisState(); std::unordered_map<Address, Account> const& genesisState();
ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); 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). /// 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 /// @returns set including the header-hash of every parent (including @a _parent) up to and including generation +5
/// togther with all their quoted uncles. /// 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. /// Run through database and verify all blocks by reevaluating.
/// Will call _progress with the progress in this operation first param done, second total. /// Will call _progress with the progress in this operation first param done, second total.

12
libethereum/BlockQueue.h

@ -107,13 +107,13 @@ private:
bool invariants() const override; bool invariants() const override;
mutable boost::shared_mutex m_lock; ///< General lock. mutable boost::shared_mutex m_lock; ///< General lock.
std::set<h256> m_drainingSet; ///< All blocks being imported. h256Hash m_drainingSet; ///< All blocks being imported.
std::set<h256> m_readySet; ///< All blocks ready for chain-import. h256Hash m_readySet; ///< All blocks ready for chain-import.
std::vector<std::pair<h256, bytes>> m_ready; ///< List of blocks, in correct order, ready for chain-import. std::vector<std::pair<h256, bytes>> m_ready; ///< List of blocks, in correct order, ready for chain-import.
std::set<h256> m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain. h256Hash m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain.
std::multimap<h256, std::pair<h256, bytes>> 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::unordered_multimap<h256, std::pair<h256, bytes>> 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<h256> m_knownBad; ///< Set of blocks that we know will never be valid. h256Hash m_knownBad; ///< Set of blocks that we know will never be valid.
std::multimap<unsigned, std::pair<h256, bytes>> m_future;///< Set of blocks that are not yet valid. std::multimap<unsigned, std::pair<h256, bytes>> 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. Signal m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast.
}; };

4
libethereum/CachedAddressState.cpp

@ -51,9 +51,9 @@ bytes CachedAddressState::code() const
return h == EmptySHA3 ? bytes() : asBytes(m_o->lookup(h)); return h == EmptySHA3 ? bytes() : asBytes(m_o->lookup(h));
} }
std::map<u256, u256> CachedAddressState::storage() const std::unordered_map<u256, u256> CachedAddressState::storage() const
{ {
std::map<u256, u256> ret; std::unordered_map<u256, u256> ret;
if (m_r) if (m_r)
{ {
SecureTrieDB<h256, OverlayDB> memdb(const_cast<OverlayDB*>(m_o), m_r[2].toHash<h256>()); // promise we won't alter the overlay! :) SecureTrieDB<h256, OverlayDB> memdb(const_cast<OverlayDB*>(m_o), m_r[2].toHash<h256>()); // promise we won't alter the overlay! :)

2
libethereum/CachedAddressState.h

@ -47,7 +47,7 @@ public:
bytes code() const; bytes code() const;
// TODO: DEPRECATE. // TODO: DEPRECATE.
std::map<u256, u256> storage() const; std::unordered_map<u256, u256> storage() const;
AccountDiff diff(CachedAddressState const& _c); AccountDiff diff(CachedAddressState const& _c);

4
libethereum/CanonBlockChain.cpp

@ -41,9 +41,9 @@ namespace js = json_spirit;
#define ETH_CATCH 1 #define ETH_CATCH 1
std::map<Address, Account> const& dev::eth::genesisState() std::unordered_map<Address, Account> const& dev::eth::genesisState()
{ {
static std::map<Address, Account> s_ret; static std::unordered_map<Address, Account> s_ret;
if (s_ret.empty()) if (s_ret.empty())
{ {

2
libethereum/CanonBlockChain.h

@ -45,7 +45,7 @@ namespace eth
{ {
// TODO: Move all this Genesis stuff into Genesis.h/.cpp // TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::map<Address, Account> const& genesisState(); std::unordered_map<Address, Account> const& genesisState();
/** /**
* @brief Implements the blockchain database. All data this gives is disk-backed. * @brief Implements the blockchain database. All data this gives is disk-backed.

12
libethereum/Client.cpp

@ -308,7 +308,7 @@ void Client::killChain()
void Client::clearPending() void Client::clearPending()
{ {
h256Set changeds; h256Hash changeds;
DEV_WRITE_GUARDED(x_postMine) DEV_WRITE_GUARDED(x_postMine)
{ {
if (!m_postMine.pending().size()) if (!m_postMine.pending().size())
@ -345,7 +345,7 @@ static S& filtersStreamOut(S& _out, T const& _fs)
return _out; 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); Guard l(x_filtersWatches);
for (pair<h256 const, InstalledFilter>& i: m_filters) for (pair<h256 const, InstalledFilter>& 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. // TODO: more precise check on whether the txs match.
auto d = m_bc.info(_block); auto d = m_bc.info(_block);
@ -496,7 +496,7 @@ void Client::syncTransactionQueue()
// returns TransactionReceipts, once for each transaction. // returns TransactionReceipts, once for each transaction.
cwork << "postSTATE <== TQ"; cwork << "postSTATE <== TQ";
h256Set changeds; h256Hash changeds;
TransactionReceipts newPendingReceipts; TransactionReceipts newPendingReceipts;
DEV_TIMED(working) DEV_WRITE_GUARDED(x_working) DEV_TIMED(working) DEV_WRITE_GUARDED(x_working)
@ -552,7 +552,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
if (auto h = m_host.lock()) if (auto h = m_host.lock())
h->noteNewBlocks(); h->noteNewBlocks();
h256Set changeds; h256Hash changeds;
for (auto const& h: _ir.first) for (auto const& h: _ir.first)
appendFromNewBlock(h, changeds); appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter); changeds.insert(ChainChangedFilter);
@ -631,7 +631,7 @@ void Client::startMining()
onPostStateChanged(); onPostStateChanged();
} }
void Client::noteChanged(h256Set const& _filters) void Client::noteChanged(h256Hash const& _filters)
{ {
Guard l(x_filtersWatches); Guard l(x_filtersWatches);
if (_filters.size()) if (_filters.size())

7
libethereum/Client.h

@ -228,15 +228,15 @@ protected:
/// Collate the changed filters for the bloom filter of the given pending transaction. /// Collate the changed filters for the bloom filter of the given pending transaction.
/// Insert any filters that are activated into @a o_changed. /// 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. /// Collate the changed filters for the hash of the given block.
/// Insert any filters that are activated into @a o_changed. /// 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. /// Record that the set of filters @a _filters have changed.
/// This doesn't actually make any callbacks, but incrememnts some counters in m_watches. /// 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: private:
/// Called when Worker is starting. /// Called when Worker is starting.
@ -309,7 +309,6 @@ private:
ActivityReport m_report; ActivityReport m_report;
// TODO!!!!!! REPLACE WITH A PROPER X-THREAD ASIO SIGNAL SYSTEM (could just be condition variables)
std::condition_variable m_signalled; std::condition_variable m_signalled;
Mutex x_signalled; Mutex x_signalled;
std::atomic<bool> m_syncTransactionQueue = {false}; std::atomic<bool> m_syncTransactionQueue = {false};

22
libethereum/ClientBase.cpp

@ -75,17 +75,17 @@ Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes con
} }
// TODO: remove try/catch, allow exceptions // 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; ExecutionResult ret;
try try
{ {
State temp = asOf(_blockNumber); State temp = asOf(_blockNumber);
Address a = toAddress(_secret); u256 n = temp.transactionsFrom(_from);
u256 n = temp.transactionsFrom(a); Transaction t(_value, _gasPrice, _gas, _dest, _data, n);
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); t.forceSender(_from);
if (_ff == FudgeFactor::Lenient) 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); ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted);
} }
catch (...) catch (...)
@ -95,19 +95,19 @@ ExecutionResult ClientBase::call(Secret _secret, u256 _value, Address _dest, byt
return ret; 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; ExecutionResult ret;
try try
{ {
State temp = asOf(_blockNumber); State temp = asOf(_blockNumber);
Address a = toAddress(_secret); u256 n = temp.transactionsFrom(_from);
u256 n = temp.transactionsFrom(a);
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); // 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) 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); ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted);
} }
catch (...) catch (...)
@ -147,7 +147,7 @@ h256 ClientBase::codeHashAt(Address _a, BlockNumber _block) const
return asOf(_block).codeHash(_a); return asOf(_block).codeHash(_a);
} }
map<u256, u256> ClientBase::storageAt(Address _a, BlockNumber _block) const unordered_map<u256, u256> ClientBase::storageAt(Address _a, BlockNumber _block) const
{ {
return asOf(_block).storage(_a); return asOf(_block).storage(_a);
} }

10
libethereum/ClientBase.h

@ -83,10 +83,12 @@ public:
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; 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. /// 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. /// 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::balanceAt;
using Interface::countAt; using Interface::countAt;
@ -100,7 +102,7 @@ public:
virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const override; virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const override;
virtual bytes codeAt(Address _a, BlockNumber _block) const override; virtual bytes codeAt(Address _a, BlockNumber _block) const override;
virtual h256 codeHashAt(Address _a, BlockNumber _block) const override; virtual h256 codeHashAt(Address _a, BlockNumber _block) const override;
virtual std::map<u256, u256> storageAt(Address _a, BlockNumber _block) const override; virtual std::unordered_map<u256, u256> storageAt(Address _a, BlockNumber _block) const override;
virtual LocalisedLogEntries logs(unsigned _watchId) const override; virtual LocalisedLogEntries logs(unsigned _watchId) const override;
virtual LocalisedLogEntries logs(LogFilter const& _filter) const override; virtual LocalisedLogEntries logs(LogFilter const& _filter) const override;
@ -173,7 +175,7 @@ protected:
// filters // filters
mutable Mutex x_filtersWatches; ///< Our lock. mutable Mutex x_filtersWatches; ///< Our lock.
std::map<h256, InstalledFilter> m_filters; ///< The dictionary of filters that are active. std::unordered_map<h256, InstalledFilter> m_filters; ///< The dictionary of filters that are active.
std::map<unsigned, ClientWatch> m_watches; ///< Each and every watch - these reference a filter. std::map<unsigned, ClientWatch> m_watches; ///< Each and every watch - these reference a filter.
}; };

4
libethereum/DownloadMan.cpp

@ -39,7 +39,7 @@ DownloadSub::~DownloadSub()
} }
} }
h256Set DownloadSub::nextFetch(unsigned _n) h256Hash DownloadSub::nextFetch(unsigned _n)
{ {
Guard l(m_fetch); Guard l(m_fetch);
@ -51,7 +51,7 @@ h256Set DownloadSub::nextFetch(unsigned _n)
m_remaining.clear(); m_remaining.clear();
if (!m_man || m_man->chainEmpty()) if (!m_man || m_man->chainEmpty())
return h256Set(); return h256Hash();
m_asked = (~(m_man->taken() + m_attempted)).lowest(_n); m_asked = (~(m_man->taken() + m_attempted)).lowest(_n);
if (m_asked.empty()) if (m_asked.empty())

12
libethereum/DownloadMan.h

@ -21,9 +21,9 @@
#pragma once #pragma once
#include <map>
#include <vector> #include <vector>
#include <set> #include <unordered_set>
#include <unordered_map>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libdevcore/Worker.h> #include <libdevcore/Worker.h>
#include <libdevcore/RangeMask.h> #include <libdevcore/RangeMask.h>
@ -46,7 +46,7 @@ public:
~DownloadSub(); ~DownloadSub();
/// Finished last fetch - grab the next bunch of block hashes to download. /// 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. /// 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); bool noteBlock(h256 _hash);
@ -71,8 +71,8 @@ private:
DownloadMan* m_man = nullptr; DownloadMan* m_man = nullptr;
mutable Mutex m_fetch; mutable Mutex m_fetch;
h256Set m_remaining; h256Hash m_remaining;
std::map<h256, unsigned> m_indices; std::unordered_map<h256, unsigned> m_indices;
RangeMask<unsigned> m_asked; RangeMask<unsigned> m_asked;
RangeMask<unsigned> m_attempted; RangeMask<unsigned> m_attempted;
}; };
@ -155,7 +155,7 @@ private:
RangeMask<unsigned> m_blocksGot; RangeMask<unsigned> m_blocksGot;
mutable SharedMutex x_subs; mutable SharedMutex x_subs;
std::set<DownloadSub*> m_subs; std::unordered_set<DownloadSub*> m_subs;
}; };
} }

3
libethereum/EthereumHost.cpp

@ -21,7 +21,6 @@
#include "EthereumHost.h" #include "EthereumHost.h"
#include <set>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
@ -184,7 +183,7 @@ void EthereumHost::doWork()
void EthereumHost::maintainTransactions() void EthereumHost::maintainTransactions()
{ {
// Send any new transactions. // Send any new transactions.
map<std::shared_ptr<EthereumPeer>, h256s> peerTransactions; unordered_map<std::shared_ptr<EthereumPeer>, h256s> peerTransactions;
auto ts = m_tq.transactions(); auto ts = m_tq.transactions();
for (auto const& i: ts) for (auto const& i: ts)
{ {

10
libethereum/EthereumHost.h

@ -22,9 +22,9 @@
#pragma once #pragma once
#include <mutex> #include <mutex>
#include <map> #include <unordered_map>
#include <vector> #include <vector>
#include <set> #include <unordered_set>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <thread> #include <thread>
@ -97,7 +97,7 @@ private:
/// Get a bunch of needed blocks. /// Get a bunch of needed blocks.
/// Removes them from our list 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. /// @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. /// Check to see if the network peer-state initialisation has happened.
bool isInitialised() const { return (bool)m_latestBlockSent; } bool isInitialised() const { return (bool)m_latestBlockSent; }
@ -121,9 +121,9 @@ private:
DownloadMan m_man; DownloadMan m_man;
h256 m_latestBlockSent; h256 m_latestBlockSent;
h256Set m_transactionsSent; h256Hash m_transactionsSent;
std::set<p2p::NodeId> m_banned; std::unordered_set<p2p::NodeId> m_banned;
bool m_newTransactions = false; bool m_newTransactions = false;
bool m_newBlocks = false; bool m_newBlocks = false;

5
libethereum/EthereumPeer.h

@ -23,7 +23,6 @@
#include <mutex> #include <mutex>
#include <array> #include <array>
#include <set>
#include <memory> #include <memory>
#include <utility> #include <utility>
@ -140,9 +139,9 @@ private:
bool m_requireTransactions = false; bool m_requireTransactions = false;
Mutex x_knownBlocks; 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; Mutex x_knownTransactions;
h256Set m_knownTransactions; ///< Transactions that the peer already knows of. h256Hash m_knownTransactions; ///< Transactions that the peer already knows of.
}; };

2
libethereum/ExtVM.h

@ -92,7 +92,7 @@ public:
private: private:
State& m_s; ///< A reference to the base state. State& m_s; ///< A reference to the base state.
std::map<Address, Account> m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution. std::unordered_map<Address, Account> m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution.
}; };
} }

16
libethereum/Interface.h

@ -76,13 +76,17 @@ public:
virtual void flushTransactions() = 0; virtual void flushTransactions() = 0;
/// Makes the given call. Nothing is recorded into the state. /// 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; 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(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); } 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. /// Does the given creation. Nothing is recorded into the state.
/// @returns the pair of the Address of the created contract together with its code. /// @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; virtual ExecutionResult create(Address const& _from, 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); } 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. /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly.
virtual ImportResult injectTransaction(bytes const& _rlp) = 0; 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); } u256 stateAt(Address _a, u256 _l) const { return stateAt(_a, _l, m_default); }
bytes codeAt(Address _a) const { return codeAt(_a, m_default); } bytes codeAt(Address _a) const { return codeAt(_a, m_default); }
h256 codeHashAt(Address _a) const { return codeHashAt(_a, m_default); } h256 codeHashAt(Address _a) const { return codeHashAt(_a, m_default); }
std::map<u256, u256> storageAt(Address _a) const { return storageAt(_a, m_default); } std::unordered_map<u256, u256> storageAt(Address _a) const { return storageAt(_a, m_default); }
virtual u256 balanceAt(Address _a, BlockNumber _block) const = 0; virtual u256 balanceAt(Address _a, BlockNumber _block) const = 0;
virtual u256 countAt(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 u256 stateAt(Address _a, u256 _l, BlockNumber _block) const = 0;
virtual bytes codeAt(Address _a, BlockNumber _block) const = 0; virtual bytes codeAt(Address _a, BlockNumber _block) const = 0;
virtual h256 codeHashAt(Address _a, BlockNumber _block) const = 0; virtual h256 codeHashAt(Address _a, BlockNumber _block) const = 0;
virtual std::map<u256, u256> storageAt(Address _a, BlockNumber _block) const = 0; virtual std::unordered_map<u256, u256> storageAt(Address _a, BlockNumber _block) const = 0;
// [LOGS API] // [LOGS API]

203
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 <http://www.gnu.org/licenses/>.
*/
/** @file KeyManager.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "KeyManager.h"
#include <thread>
#include <mutex>
#include <boost/filesystem.hpp>
#include <libdevcore/Log.h>
#include <libdevcore/Guards.h>
#include <libdevcore/RLP.h>
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<std::string()> 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<std::string()> 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<Address, std::pair<std::string, std::string>> KeyManager::keys() const
{
std::map<Address, std::pair<std::string, std::string>> 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;
}

110
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 <http://www.gnu.org/licenses/>.
*/
/** @file KeyManager.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <functional>
#include <mutex>
#include <libdevcrypto/SecretStore.h>
#include <libdevcrypto/FileSystem.h>
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<Address, std::pair<std::string, std::string>> 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<std::string()> const& _pass = DontKnowThrow) const;
Secret secret(h128 const& _uuid, std::function<std::string()> 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<Address, h128> m_addrLookup;
std::map<h128, KeyInfo> m_keyInfo;
std::map<h256, std::string> m_passwordInfo;
// Passwords that we're storing.
mutable std::map<h256, std::string> 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;
};
}

4
libethereum/LogFilter.h

@ -67,8 +67,8 @@ public:
friend std::ostream& dev::eth::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s); friend std::ostream& dev::eth::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
private: private:
AddressSet m_addresses; AddressHash m_addresses;
std::array<h256Set, 4> m_topics; std::array<h256Hash, 4> m_topics;
unsigned m_earliest = 0; unsigned m_earliest = 0;
unsigned m_latest = LatestBlock; unsigned m_latest = LatestBlock;
}; };

4
libethereum/Precompiled.cpp

@ -81,7 +81,7 @@ static bytes identityCode(bytesConstRef _in)
return _in.toBytes(); return _in.toBytes();
} }
static const std::map<unsigned, PrecompiledAddress> c_precompiled = static const std::unordered_map<unsigned, PrecompiledAddress> c_precompiled =
{ {
{ 1, { [](bytesConstRef) -> bigint { return c_ecrecoverGas; }, ecrecoverCode }}, { 1, { [](bytesConstRef) -> bigint { return c_ecrecoverGas; }, ecrecoverCode }},
{ 2, { [](bytesConstRef i) -> bigint { return c_sha256Gas + (i.size() + 31) / 32 * c_sha256WordGas; }, sha256Code }}, { 2, { [](bytesConstRef i) -> bigint { return c_sha256Gas + (i.size() + 31) / 32 * c_sha256WordGas; }, sha256Code }},
@ -89,7 +89,7 @@ static const std::map<unsigned, PrecompiledAddress> c_precompiled =
{ 4, { [](bytesConstRef i) -> bigint { return c_identityGas + (i.size() + 31) / 32 * c_identityWordGas; }, identityCode }} { 4, { [](bytesConstRef i) -> bigint { return c_identityGas + (i.size() + 31) / 32 * c_identityWordGas; }, identityCode }}
}; };
std::map<unsigned, PrecompiledAddress> const& dev::eth::precompiled() std::unordered_map<unsigned, PrecompiledAddress> const& dev::eth::precompiled()
{ {
return c_precompiled; return c_precompiled;
} }

4
libethereum/Precompiled.h

@ -21,7 +21,7 @@
#pragma once #pragma once
#include <map> #include <unordered_map>
#include <functional> #include <functional>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
@ -38,7 +38,7 @@ struct PrecompiledAddress
}; };
/// Info on precompiled contract accounts baked into the protocol. /// Info on precompiled contract accounts baked into the protocol.
std::map<unsigned, PrecompiledAddress> const& precompiled(); std::unordered_map<unsigned, PrecompiledAddress> const& precompiled();
} }
} }

23
libethereum/State.cpp

@ -208,9 +208,9 @@ StateDiff State::diff(State const& _c) const
{ {
StateDiff ret; StateDiff ret;
std::set<Address> ads; std::unordered_set<Address> ads;
std::set<Address> trieAds; std::unordered_set<Address> trieAds;
std::set<Address> trieAdsD; std::unordered_set<Address> trieAdsD;
auto trie = SecureTrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&m_db), rootHash()); auto trie = SecureTrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&m_db), rootHash());
auto trieD = SecureTrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&_c.m_db), _c.rootHash()); auto trieD = SecureTrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&_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); ensureCached(m_cache, _a, _requireCode, _forceCreate);
} }
void State::ensureCached(std::map<Address, Account>& _cache, Address _a, bool _requireCode, bool _forceCreate) const void State::ensureCached(std::unordered_map<Address, Account>& _cache, Address _a, bool _requireCode, bool _forceCreate) const
{ {
auto it = _cache.find(_a); auto it = _cache.find(_a);
if (it == _cache.end()) if (it == _cache.end())
@ -426,10 +426,10 @@ u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const
return ret; return ret;
} }
map<Address, u256> State::addresses() const unordered_map<Address, u256> State::addresses() const
{ {
#if ETH_FATDB #if ETH_FATDB
map<Address, u256> ret; unordered_map<Address, u256> ret;
for (auto i: m_cache) for (auto i: m_cache)
if (i.second.isAlive()) if (i.second.isAlive())
ret[i.first] = i.second.balance(); 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) if (receiptsTrie.root() != m_currentBlock.receiptsRoot)
{ {
cwarn << "Bad receipts state root."; cwarn << "Bad receipts state root.";
cwarn << "Expected: " << toString(receiptsTrie.root()) << " received: " << toString(m_currentBlock.receiptsRoot);
cwarn << "Block:" << toHex(_block); cwarn << "Block:" << toHex(_block);
cwarn << "Block RLP:" << rlp; cwarn << "Block RLP:" << rlp;
cwarn << "Calculated: " << receiptsTrie.root(); cwarn << "Calculated: " << receiptsTrie.root();
@ -649,9 +650,9 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
if (rlp[2].itemCount() > 2) if (rlp[2].itemCount() > 2)
BOOST_THROW_EXCEPTION(TooManyUncles()); BOOST_THROW_EXCEPTION(TooManyUncles());
set<Nonce> nonces = { m_currentBlock.nonce }; unordered_set<Nonce> nonces = { m_currentBlock.nonce };
vector<BlockInfo> rewarded; vector<BlockInfo> rewarded;
set<h256> knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); h256Hash knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash);
for (auto const& i: rlp[2]) 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. // 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; // cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl;
set<h256> knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); h256Hash knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash);
auto p = m_previousBlock.parentHash; auto p = m_previousBlock.parentHash;
for (unsigned gen = 0; gen < 6 && p != _bc.genesisHash() && unclesCount < 2; ++gen, p = _bc.details(p).parent) 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; return ret;
} }
map<u256, u256> State::storage(Address _id) const unordered_map<u256, u256> State::storage(Address _id) const
{ {
map<u256, u256> ret; unordered_map<u256, u256> ret;
ensureCached(_id, false, false); ensureCached(_id, false, false);
auto it = m_cache.find(_id); auto it = m_cache.find(_id);

17
libethereum/State.h

@ -22,7 +22,6 @@
#pragma once #pragma once
#include <array> #include <array>
#include <map>
#include <unordered_map> #include <unordered_map>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
@ -142,7 +141,7 @@ public:
/// @returns the set containing all addresses currently in use in Ethereum. /// @returns the set containing all addresses currently in use in Ethereum.
/// @throws InterfaceNotSupported if compiled without ETH_FATDB. /// @throws InterfaceNotSupported if compiled without ETH_FATDB.
std::map<Address, u256> addresses() const; std::unordered_map<Address, u256> addresses() const;
/// Get the header information on the present block. /// Get the header information on the present block.
BlockInfo const& info() const { return m_currentBlock; } BlockInfo const& info() const { return m_currentBlock; }
@ -238,8 +237,8 @@ public:
/// Get the storage of an account. /// Get the storage of an account.
/// @note This is expensive. Don't use it unless you need to. /// @note This is expensive. Don't use it unless you need to.
/// @returns std::map<u256, u256> if no account exists at that address. /// @returns std::unordered_map<u256, u256> if no account exists at that address.
std::map<u256, u256> storage(Address _contract) const; std::unordered_map<u256, u256> storage(Address _contract) const;
/// Get the code of an account. /// Get the code of an account.
/// @returns bytes() if no account exists at that address. /// @returns bytes() if no account exists at that address.
@ -263,7 +262,7 @@ public:
Transactions const& pending() const { return m_transactions; } Transactions const& pending() const { return m_transactions; }
/// Get the list of hashes of pending 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. /// Get the transaction receipt for the transaction of the given index.
TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; } TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; }
@ -334,7 +333,7 @@ private:
void ensureCached(Address _a, bool _requireCode, bool _forceCreate) const; void ensureCached(Address _a, bool _requireCode, bool _forceCreate) const;
/// Retrieve all information about a given address into a cache. /// Retrieve all information about a given address into a cache.
void ensureCached(std::map<Address, Account>& _cache, Address _a, bool _requireCode, bool _forceCreate) const; void ensureCached(std::unordered_map<Address, Account>& _cache, Address _a, bool _requireCode, bool _forceCreate) const;
/// Execute the given block, assuming it corresponds to m_currentBlock. /// Execute the given block, assuming it corresponds to m_currentBlock.
/// Throws on failure. /// Throws on failure.
@ -355,10 +354,10 @@ private:
SecureTrieDB<Address, OverlayDB> m_state; ///< Our state tree, as an OverlayDB DB. SecureTrieDB<Address, OverlayDB> m_state; ///< Our state tree, as an OverlayDB DB.
Transactions m_transactions; ///< The current list of transactions that we've included in the state. Transactions m_transactions; ///< The current list of transactions that we've included in the state.
TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts. TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts.
std::set<h256> 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; OverlayDB m_lastTx;
mutable std::map<Address, Account> 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<Address, Account> 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_previousBlock; ///< The previous block's information.
BlockInfo m_currentBlock; ///< The current 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); std::ostream& operator<<(std::ostream& _out, State const& _s);
template <class DB> template <class DB>
void commit(std::map<Address, Account> const& _cache, DB& _db, SecureTrieDB<Address, DB>& _state) void commit(std::unordered_map<Address, Account> const& _cache, DB& _db, SecureTrieDB<Address, DB>& _state)
{ {
for (auto const& i: _cache) for (auto const& i: _cache)
if (i.second.isDirty()) if (i.second.isDirty())

6
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); } 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. /// 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. /// 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. /// Constructs a transaction from the given RLP.
explicit Transaction(bytesConstRef _rlp, CheckTransaction _checkSig); explicit Transaction(bytesConstRef _rlp, CheckTransaction _checkSig);
@ -132,6 +132,8 @@ public:
Address const& sender() const; Address const& sender() const;
/// Like sender() but will never throw. @returns a null Address if the signature is invalid. /// Like sender() but will never throw. @returns a null Address if the signature is invalid.
Address const& safeSender() const noexcept; 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. /// @returns true if transaction is non-null.
explicit operator bool() const { return m_type != NullTransaction; } explicit operator bool() const { return m_type != NullTransaction; }

12
libethereum/TransactionQueue.h

@ -55,7 +55,7 @@ public:
void drop(h256 const& _txHash); void drop(h256 const& _txHash);
std::map<h256, Transaction> transactions() const { ReadGuard l(m_lock); return m_current; } std::unordered_map<h256, Transaction> transactions() const { ReadGuard l(m_lock); return m_current; }
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_unknown.size()); } std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_unknown.size()); }
u256 maxNonce(Address const& _a) const; u256 maxNonce(Address const& _a) const;
@ -73,11 +73,11 @@ private:
bool removeCurrent_WITH_LOCK(h256 const& _txHash); bool removeCurrent_WITH_LOCK(h256 const& _txHash);
mutable SharedMutex m_lock; ///< General lock. mutable SharedMutex m_lock; ///< General lock.
std::set<h256> m_known; ///< Hashes of transactions in both sets. h256Hash m_known; ///< Hashes of transactions in both sets.
std::map<h256, Transaction> m_current; ///< Map of SHA3(tx) to tx. std::unordered_map<h256, Transaction> m_current; ///< Map of SHA3(tx) to tx.
std::multimap<Address, std::pair<h256, Transaction>> 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_multimap<Address, std::pair<h256, Transaction>> 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<h256, std::function<void(ImportResult)>> m_callbacks; ///< Called once. std::unordered_map<h256, std::function<void(ImportResult)>> m_callbacks; ///< Called once.
std::set<h256> m_dropped; ///< Transactions that have previously been dropped. h256Hash m_dropped; ///< Transactions that have previously been dropped.
std::multimap<Address, h256> m_senders; ///< Mapping from the sender address to the transaction hash; useful for determining the nonce of a given sender. std::multimap<Address, h256> 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. Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast.
}; };

1
libethereum/TransactionReceipt.h

@ -22,7 +22,6 @@
#pragma once #pragma once
#include <array> #include <array>
#include <map>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libevm/ExtVMFace.h> #include <libevm/ExtVMFace.h>

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

2
libethereumx/Ethereum.h

@ -75,7 +75,7 @@ public:
void flushTransactions(); void flushTransactions();
/// Makes the given call. Nothing is recorded into the state. /// 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 // Informational stuff

16
libevmasm/Assembly.cpp

@ -304,9 +304,6 @@ Assembly& Assembly::optimise(bool _enable)
{ {
if (!_enable) if (!_enable)
return *this; return *this;
std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> 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; unsigned total = 0;
for (unsigned count = 1; count > 0; total += count) for (unsigned count = 1; count > 0; total += count)
@ -314,10 +311,17 @@ Assembly& Assembly::optimise(bool _enable)
copt << toString(*this); copt << toString(*this);
count = 0; 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..."; copt << "Performing control flow analysis...";
{ {
ControlFlowGraph cfg(m_items); 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()) if (optItems.size() < m_items.size())
{ {
copt << "Old size: " << m_items.size() << ", new size: " << optItems.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..."; copt << "Performing common subexpression elimination...";
for (auto iter = m_items.begin(); iter != m_items.end();) 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<ExpressionClasses>());
CommonSubexpressionEliminator eliminator(state);
auto orig = iter; auto orig = iter;
iter = eliminator.feedItems(iter, m_items.end()); iter = eliminator.feedItems(iter, m_items.end());
AssemblyItems optItems; AssemblyItems optItems;

271
libevmasm/CommonSubexpressionEliminator.cpp

@ -37,18 +37,19 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
map<int, Id> initialStackContents; map<int, Id> initialStackContents;
map<int, Id> targetStackContents; map<int, Id> targetStackContents;
int minHeight = m_stackHeight + 1; int minHeight = m_state.stackHeight() + 1;
if (!m_stackElements.empty()) if (!m_state.stackElements().empty())
minHeight = min(minHeight, m_stackElements.begin()->first); minHeight = min(minHeight, m_state.stackElements().begin()->first);
for (int height = minHeight; height <= 0; ++height) for (int height = minHeight; height <= m_initialState.stackHeight(); ++height)
initialStackContents[height] = initialStackElement(height, SourceLocation()); initialStackContents[height] = m_initialState.stackElement(height, SourceLocation());
for (int height = minHeight; height <= m_stackHeight; ++height) for (int height = minHeight; height <= m_state.stackHeight(); ++height)
targetStackContents[height] = stackElement(height, SourceLocation()); targetStackContents[height] = m_state.stackElement(height, SourceLocation());
// Debug info: // Debug info:
//stream(cout, initialStackContents, targetStackContents); //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, initialStackContents,
targetStackContents targetStackContents
); );
@ -57,103 +58,11 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
return items; return items;
} }
ostream& CommonSubexpressionEliminator::stream(
ostream& _out,
map<int, Id> _initialStack,
map<int, Id> _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) void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _copyItem)
{ {
if (_item.type() != Operation) StoreOperation op = m_state.feedItem(_item, _copyItem);
{ if (op.isValid())
assertThrow(_item.deposit() == 1, InvalidDeposit, ""); m_storeOperations.push_back(op);
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<Id> 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();
}
} }
void CommonSubexpressionEliminator::optimizeBreakingItem() void CommonSubexpressionEliminator::optimizeBreakingItem()
@ -164,20 +73,20 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
SourceLocation const& location = m_breakingItem->getLocation(); SourceLocation const& location = m_breakingItem->getLocation();
AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType(); AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType();
Id condition = stackElement(m_stackHeight - 1, location); Id condition = m_state.stackElement(m_state.stackHeight() - 1, location);
Id zero = m_expressionClasses.find(u256(0)); Id zero = m_state.expressionClasses().find(u256(0));
if (m_expressionClasses.knownToBeDifferent(condition, zero)) if (m_state.expressionClasses().knownToBeDifferent(condition, zero))
{ {
feedItem(AssemblyItem(Instruction::SWAP1, location), true); feedItem(AssemblyItem(Instruction::SWAP1, location), true);
feedItem(AssemblyItem(Instruction::POP, location), true); feedItem(AssemblyItem(Instruction::POP, location), true);
AssemblyItem item(Instruction::JUMP, location); AssemblyItem item(Instruction::JUMP, location);
item.setJumpType(jumpType); item.setJumpType(jumpType);
m_breakingItem = m_expressionClasses.storeItem(item); m_breakingItem = m_state.expressionClasses().storeItem(item);
return; return;
} }
Id negatedCondition = m_expressionClasses.find(Instruction::ISZERO, {condition}); Id negatedCondition = m_state.expressionClasses().find(Instruction::ISZERO, {condition});
if (m_expressionClasses.knownToBeDifferent(negatedCondition, zero)) if (m_state.expressionClasses().knownToBeDifferent(negatedCondition, zero))
{ {
AssemblyItem it(Instruction::POP, location); AssemblyItem it(Instruction::POP, location);
feedItem(it, true); 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<Id> 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( CSECodeGenerator::CSECodeGenerator(
ExpressionClasses& _expressionClasses, ExpressionClasses& _expressionClasses,
vector<CSECodeGenerator::StoreOperation> const& _storeOperations vector<CSECodeGenerator::StoreOperation> const& _storeOperations
@ -339,10 +106,12 @@ CSECodeGenerator::CSECodeGenerator(
} }
AssemblyItems CSECodeGenerator::generateCode( AssemblyItems CSECodeGenerator::generateCode(
int _initialStackHeight,
map<int, Id> const& _initialStack, map<int, Id> const& _initialStack,
map<int, Id> const& _targetStackContents map<int, Id> const& _targetStackContents
) )
{ {
m_stackHeight = _initialStackHeight;
m_stack = _initialStack; m_stack = _initialStack;
for (auto const& item: m_stack) for (auto const& item: m_stack)
if (!m_classPositions.count(item.second)) if (!m_classPositions.count(item.second))

63
libevmasm/CommonSubexpressionEliminator.h

@ -32,6 +32,7 @@
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libevmasm/ExpressionClasses.h> #include <libevmasm/ExpressionClasses.h>
#include <libevmasm/SemanticInformation.h> #include <libevmasm/SemanticInformation.h>
#include <libevmasm/KnownState.h>
namespace dev namespace dev
{ {
@ -58,20 +59,9 @@ class CommonSubexpressionEliminator
{ {
public: public:
using Id = ExpressionClasses::Id; using Id = ExpressionClasses::Id;
struct StoreOperation using StoreOperation = KnownState::StoreOperation;
{
enum Target { Memory, Storage }; CommonSubexpressionEliminator(KnownState const& _state): m_initialState(_state), m_state(_state) {}
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;
};
/// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first /// 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. /// 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. /// Tries to optimize the item that breaks the basic block at the end.
void optimizeBreakingItem(); void optimizeBreakingItem();
/// Simplifies the given item using KnownState m_initialState;
/// Assigns a new equivalence class to the next sequence number of the given stack element. KnownState m_state;
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<int, Id> 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<Id, Id> 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<Id, Id> m_memoryContent;
/// Keeps record of all sha3 hashes that are computed.
std::map<std::vector<Id>, Id> m_knownSha3Hashes;
/// Keeps information about which storage or memory slots were written to at which sequence /// Keeps information about which storage or memory slots were written to at which sequence
/// number with what instruction. /// number with what instruction.
std::vector<StoreOperation> m_storeOperations; std::vector<StoreOperation> m_storeOperations;
/// Structure containing the classes of equivalent expressions.
ExpressionClasses m_expressionClasses;
/// The item that breaks the basic block, can be nullptr. /// The item that breaks the basic block, can be nullptr.
/// It is usually appended to the block but can be optimized in some cases. /// 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 /// @param _targetStackContents final contents of the stack, by stack height relative to initial
/// @note should only be called once on each object. /// @note should only be called once on each object.
AssemblyItems generateCode( AssemblyItems generateCode(
int _initialStackHeight,
std::map<int, Id> const& _initialStack, std::map<int, Id> const& _initialStack,
std::map<int, Id> const& _targetStackContents std::map<int, Id> const& _targetStackContents
); );
@ -199,7 +152,7 @@ private:
AssemblyItems m_generatedItems; AssemblyItems m_generatedItems;
/// Current height of the stack relative to the start. /// 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. /// If (b, a) is in m_requests then b is needed to compute a.
std::multimap<Id, Id> m_neededBy; std::multimap<Id, Id> m_neededBy;
/// Current content of the stack. /// Current content of the stack.

109
libevmasm/ControlFlowGraph.cpp

@ -23,9 +23,11 @@
#include <libevmasm/ControlFlowGraph.h> #include <libevmasm/ControlFlowGraph.h>
#include <map> #include <map>
#include <memory>
#include <libevmasm/Exceptions.h> #include <libevmasm/Exceptions.h>
#include <libevmasm/AssemblyItem.h> #include <libevmasm/AssemblyItem.h>
#include <libevmasm/SemanticInformation.h> #include <libevmasm/SemanticInformation.h>
#include <libevmasm/KnownState.h>
using namespace std; using namespace std;
using namespace dev; 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."); assertThrow( _id < initial().m_id, OptimizerException, "Tag number too large.");
} }
AssemblyItems ControlFlowGraph::optimisedItems() BasicBlocks ControlFlowGraph::optimisedBlocks()
{ {
if (m_items.empty()) if (m_items.empty())
return m_items; return BasicBlocks();
findLargestTag(); findLargestTag();
splitBlocks(); splitBlocks();
resolveNextLinks(); resolveNextLinks();
removeUnusedBlocks(); removeUnusedBlocks();
setPrevLinks(); setPrevLinks();
gatherKnowledge();
return rebuildCode(); 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<KnownState>();
ExpressionClasses& expr = emptyState->expressionClasses();
bool unknownJumpEncountered = false;
vector<pair<BlockId, KnownStatePointer>> 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<BlockId, unsigned> pushes; map<BlockId, unsigned> pushes;
for (auto& idAndBlock: m_blocks) for (auto& idAndBlock: m_blocks)
@ -220,7 +294,7 @@ AssemblyItems ControlFlowGraph::rebuildCode()
for (auto it: m_blocks) for (auto it: m_blocks)
blocksToAdd.insert(it.first); blocksToAdd.insert(it.first);
set<BlockId> blocksAdded; set<BlockId> blocksAdded;
AssemblyItems code; BasicBlocks blocks;
for ( for (
BlockId blockId = BlockId::initial(); BlockId blockId = BlockId::initial();
@ -233,23 +307,34 @@ AssemblyItems ControlFlowGraph::rebuildCode()
blockId = m_blocks.at(blockId).prev; blockId = m_blocks.at(blockId).prev;
for (; blockId; blockId = m_blocks.at(blockId).next) for (; blockId; blockId = m_blocks.at(blockId).next)
{ {
BasicBlock const& block = m_blocks.at(blockId); BasicBlock& block = m_blocks.at(blockId);
blocksToAdd.erase(blockId); blocksToAdd.erase(blockId);
blocksAdded.insert(blockId); blocksAdded.insert(blockId);
auto begin = m_items.begin() + block.begin; if (block.begin == block.end)
auto end = m_items.begin() + block.end;
if (begin == end)
continue; continue;
// If block starts with unused tag, skip it. // If block starts with unused tag, skip it.
if (previousHandedOver && !pushes[blockId] && begin->type() == Tag) if (previousHandedOver && !pushes[blockId] && m_items[block.begin].type() == Tag)
++begin; ++block.begin;
if (block.begin < block.end)
blocks.push_back(block);
previousHandedOver = (block.endType == BasicBlock::EndType::HANDOVER); 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() BlockId ControlFlowGraph::generateNewId()

32
libevmasm/ControlFlowGraph.h

@ -24,16 +24,18 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <memory>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Assertions.h> #include <libdevcore/Assertions.h>
#include <libevmasm/ExpressionClasses.h>
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
class AssemblyItem; class KnownState;
using AssemblyItems = std::vector<AssemblyItem>; using KnownStatePointer = std::shared_ptr<KnownState>;
/** /**
* Identifier for a block, coincides with the tag number of an AssemblyItem but adds a special * 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; unsigned end = 0;
/// Tags pushed inside this block, with multiplicity. /// Tags pushed inside this block, with multiplicity.
std::vector<BlockId> pushedTags; std::vector<BlockId> pushedTags;
/// ID of the block that always follows this one (either JUMP or flow into new block), /// ID of the block that always follows this one (either non-branching part of JUMPI or flow
/// or BlockId::invalid() otherwise /// into new block), or BlockId::invalid() otherwise
BlockId next = BlockId::invalid(); 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(); BlockId prev = BlockId::invalid();
enum class EndType { JUMP, JUMPI, STOP, HANDOVER }; enum class EndType { JUMP, JUMPI, STOP, HANDOVER };
EndType endType = EndType::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<BasicBlock>;
class ControlFlowGraph class ControlFlowGraph
{ {
public: public:
/// Initializes the control flow graph. /// Initializes the control flow graph.
/// @a _items has to persist across the usage of this class. /// @a _items has to persist across the usage of this class.
ControlFlowGraph(AssemblyItems const& _items): m_items(_items) {} ControlFlowGraph(AssemblyItems const& _items): m_items(_items) {}
/// @returns the collection of optimised items, should be called only once. /// @returns vector of basic blocks in the order they should be used in the final code.
AssemblyItems optimisedItems(); /// Should be called only once.
BasicBlocks optimisedBlocks();
private: private:
void findLargestTag(); void findLargestTag();
void splitBlocks(); void splitBlocks();
void resolveNextLinks(); void resolveNextLinks();
void removeUnusedBlocks(); void removeUnusedBlocks();
void gatherKnowledge();
void setPrevLinks(); 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(); BlockId generateNewId();

28
libevmasm/ExpressionClasses.cpp

@ -37,6 +37,7 @@ using namespace dev::eth;
bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression const& _other) const bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression const& _other) const
{ {
assertThrow(!!item && !!_other.item, OptimizerException, "");
auto type = item->type(); auto type = item->type();
auto otherType = _other.item->type(); auto otherType = _other.item->type();
return std::tie(type, item->data(), arguments, sequenceNumber) < return std::tie(type, item->data(), arguments, sequenceNumber) <
@ -56,12 +57,15 @@ ExpressionClasses::Id ExpressionClasses::find(
exp.arguments = _arguments; exp.arguments = _arguments;
exp.sequenceNumber = _sequenceNumber; exp.sequenceNumber = _sequenceNumber;
if (SemanticInformation::isDeterministic(_item))
{
if (SemanticInformation::isCommutativeOperation(_item)) if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end()); sort(exp.arguments.begin(), exp.arguments.end());
auto it = m_expressions.find(exp); auto it = m_expressions.find(exp);
if (it != m_expressions.end()) if (it != m_expressions.end())
return it->id; return it->id;
}
if (_copyItem) if (_copyItem)
exp.item = storeItem(_item); exp.item = storeItem(_item);
@ -122,10 +126,16 @@ string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const
{ {
Expression const& expr = representative(_id); Expression const& expr = representative(_id);
stringstream str; stringstream str;
str << dec << expr.id << ":" << *expr.item << "("; str << dec << expr.id << ":";
if (expr.item)
{
str << *expr.item << "(";
for (Id arg: expr.arguments) for (Id arg: expr.arguments)
str << fullDAGToString(arg) << ","; str << fullDAGToString(arg) << ",";
str << ")"; str << ")";
}
else
str << " UNIQUE";
return str.str(); return str.str();
} }
@ -279,7 +289,11 @@ ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr,
{ {
static Rules rules; static Rules rules;
if (_expr.item->type() != Operation) if (
!_expr.item ||
_expr.item->type() != Operation ||
!SemanticInformation::isDeterministic(*_expr.item)
)
return -1; return -1;
for (auto const& rule: rules.rules()) for (auto const& rule: rules.rules())
@ -337,7 +351,7 @@ void Pattern::setMatchGroup(unsigned _group, map<unsigned, Expression const*>& _
bool Pattern::matches(Expression const& _expr, ExpressionClasses const& _classes) const bool Pattern::matches(Expression const& _expr, ExpressionClasses const& _classes) const
{ {
if (!matchesBaseItem(*_expr.item)) if (!matchesBaseItem(_expr.item))
return false; return false;
if (m_matchGroup) if (m_matchGroup)
{ {
@ -387,13 +401,15 @@ string Pattern::toString() const
return s.str(); return s.str();
} }
bool Pattern::matchesBaseItem(AssemblyItem const& _item) const bool Pattern::matchesBaseItem(AssemblyItem const* _item) const
{ {
if (m_type == UndefinedItem) if (m_type == UndefinedItem)
return true; return true;
if (m_type != _item.type()) if (!_item)
return false;
if (m_type != _item->type())
return false; return false;
if (m_requireDataMatch && m_data != _item.data()) if (m_requireDataMatch && m_data != _item->data())
return false; return false;
return true; return true;
} }

4
libevmasm/ExpressionClasses.h

@ -50,7 +50,7 @@ public:
struct Expression struct Expression
{ {
Id id; Id id;
AssemblyItem const* item; AssemblyItem const* item = nullptr;
Ids arguments; Ids arguments;
unsigned sequenceNumber; ///< Storage modification sequence, only used for SLOAD/SSTORE instructions. 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). /// Behaves as if this was a tuple of (item->type(), item->data(), arguments, sequenceNumber).
@ -149,7 +149,7 @@ public:
std::string toString() const; std::string toString() const;
private: private:
bool matchesBaseItem(AssemblyItem const& _item) const; bool matchesBaseItem(AssemblyItem const* _item) const;
Expression const& matchGroupValue() const; Expression const& matchGroupValue() const;
AssemblyItemType m_type; AssemblyItemType m_type;

326
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 <http://www.gnu.org/licenses/>.
*/
/**
* @file KnownState.cpp
* @author Christian <c@ethdev.com>
* @date 2015
* Contains knowledge about the state of the virtual machine at a specific instruction.
*/
#include "KnownState.h"
#include <functional>
#include <libdevcrypto/SHA3.h>
#include <libevmasm/AssemblyItem.h>
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<Id> 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<Id> 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;
}

163
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 <http://www.gnu.org/licenses/>.
*/
/**
* @file KnownState.h
* @author Christian <c@ethdev.com>
* @date 2015
* Contains knowledge about the state of the virtual machine at a specific instruction.
*/
#pragma once
#include <vector>
#include <map>
#include <set>
#include <tuple>
#include <memory>
#include <ostream>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h>
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/SemanticInformation.h>
namespace dev
{
namespace eth
{
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
/**
* 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> _expressionClasses = std::make_shared<ExpressionClasses>()
): 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<KnownState> copy() const { return std::make_shared<KnownState>(*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<int, Id> 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<int, Id> 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<Id, Id> 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<Id, Id> m_memoryContent;
/// Keeps record of all sha3 hashes that are computed.
std::map<std::vector<Id>, Id> m_knownSha3Hashes;
/// Structure containing the classes of equivalent expressions.
std::shared_ptr<ExpressionClasses> m_expressionClasses;
};
}
}

53
libevmasm/SemanticInformation.cpp

@ -122,3 +122,56 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
return false; 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;
}
}

8
libevmasm/SemanticInformation.h

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <libevmcore/Instruction.h>
namespace dev namespace dev
{ {
@ -45,6 +46,13 @@ struct SemanticInformation
static bool isSwapInstruction(AssemblyItem const& _item); static bool isSwapInstruction(AssemblyItem const& _item);
static bool isJumpInstruction(AssemblyItem const& _item); static bool isJumpInstruction(AssemblyItem const& _item);
static bool altersControlFlow(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);
}; };
} }

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

86
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSConsole.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include <iostream>
#include <libdevcore/Log.h>
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include "JSConsole.h"
#include "JSV8Connector.h"
#include "libjsconsole/JSConsoleResources.hpp"
// TODO! make readline optional!
#include <readline/readline.h>
#include <readline/history.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
JSConsole::JSConsole(WebThreeDirect& _web3, std::vector<dev::KeyPair> 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, ' ');
}

53
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSConsole.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <libjsengine/JSV8Engine.h>
#include <libjsengine/JSV8Printer.h>
class WebThreeStubServer;
namespace jsonrpc { class AbstractServerConnector; }
namespace dev
{
namespace eth
{
class JSConsole
{
public:
JSConsole(WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts);
~JSConsole();
void repl() const;
private:
std::string promptForIndentionLevel(int _i) const;
JSV8Engine m_engine;
JSV8Printer m_printer;
std::unique_ptr<WebThreeStubServer> m_jsonrpcServer;
std::unique_ptr<jsonrpc::AbstractServerConnector> m_jsonrpcConnector;
};
}
}

54
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSV8Connector.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @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();
}

50
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSV8Connector.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <jsonrpccpp/server/abstractserverconnector.h>
#include <libjsengine/JSV8RPC.h>
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);
};
}
}

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

11
libjsengine/Common.js

@ -0,0 +1,11 @@
console = {};
console.log = function () {
};
console.warn = function () {
};
console.error = function () {
};
setTimeout = function () {
};

36
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSEngine.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include <string.h>
#include <stdlib.h>
#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);
}

60
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSEngine.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <exception>
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 <typename T>
class JSEngine
{
public:
// should be used to evalute javascript expression
virtual T eval(char const* _cstr) const = 0;
};
}
}

23
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSPrinter.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include "JSPrinter.h"

41
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSPrinter.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include "JSEngine.h"
namespace dev
{
namespace eth
{
template <typename T>
class JSPrinter
{
public:
virtual JSString print(T const& _value) const { return _value.toString(); }
virtual JSString prettyPrint(T const& _value) const { return print(_value); }
};
}
}

8
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")

187
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSV8Engine.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include <memory>
#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<v8::Message> 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 <v8::Context> const& context() const { return m_context; }
private:
v8::HandleScope m_handleScope;
v8::Persistent <v8::Context> 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<v8::String> source = v8::String::New(_cstr);
v8::Local<v8::String> name(v8::String::New("(shell)"));
v8::ScriptOrigin origin(name);
v8::Handle<v8::Script> 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<v8::String>::New(tryCatch.Message()->Get()));
}
auto result = script->Run();
if (result.IsEmpty())
{
reportException(&tryCatch);
return v8::Exception::Error(v8::Local<v8::String>::New(tryCatch.Message()->Get()));
}
return result;
}
v8::Handle<v8::Context> const& JSV8Engine::context() const
{
return m_scope->context();
}

61
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSV8Engine.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <v8.h>
#include "JSEngine.h"
namespace dev
{
namespace eth
{
class JSV8Env;
class JSV8Scope;
class JSV8Value: public JSValue
{
public:
JSV8Value(v8::Handle<v8::Value> _value): m_value(_value) {}
JSString toString() const;
v8::Handle<v8::Value> const& value() const { return m_value; }
private:
v8::Handle<v8::Value> m_value;
};
class JSV8Engine: public JSEngine<JSV8Value>
{
public:
JSV8Engine();
virtual ~JSV8Engine();
JSV8Value eval(char const* _cstr) const;
v8::Handle<v8::Context> const& context() const;
private:
static JSV8Env s_env;
JSV8Scope* m_scope;
};
}
}

49
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSV8Printer.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include <string>
#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<v8::String> pp = v8::String::New("prettyPrint");
v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(m_engine.context()->Global()->Get(pp));
v8::Local<v8::Value> values[1] = {v8::Local<v8::Value>::New(_value.value())};
v8::Local<v8::Value> res = func->Call(func, 1, values);
v8::String::Utf8Value str(res);
if (*str)
return *str;
throw JSPrintException();
}

43
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSV8Printer.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include "JSPrinter.h"
#include "JSV8Engine.h"
namespace dev
{
namespace eth
{
class JSV8Printer: public JSPrinter<JSV8Value>
{
public:
JSV8Printer(JSV8Engine const& _engine);
JSString prettyPrint(JSV8Value const& _value) const;
private:
JSV8Engine const& m_engine;
};
}
}

84
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSV8RPC.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include "JSV8RPC.h"
using namespace dev;
using namespace dev::eth;
namespace dev
{
namespace eth
{
v8::Handle<v8::Value> JSV8RPCSend(v8::Arguments const& _args)
{
v8::Local<v8::String> JSON = v8::String::New("JSON");
v8::Local<v8::String> parse = v8::String::New("parse");
v8::Local<v8::String> stringify = v8::String::New("stringify");
v8::Handle<v8::Object> jsonObject = v8::Handle<v8::Object>::Cast(v8::Context::GetCurrent()->Global()->Get(JSON));
v8::Handle<v8::Function> parseFunc = v8::Handle<v8::Function>::Cast(jsonObject->Get(parse));
v8::Handle<v8::Function> stringifyFunc = v8::Handle<v8::Function>::Cast(jsonObject->Get(stringify));
v8::Local<v8::Object> self = _args.Holder();
v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(self->GetInternalField(0));
JSV8RPC* that = static_cast<JSV8RPC*>(wrap->Value());
v8::Local<v8::Value> vals[1] = {_args[0]->ToObject()};
v8::Local<v8::Value> stringifiedArg = stringifyFunc->Call(stringifyFunc, 1, vals);
v8::String::Utf8Value str(stringifiedArg);
that->onSend(*str);
v8::Local<v8::Value> 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<v8::ObjectTemplate> 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<v8::Object> obj = rpcTemplate->NewInstance();
obj->SetInternalField(0, v8::External::New(this));
v8::Local<v8::String> web3 = v8::String::New("web3");
v8::Local<v8::String> setProvider = v8::String::New("setProvider");
v8::Handle<v8::Object> web3object = v8::Handle<v8::Object>::Cast(m_engine.context()->Global()->Get(web3));
v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(web3object->Get(setProvider));
v8::Local<v8::Value> values[1] = {obj};
func->Call(func, 1, values);
m_lastResponse = R"(
{
"id": 1,
"jsonrpc": "2.0",
"error": "Uninitalized JSV8RPC!"
}
)";
}

47
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 <http://www.gnu.org/licenses/>.
*/
/** @file JSV8RPC.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <libjsengine/JSV8Engine.h>
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;
};
}
}

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

23
libp2p/Common.h

@ -214,3 +214,26 @@ struct Node
/// Simple stream output for a NodeIPEndpoint. /// Simple stream output for a NodeIPEndpoint.
std::ostream& operator<<(std::ostream& _out, dev::p2p::NodeIPEndpoint const& _ep); std::ostream& operator<<(std::ostream& _out, dev::p2p::NodeIPEndpoint const& _ep);
} }
/// std::hash for asio::adress
namespace std
{
template <> struct hash<bi::address>
{
size_t operator()(bi::address const& _a) const
{
if (_a.is_v4())
return std::hash<unsigned long>()(_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<size_t>(0x3487194039229152ul); // Chosen by fair dice roll, guaranteed to be random
return std::hash<std::string>()(_a.to_string());
}
};
}

4
libp2p/Host.h

@ -227,7 +227,7 @@ private:
std::shared_ptr<NodeTable> m_nodeTable; ///< Node table (uses kademlia-like discovery). std::shared_ptr<NodeTable> 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; /// 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<NodeId, std::shared_ptr<Peer>> m_peers; std::unordered_map<NodeId, std::shared_ptr<Peer>> m_peers;
/// Peers we try to connect regardless of p2p network. /// Peers we try to connect regardless of p2p network.
std::set<NodeId> m_requiredPeers; std::set<NodeId> 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()) /// 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 because we flush zombie entries (null-weakptrs) as regular maintenance from a const method.
mutable std::map<NodeId, std::weak_ptr<Session>> m_sessions; mutable std::unordered_map<NodeId, std::weak_ptr<Session>> m_sessions;
mutable RecursiveMutex x_sessions; mutable RecursiveMutex x_sessions;
std::list<std::weak_ptr<RLPXHandshake>> m_connecting; ///< Pending connections. std::list<std::weak_ptr<RLPXHandshake>> m_connecting; ///< Pending connections.

2
libp2p/Network.cpp

@ -154,12 +154,12 @@ int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, NetworkPreferences const&
_acceptor.bind(endpoint); _acceptor.bind(endpoint);
_acceptor.listen(); _acceptor.listen();
retport = _acceptor.local_endpoint().port(); retport = _acceptor.local_endpoint().port();
assert(retport == _netPrefs.listenPort);
} }
catch (...) catch (...)
{ {
clog(NetWarn) << "Couldn't start accepting connections on host. Failed to accept socket.\n" << boost::current_exception_diagnostic_information(); 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;
} }
return retport; return retport;

16
libp2p/NodeTable.cpp

@ -81,6 +81,7 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relati
{ {
shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, _node.endpoint)); shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, _node.endpoint));
ret->pending = false; ret->pending = false;
DEV_GUARDED(x_nodes)
m_nodes[_node.id] = ret; m_nodes[_node.id] = ret;
noteActiveNode(_node.id, _node.endpoint); noteActiveNode(_node.id, _node.endpoint);
return ret; return ret;
@ -101,13 +102,12 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relati
return move(shared_ptr<NodeEntry>()); return move(shared_ptr<NodeEntry>());
} }
{ DEV_GUARDED(x_nodes)
Guard ln(x_nodes);
if (m_nodes.count(_node.id)) if (m_nodes.count(_node.id))
return m_nodes[_node.id]; return m_nodes[_node.id];
}
shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, _node.endpoint)); shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, _node.endpoint));
DEV_GUARDED(x_nodes)
m_nodes[_node.id] = ret; m_nodes[_node.id] = ret;
clog(NodeTableConnect) << "addNode pending for" << _node.endpoint; clog(NodeTableConnect) << "addNode pending for" << _node.endpoint;
ping(_node.endpoint); ping(_node.endpoint);
@ -186,6 +186,7 @@ void NodeTable::discover(NodeId _node, unsigned _round, shared_ptr<set<shared_pt
tried.push_back(r); tried.push_back(r);
FindNode p(r->endpoint, _node); FindNode p(r->endpoint, _node);
p.sign(m_secret); p.sign(m_secret);
DEV_GUARDED(x_findNodeTimeout)
m_findNodeTimeout.push_back(make_pair(r->id, chrono::steady_clock::now())); m_findNodeTimeout.push_back(make_pair(r->id, chrono::steady_clock::now()));
m_socketPointer->send(p); m_socketPointer->send(p);
} }
@ -447,17 +448,17 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
{ {
if (auto n = nodeEntry(nodeid)) if (auto n = nodeEntry(nodeid))
n->pending = false; 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()); m_pubkDiscoverPings.erase(_from.address());
} }
if (!haveNode(nodeid)) if (!haveNode(nodeid))
addNode(Node(nodeid, NodeIPEndpoint(_from.address(), _from.port(), _from.port()))); 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 // update our endpoint address and UDP port
@ -473,6 +474,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
{ {
bool expected = false; bool expected = false;
auto now = chrono::steady_clock::now(); auto now = chrono::steady_clock::now();
DEV_GUARDED(x_findNodeTimeout)
m_findNodeTimeout.remove_if([&](NodeIdTimePoint const& t) m_findNodeTimeout.remove_if([&](NodeIdTimePoint const& t)
{ {
if (t.first == nodeid && now - t.second < c_reqTimeout) if (t.first == nodeid && now - t.second < c_reqTimeout)

6
libp2p/NodeTable.h

@ -80,7 +80,7 @@ protected:
Mutex x_events; Mutex x_events;
std::list<NodeId> m_nodeEventHandler; std::list<NodeId> m_nodeEventHandler;
std::map<NodeId, NodeTableEventType> m_events; std::unordered_map<NodeId, NodeTableEventType> m_events;
}; };
class NodeTable; class NodeTable;
@ -249,7 +249,7 @@ private:
Secret m_secret; ///< This nodes secret key. 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. mutable Mutex x_nodes; ///< LOCK x_state first if both locks are required. Mutable for thread-safe copy in nodes() const.
std::map<NodeId, std::shared_ptr<NodeEntry>> m_nodes; ///< Nodes std::unordered_map<NodeId, std::shared_ptr<NodeEntry>> m_nodes; ///< Nodes
mutable Mutex x_state; ///< LOCK x_state first if both x_nodes and x_state locks are required. mutable Mutex x_state; ///< LOCK x_state first if both x_nodes and x_state locks are required.
std::array<NodeBucket, s_bins> m_state; ///< State of p2p node network. std::array<NodeBucket, s_bins> m_state; ///< State of p2p node network.
@ -258,7 +258,7 @@ private:
std::deque<EvictionTimeout> m_evictions; ///< Eviction timeouts. std::deque<EvictionTimeout> m_evictions; ///< Eviction timeouts.
Mutex x_pubkDiscoverPings; ///< LOCK x_nodes first if both x_nodes and x_pubkDiscoverPings locks are required. Mutex x_pubkDiscoverPings; ///< LOCK x_nodes first if both x_nodes and x_pubkDiscoverPings locks are required.
std::map<bi::address, TimePoint> m_pubkDiscoverPings; ///< List of pending pings where node entry wasn't created due to unkown pubk. std::unordered_map<bi::address, TimePoint> m_pubkDiscoverPings; ///< List of pending pings where node entry wasn't created due to unkown pubk.
Mutex x_findNodeTimeout; Mutex x_findNodeTimeout;
std::list<NodeIdTimePoint> m_findNodeTimeout; ///< Timeouts for pending Ping and FindNode requests. std::list<NodeIdTimePoint> m_findNodeTimeout; ///< Timeouts for pending Ping and FindNode requests.

4
libp2p/UDP.cpp

@ -20,9 +20,13 @@
*/ */
#include "UDP.h" #include "UDP.h"
using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
const char* RLPXWarn::name() { return "!X!"; }
const char* RLPXNote::name() { return "-X-"; }
h256 RLPXDatagramFace::sign(Secret const& _k) h256 RLPXDatagramFace::sign(Secret const& _k)
{ {
assert(packetType()); assert(packetType());

26
libp2p/UDP.h

@ -30,6 +30,7 @@
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include <libdevcore/Log.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include "Common.h" #include "Common.h"
namespace ba = boost::asio; namespace ba = boost::asio;
@ -40,6 +41,9 @@ namespace dev
namespace p2p 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 * UDP Datagram
* @todo make data protected/functional * @todo make data protected/functional
@ -203,13 +207,13 @@ void UDPSocket<Handler, MaxDatagramSize>::doRead()
auto self(UDPSocket<Handler, MaxDatagramSize>::shared_from_this()); auto self(UDPSocket<Handler, MaxDatagramSize>::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) 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 if (m_closed)
// and after the socket has been disconnected. Checking m_closed
// guarantees that m_host will not be called after disconnect().
if (_ec || m_closed)
return disconnectWithError(_ec); return disconnectWithError(_ec);
assert(_len); if (_ec != boost::system::errc::success)
clog(NetWarn) << "Receiving UDP message failed. " << _ec.value() << ":" << _ec.message();
if (_len)
m_host.onReceived(this, m_recvEndpoint, bytesConstRef(m_recvData.data(), _len)); m_host.onReceived(this, m_recvEndpoint, bytesConstRef(m_recvData.data(), _len));
doRead(); doRead();
}); });
@ -223,17 +227,19 @@ void UDPSocket<Handler, MaxDatagramSize>::doWrite()
const UDPDatagram& datagram = m_sendQ[0]; const UDPDatagram& datagram = m_sendQ[0];
auto self(UDPSocket<Handler, MaxDatagramSize>::shared_from_this()); auto self(UDPSocket<Handler, MaxDatagramSize>::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); return disconnectWithError(_ec);
else
{ if (_ec != boost::system::errc::success)
clog(NetWarn) << "Failed delivering UDP message. " << _ec.value() << ":" << _ec.message();
Guard l(x_sendQ); Guard l(x_sendQ);
m_sendQ.pop_front(); m_sendQ.pop_front();
if (m_sendQ.empty()) if (m_sendQ.empty())
return; return;
}
doWrite(); doWrite();
}); });
} }

6
libsolidity/AST.h

@ -1220,7 +1220,7 @@ public:
/// Stores a set of possible declarations referenced by this identifier. Has to be resolved /// Stores a set of possible declarations referenced by this identifier. Has to be resolved
/// providing argument types using overloadResolution before the referenced declaration /// providing argument types using overloadResolution before the referenced declaration
/// is accessed. /// is accessed.
void setOverloadedDeclarations(std::set<Declaration const*> const& _declarations) void setOverloadedDeclarations(std::vector<Declaration const*> const& _declarations)
{ {
m_overloadedDeclarations = _declarations; m_overloadedDeclarations = _declarations;
} }
@ -1237,8 +1237,8 @@ private:
/// Stores a reference to the current contract. This is needed because types of base contracts /// Stores a reference to the current contract. This is needed because types of base contracts
/// change depending on the context. /// change depending on the context.
ContractDefinition const* m_currentContract = nullptr; ContractDefinition const* m_currentContract = nullptr;
/// A set of overloaded declarations, right now only FunctionDefinition has overloaded declarations. /// A vector of overloaded declarations, right now only FunctionDefinition has overloaded declarations.
std::set<Declaration const*> m_overloadedDeclarations; std::vector<Declaration const*> m_overloadedDeclarations;
}; };
/** /**

2
libsolidity/ASTPrinter.h

@ -42,7 +42,7 @@ public:
ASTPrinter( ASTPrinter(
ASTNode const& _ast, ASTNode const& _ast,
std::string const& _source = std::string(), 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. /// Output the string representation of the AST to _stream.
void print(std::ostream& _stream); void print(std::ostream& _stream);

44
libsolidity/Compiler.cpp

@ -206,16 +206,9 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory)
{ {
// We do not check the calldata size, everything is zero-padded. // We do not check the calldata size, everything is zero-paddedd
unsigned offset(CompilerUtils::dataStartOffset);
bigint parameterHeadEnd = offset; m_context << u256(CompilerUtils::dataStartOffset);
for (TypePointer const& type: _typeParameters)
parameterHeadEnd += type->isDynamicallySized() ? 32 : type->getCalldataEncodedSize();
solAssert(parameterHeadEnd <= numeric_limits<unsigned>::max(), "Arguments too large.");
unsigned stackHeightOfPreviousDynamicArgument = 0;
ArrayType const* previousDynamicType = nullptr;
for (TypePointer const& type: _typeParameters) for (TypePointer const& type: _typeParameters)
{ {
switch (type->getCategory()) switch (type->getCategory())
@ -223,34 +216,31 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
case Type::Category::Array: case Type::Category::Array:
if (type->isDynamicallySized()) if (type->isDynamicallySized())
{ {
// put on stack: data_offset length // put on stack: data_pointer length
unsigned newStackHeight = m_context.getStackHeight(); CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
if (previousDynamicType) // stack: data_offset next_pointer
{ //@todo once we support nested arrays, this offset needs to be dynamic.
// Retrieve data start offset by adding length to start offset of previous dynamic type m_context << eth::Instruction::SWAP1 << u256(CompilerUtils::dataStartOffset);
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; m_context << eth::Instruction::ADD;
} // stack: next_pointer data_pointer
else // retrieve length
m_context << u256(parameterHeadEnd); CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
stackHeightOfPreviousDynamicArgument = newStackHeight; // stack: next_pointer length data_pointer
previousDynamicType = &dynamic_cast<ArrayType const&>(*type); m_context << eth::Instruction::SWAP2;
offset += CompilerUtils(m_context).loadFromMemory(offset, IntegerType(256), !_fromMemory);
} }
else else
{ {
m_context << u256(offset); // leave the pointer on the stack
offset += type->getCalldataEncodedSize(); m_context << eth::Instruction::DUP1;
m_context << u256(type->getCalldataEncodedSize()) << eth::Instruction::ADD;
} }
break; break;
default: default:
solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); 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) void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)

10
libsolidity/DeclarationContainer.cpp

@ -37,6 +37,7 @@ Declaration const* DeclarationContainer::conflictingDeclaration(Declaration cons
declarations += m_declarations.at(name); declarations += m_declarations.at(name);
if (m_invisibleDeclarations.count(name)) if (m_invisibleDeclarations.count(name))
declarations += m_invisibleDeclarations.at(name); declarations += m_invisibleDeclarations.at(name);
if (dynamic_cast<FunctionDefinition const*>(&_declaration)) if (dynamic_cast<FunctionDefinition const*>(&_declaration))
{ {
// check that all other declarations with the same name are functions // check that all other declarations with the same name are functions
@ -66,14 +67,13 @@ bool DeclarationContainer::registerDeclaration(Declaration const& _declaration,
return false; return false;
if (_invisible) if (_invisible)
m_invisibleDeclarations[name].insert(&_declaration); m_invisibleDeclarations[name].push_back(&_declaration);
else else
m_declarations[name].insert(&_declaration); m_declarations[name].push_back(&_declaration);
return true; return true;
} }
set<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const std::vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
{ {
solAssert(!_name.empty(), "Attempt to resolve empty name."); solAssert(!_name.empty(), "Attempt to resolve empty name.");
auto result = m_declarations.find(_name); auto result = m_declarations.find(_name);
@ -81,5 +81,5 @@ set<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name
return result->second; return result->second;
if (_recursive && m_enclosingContainer) if (_recursive && m_enclosingContainer)
return m_enclosingContainer->resolveName(_name, true); return m_enclosingContainer->resolveName(_name, true);
return set<Declaration const*>({}); return vector<Declaration const*>({});
} }

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save