Browse Source

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

cl-refactor
CJentzsch 10 years ago
parent
commit
d66ea7f581
  1. 220
      CMakeLists.txt
  2. 5
      alethzero/Connect.cpp
  3. 3
      alethzero/Connect.ui
  4. 6
      alethzero/MainWin.cpp
  5. 10
      cmake/EthCompilerSettings.cmake
  6. 8
      eth/main.cpp
  7. 1
      evmjit/libevmjit/BasicBlock.cpp
  8. 4
      evmjit/libevmjit/BasicBlock.h
  9. 31
      evmjit/libevmjit/Cache.cpp
  10. 12
      evmjit/libevmjit/Cache.h
  11. 3
      evmjit/libevmjit/Compiler.cpp
  12. 17
      evmjit/libevmjit/ExecutionEngine.cpp
  13. 44
      evmjit/libevmjit/RuntimeManager.cpp
  14. 5
      evmjit/libevmjit/RuntimeManager.h
  15. 2
      libdevcore/Common.cpp
  16. 33
      libethcore/BlockInfo.cpp
  17. 21
      libethcore/BlockInfo.h
  18. 2
      libethcore/Common.cpp
  19. 10
      libethcore/Common.h
  20. 48
      libethcore/Ethasher.cpp
  21. 4
      libethcore/Ethasher.h
  22. 330
      libethereum/BlockChain.cpp
  23. 63
      libethereum/BlockChain.h
  24. 22
      libethereum/BlockQueue.cpp
  25. 18
      libethereum/BlockQueue.h
  26. 2
      libethereum/CanonBlockChain.cpp
  27. 4
      libethereum/CanonBlockChain.h
  28. 33
      libethereum/Client.cpp
  29. 14
      libethereum/ClientBase.cpp
  30. 27
      libethereum/EthereumPeer.cpp
  31. 66
      libethereum/State.cpp
  32. 19
      libethereum/State.h
  33. 12
      libethereum/TransactionQueue.cpp
  34. 8
      libethereum/TransactionQueue.h
  35. 2
      libp2p/Capability.cpp
  36. 2
      libp2p/Capability.h
  37. 31
      libp2p/Host.cpp
  38. 1
      libp2p/Peer.cpp
  39. 3
      libp2p/Peer.h
  40. 18
      libp2p/Session.cpp
  41. 2
      libp2p/Session.h
  42. 2
      libtestutils/StateLoader.cpp
  43. 14
      libweb3jsonrpc/CMakeLists.txt
  44. 5
      libweb3jsonrpc/WebThreeStubServer.cpp
  45. 4
      libweb3jsonrpc/WebThreeStubServer.h
  46. 21
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  47. 1
      libweb3jsonrpc/WebThreeStubServerBase.h
  48. 7
      libweb3jsonrpc/abstractwebthreestubserver.h
  49. 1
      libweb3jsonrpc/spec.json
  50. 2
      libwebthree/WebThree.h
  51. 2
      libwhisper/WhisperHost.cpp
  52. 49
      mix/CMakeLists.txt
  53. 7
      mix/ClientModel.cpp
  54. 2
      mix/ClientModel.h
  55. 44
      mix/MixApplication.cpp
  56. 21
      mix/MixApplication.h
  57. 2
      mix/MixClient.cpp
  58. 41
      mix/QBasicNodeDefinition.cpp
  59. 12
      mix/QBasicNodeDefinition.h
  60. 1
      mix/QVariableDeclaration.cpp
  61. 5
      mix/QVariableDeclaration.h
  62. 37
      mix/QVariableDefinition.cpp
  63. 8
      mix/QVariableDefinition.h
  64. 18
      mix/main.cpp
  65. 7
      mix/qml.qrc
  66. 15
      mix/qml/Application.qml
  67. 5
      mix/qml/CallStack.qml
  68. 51
      mix/qml/CodeEditor.qml
  69. 1
      mix/qml/CodeEditorStyle.qml
  70. 45
      mix/qml/CodeEditorView.qml
  71. 2
      mix/qml/CommonSeparator.qml
  72. 12
      mix/qml/Debugger.qml
  73. 1
      mix/qml/DebuggerPaneStyle.qml
  74. 1
      mix/qml/DefaultLabel.qml
  75. 4
      mix/qml/DeploymentDialog.qml
  76. 30
      mix/qml/FilesSection.qml
  77. 4
      mix/qml/ItemDelegateDataDump.qml
  78. 115
      mix/qml/LogsPane.qml
  79. 1
      mix/qml/LogsPaneStyle.qml
  80. 1
      mix/qml/MainContent.qml
  81. 1
      mix/qml/NewProjectDialog.qml
  82. 1
      mix/qml/ProjectFilesStyle.qml
  83. 21
      mix/qml/ProjectList.qml
  84. 2
      mix/qml/ProjectModel.qml
  85. 3
      mix/qml/Splitter.qml
  86. 16
      mix/qml/StateDialog.qml
  87. 1
      mix/qml/StateDialogStyle.qml
  88. 1
      mix/qml/StateListModel.qml
  89. 6
      mix/qml/StatusPane.qml
  90. 1
      mix/qml/StatusPaneStyle.qml
  91. 4
      mix/qml/StorageView.qml
  92. 1
      mix/qml/Style.qml
  93. 45
      mix/qml/TransactionDialog.qml
  94. 19
      mix/qml/WebCodeEditor.qml
  95. 21
      mix/qml/WebPreview.qml
  96. 1
      mix/qml/WebPreviewStyle.qml
  97. 8
      mix/qml/qmldir
  98. 6
      mix/test.qrc
  99. 50
      mix/test/TestMain.cpp
  100. 194
      mix/test/TestService.cpp

220
CMakeLists.txt

@ -14,15 +14,21 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# user defined, defaults # user defined, defaults
# Normally, set(...CACHE...) creates cache variables, but does not modify them. # Normally, set(...CACHE...) creates cache variables, but does not modify them.
function(createDefaultCacheConfig) function(createDefaultCacheConfig)
set(HEADLESS OFF CACHE BOOL "Do not compile GUI (AlethZero)")
set(VMTRACE OFF CACHE BOOL "VM tracing and run-time checks (useful for cross-implementation VM debugging)") set(VMTRACE OFF CACHE BOOL "VM tracing and run-time checks (useful for cross-implementation VM debugging)")
set(PARANOIA OFF CACHE BOOL "Additional run-time checks") set(PARANOIA OFF CACHE BOOL "Additional run-time checks")
set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on") set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on")
set(EVMJIT OFF CACHE BOOL "Build a just-in-time compiler for EVM code (requires LLVM)")
set(FATDB OFF CACHE BOOL "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents.") set(FATDB OFF CACHE BOOL "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents.")
set(JUSTTESTS OFF CACHE BOOL "Build only for tests.")
set(SOLIDITY ON CACHE BOOL "Build the Solidity language components (requried unless HEADLESS)")
set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed") set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed")
set(PROFILING OFF CACHE BOOL "Build in support for profiling")
set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).")
set(SOLIDITY ON CACHE BOOL "Build the Solidity language components")
set(SERPENT ON CACHE BOOL "Build the Serpent language components")
set(TOOLS ON CACHE BOOL "Build the tools components")
set(NCURSES ON CACHE BOOL "Build the NCurses components")
set(GUI ON CACHE BOOL "Build GUI components (AlethZero, Mix)")
set(TESTS ON CACHE BOOL "Build the tests.")
set(EVMJIT OFF CACHE BOOL "Build just-in-time compiler for EVM code (requires LLVM)")
set(ETHASHCL OFF CACHE BOOL "Build in support for GPU mining via OpenCL") set(ETHASHCL OFF CACHE BOOL "Build in support for GPU mining via OpenCL")
endfunction() endfunction()
@ -53,8 +59,8 @@ function(configureProject)
add_definitions(-DETH_SOLIDITY) add_definitions(-DETH_SOLIDITY)
endif() endif()
if (HEADLESS OR JUSTTESTS) if (GUI)
add_definitions(-DETH_HEADLESS) add_definitions(-DETH_GUI)
endif() endif()
endfunction() endfunction()
@ -133,9 +139,128 @@ configureProject()
# Force chromium. # Force chromium.
set (ETH_HAVE_WEBENGINE 1) set (ETH_HAVE_WEBENGINE 1)
message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}") # Normalise build options
message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}; FATDB: ${FATDB}; CHROMIUM: ${ETH_HAVE_WEBENGINE}; USENPM: ${USENPM}; ETHASHCL: ${ETHASHCL}") # TODO: Abstract into something sensible and move into a function.
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(DECENT_PLATFORM OFF)
else ()
set(DECENT_PLATFORM ON)
endif ()
if (PARANOIA)
set(PARANOIA ON)
else ()
set(PARANOIA OFF)
endif ()
if (VMTRACE)
set(VMTRACE ON)
else ()
set(VMTRACE OFF)
endif ()
if (EVMJIT)
set(EVMJIT ON)
else ()
set(EVMJIT OFF)
endif()
if (FATDB)
set(FATDB ON)
else ()
set(FATDB OFF)
endif()
if (JSONRPC)
set(JSONRPC ON)
else ()
set(JSONRPC OFF)
endif ()
if (USENPM)
set(USENPM ON)
else ()
set(USENPM OFF)
endif ()
if (PROFILING)
set(PROFILING ON)
else ()
set(PROFILING OFF)
endif ()
if (SOLIDITY)
set(SOLIDITY ON)
else ()
set(SOLIDITY OFF)
endif()
if (SERPENT)
set(SERPENT ON)
else ()
set(SERPENT OFF)
endif()
if (GUI)
set(GUI ON)
else ()
set(GUI OFF)
endif ()
if (TESTS)
set(TESTS ON)
else ()
set(TESTS OFF)
endif ()
if (TOOLS)
set(TOOLS ON)
else ()
set(TOOLS OFF)
endif ()
if (ETHASHCL)
set(ETHASHCL ON)
else ()
set(ETHASHCL OFF)
endif()
if (NCURSES)
set(NCURSES ON)
else ()
set(NCURSES OFF)
endif ()
if (BUNDLE STREQUAL "minimal")
set(SERPENT OFF)
set(SOLIDITY OFF)
set(USENPM OFF)
set(GUI OFF)
set(NCURSES OFF)
set(TOOLS ON)
set(TESTS OFF)
elseif (BUNDLE STREQUAL "full")
set(SERPENT ON)
set(SOLIDITY ON)
set(USENPM ON)
set(GUI ON)
set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON)
set(TESTS ON)
set(FATDB ON)
elseif (BUNDLE STREQUAL "tests")
set(SERPENT ON)
set(SOLIDITY ON)
set(USENPM OFF)
set(GUI OFF)
set(NCURSES OFF)
set(TOOLS OFF)
set(TESTS ON)
set(FATDB ON)
elseif (BUNDLE STREQUAL "user")
set(SERPENT OFF)
set(SOLIDITY OFF)
set(USENPM OFF)
set(GUI ON)
set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON)
set(TESTS OFF)
endif ()
# Default CMAKE_BUILD_TYPE to "Release".
set(CMAKE_BUILD_TYPE CACHE STRING "Release")
if ("x${CMAKE_BUILD_TYPE}" STREQUAL "x")
set(CMAKE_BUILD_TYPE "Release")
endif ()
# Default TARGET_PLATFORM to "linux". # Default TARGET_PLATFORM to "linux".
set(TARGET_PLATFORM CACHE STRING "linux") set(TARGET_PLATFORM CACHE STRING "linux")
@ -143,6 +268,32 @@ if ("x${TARGET_PLATFORM}" STREQUAL "x")
set(TARGET_PLATFORM "linux") set(TARGET_PLATFORM "linux")
endif () endif ()
message("------------------------------------------------------------------------")
message("-- CMake Version ${CMAKE_VERSION}")
message("-- CMAKE_BUILD_TYPE Build type ${CMAKE_BUILD_TYPE}")
message("-- TARGET_PLATFORM Target platform ${TARGET_PLATFORM}")
message("-- BUNDLE Build bundle ${BUNDLE}")
message("--------------------------------------------------------------- features")
message("-- Chromium support ${ETH_HAVE_WEBENGINE}")
message("-- VMTRACE VM execution tracing ${VMTRACE}")
message("-- PROFILING Profiling support ${PROFILING}")
message("-- PARANOIA Additional (SLOW) database checking ${PARANOIA}")
message("-- FATDB Full database exploring ${FATDB}")
message("-- JSONRPC JSON-RPC support ${JSONRPC}")
message("-- USENPM Javascript source building ${USENPM}")
message("------------------------------------------------------------- components")
message("-- TOOLS Build basic tools ${TOOLS}")
message("-- SOLIDITY Build Solidity language components ${SOLIDITY}")
message("-- SERPENT Build Serpent language components ${SERPENT}")
message("-- GUI Build GUI components ${GUI}")
message("-- NCURSES Build NCurses components ${NCURSES}")
message("-- TESTS Build tests ${TESTS}")
message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}")
message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}")
message("------------------------------------------------------------------------")
message("")
if ("${TARGET_PLATFORM}" STREQUAL "linux") if ("${TARGET_PLATFORM}" STREQUAL "linux")
set(CMAKE_THREAD_LIBS_INIT pthread) set(CMAKE_THREAD_LIBS_INIT pthread)
endif () endif ()
@ -166,21 +317,24 @@ add_subdirectory(libdevcore)
add_subdirectory(libevmcore) add_subdirectory(libevmcore)
add_subdirectory(liblll) add_subdirectory(liblll)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) if (SERPENT)
add_subdirectory(libserpent) add_subdirectory(libserpent)
add_subdirectory(sc) add_subdirectory(sc)
endif () endif ()
add_subdirectory(libsolidity) if (SOLIDITY)
add_subdirectory(libsolidity)
endif ()
if (NOT JUSTTESTS) if (TOOLS)
add_subdirectory(lllc) add_subdirectory(lllc)
add_subdirectory(solc) if (SOLIDITY)
add_subdirectory(solc)
endif ()
endif() endif()
if (JSONRPC) if (JSONRPC)
add_subdirectory(libweb3jsonrpc) add_subdirectory(libweb3jsonrpc)
add_subdirectory(ethrpctest)
endif() endif()
add_subdirectory(secp256k1) add_subdirectory(secp256k1)
@ -196,12 +350,17 @@ endif ()
add_subdirectory(libethcore) add_subdirectory(libethcore)
add_subdirectory(libevm) add_subdirectory(libevm)
add_subdirectory(libethereum) add_subdirectory(libethereum)
add_subdirectory(libwebthree) add_subdirectory(libwebthree)
add_subdirectory(libtestutils)
add_subdirectory(test)
if (NOT JUSTTESTS) if (TESTS)
add_subdirectory(libtestutils)
add_subdirectory(test)
if (JSONRPC)
add_subdirectory(ethrpctest)
endif ()
endif ()
if (TOOLS)
add_subdirectory(rlp) add_subdirectory(rlp)
add_subdirectory(abi) add_subdirectory(abi)
@ -211,25 +370,26 @@ if (NOT JUSTTESTS)
add_subdirectory(exp) add_subdirectory(exp)
endif () endif ()
# TODO check msvc endif()
if(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(neth)
endif ()
if (NOT HEADLESS)
add_subdirectory(libnatspec) if (NCURSES)
add_subdirectory(libjsqrc) add_subdirectory(neth)
endif ()
if (ETH_HAVE_WEBENGINE) if (GUI)
add_subdirectory(alethzero)
# add_subdirectory(third) // reenable once not qtwebkit.
endif()
add_subdirectory(mix) add_subdirectory(libnatspec)
add_subdirectory(libjsqrc)
if (ETH_HAVE_WEBENGINE)
add_subdirectory(alethzero)
# add_subdirectory(third) // reenable once not qtwebkit.
endif() endif()
if (SOLIDITY)
add_subdirectory(mix)
endif ()
endif() endif()
#unset(TARGET_PLATFORM CACHE) #unset(TARGET_PLATFORM CACHE)

5
alethzero/Connect.cpp

@ -38,13 +38,14 @@ Connect::~Connect()
void Connect::setEnvironment(QStringList const& _nodes) void Connect::setEnvironment(QStringList const& _nodes)
{ {
ui->host->addItems(_nodes); if (ui->host->count() == 0)
ui->host->addItems(_nodes);
} }
void Connect::reset() void Connect::reset()
{ {
ui->nodeId->clear(); ui->nodeId->clear();
ui->required->setChecked(false); ui->required->setChecked(true);
} }
QString Connect::host() QString Connect::host()

3
alethzero/Connect.ui

@ -59,6 +59,9 @@
<property name="text"> <property name="text">
<string>Required (Always Connect to this Peer)</string> <string>Required (Always Connect to this Peer)</string>
</property> </property>
<property name="checked">
<bool>true</bool>
</property>
<property name="tristate"> <property name="tristate">
<bool>false</bool> <bool>false</bool>
</property> </property>

6
alethzero/MainWin.cpp

@ -148,7 +148,7 @@ Main::Main(QWidget *parent) :
cerr << "State root: " << CanonBlockChain::genesis().stateRoot << endl; cerr << "State root: " << CanonBlockChain::genesis().stateRoot << endl;
auto block = CanonBlockChain::createGenesisBlock(); auto block = CanonBlockChain::createGenesisBlock();
cerr << "Block Hash: " << CanonBlockChain::genesis().hash << endl; cerr << "Block Hash: " << CanonBlockChain::genesis().hash() << endl;
cerr << "Block RLP: " << RLP(block) << endl; cerr << "Block RLP: " << RLP(block) << endl;
cerr << "Block Hex: " << toHex(block) << endl; cerr << "Block Hex: " << toHex(block) << endl;
cerr << "eth Network protocol version: " << eth::c_protocolVersion << endl; cerr << "eth Network protocol version: " << eth::c_protocolVersion << endl;
@ -285,7 +285,7 @@ void Main::onKeysChanged()
unsigned Main::installWatch(LogFilter const& _tf, WatchHandler const& _f) unsigned Main::installWatch(LogFilter const& _tf, WatchHandler const& _f)
{ {
auto ret = ethereum()->installWatch(_tf); auto ret = ethereum()->installWatch(_tf, Reaping::Manual);
m_handlers[ret] = _f; m_handlers[ret] = _f;
_f(LocalisedLogEntries()); _f(LocalisedLogEntries());
return ret; return ret;
@ -1491,7 +1491,7 @@ void Main::on_blocks_currentItemChanged()
{ {
BlockInfo uncle = BlockInfo::fromHeader(u.data()); BlockInfo uncle = BlockInfo::fromHeader(u.data());
char const* line = "<div><span style=\"margin-left: 2em\">&nbsp;</span>"; char const* line = "<div><span style=\"margin-left: 2em\">&nbsp;</span>";
s << line << "Hash: <b>" << uncle.hash << "</b>" << "</div>"; s << line << "Hash: <b>" << uncle.hash() << "</b>" << "</div>";
s << line << "Parent: <b>" << uncle.parentHash << "</b>" << "</div>"; s << line << "Parent: <b>" << uncle.parentHash << "</b>" << "</div>";
s << line << "Number: <b>" << uncle.number << "</b>" << "</div>"; s << line << "Number: <b>" << uncle.number << "</b>" << "</div>";
s << line << "Coinbase: <b>" << pretty(uncle.coinbaseAddress).toHtmlEscaped().toStdString() << " " << uncle.coinbaseAddress << "</b>" << "</div>"; s << line << "Coinbase: <b>" << pretty(uncle.coinbaseAddress).toHtmlEscaped().toStdString() << " " << uncle.coinbaseAddress << "</b>" << "</div>";

10
cmake/EthCompilerSettings.cmake

@ -7,9 +7,17 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG -DETH_RELEASE") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG -DETH_RELEASE")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DETH_RELEASE") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DETH_RELEASE")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DETH_DEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DETH_RELEASE")
set(ETH_SHARED 1) set(ETH_SHARED 1)
if (PROFILING)
set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}")
add_definitions(-DETH_PROFILING_GPERF)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lprofiler")
# set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -lprofiler")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lprofiler")
endif ()
execute_process( execute_process(
COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
if (NOT (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)) if (NOT (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7))

8
eth/main.cpp

@ -120,7 +120,7 @@ void help()
<< " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl
<< " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl
#endif #endif
<< " -K,--kill-blockchain First kill the blockchain." << endl << " -K,--kill First kill the blockchain." << endl
<< " --listen-ip <port> Listen on the given port for incoming connections (default: 30303)." << endl << " --listen-ip <port> Listen on the given port for incoming connections (default: 30303)." << endl
<< " -l,--listen <ip> Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl << " -l,--listen <ip> Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl
<< " -u,--public-ip <ip> Force public ip to given (default: auto)." << endl << " -u,--public-ip <ip> Force public ip to given (default: auto)." << endl
@ -129,7 +129,7 @@ void help()
<< " -o,--mode <full/peer> Start a full node or a peer node (Default: full)." << endl << " -o,--mode <full/peer> Start a full node or a peer node (Default: full)." << endl
<< " -p,--port <port> Connect to remote port (default: 30303)." << endl << " -p,--port <port> Connect to remote port (default: 30303)." << endl
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl << " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl
<< " -R,--rebuild-blockchain First rebuild the blockchain from the existing database." << endl << " -R,--rebuild First rebuild the blockchain from the existing database." << endl
<< " -r,--remote <host> Connect to remote host (default: none)." << endl << " -r,--remote <host> Connect to remote host (default: none)." << 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
<< " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl << " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl
@ -274,9 +274,9 @@ int main(int argc, char** argv)
return -1; return -1;
} }
} }
else if (arg == "-K" || arg == "--kill-blockchain") else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill")
killChain = WithExisting::Kill; killChain = WithExisting::Kill;
else if (arg == "-B" || arg == "--rebuild-blockchain") else if (arg == "-B" || arg == "--rebuild")
killChain = WithExisting::Verify; killChain = WithExisting::Verify;
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i]; clientName = argv[++i];

1
evmjit/libevmjit/BasicBlock.cpp

@ -49,6 +49,7 @@ void BasicBlock::LocalStack::push(llvm::Value* _value)
assert(_value->getType() == Type::Word); assert(_value->getType() == Type::Word);
m_bblock.m_currentStack.push_back(_value); m_bblock.m_currentStack.push_back(_value);
m_bblock.m_tosOffset += 1; m_bblock.m_tosOffset += 1;
m_maxSize = std::max(m_maxSize, m_bblock.m_currentStack.size());
} }
llvm::Value* BasicBlock::LocalStack::pop() llvm::Value* BasicBlock::LocalStack::pop()

4
evmjit/libevmjit/BasicBlock.h

@ -33,6 +33,9 @@ public:
/// @param _index Index of value to be swaped. Must be > 0. /// @param _index Index of value to be swaped. Must be > 0.
void swap(size_t _index); void swap(size_t _index);
size_t getMaxSize() const { return m_maxSize; }
int getDiff() const { return m_bblock.m_tosOffset; }
private: private:
LocalStack(BasicBlock& _owner); LocalStack(BasicBlock& _owner);
LocalStack(LocalStack const&) = delete; LocalStack(LocalStack const&) = delete;
@ -49,6 +52,7 @@ public:
private: private:
BasicBlock& m_bblock; BasicBlock& m_bblock;
size_t m_maxSize = 0; ///< Max size reached by the stack.
}; };
explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);

31
evmjit/libevmjit/Cache.cpp

@ -4,6 +4,7 @@
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h> #include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Instructions.h> #include <llvm/IR/Instructions.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/Support/Path.h> #include <llvm/Support/Path.h>
#include <llvm/Support/FileSystem.h> #include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_os_ostream.h> #include <llvm/Support/raw_os_ostream.h>
@ -59,6 +60,36 @@ void Cache::clear()
fs::remove(it->path()); fs::remove(it->path());
} }
void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache)
{
// TODO: Cache dir should be in one place
using namespace llvm::sys;
llvm::SmallString<256> cachePath;
path::system_temp_directory(false, cachePath);
path::append(cachePath, "evm_objs");
// Disable listener
auto listener = g_listener;
g_listener = nullptr;
std::error_code err;
for (auto it = fs::directory_iterator{cachePath.str(), err}; it != fs::directory_iterator{}; it.increment(err))
{
auto name = it->path().substr(cachePath.size() + 1);
if (auto module = getObject(name))
{
DLOG(cache) << "Preload: " << name << "\n";
_ee.addModule(module.get());
module.release();
auto addr = _ee.getFunctionAddress(name);
assert(addr);
_funcCache[std::move(name)] = addr;
}
}
g_listener = listener;
}
std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id) std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
{ {
if (g_mode != CacheMode::on && g_mode != CacheMode::read) if (g_mode != CacheMode::on && g_mode != CacheMode::read)

12
evmjit/libevmjit/Cache.h

@ -1,9 +1,15 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <unordered_map>
#include <llvm/ExecutionEngine/ObjectCache.h> #include <llvm/ExecutionEngine/ObjectCache.h>
namespace llvm
{
class ExecutionEngine;
}
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -18,7 +24,8 @@ enum class CacheMode
off, off,
read, read,
write, write,
clear clear,
preload
}; };
class ObjectCache : public llvm::ObjectCache class ObjectCache : public llvm::ObjectCache
@ -43,6 +50,9 @@ public:
/// Clears cache storage /// Clears cache storage
static void clear(); static void clear();
/// Loads all available cached objects to ExecutionEngine
static void preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache);
}; };
} }

3
evmjit/libevmjit/Compiler.cpp

@ -838,6 +838,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
// Block may have no terminator if the next instruction is a jump destination. // Block may have no terminator if the next instruction is a jump destination.
if (!_basicBlock.llvm()->getTerminator()) if (!_basicBlock.llvm()->getTerminator())
m_builder.CreateBr(_nextBasicBlock); m_builder.CreateBr(_nextBasicBlock);
m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI());
_runtimeManager.checkStackLimit(_basicBlock.localStack().getMaxSize(), _basicBlock.localStack().getDiff());
} }

17
evmjit/libevmjit/ExecutionEngine.cpp

@ -76,6 +76,7 @@ cl::opt<CacheMode> g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"},
clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."),
clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."),
clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."),
clEnumValN(CacheMode::preload, "p", "Preload all cached objects."),
clEnumValEnd)}; clEnumValEnd)};
cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}}; cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}};
cl::opt<bool> g_dump{"dump", cl::desc{"Dump LLVM IR module"}}; cl::opt<bool> g_dump{"dump", cl::desc{"Dump LLVM IR module"}};
@ -111,8 +112,15 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
std::unique_ptr<ExecStats> listener{new ExecStats}; std::unique_ptr<ExecStats> listener{new ExecStats};
listener->stateChanged(ExecState::Started); listener->stateChanged(ExecState::Started);
bool preloadCache = g_cache == CacheMode::preload;
if (preloadCache)
g_cache = CacheMode::on;
// TODO: Do not pseudo-init the cache every time
auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr; auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr;
static std::unordered_map<std::string, uint64_t> funcCache;
static std::unique_ptr<llvm::ExecutionEngine> ee; static std::unique_ptr<llvm::ExecutionEngine> ee;
if (!ee) if (!ee)
{ {
@ -138,6 +146,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
return ReturnCode::LLVMConfigError; return ReturnCode::LLVMConfigError;
module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module
ee->setObjectCache(objectCache); ee->setObjectCache(objectCache);
if (preloadCache)
Cache::preload(*ee, funcCache);
} }
static StatsCollector statsCollector; static StatsCollector statsCollector;
@ -146,10 +157,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
m_runtime.init(_data, _env); m_runtime.init(_data, _env);
EntryFuncPtr entryFuncPtr = nullptr; EntryFuncPtr entryFuncPtr = nullptr;
static std::unordered_map<std::string, EntryFuncPtr> funcCache;
auto it = funcCache.find(mainFuncName); auto it = funcCache.find(mainFuncName);
if (it != funcCache.end()) if (it != funcCache.end())
entryFuncPtr = it->second; entryFuncPtr = (EntryFuncPtr) it->second;
if (!entryFuncPtr) if (!entryFuncPtr)
{ {
@ -177,7 +187,8 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
if (!CHECK(entryFuncPtr)) if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError; return ReturnCode::LLVMLinkError;
funcCache[mainFuncName] = entryFuncPtr; if (it == funcCache.end())
funcCache[mainFuncName] = (uint64_t) entryFuncPtr;
listener->stateChanged(ExecState::Execution); listener->stateChanged(ExecState::Execution);
auto returnCode = entryFuncPtr(&m_runtime); auto returnCode = entryFuncPtr(&m_runtime);

44
evmjit/libevmjit/RuntimeManager.cpp

@ -78,7 +78,7 @@ llvm::Twine getName(RuntimeData::Index _index)
case RuntimeData::CodeSize: return "code"; case RuntimeData::CodeSize: return "code";
case RuntimeData::CallDataSize: return "callDataSize"; case RuntimeData::CallDataSize: return "callDataSize";
case RuntimeData::Gas: return "gas"; case RuntimeData::Gas: return "gas";
case RuntimeData::Number: return "number"; case RuntimeData::Number: return "number";
case RuntimeData::Timestamp: return "timestamp"; case RuntimeData::Timestamp: return "timestamp";
} }
} }
@ -101,6 +101,48 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
assert(m_memPtr->getType() == Array::getType()->getPointerTo()); assert(m_memPtr->getType() == Array::getType()->getPointerTo());
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env"); m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env");
assert(m_envPtr->getType() == Type::EnvPtr); assert(m_envPtr->getType() == Type::EnvPtr);
m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize");
m_builder.CreateStore(m_builder.getInt64(0), m_stackSize);
llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr};
m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule());
m_checkStackLimit->setDoesNotThrow();
m_checkStackLimit->setDoesNotCapture(1);
auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_checkStackLimit);
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_checkStackLimit);
auto outOfStackBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfStack", m_checkStackLimit);
auto currSizePtr = &m_checkStackLimit->getArgumentList().front();
currSizePtr->setName("currSize");
auto max = currSizePtr->getNextNode();
max->setName("max");
auto diff = max->getNextNode();
diff->setName("diff");
auto jmpBuf = diff->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(checkBB);
auto currSize = m_builder.CreateLoad(currSizePtr, "cur");
auto maxSize = m_builder.CreateNUWAdd(currSize, max, "maxSize");
auto ok = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "ok");
m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue);
m_builder.SetInsertPoint(updateBB);
auto newSize = m_builder.CreateNSWAdd(currSize, diff);
m_builder.CreateStore(newSize, currSizePtr);
m_builder.CreateRetVoid();
m_builder.SetInsertPoint(outOfStackBB);
abort(jmpBuf);
m_builder.CreateUnreachable();
}
void RuntimeManager::checkStackLimit(size_t _max, int _diff)
{
createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()});
} }
llvm::Value* RuntimeManager::getRuntimePtr() llvm::Value* RuntimeManager::getRuntimePtr()

5
evmjit/libevmjit/RuntimeManager.h

@ -48,6 +48,8 @@ public:
static llvm::StructType* getRuntimeType(); static llvm::StructType* getRuntimeType();
static llvm::StructType* getRuntimeDataType(); static llvm::StructType* getRuntimeDataType();
void checkStackLimit(size_t _max, int _diff);
private: private:
llvm::Value* getPtr(RuntimeData::Index _index); llvm::Value* getPtr(RuntimeData::Index _index);
void set(RuntimeData::Index _index, llvm::Value* _value); void set(RuntimeData::Index _index, llvm::Value* _value);
@ -59,6 +61,9 @@ private:
llvm::Value* m_memPtr = nullptr; llvm::Value* m_memPtr = nullptr;
llvm::Value* m_envPtr = nullptr; llvm::Value* m_envPtr = nullptr;
llvm::Value* m_stackSize = nullptr;
llvm::Function* m_checkStackLimit = nullptr;
code_iterator m_codeBegin = {}; code_iterator m_codeBegin = {};
code_iterator m_codeEnd = {}; code_iterator m_codeEnd = {};

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using namespace dev;
namespace dev namespace dev
{ {
char const* Version = "0.9.2"; char const* Version = "0.9.4";
} }

33
libethcore/BlockInfo.cpp

@ -35,9 +35,9 @@ BlockInfo::BlockInfo(): timestamp(Invalid256)
{ {
} }
BlockInfo::BlockInfo(bytesConstRef _block, Strictness _s) BlockInfo::BlockInfo(bytesConstRef _block, Strictness _s, h256 const& _h)
{ {
populate(_block, _s); populate(_block, _s, _h);
} }
void BlockInfo::setEmpty() void BlockInfo::setEmpty()
@ -57,8 +57,7 @@ void BlockInfo::setEmpty()
extraData.clear(); extraData.clear();
mixHash = h256(); mixHash = h256();
nonce = Nonce(); nonce = Nonce();
m_seedHash = h256(); m_hash = m_seedHash = h256();
hash = headerHash(WithNonce);
} }
h256 const& BlockInfo::seedHash() const h256 const& BlockInfo::seedHash() const
@ -69,10 +68,17 @@ h256 const& BlockInfo::seedHash() const
return m_seedHash; return m_seedHash;
} }
BlockInfo BlockInfo::fromHeader(bytesConstRef _block, Strictness _s) h256 const& BlockInfo::hash() const
{
if (!m_hash)
m_hash = headerHash(WithNonce);
return m_hash;
}
BlockInfo BlockInfo::fromHeader(bytesConstRef _header, Strictness _s, h256 const& _h)
{ {
BlockInfo ret; BlockInfo ret;
ret.populateFromHeader(RLP(_block), _s); ret.populateFromHeader(RLP(_header), _s, _h);
return ret; return ret;
} }
@ -97,9 +103,11 @@ h256 BlockInfo::headerHash(bytesConstRef _block)
return sha3(RLP(_block)[0].data()); return sha3(RLP(_block)[0].data());
} }
void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s) void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const& _h)
{ {
hash = dev::sha3(_header.data()); // m_hash = dev::sha3(_header.data());
m_hash = _h;
m_seedHash = h256();
int field = 0; int field = 0;
try try
@ -149,14 +157,14 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s)
} }
} }
void BlockInfo::populate(bytesConstRef _block, Strictness _s) void BlockInfo::populate(bytesConstRef _block, Strictness _s, h256 const& _h)
{ {
RLP root(_block); RLP root(_block);
RLP header = root[0]; RLP header = root[0];
if (!header.isList()) if (!header.isList())
BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block header needs to be a list") << BadFieldError(0, header.data().toString())); BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block header needs to be a list") << BadFieldError(0, header.data().toString()));
populateFromHeader(header, _s); populateFromHeader(header, _s, _h);
if (!root[1].isList()) if (!root[1].isList())
BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block transactions need to be a list") << BadFieldError(1, root[1].data().toString())); BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block transactions need to be a list") << BadFieldError(1, root[1].data().toString()));
@ -191,8 +199,9 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
void BlockInfo::populateFromParent(BlockInfo const& _parent) void BlockInfo::populateFromParent(BlockInfo const& _parent)
{ {
m_hash = m_seedHash = h256();
stateRoot = _parent.stateRoot; stateRoot = _parent.stateRoot;
parentHash = _parent.hash; parentHash = _parent.hash();
number = _parent.number + 1; number = _parent.number + 1;
gasLimit = selectGasLimit(_parent); gasLimit = selectGasLimit(_parent);
gasUsed = 0; gasUsed = 0;
@ -230,7 +239,7 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const
// Check timestamp is after previous timestamp. // Check timestamp is after previous timestamp.
if (parentHash) if (parentHash)
{ {
if (parentHash != _parent.hash) if (parentHash != _parent.hash())
BOOST_THROW_EXCEPTION(InvalidParentHash()); BOOST_THROW_EXCEPTION(InvalidParentHash());
if (timestamp <= _parent.timestamp) if (timestamp <= _parent.timestamp)

21
libethcore/BlockInfo.h

@ -68,7 +68,6 @@ struct BlockInfo
{ {
public: public:
// TODO: make them all private! // TODO: make them all private!
h256 hash; ///< SHA3 hash of the block header! Not serialised (the only member not contained in a block header).
h256 parentHash; h256 parentHash;
h256 sha3Uncles; h256 sha3Uncles;
Address coinbaseAddress; Address coinbaseAddress;
@ -86,14 +85,14 @@ public:
Nonce nonce; Nonce nonce;
BlockInfo(); BlockInfo();
explicit BlockInfo(bytes const& _block, Strictness _s = CheckEverything): BlockInfo(&_block, _s) {} explicit BlockInfo(bytes const& _block, Strictness _s = IgnoreNonce, h256 const& _h = h256()): BlockInfo(&_block, _s, _h) {}
explicit BlockInfo(bytesConstRef _block, Strictness _s = CheckEverything); explicit BlockInfo(bytesConstRef _block, Strictness _s = IgnoreNonce, h256 const& _h = h256());
static h256 headerHash(bytes const& _block) { return headerHash(&_block); } static h256 headerHash(bytes const& _block) { return headerHash(&_block); }
static h256 headerHash(bytesConstRef _block); static h256 headerHash(bytesConstRef _block);
static BlockInfo fromHeader(bytes const& _block, Strictness _s = CheckEverything) { return fromHeader(bytesConstRef(&_block), _s); } static BlockInfo fromHeader(bytes const& _header, Strictness _s = IgnoreNonce, h256 const& _h = h256()) { return fromHeader(bytesConstRef(&_header), _s, _h); }
static BlockInfo fromHeader(bytesConstRef _block, Strictness _s = CheckEverything); static BlockInfo fromHeader(bytesConstRef _header, Strictness _s = IgnoreNonce, h256 const& _h = h256());
explicit operator bool() const { return timestamp != Invalid256; } explicit operator bool() const { return timestamp != Invalid256; }
@ -119,9 +118,11 @@ public:
void setEmpty(); void setEmpty();
void populateFromHeader(RLP const& _header, Strictness _s = CheckEverything); void noteDirty() const { m_hash = m_seedHash= h256(); }
void populate(bytesConstRef _block, Strictness _s = CheckEverything);
void populate(bytes const& _block, Strictness _s = CheckEverything) { populate(&_block, _s); } void populateFromHeader(RLP const& _header, Strictness _s = IgnoreNonce, h256 const& _h = h256());
void populate(bytesConstRef _block, Strictness _s = IgnoreNonce, h256 const& _h = h256());
void populate(bytes const& _block, Strictness _s = IgnoreNonce, h256 const& _h = h256()) { populate(&_block, _s, _h); }
void verifyInternals(bytesConstRef _block) const; void verifyInternals(bytesConstRef _block) const;
void verifyParent(BlockInfo const& _parent) const; void verifyParent(BlockInfo const& _parent) const;
void populateFromParent(BlockInfo const& parent); void populateFromParent(BlockInfo const& parent);
@ -129,6 +130,7 @@ public:
u256 calculateDifficulty(BlockInfo const& _parent) const; u256 calculateDifficulty(BlockInfo const& _parent) const;
u256 selectGasLimit(BlockInfo const& _parent) const; u256 selectGasLimit(BlockInfo const& _parent) const;
h256 const& seedHash() const; h256 const& seedHash() const;
h256 const& hash() const;
/// sha3 of the header only. /// sha3 of the header only.
h256 headerHash(IncludeNonce _n) const; h256 headerHash(IncludeNonce _n) const;
@ -136,11 +138,12 @@ public:
private: private:
mutable h256 m_seedHash; mutable h256 m_seedHash;
mutable h256 m_hash; ///< SHA3 hash of the block header! Not serialised.
}; };
inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi) inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi)
{ {
_out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " << _out << _bi.hash() << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " <<
_bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.gasLimit << " " << _bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.gasLimit << " " <<
_bi.gasUsed << " " << _bi.timestamp << " " << _bi.mixHash << " " << _bi.nonce << " (" << _bi.seedHash() << ")"; _bi.gasUsed << " " << _bi.timestamp << " " << _bi.mixHash << " " << _bi.nonce << " (" << _bi.seedHash() << ")";
return _out; return _out;

2
libethcore/Common.cpp

@ -36,7 +36,7 @@ namespace eth
const unsigned c_ethashVersion = c_ethashRevision; const unsigned c_ethashVersion = c_ethashRevision;
const unsigned c_protocolVersion = 60; const unsigned c_protocolVersion = 60;
const unsigned c_minorProtocolVersion = 0; const unsigned c_minorProtocolVersion = 0;
const unsigned c_databaseBaseVersion = 8; const unsigned c_databaseBaseVersion = 9;
#if ETH_FATDB #if ETH_FATDB
const unsigned c_databaseVersionModifier = 1; const unsigned c_databaseVersionModifier = 1;
#else #else

10
libethcore/Common.h

@ -85,5 +85,15 @@ enum class RelativeBlock: BlockNumber
Pending = PendingBlock Pending = PendingBlock
}; };
enum class ImportResult
{
Success = 0,
UnknownParent,
FutureTime,
AlreadyInChain,
AlreadyKnown,
Malformed
};
} }
} }

48
libethcore/Ethasher.cpp

@ -43,21 +43,21 @@ Ethasher* dev::eth::Ethasher::s_this = nullptr;
Ethasher::~Ethasher() Ethasher::~Ethasher()
{ {
while (!m_caches.empty()) while (!m_lights.empty())
killCache(m_caches.begin()->first); killCache(m_lights.begin()->first);
} }
void Ethasher::killCache(h256 const& _s) void Ethasher::killCache(h256 const& _s)
{ {
RecursiveGuard l(x_this); RecursiveGuard l(x_this);
if (m_caches.count(_s)) if (m_lights.count(_s))
{ {
ethash_delete_light(m_caches.at(_s)); ethash_delete_light(m_lights.at(_s));
m_caches.erase(_s); m_lights.erase(_s);
} }
} }
void const* Ethasher::cache(BlockInfo const& _header) void const* Ethasher::light(BlockInfo const& _header)
{ {
RecursiveGuard l(x_this); RecursiveGuard l(x_this);
if (_header.number > c_ethashEpochLength * 2048) if (_header.number > c_ethashEpochLength * 2048)
@ -67,41 +67,52 @@ void const* Ethasher::cache(BlockInfo const& _header)
throw std::invalid_argument( error.str() ); throw std::invalid_argument( error.str() );
} }
if (!m_caches.count(_header.seedHash())) if (!m_lights.count(_header.seedHash()))
{ {
ethash_params p = params((unsigned)_header.number); ethash_params p = params((unsigned)_header.number);
m_caches[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data()); m_lights[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data());
} }
return m_caches[_header.seedHash()]; return m_lights[_header.seedHash()];
} }
#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
bytesConstRef Ethasher::full(BlockInfo const& _header) bytesConstRef Ethasher::full(BlockInfo const& _header)
{ {
RecursiveGuard l(x_this); RecursiveGuard l(x_this);
if (!m_fulls.count(_header.seedHash())) if (!m_fulls.count(_header.seedHash()))
{ {
if (!m_fulls.empty()) // @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr.
/* if (!m_fulls.empty())
{ {
delete [] m_fulls.begin()->second.data(); delete [] m_fulls.begin()->second.data();
m_fulls.erase(m_fulls.begin()); m_fulls.erase(m_fulls.begin());
} }*/
try { try {
boost::filesystem::create_directories(getDataDir("ethash")); boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {} } catch (...) {}
std::string memoFile = getDataDir("ethash") + "/full";
auto info = rlpList(c_ethashRevision, _header.seedHash()); auto info = rlpList(c_ethashRevision, _header.seedHash());
if (boost::filesystem::exists(memoFile) && contents(memoFile + ".info") != info) std::string oldMemoFile = getDataDir("ethash") + "/full";
boost::filesystem::remove(memoFile); std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8));
if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info)
{
// memofile valid - rename.
boost::filesystem::rename(oldMemoFile, memoFile);
}
IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile));
IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info"));
m_fulls[_header.seedHash()] = contentsNew(memoFile); m_fulls[_header.seedHash()] = contentsNew(memoFile);
if (!m_fulls[_header.seedHash()]) if (!m_fulls[_header.seedHash()])
{ {
ethash_params p = params((unsigned)_header.number); ethash_params p = params((unsigned)_header.number);
m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size); m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size);
auto c = cache(_header); auto c = light(_header);
ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c); ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c);
writeFile(memoFile, m_fulls[_header.seedHash()]); writeFile(memoFile, m_fulls[_header.seedHash()]);
writeFile(memoFile + ".info", info);
} }
} }
return m_fulls[_header.seedHash()]; return m_fulls[_header.seedHash()];
@ -162,7 +173,10 @@ Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce)
{ {
auto p = Ethasher::params(_header); auto p = Ethasher::params(_header);
ethash_return_value r; ethash_return_value r;
ethash_compute_light(&r, Ethasher::get()->cache(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); if (Ethasher::get()->m_fulls.count(_header.seedHash()))
ethash_compute_full(&r, Ethasher::get()->full(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
else
ethash_compute_light(&r, Ethasher::get()->light(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
// cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); // cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer);
return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
} }

4
libethcore/Ethasher.h

@ -51,7 +51,7 @@ public:
using LightType = void const*; using LightType = void const*;
using FullType = void const*; using FullType = void const*;
LightType cache(BlockInfo const& _header); LightType light(BlockInfo const& _header);
bytesConstRef full(BlockInfo const& _header); bytesConstRef full(BlockInfo const& _header);
static ethash_params params(BlockInfo const& _header); static ethash_params params(BlockInfo const& _header);
static ethash_params params(unsigned _n); static ethash_params params(unsigned _n);
@ -99,7 +99,7 @@ private:
static Ethasher* s_this; static Ethasher* s_this;
RecursiveMutex x_this; RecursiveMutex x_this;
std::map<h256, LightType> m_caches; std::map<h256, LightType> m_lights;
std::map<h256, bytesRef> m_fulls; std::map<h256, bytesRef> m_fulls;
}; };

330
libethereum/BlockChain.cpp

@ -19,10 +19,13 @@
* @date 2014 * @date 2014
*/ */
#include <leveldb/db.h>
#include "BlockChain.h" #include "BlockChain.h"
#if ETH_PROFILING_GPERF
#include <gperftools/profiler.h>
#endif
#include <leveldb/db.h>
#include <boost/timer.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <test/JsonSpiritHeaders.h> #include <test/JsonSpiritHeaders.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
@ -33,6 +36,7 @@
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libethcore/ProofOfWork.h> #include <libethcore/ProofOfWork.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
#include <libethcore/Ethasher.h>
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include "GenesisInfo.h" #include "GenesisInfo.h"
#include "State.h" #include "State.h"
@ -43,6 +47,7 @@ using namespace dev::eth;
namespace js = json_spirit; namespace js = json_spirit;
#define ETH_CATCH 1 #define ETH_CATCH 1
#define ETH_TIMED_IMPORTS 0
std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc)
{ {
@ -63,7 +68,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc)
return _out; return _out;
} }
ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub) ldb::Slice dev::eth::oldToSlice(h256 const& _h, unsigned _sub)
{ {
#if ALL_COMPILERS_ARE_CPP11_COMPLIANT #if ALL_COMPILERS_ARE_CPP11_COMPLIANT
static thread_local h256 h = _h ^ sha3(h256(u256(_sub))); static thread_local h256 h = _h ^ sha3(h256(u256(_sub)));
@ -77,6 +82,21 @@ ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub)
#endif #endif
} }
ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub)
{
#if ALL_COMPILERS_ARE_CPP11_COMPLIANT
static thread_local h256 h = _h ^ sha3(h256(u256(_sub)));
return ldb::Slice((char const*)&h, 32);
#else
static boost::thread_specific_ptr<FixedHash<33>> t_h;
if (!t_h.get())
t_h.reset(new FixedHash<33>);
*t_h = FixedHash<33>(_h);
(*t_h)[32] = (uint8_t)_sub;
return (ldb::Slice)t_h->ref();//(char const*)t_h.get(), 32);
#endif
}
#if ETH_DEBUG #if ETH_DEBUG
static const chrono::system_clock::duration c_collectionDuration = chrono::seconds(15); static const chrono::system_clock::duration c_collectionDuration = chrono::seconds(15);
static const unsigned c_collectionQueueSize = 2; static const unsigned c_collectionQueueSize = 2;
@ -146,7 +166,7 @@ void BlockChain::open(std::string const& _path, WithExisting _we)
} }
} }
if (!details(m_genesisHash)) if (_we != WithExisting::Verify && !details(m_genesisHash))
{ {
// Insert details of genesis block. // Insert details of genesis block.
m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {}); m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {});
@ -154,7 +174,9 @@ void BlockChain::open(std::string const& _path, WithExisting _we)
m_extrasDB->Put(m_writeOptions, toSlice(m_genesisHash, ExtraDetails), (ldb::Slice)dev::ref(r)); m_extrasDB->Put(m_writeOptions, toSlice(m_genesisHash, ExtraDetails), (ldb::Slice)dev::ref(r));
} }
#if ETH_PARANOIA
checkConsistency(); checkConsistency();
#endif
// TODO: Implement ability to rebuild details map from DB. // TODO: Implement ability to rebuild details map from DB.
std::string l; std::string l;
@ -174,13 +196,21 @@ void BlockChain::close()
m_blocks.clear(); m_blocks.clear();
} }
#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned, unsigned)> const& _progress) void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned, unsigned)> const& _progress)
{ {
#if ETH_PROFILING_GPERF
ProfilerStart("BlockChain_rebuild.log");
#endif
// unsigned originalNumber = (unsigned)BlockInfo(oldBlock(m_lastBlockHash)).number;
unsigned originalNumber = number(); unsigned originalNumber = number();
// Keep extras DB around, but under a temp name // Keep extras DB around, but under a temp name
delete m_extrasDB; delete m_extrasDB;
m_extrasDB = nullptr; m_extrasDB = nullptr;
IGNORE_EXCEPTIONS(boost::filesystem::remove_all(_path + "/details.old"));
boost::filesystem::rename(_path + "/details", _path + "/details.old"); boost::filesystem::rename(_path + "/details", _path + "/details.old");
ldb::DB* oldExtrasDB; ldb::DB* oldExtrasDB;
ldb::Options o; ldb::Options o;
@ -189,7 +219,7 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
ldb::DB::Open(o, _path + "/details", &m_extrasDB); ldb::DB::Open(o, _path + "/details", &m_extrasDB);
// Open a fresh state DB // Open a fresh state DB
OverlayDB db = State::openDB(_path, WithExisting::Kill); State s(State::openDB(_path, WithExisting::Kill), BaseState::CanonGenesis);
// Clear all memos ready for replay. // Clear all memos ready for replay.
m_details.clear(); m_details.clear();
@ -201,11 +231,30 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
m_lastLastHashes.clear(); m_lastLastHashes.clear();
m_lastBlockHash = genesisHash(); m_lastBlockHash = genesisHash();
for (unsigned d = 0; d < originalNumber; ++d) h256 lastHash = genesisHash();
boost::timer t;
for (unsigned d = 1; d < originalNumber; ++d)
{ {
if (!(d % 1000))
{
cerr << "\n1000 blocks in " << t.elapsed() << "s = " << (1000.0 / t.elapsed()) << "b/s" << endl;
t.restart();
}
try try
{ {
import(block(queryExtras<BlockHash, ExtraBlockHash>(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value), db); bytes b = block(queryExtras<BlockHash, ExtraBlockHash>(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value);
BlockInfo bi(b);
if (bi.number % c_ethashEpochLength == 1)
Ethasher::get()->full(bi);
if (bi.parentHash != lastHash)
{
cwarn << "DISJOINT CHAIN DETECTED; " << bi.hash().abridged() << "#" << d << " -> parent is" << bi.parentHash.abridged() << "; expected" << lastHash.abridged() << "#" << (d - 1);
return;
}
lastHash = bi.hash();
import(b, s.db(), true);
} }
catch (...) catch (...)
{ {
@ -217,6 +266,10 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
_progress(d, originalNumber); _progress(d, originalNumber);
} }
#if ETH_PROFILING_GPERF
ProfilerStop();
#endif
delete oldExtrasDB; delete oldExtrasDB;
boost::filesystem::remove_all(_path + "/details.old"); boost::filesystem::remove_all(_path + "/details.old");
} }
@ -253,12 +306,12 @@ LastHashes BlockChain::lastHashes(unsigned _n) const
return m_lastLastHashes; return m_lastLastHashes;
} }
h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max) pair<h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max)
{ {
_bq.tick(*this); _bq.tick(*this);
vector<bytes> blocks; vector<bytes> blocks;
_bq.drain(blocks); _bq.drain(blocks, _max);
h256s ret; h256s ret;
for (auto const& block: blocks) for (auto const& block: blocks)
@ -266,10 +319,7 @@ h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max
try try
{ {
for (auto h: import(block, _stateDB)) for (auto h: import(block, _stateDB))
if (!_max--) ret.push_back(h);
break;
else
ret.push_back(h);
} }
catch (UnknownParent) catch (UnknownParent)
{ {
@ -284,15 +334,15 @@ h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max
catch (...) catch (...)
{} {}
} }
_bq.doneDrain(); bool yetMore = _bq.doneDrain();
return ret; return make_pair(ret, yetMore);
} }
h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _force) noexcept
{ {
try try
{ {
return import(_block, _stateDB); return import(_block, _stateDB, _force);
} }
catch (...) catch (...)
{ {
@ -301,8 +351,20 @@ h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB)
} }
} }
h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force)
{ {
//@tidy This is a behemoth of a method - could do to be split into a few smaller ones.
#if ETH_TIMED_IMPORTS
boost::timer total;
double preliminaryChecks;
double enactment;
double collation;
double writing;
double checkBest;
boost::timer t;
#endif
// VERIFY: populates from the block and checks the block is internally coherent. // VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi; BlockInfo bi;
@ -329,7 +391,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
auto newHash = BlockInfo::headerHash(_block); auto newHash = BlockInfo::headerHash(_block);
// Check block doesn't already exist first! // Check block doesn't already exist first!
if (isKnown(newHash)) if (isKnown(newHash) && !_force)
{ {
clog(BlockChainNote) << newHash << ": Not new."; clog(BlockChainNote) << newHash << ": Not new.";
BOOST_THROW_EXCEPTION(AlreadyHaveBlock()); BOOST_THROW_EXCEPTION(AlreadyHaveBlock());
@ -362,6 +424,11 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
clog(BlockChainNote) << "Attempting import of " << newHash.abridged() << "..."; clog(BlockChainNote) << "Attempting import of " << newHash.abridged() << "...";
#if ETH_TIMED_IMPORTS
preliminaryChecks = t.elapsed();
t.restart();
#endif
u256 td; u256 td;
#if ETH_CATCH #if ETH_CATCH
try try
@ -369,8 +436,9 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
{ {
// Check transactions are valid and that they result in a state equivalent to our state_root. // Check transactions are valid and that they result in a state equivalent to our state_root.
// Get total difficulty increase and update state, checking it. // Get total difficulty increase and update state, checking it.
State s(bi.coinbaseAddress, _db); State s(_db); //, bi.coinbaseAddress
auto tdIncrease = s.enactOn(&_block, bi, *this); auto tdIncrease = s.enactOn(&_block, bi, *this);
BlockLogBlooms blb; BlockLogBlooms blb;
BlockReceipts br; BlockReceipts br;
for (unsigned i = 0; i < s.pending().size(); ++i) for (unsigned i = 0; i < s.pending().size(); ++i)
@ -381,6 +449,11 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
s.cleanup(true); s.cleanup(true);
td = pd.totalDifficulty + tdIncrease; td = pd.totalDifficulty + tdIncrease;
#if ETH_TIMED_IMPORTS
enactment = t.elapsed();
t.restart();
#endif
#if ETH_PARANOIA #if ETH_PARANOIA
checkConsistency(); checkConsistency();
#endif #endif
@ -397,37 +470,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}); m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {});
m_details[bi.parentHash].children.push_back(newHash); m_details[bi.parentHash].children.push_back(newHash);
} }
{
WriteGuard l(x_blockHashes);
m_blockHashes[h256(bi.number)].value = newHash;
}
h256s alteredBlooms;
{
WriteGuard l(x_blocksBlooms);
LogBloom blockBloom = bi.logBloom;
blockBloom.shiftBloom<3>(sha3(bi.coinbaseAddress.ref()));
unsigned index = (unsigned)bi.number;
for (unsigned level = 0; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize)
{
unsigned i = index / c_bloomIndexSize;
unsigned o = index % c_bloomIndexSize;
alteredBlooms.push_back(chunkId(level, i));
m_blocksBlooms[alteredBlooms.back()].blooms[o] |= blockBloom;
}
}
// Collate transaction hashes and remember who they were.
h256s newTransactionAddresses;
{
RLP blockRLP(_block);
TransactionAddress ta;
ta.blockHash = newHash;
WriteGuard l(x_transactionAddresses);
for (ta.index = 0; ta.index < blockRLP[1].itemCount(); ++ta.index)
{
newTransactionAddresses.push_back(sha3(blockRLP[1][ta.index].data()));
m_transactionAddresses[newTransactionAddresses.back()] = ta;
}
}
{ {
WriteGuard l(x_logBlooms); WriteGuard l(x_logBlooms);
m_logBlooms[newHash] = blb; m_logBlooms[newHash] = blb;
@ -437,25 +479,27 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
m_receipts[newHash] = br; m_receipts[newHash] = br;
} }
#if ETH_TIMED_IMPORTS
collation = t.elapsed();
t.restart();
#endif
{ {
ReadGuard l1(x_blocksBlooms);
ReadGuard l2(x_details); ReadGuard l2(x_details);
ReadGuard l3(x_blockHashes);
ReadGuard l4(x_receipts); ReadGuard l4(x_receipts);
ReadGuard l5(x_logBlooms); ReadGuard l5(x_logBlooms);
ReadGuard l6(x_transactionAddresses);
m_blocksDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block)); m_blocksDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[newHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(h256(bi.number), ExtraBlockHash), (ldb::Slice)dev::ref(m_blockHashes[h256(bi.number)].rlp()));
for (auto const& h: newTransactionAddresses)
m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp()));
for (auto const& h: alteredBlooms)
m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraBlocksBlooms), (ldb::Slice)dev::ref(m_blocksBlooms[h].rlp()));
} }
#if ETH_TIMED_IMPORTS
writing = t.elapsed();
t.restart();
#endif
#if ETH_PARANOIA #if ETH_PARANOIA
checkConsistency(); checkConsistency();
#endif #endif
@ -471,7 +515,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
{ {
clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(_e); clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(_e);
_e << errinfo_comment("Malformed block "); _e << errinfo_comment("Malformed block ");
clog(BlockChainWarn) << "Block: " << bi.hash; clog(BlockChainWarn) << "Block: " << bi.hash();
clog(BlockChainWarn) << bi; clog(BlockChainWarn) << bi;
clog(BlockChainWarn) << "Block parent: " << bi.parentHash; clog(BlockChainWarn) << "Block parent: " << bi.parentHash;
clog(BlockChainWarn) << BlockInfo(block(bi.parentHash)); clog(BlockChainWarn) << BlockInfo(block(bi.parentHash));
@ -493,16 +537,78 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
h256 last = currentHash(); h256 last = currentHash();
if (td > details(last).totalDifficulty) if (td > details(last).totalDifficulty)
{ {
ret = treeRoute(last, newHash); h256 common;
unsigned commonIndex;
tie(ret, common, commonIndex) = treeRoute(last, newHash);
{ {
WriteGuard l(x_lastBlockHash); WriteGuard l(x_lastBlockHash);
m_lastBlockHash = newHash; m_lastBlockHash = newHash;
} }
noteCanonChanged();
m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32)); m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32));
// Most of the time these two will be equal - only when we're doing a chain revert will they not be
if (common != last)
// If we are reverting previous blocks, we need to clear their blooms (in particular, to
// rebuild any higher level blooms that they contributed to).
clearBlockBlooms(number(common) + 1, number(last) + 1);
// Go through ret backwards until hash != last.parent and update m_transactionAddresses, m_blockHashes
for (auto i = ret.rbegin(); i != ret.rend() && *i != common; ++i)
{
auto b = block(*i);
BlockInfo bi(b);
// Collate logs into blooms.
h256s alteredBlooms;
{
LogBloom blockBloom = bi.logBloom;
blockBloom.shiftBloom<3>(sha3(bi.coinbaseAddress.ref()));
// Pre-memoize everything we need before locking x_blocksBlooms
for (unsigned level = 0, index = (unsigned)bi.number; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize)
blocksBlooms(chunkId(level, index / c_bloomIndexSize));
WriteGuard l(x_blocksBlooms);
for (unsigned level = 0, index = (unsigned)bi.number; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize)
{
unsigned i = index / c_bloomIndexSize;
unsigned o = index % c_bloomIndexSize;
alteredBlooms.push_back(chunkId(level, i));
m_blocksBlooms[alteredBlooms.back()].blooms[o] |= blockBloom;
}
}
// Collate transaction hashes and remember who they were.
h256s newTransactionAddresses;
{
RLP blockRLP(b);
TransactionAddress ta;
ta.blockHash = bi.hash();
WriteGuard l(x_transactionAddresses);
for (ta.index = 0; ta.index < blockRLP[1].itemCount(); ++ta.index)
{
newTransactionAddresses.push_back(sha3(blockRLP[1][ta.index].data()));
m_transactionAddresses[newTransactionAddresses.back()] = ta;
}
}
{
WriteGuard l(x_blockHashes);
m_blockHashes[h256(bi.number)].value = bi.hash();
}
// Update database with them.
ReadGuard l1(x_blocksBlooms);
ReadGuard l3(x_blockHashes);
ReadGuard l6(x_transactionAddresses);
for (auto const& h: alteredBlooms)
m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraBlocksBlooms), (ldb::Slice)dev::ref(m_blocksBlooms[h].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(h256(bi.number), ExtraBlockHash), (ldb::Slice)dev::ref(m_blockHashes[h256(bi.number)].rlp()));
for (auto const& h: newTransactionAddresses)
m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp()));
}
clog(BlockChainNote) << " Imported and best" << td << ". Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(ret); clog(BlockChainNote) << " Imported and best" << td << ". Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(ret);
noteCanonChanged();
StructuredLogger::chainNewHead( StructuredLogger::chainNewHead(
bi.headerHash(WithoutNonce).abridged(), bi.headerHash(WithoutNonce).abridged(),
bi.nonce.abridged(), bi.nonce.abridged(),
@ -514,14 +620,67 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
{ {
clog(BlockChainNote) << " Imported but not best (oTD:" << details(last).totalDifficulty << " > TD:" << td << ")"; clog(BlockChainNote) << " Imported but not best (oTD:" << details(last).totalDifficulty << " > TD:" << td << ")";
} }
#if ETH_TIMED_IMPORTS
checkBest = t.elapsed();
cnote << "Import took:" << total.elapsed();
cnote << "preliminaryChecks:" << preliminaryChecks;
cnote << "enactment:" << enactment;
cnote << "collation:" << collation;
cnote << "writing:" << writing;
cnote << "checkBest:" << checkBest;
#endif
return ret; return ret;
} }
h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end)
{
// ... c c c c c c c c c c C o o o o o o
// ... /=15 /=21
// L0...| ' | ' | ' | ' | ' | ' | ' | 'b|x'x|x'x|x'e| /=11
// L1...| ' | ' | ' | ' b | x ' x | x ' e | /=6
// L2...| ' | ' b | x ' x | e /=3
// L3...| ' b | x ' e
// model: c_bloomIndexLevels = 4, c_bloomIndexSize = 2
// ... /=15 /=21
// L0...| ' ' ' | ' ' ' | ' ' ' | ' ' 'b|x'x'x'x|x'e' ' |
// L1...| ' ' ' b | x ' x ' e ' |
// L2...| b ' x ' e ' |
// model: c_bloomIndexLevels = 2, c_bloomIndexSize = 4
// algorithm doesn't have the best memoisation coherence, but eh well...
unsigned beginDirty = _begin;
unsigned endDirty = _end;
for (unsigned level = 0; level < c_bloomIndexLevels; level++, beginDirty /= c_bloomIndexSize, endDirty = (endDirty - 1) / c_bloomIndexSize + 1)
{
// compute earliest & latest index for each level, rebuild from previous levels.
for (unsigned item = beginDirty; item != endDirty; ++item)
{
unsigned bunch = item / c_bloomIndexSize;
unsigned offset = item % c_bloomIndexSize;
auto id = chunkId(level, bunch);
LogBloom acc;
if (!!level)
{
// rebuild the bloom from the previous (lower) level (if there is one).
auto lowerChunkId = chunkId(level - 1, item);
for (auto const& bloom: blocksBlooms(lowerChunkId).blooms)
acc |= bloom;
}
blocksBlooms(id); // make sure it has been memoized.
m_blocksBlooms[id].blooms[offset] = acc;
}
}
}
tuple<h256s, h256, unsigned> BlockChain::treeRoute(h256 const& _from, h256 const& _to, bool _common, bool _pre, bool _post) const
{ {
// cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
if (!_from || !_to) if (!_from || !_to)
return h256s(); return make_tuple(h256s(), h256(), 0);
h256s ret; h256s ret;
h256s back; h256s back;
unsigned fn = details(_from).number; unsigned fn = details(_from).number;
@ -553,20 +712,19 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
assert(to); assert(to);
from = details(from).parent; from = details(from).parent;
to = details(to).parent; to = details(to).parent;
if (_pre) if (_pre && (from != to || _common))
ret.push_back(from); ret.push_back(from);
if (_post) if (_post && (from != to || (!_pre && _common)))
back.push_back(to); back.push_back(to);
fn--; fn--;
tn--; tn--;
// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); // cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged();
} }
if (o_common)
*o_common = from;
ret.reserve(ret.size() + back.size()); ret.reserve(ret.size() + back.size());
for (auto it = back.cbegin(); it != back.cend(); ++it) unsigned i = ret.size() - (int)(_common && !ret.empty() && !back.empty());
for (auto it = back.rbegin(); it != back.rend(); ++it)
ret.push_back(*it); ret.push_back(*it);
return ret; return make_tuple(ret, from, i);
} }
void BlockChain::noteUsed(h256 const& _h, unsigned _extra) const void BlockChain::noteUsed(h256 const& _h, unsigned _extra) const
@ -828,3 +986,33 @@ bytes BlockChain::block(h256 const& _hash) const
return m_blocks[_hash]; return m_blocks[_hash];
} }
bytes BlockChain::oldBlock(h256 const& _hash) const
{
if (_hash == m_genesisHash)
return m_genesisBlock;
{
ReadGuard l(x_blocks);
auto it = m_blocks.find(_hash);
if (it != m_blocks.end())
return it->second;
}
string d;
m_blocksDB->Get(m_readOptions, oldToSlice(_hash), &d);
if (!d.size())
{
cwarn << "Couldn't find requested block:" << _hash.abridged();
return bytes();
}
WriteGuard l(x_blocks);
m_blocks[_hash].resize(d.size());
memcpy(m_blocks[_hash].data(), d.data(), d.size());
noteUsed(_hash);
return m_blocks[_hash];
}

63
libethereum/BlockChain.h

@ -64,6 +64,7 @@ struct BlockChainWarn: public LogChannel { static const char* name() { return "=
std::map<Address, Account> const& genesisState(); std::map<Address, Account> const& genesisState();
ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0);
ldb::Slice oldToSlice(h256 const& _h, unsigned _sub = 0);
using BlocksHash = std::map<h256, bytes>; using BlocksHash = std::map<h256, bytes>;
using TransactionHashes = h256s; using TransactionHashes = h256s;
@ -99,26 +100,27 @@ public:
void process(); void process();
/// Sync the chain with any incoming blocks. All blocks should, if processed in order /// Sync the chain with any incoming blocks. All blocks should, if processed in order
h256s sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max); std::pair<h256s, bool> sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max);
/// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB. /// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB.
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain. /// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept; h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _force = false) noexcept;
/// Import block into disk-backed DB /// Import block into disk-backed DB
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain. /// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
h256s import(bytes const& _block, OverlayDB const& _stateDB); h256s import(bytes const& _block, OverlayDB const& _stateDB, bool _force = false);
/// Returns true if the given block is known (though not necessarily a part of the canon chain). /// Returns true if the given block is known (though not necessarily a part of the canon chain).
bool isKnown(h256 const& _hash) const; bool isKnown(h256 const& _hash) const;
/// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe.
BlockInfo info(h256 const& _hash) const { return BlockInfo(block(_hash)); } BlockInfo info(h256 const& _hash) const { return BlockInfo(block(_hash), IgnoreNonce, _hash); }
BlockInfo info() const { return BlockInfo(block()); } BlockInfo info() const { return info(currentHash()); }
/// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe. /// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe.
bytes block(h256 const& _hash) const; bytes block(h256 const& _hash) const;
bytes block() const { return block(currentHash()); } bytes block() const { return block(currentHash()); }
bytes oldBlock(h256 const& _hash) const;
/// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe.
BlockDetails details(h256 const& _hash) const { return queryExtras<BlockDetails, ExtraDetails>(_hash, m_details, x_details, NullBlockDetails); } BlockDetails details(h256 const& _hash) const { return queryExtras<BlockDetails, ExtraDetails>(_hash, m_details, x_details, NullBlockDetails); }
@ -163,6 +165,7 @@ public:
*/ */
BlocksBlooms blocksBlooms(unsigned _level, unsigned _index) const { return blocksBlooms(chunkId(_level, _index)); } BlocksBlooms blocksBlooms(unsigned _level, unsigned _index) const { return blocksBlooms(chunkId(_level, _index)); }
BlocksBlooms blocksBlooms(h256 const& _chunkId) const { return queryExtras<BlocksBlooms, ExtraBlocksBlooms>(_chunkId, m_blocksBlooms, x_blocksBlooms, NullBlocksBlooms); } BlocksBlooms blocksBlooms(h256 const& _chunkId) const { return queryExtras<BlocksBlooms, ExtraBlocksBlooms>(_chunkId, m_blocksBlooms, x_blocksBlooms, NullBlocksBlooms); }
void clearBlockBlooms(unsigned _begin, unsigned _end);
LogBloom blockBloom(unsigned _number) const { return blocksBlooms(chunkId(0, _number / c_bloomIndexSize)).blooms[_number % c_bloomIndexSize]; } LogBloom blockBloom(unsigned _number) const { return blocksBlooms(chunkId(0, _number / c_bloomIndexSize)).blooms[_number % c_bloomIndexSize]; }
std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const; std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const;
std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const; std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const;
@ -194,21 +197,31 @@ public:
/// 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.
void rebuild(std::string const& _path, ProgressCallback const& _progress = std::function<void(unsigned, unsigned)>()); void rebuild(std::string const& _path, ProgressCallback const& _progress = std::function<void(unsigned, unsigned)>());
/** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of /** @returns a tuple of:
* blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent. * - an vector of hashes of all blocks between @a _from and @a _to, all blocks are ordered first by a number of
* blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent;
* - the block hash of the latest common ancestor of both blocks;
* - the index where the latest common ancestor of both blocks would either be found or inserted, depending
* on whether it is included.
* *
* If non-null, the h256 at @a o_common is set to the latest common ancestor of both blocks. * @param _common if true, include the common ancestor in the returned vector.
* @param _pre if true, include all block hashes running from @a _from until the common ancestor in the returned vector.
* @param _post if true, include all block hashes running from the common ancestor until @a _to in the returned vector.
* *
* e.g. if the block tree is 3a -> 2a -> 1a -> g and 2b -> 1b -> g (g is genesis, *a, *b are competing chains), * e.g. if the block tree is 3a -> 2a -> 1a -> g and 2b -> 1b -> g (g is genesis, *a, *b are competing chains),
* then: * then:
* @code * @code
* treeRoute(3a, 2b) == { 3a, 2a, 1a, 1b, 2b }; // *o_common == g * treeRoute(3a, 2b, false) == make_tuple({ 3a, 2a, 1a, 1b, 2b }, g, 3);
* treeRoute(2a, 1a) == { 2a, 1a }; // *o_common == 1a * treeRoute(2a, 1a, false) == make_tuple({ 2a, 1a }, 1a, 1)
* treeRoute(1a, 2a) == { 1a, 2a }; // *o_common == 1a * treeRoute(1a, 2a, false) == make_tuple({ 1a, 2a }, 1a, 0)
* treeRoute(1b, 2a) == { 1b, 1a, 2a }; // *o_common == g * treeRoute(1b, 2a, false) == make_tuple({ 1b, 1a, 2a }, g, 1)
* treeRoute(3a, 2b, true) == make_tuple({ 3a, 2a, 1a, g, 1b, 2b }, g, 3);
* treeRoute(2a, 1a, true) == make_tuple({ 2a, 1a }, 1a, 1)
* treeRoute(1a, 2a, true) == make_tuple({ 1a, 2a }, 1a, 0)
* treeRoute(1b, 2a, true) == make_tuple({ 1b, g, 1a, 2a }, g, 1)
* @endcode * @endcode
*/ */
h256s treeRoute(h256 const& _from, h256 const& _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; std::tuple<h256s, h256, unsigned> treeRoute(h256 const& _from, h256 const& _to, bool _common = true, bool _pre = true, bool _post = true) const;
struct Statistics struct Statistics
{ {
@ -257,6 +270,30 @@ private:
return ret.first->second; return ret.first->second;
} }
template<class T, unsigned N> T oldQueryExtras(h256 const& _h, std::map<h256, T>& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const
{
{
ReadGuard l(_x);
auto it = _m.find(_h);
if (it != _m.end())
return it->second;
}
std::string s;
(_extrasDB ? _extrasDB : m_extrasDB)->Get(m_readOptions, oldToSlice(_h, N), &s);
if (s.empty())
{
// cout << "Not found in DB: " << _h << endl;
return _n;
}
noteUsed(_h, N);
WriteGuard l(_x);
auto ret = _m.insert(std::make_pair(_h, T(RLP(s))));
return ret.first->second;
}
void checkConsistency(); void checkConsistency();
/// The caches of the disk DB and their locks. /// The caches of the disk DB and their locks.

22
libethereum/BlockQueue.cpp

@ -114,13 +114,29 @@ void BlockQueue::tick(BlockChain const& _bc)
m_future.erase(m_future.begin(), m_future.upper_bound(t)); m_future.erase(m_future.begin(), m_future.upper_bound(t));
} }
void BlockQueue::drain(std::vector<bytes>& o_out) template <class T> T advanced(T _t, unsigned _n)
{
std::advance(_t, _n);
return _t;
}
void BlockQueue::drain(std::vector<bytes>& o_out, unsigned _max)
{ {
WriteGuard l(m_lock); WriteGuard l(m_lock);
if (m_drainingSet.empty()) if (m_drainingSet.empty())
{ {
swap(o_out, m_ready); o_out.resize(min<unsigned>(_max, m_ready.size()));
swap(m_drainingSet, m_readySet); for (unsigned i = 0; i < o_out.size(); ++i)
swap(o_out[i], m_ready[i]);
m_ready.erase(m_ready.begin(), advanced(m_ready.begin(), o_out.size()));
for (auto const& bs: o_out)
{
auto h = sha3(bs);
m_drainingSet.insert(h);
m_readySet.erase(h);
}
// swap(o_out, m_ready);
// swap(m_drainingSet, m_readySet);
} }
} }

18
libethereum/BlockQueue.h

@ -26,6 +26,7 @@
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libethcore/Common.h>
namespace dev namespace dev
{ {
@ -37,16 +38,6 @@ class BlockChain;
struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; }; struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; };
#define cblockq dev::LogOutputStream<dev::eth::BlockQueueChannel, true>() #define cblockq dev::LogOutputStream<dev::eth::BlockQueueChannel, true>()
enum class ImportResult
{
Success = 0,
UnknownParent,
FutureTime,
AlreadyInChain,
AlreadyKnown,
Malformed
};
/** /**
* @brief A queue of blocks. Sits between network or other I/O and the BlockChain. * @brief A queue of blocks. Sits between network or other I/O and the BlockChain.
* Sorts them ready for blockchain insertion (with the BlockChain::sync() method). * Sorts them ready for blockchain insertion (with the BlockChain::sync() method).
@ -61,12 +52,13 @@ public:
/// Notes that time has moved on and some blocks that used to be "in the future" may no be valid. /// Notes that time has moved on and some blocks that used to be "in the future" may no be valid.
void tick(BlockChain const& _bc); void tick(BlockChain const& _bc);
/// Grabs the blocks that are ready, giving them in the correct order for insertion into the chain. /// Grabs at most @a _max of the blocks that are ready, giving them in the correct order for insertion into the chain.
/// Don't forget to call doneDrain() once you're done importing. /// Don't forget to call doneDrain() once you're done importing.
void drain(std::vector<bytes>& o_out); void drain(std::vector<bytes>& o_out, unsigned _max);
/// Must be called after a drain() call. Notes that the drained blocks have been imported into the blockchain, so we can forget about them. /// Must be called after a drain() call. Notes that the drained blocks have been imported into the blockchain, so we can forget about them.
void doneDrain() { WriteGuard l(m_lock); m_drainingSet.clear(); } /// @returns true iff there are additional blocks ready to be processed.
bool doneDrain() { WriteGuard l(m_lock); m_drainingSet.clear(); return !m_readySet.empty(); }
/// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain). /// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain).
void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); } void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); }

2
libethereum/CanonBlockChain.cpp

@ -92,6 +92,6 @@ bytes CanonBlockChain::createGenesisBlock()
return block.out(); return block.out();
} }
CanonBlockChain::CanonBlockChain(std::string const& _path, WithExisting _we): BlockChain(CanonBlockChain::createGenesisBlock(), _path, _we) CanonBlockChain::CanonBlockChain(std::string const& _path, WithExisting _we, ProgressCallback const& _pc): BlockChain(CanonBlockChain::createGenesisBlock(), _path, _we, _pc)
{ {
} }

4
libethereum/CanonBlockChain.h

@ -55,8 +55,8 @@ std::map<Address, Account> const& genesisState();
class CanonBlockChain: public BlockChain class CanonBlockChain: public BlockChain
{ {
public: public:
CanonBlockChain(WithExisting _we = WithExisting::Trust): CanonBlockChain(std::string(), _we) {} CanonBlockChain(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()): CanonBlockChain(std::string(), _we, _pc) {}
CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust); CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback());
~CanonBlockChain() {} ~CanonBlockChain() {}
/// @returns the genesis block header. /// @returns the genesis block header.

33
libethereum/Client.cpp

@ -90,7 +90,7 @@ void BasicGasPricer::update(BlockChain const& _bc)
{ {
auto bb = _bc.block(p); auto bb = _bc.block(p);
RLP r(bb); RLP r(bb);
BlockReceipts brs(_bc.receipts(bi.hash)); BlockReceipts brs(_bc.receipts(bi.hash()));
for (unsigned i = 0; i < r[1].size(); ++i) for (unsigned i = 0; i < r[1].size(); ++i)
{ {
auto gu = brs.receipts[i].gasUsed(); auto gu = brs.receipts[i].gasUsed();
@ -120,11 +120,11 @@ void BasicGasPricer::update(BlockChain const& _bc)
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners): Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners):
Worker("eth"), Worker("eth"),
m_vc(_dbPath), m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction)), m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(new TrivialGasPricer), m_gp(new TrivialGasPricer),
m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))), m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(Address(), m_stateDB), m_preMine(m_stateDB, BaseState::CanonGenesis),
m_postMine(Address(), m_stateDB) m_postMine(m_stateDB)
{ {
m_gp->update(m_bc); m_gp->update(m_bc);
@ -145,11 +145,11 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for
Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners): Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners):
Worker("eth"), Worker("eth"),
m_vc(_dbPath), m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction)), m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(_gp), m_gp(_gp),
m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))), m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(Address(), m_stateDB), m_preMine(m_stateDB),
m_postMine(Address(), m_stateDB) m_postMine(m_stateDB)
{ {
m_gp->update(m_bc); m_gp->update(m_bc);
@ -221,8 +221,8 @@ void Client::killChain()
} }
m_bc.reopen(Defaults::dbPath(), WithExisting::Kill); m_bc.reopen(Defaults::dbPath(), WithExisting::Kill);
m_preMine = State(Address(), m_stateDB); m_preMine = State(m_stateDB);
m_postMine = State(Address(), m_stateDB); m_postMine = State(m_stateDB);
if (auto h = m_host.lock()) if (auto h = m_host.lock())
h->reset(); h->reset();
@ -313,7 +313,7 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed)
auto m = i.second.filter.matches(tr); auto m = i.second.filter.matches(tr);
if (m.size()) if (m.size())
{ {
auto transactionHash = transaction(d.hash, j).sha3(); auto transactionHash = transaction(d.hash(), j).sha3();
// filter catches them // filter catches them
for (LogEntry const& l: m) for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, (unsigned)d.number, transactionHash)); i.second.changes.push_back(LocalisedLogEntry(l, (unsigned)d.number, transactionHash));
@ -441,6 +441,8 @@ void Client::doWork()
{ {
// TODO: Use condition variable rather than polling. // TODO: Use condition variable rather than polling.
bool stillGotWork = false;
cworkin << "WORK"; cworkin << "WORK";
h256Set changeds; h256Set changeds;
@ -496,7 +498,10 @@ void Client::doWork()
cwork << "BQ ==> CHAIN ==> STATE"; cwork << "BQ ==> CHAIN ==> STATE";
OverlayDB db = m_stateDB; OverlayDB db = m_stateDB;
x_stateDB.unlock(); x_stateDB.unlock();
h256s newBlocks = m_bc.sync(m_bq, db, 100); // TODO: remove transactions from m_tq nicely rather than relying on out of date nonce later on. h256s newBlocks;
bool sgw;
tie(newBlocks, sgw) = m_bc.sync(m_bq, db, 100); // TODO: remove transactions from m_tq nicely rather than relying on out of date nonce later on.
stillGotWork = stillGotWork | sgw;
if (newBlocks.size()) if (newBlocks.size())
{ {
for (auto i: newBlocks) for (auto i: newBlocks)
@ -544,7 +549,9 @@ void Client::doWork()
noteChanged(changeds); noteChanged(changeds);
cworkout << "WORK"; cworkout << "WORK";
this_thread::sleep_for(chrono::milliseconds(100)); if (!stillGotWork)
this_thread::sleep_for(chrono::milliseconds(100));
if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5)) if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5))
{ {
// watches garbage collection // watches garbage collection
@ -601,7 +608,7 @@ void Client::inject(bytesConstRef _rlp)
{ {
startWorking(); startWorking();
m_tq.attemptImport(_rlp); m_tq.import(_rlp);
} }
void Client::flushTransactions() void Client::flushTransactions()

14
libethereum/ClientBase.cpp

@ -44,7 +44,7 @@ void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, b
u256 n = postMine().transactionsFrom(toAddress(_secret)); u256 n = postMine().transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
m_tq.attemptImport(t.rlp()); m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t; cnote << "New transaction " << t;
@ -56,7 +56,7 @@ Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes con
u256 n = postMine().transactionsFrom(toAddress(_secret)); u256 n = postMine().transactionsFrom(toAddress(_secret));
Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
m_tq.attemptImport(t.rlp()); m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t; cnote << "New transaction " << t;
@ -182,7 +182,7 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
if (_f.matches(receipt.bloom())) if (_f.matches(receipt.bloom()))
{ {
auto info = bc().info(h); auto info = bc().info(h);
auto th = transaction(info.hash, i).sha3(); auto th = transaction(info.hash(), i).sha3();
LogEntries le = _f.matches(receipt); LogEntries le = _f.matches(receipt);
if (le.size()) if (le.size())
{ {
@ -265,7 +265,8 @@ LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const
// cwatch << "peekWatch" << _watchId; // cwatch << "peekWatch" << _watchId;
auto& w = m_watches.at(_watchId); auto& w = m_watches.at(_watchId);
// cwatch << "lastPoll updated to " << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count(); // cwatch << "lastPoll updated to " << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count();
w.lastPoll = chrono::system_clock::now(); if (w.lastPoll != chrono::system_clock::time_point::max())
w.lastPoll = chrono::system_clock::now();
return w.changes; return w.changes;
} }
@ -278,8 +279,9 @@ LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId)
auto& w = m_watches.at(_watchId); auto& w = m_watches.at(_watchId);
// cwatch << "lastPoll updated to " << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count(); // cwatch << "lastPoll updated to " << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count();
std::swap(ret, w.changes); std::swap(ret, w.changes);
w.lastPoll = chrono::system_clock::now(); if (w.lastPoll != chrono::system_clock::time_point::max())
w.lastPoll = chrono::system_clock::now();
return ret; return ret;
} }

27
libethereum/EthereumPeer.cpp

@ -326,15 +326,27 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case TransactionsPacket: case TransactionsPacket:
{ {
clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)"; clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)";
addRating(_r.itemCount());
Guard l(x_knownTransactions); Guard l(x_knownTransactions);
for (unsigned i = 0; i < _r.itemCount(); ++i) for (unsigned i = 0; i < _r.itemCount(); ++i)
{ {
auto h = sha3(_r[i].data()); auto h = sha3(_r[i].data());
m_knownTransactions.insert(h); m_knownTransactions.insert(h);
if (!host()->m_tq.import(_r[i].data())) ImportResult ir = host()->m_tq.import(_r[i].data());
switch (ir)
{
case ImportResult::Malformed:
addRating(-100);
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on. // if we already had the transaction, then don't bother sending it on.
addRating(0);
break;
case ImportResult::Success:
addRating(100);
host()->m_transactionsSent.insert(h); host()->m_transactionsSent.insert(h);
break;
default:;
}
} }
break; break;
} }
@ -352,6 +364,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
for (unsigned i = 0; i < c && p; ++i, p = host()->m_chain.details(p).parent) for (unsigned i = 0; i < c && p; ++i, p = host()->m_chain.details(p).parent)
s << p; s << p;
sealAndSend(s); sealAndSend(s);
addRating(0);
break; break;
} }
case BlockHashesPacket: case BlockHashesPacket:
@ -370,6 +383,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
} }
for (unsigned i = 0; i < _r.itemCount(); ++i) for (unsigned i = 0; i < _r.itemCount(); ++i)
{ {
addRating(1);
auto h = _r[i].toHash<h256>(); auto h = _r[i].toHash<h256>();
if (host()->m_chain.isKnown(h)) if (host()->m_chain.isKnown(h))
{ {
@ -398,6 +412,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
++n; ++n;
} }
} }
addRating(0);
RLPStream s; RLPStream s;
prep(s, BlocksPacket, n).appendRaw(rlp, n); prep(s, BlocksPacket, n).appendRaw(rlp, n);
sealAndSend(s); sealAndSend(s);
@ -463,7 +478,12 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
clogS(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received."; clogS(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
if (m_asking == Asking::Blocks) if (m_asking == Asking::Blocks)
transition(Asking::Blocks); {
if (!got)
transition(Asking::Blocks);
else
transition(Asking::Nothing);
}
break; break;
} }
case NewBlockPacket: case NewBlockPacket:
@ -497,6 +517,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
setNeedsSyncing(h, _r[1].toInt<u256>()); setNeedsSyncing(h, _r[1].toInt<u256>());
break; break;
} }
Guard l(x_knownBlocks); Guard l(x_knownBlocks);
m_knownBlocks.insert(h); m_knownBlocks.insert(h);
} }

66
libethereum/State.cpp

@ -43,6 +43,7 @@ using namespace dev;
using namespace dev::eth; using namespace dev::eth;
#define ctrace clog(StateTrace) #define ctrace clog(StateTrace)
#define ETH_TIMED_ENACTMENTS 0
static const u256 c_blockReward = 1500 * finney; static const u256 c_blockReward = 1500 * finney;
@ -77,23 +78,24 @@ OverlayDB State::openDB(std::string _path, WithExisting _we)
return OverlayDB(db); return OverlayDB(db);
} }
State::State(Address _coinbaseAddress, OverlayDB const& _db, BaseState _bs): State::State(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress):
m_db(_db), m_db(_db),
m_state(&m_db), m_state(&m_db),
m_ourAddress(_coinbaseAddress), m_ourAddress(_coinbaseAddress),
m_blockReward(c_blockReward) m_blockReward(c_blockReward)
{ {
// Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly. if (_bs != BaseState::PreExisting)
m_state.init(); // Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly.
m_state.init();
paranoia("beginning of normal construction.", true); paranoia("beginning of Genesis construction.", true);
if (_bs == BaseState::CanonGenesis) if (_bs == BaseState::CanonGenesis)
{ {
dev::eth::commit(genesisState(), m_db, m_state); dev::eth::commit(genesisState(), m_db, m_state);
m_db.commit(); m_db.commit();
paranoia("after DB commit of normal construction.", true); paranoia("after DB commit of Genesis construction.", true);
m_previousBlock = CanonBlockChain::genesis(); m_previousBlock = CanonBlockChain::genesis();
} }
else else
@ -320,7 +322,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi)
std::vector<h256> chain; std::vector<h256> chain;
while (bi.number != 0 && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block... while (bi.number != 0 && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block...
{ {
chain.push_back(bi.hash); // push back for later replay. chain.push_back(bi.hash()); // push back for later replay.
bi.populate(_bc.block(bi.parentHash)); // move to parent. bi.populate(_bc.block(bi.parentHash)); // move to parent.
} }
@ -353,16 +355,48 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi)
u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const& _bc) u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const& _bc)
{ {
#if ETH_TIMED_ENACTMENTS
boost::timer t;
double populateVerify;
double populateGrand;
double syncReset;
double enactment;
#endif
// Check family: // Check family:
BlockInfo biParent(_bc.block(_bi.parentHash)); BlockInfo biParent(_bc.block(_bi.parentHash));
_bi.verifyParent(biParent); _bi.verifyParent(biParent);
#if ETH_TIMED_ENACTMENTS
populateVerify = t.elapsed();
t.restart();
#endif
BlockInfo biGrandParent; BlockInfo biGrandParent;
if (biParent.number) if (biParent.number)
biGrandParent.populate(_bc.block(biParent.parentHash)); biGrandParent.populate(_bc.block(biParent.parentHash));
#if ETH_TIMED_ENACTMENTS
populateGrand = t.elapsed();
t.restart();
#endif
sync(_bc, _bi.parentHash); sync(_bc, _bi.parentHash);
resetCurrent(); resetCurrent();
#if ETH_TIMED_ENACTMENTS
syncReset = t.elapsed();
t.restart();
#endif
m_previousBlock = biParent; m_previousBlock = biParent;
return enact(_block, _bc); auto ret = enact(_block, _bc);
#if ETH_TIMED_ENACTMENTS
enactment = t.elapsed();
cnote << "popVer/popGrand/syncReset/enactment = " << populateVerify << "/" << populateGrand << "/" << syncReset << "/" << enactment;
#endif
return ret;
} }
map<Address, u256> State::addresses() const map<Address, u256> State::addresses() const
@ -504,18 +538,18 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
{ {
// m_currentBlock is assumed to be prepopulated and reset. // m_currentBlock is assumed to be prepopulated and reset.
#if !ETH_RELEASE
BlockInfo bi(_block, _checkNonce ? CheckEverything : IgnoreNonce); BlockInfo bi(_block, _checkNonce ? CheckEverything : IgnoreNonce);
assert(m_previousBlock.hash == bi.parentHash); #if !ETH_RELEASE
assert(m_previousBlock.hash() == bi.parentHash);
assert(m_currentBlock.parentHash == bi.parentHash); assert(m_currentBlock.parentHash == bi.parentHash);
assert(rootHash() == m_previousBlock.stateRoot); assert(rootHash() == m_previousBlock.stateRoot);
#endif #endif
if (m_currentBlock.parentHash != m_previousBlock.hash) if (m_currentBlock.parentHash != m_previousBlock.hash())
BOOST_THROW_EXCEPTION(InvalidParentHash()); BOOST_THROW_EXCEPTION(InvalidParentHash());
// Populate m_currentBlock with the correct values. // Populate m_currentBlock with the correct values.
m_currentBlock.populate(_block, _checkNonce ? CheckEverything : IgnoreNonce); m_currentBlock = bi;
m_currentBlock.verifyInternals(_block); m_currentBlock.verifyInternals(_block);
// cnote << "playback begins:" << m_state.root(); // cnote << "playback begins:" << m_state.root();
@ -565,7 +599,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
cwarn << TransactionReceipt(&b); cwarn << TransactionReceipt(&b);
} }
cwarn << "Recorded: " << m_currentBlock.receiptsRoot; cwarn << "Recorded: " << m_currentBlock.receiptsRoot;
auto rs = _bc.receipts(m_currentBlock.hash); auto rs = _bc.receipts(m_currentBlock.hash());
for (unsigned j = 0; j < rs.receipts.size(); ++j) for (unsigned j = 0; j < rs.receipts.size(); ++j)
{ {
auto b = rs.receipts[j].rlp(); auto b = rs.receipts[j].rlp();
@ -807,7 +841,7 @@ void State::commitToMine(BlockChain const& _bc)
m_currentBlock.gasUsed = gasUsed(); m_currentBlock.gasUsed = gasUsed();
m_currentBlock.stateRoot = m_state.root(); m_currentBlock.stateRoot = m_state.root();
m_currentBlock.parentHash = m_previousBlock.hash; m_currentBlock.parentHash = m_previousBlock.hash();
} }
MineInfo State::mine(unsigned _msTimeout, bool _turbo) MineInfo State::mine(unsigned _msTimeout, bool _turbo)
@ -857,10 +891,10 @@ void State::completeMine()
ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentTxs);
ret.appendRaw(m_currentUncles); ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes); ret.swapOut(m_currentBytes);
m_currentBlock.hash = sha3(RLP(m_currentBytes)[0].data()); m_currentBlock.noteDirty();
cnote << "Mined " << m_currentBlock.hash.abridged() << "(parent: " << m_currentBlock.parentHash.abridged() << ")"; cnote << "Mined " << m_currentBlock.hash().abridged() << "(parent: " << m_currentBlock.parentHash.abridged() << ")";
StructuredLogger::minedNewBlock( StructuredLogger::minedNewBlock(
m_currentBlock.hash.abridged(), m_currentBlock.hash().abridged(),
m_currentBlock.nonce.abridged(), m_currentBlock.nonce.abridged(),
"", //TODO: chain head hash here ?? "", //TODO: chain head hash here ??
m_currentBlock.parentHash.abridged() m_currentBlock.parentHash.abridged()

19
libethereum/State.h

@ -54,7 +54,12 @@ struct StateTrace: public LogChannel { static const char* name() { return "=S=";
struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; }; struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; };
struct StateSafeExceptions: public LogChannel { static const char* name() { return "(S)"; } static const int verbosity = 21; }; struct StateSafeExceptions: public LogChannel { static const char* name() { return "(S)"; } static const int verbosity = 21; };
enum class BaseState { Empty, CanonGenesis }; enum class BaseState
{
PreExisting,
Empty,
CanonGenesis
};
enum class TransactionPriority enum class TransactionPriority
{ {
@ -103,8 +108,15 @@ class State
friend class Executive; friend class Executive;
public: public:
/// Construct state object. /// Default constructor; creates with a blank database prepopulated with the genesis block.
State(Address _coinbaseAddress = Address(), OverlayDB const& _db = OverlayDB(), BaseState _bs = BaseState::CanonGenesis); State(): State(OverlayDB(), BaseState::Empty) {}
/// Basic state object from database.
/// Use the default when you already have a database and you just want to make a State object
/// which uses it. If you have no preexisting database then set BaseState to something other
/// than BaseState::PreExisting in order to prepopulate the Trie.
/// You can also set the coinbase address.
explicit State(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address());
/// Construct state object from arbitrary point in blockchain. /// Construct state object from arbitrary point in blockchain.
State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash); State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash);
@ -335,6 +347,7 @@ private:
/// Debugging only. Good for checking the Trie is in shape. /// Debugging only. Good for checking the Trie is in shape.
void paranoia(std::string const& _when, bool _enforceRefs = false) const; void paranoia(std::string const& _when, bool _enforceRefs = false) const;
OverlayDB m_db; ///< Our overlay for the state tree. OverlayDB m_db; ///< Our overlay for the state tree.
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.

12
libethereum/TransactionQueue.cpp

@ -28,14 +28,16 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
bool TransactionQueue::import(bytesConstRef _transactionRLP) ImportResult TransactionQueue::import(bytesConstRef _transactionRLP)
{ {
// Check if we already know this transaction. // Check if we already know this transaction.
h256 h = sha3(_transactionRLP); h256 h = sha3(_transactionRLP);
UpgradableGuard l(m_lock); UpgradableGuard l(m_lock);
// TODO: keep old transactions around and check in State for nonce validity
if (m_known.count(h)) if (m_known.count(h))
return false; return ImportResult::AlreadyKnown;
try try
{ {
@ -52,15 +54,15 @@ bool TransactionQueue::import(bytesConstRef _transactionRLP)
catch (Exception const& _e) catch (Exception const& _e)
{ {
cwarn << "Ignoring invalid transaction: " << diagnostic_information(_e); cwarn << "Ignoring invalid transaction: " << diagnostic_information(_e);
return false; return ImportResult::Malformed;
} }
catch (std::exception const& _e) catch (std::exception const& _e)
{ {
cwarn << "Ignoring invalid transaction: " << _e.what(); cwarn << "Ignoring invalid transaction: " << _e.what();
return false; return ImportResult::Malformed;
} }
return true; return ImportResult::Success;
} }
void TransactionQueue::setFuture(std::pair<h256, Transaction> const& _t) void TransactionQueue::setFuture(std::pair<h256, Transaction> const& _t)

8
libethereum/TransactionQueue.h

@ -23,8 +23,8 @@
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include "libethcore/Common.h"
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include "libethcore/Common.h"
#include "Transaction.h" #include "Transaction.h"
namespace dev namespace dev
@ -34,6 +34,7 @@ namespace eth
class BlockChain; class BlockChain;
/** /**
* @brief A queue of Transactions, each stored as RLP. * @brief A queue of Transactions, each stored as RLP.
* @threadsafe * @threadsafe
@ -41,9 +42,8 @@ class BlockChain;
class TransactionQueue class TransactionQueue
{ {
public: public:
bool attemptImport(bytesConstRef _tx) { try { import(_tx); return true; } catch (...) { return false; } } ImportResult import(bytes const& _tx) { return import(&_tx); }
bool attemptImport(bytes const& _tx) { return attemptImport(&_tx); } ImportResult import(bytesConstRef _tx);
bool import(bytesConstRef _tx);
void drop(h256 _txHash); void drop(h256 _txHash);

2
libp2p/Capability.cpp

@ -53,7 +53,7 @@ void Capability::sealAndSend(RLPStream& _s)
m_session->sealAndSend(_s); m_session->sealAndSend(_s);
} }
void Capability::addRating(unsigned _r) void Capability::addRating(int _r)
{ {
m_session->addRating(_r); m_session->addRating(_r);
} }

2
libp2p/Capability.h

@ -52,7 +52,7 @@ protected:
RLPStream& prep(RLPStream& _s, unsigned _id, unsigned _args = 0); RLPStream& prep(RLPStream& _s, unsigned _id, unsigned _args = 0);
void sealAndSend(RLPStream& _s); void sealAndSend(RLPStream& _s);
void addRating(unsigned _r); void addRating(int _r);
private: private:
Session* m_session; Session* m_session;

31
libp2p/Host.cpp

@ -182,10 +182,8 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
} }
else else
p = m_peers[_id]; p = m_peers[_id];
p->m_lastDisconnect = NoDisconnect;
if (p->isOffline()) if (p->isOffline())
p->m_lastConnected = std::chrono::system_clock::now(); p->m_lastConnected = std::chrono::system_clock::now();
p->m_failedAttempts = 0;
p->endpoint.tcp.address(_endpoint.address()); p->endpoint.tcp.address(_endpoint.address());
auto protocolVersion = _rlp[0].toInt<unsigned>(); auto protocolVersion = _rlp[0].toInt<unsigned>();
@ -219,16 +217,19 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
ps->disconnect(DuplicatePeer); ps->disconnect(DuplicatePeer);
return; return;
} }
// todo: mutex Session::m_capabilities and move for(:caps) out of mutex.
unsigned o = (unsigned)UserPacket;
for (auto const& i: caps)
if (haveCapability(i))
{
ps->m_capabilities[i] = shared_ptr<Capability>(m_capabilities[i]->newPeerCapability(ps.get(), o));
o += m_capabilities[i]->messageCount();
}
ps->start();
m_sessions[_id] = ps; m_sessions[_id] = ps;
} }
ps->start();
unsigned o = (unsigned)UserPacket;
for (auto const& i: caps)
if (haveCapability(i))
{
ps->m_capabilities[i] = shared_ptr<Capability>(m_capabilities[i]->newPeerCapability(ps.get(), o));
o += m_capabilities[i]->messageCount();
}
clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); clog(NetNote) << "p2p.host.peer.register" << _id.abridged();
StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount()); StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount());
} }
@ -484,12 +485,14 @@ void Host::connect(std::shared_ptr<Peer> const& _p)
auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService)); auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService));
socket->ref().async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec) socket->ref().async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec)
{ {
_p->m_lastAttempted = std::chrono::system_clock::now();
_p->m_failedAttempts++;
if (ec) if (ec)
{ {
clog(NetConnect) << "Connection refused to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "(" << ec.message() << ")"; clog(NetConnect) << "Connection refused to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "(" << ec.message() << ")";
// Manually set error (session not present)
_p->m_lastDisconnect = TCPError; _p->m_lastDisconnect = TCPError;
_p->m_lastAttempted = std::chrono::system_clock::now();
_p->m_failedAttempts++;
} }
else else
{ {
@ -499,9 +502,7 @@ void Host::connect(std::shared_ptr<Peer> const& _p)
Guard l(x_connecting); Guard l(x_connecting);
m_connecting.push_back(handshake); m_connecting.push_back(handshake);
} }
// preempt setting failedAttempts; this value is cleared upon success
_p->m_failedAttempts++;
handshake->start(); handshake->start();
} }

1
libp2p/Peer.cpp

@ -44,6 +44,7 @@ unsigned Peer::fallbackSeconds() const
return 30 * (m_failedAttempts + 1); return 30 * (m_failedAttempts + 1);
case UselessPeer: case UselessPeer:
case TooManyPeers: case TooManyPeers:
return 25 * (m_failedAttempts + 1);
case ClientQuit: case ClientQuit:
return 15 * (m_failedAttempts + 1); return 15 * (m_failedAttempts + 1);
case NoDisconnect: case NoDisconnect:

3
libp2p/Peer.h

@ -75,6 +75,9 @@ public:
/// Reason peer was previously disconnected. /// Reason peer was previously disconnected.
DisconnectReason lastDisconnect() const { return m_lastDisconnect; } DisconnectReason lastDisconnect() const { return m_lastDisconnect; }
/// Peer session is noted as useful.
void noteSessionGood() { m_failedAttempts = 0; }
protected: protected:
/// Returns number of seconds to wait until attempting connection, based on attempted connection history. /// Returns number of seconds to wait until attempting connection, based on attempted connection history.
unsigned fallbackSeconds() const; unsigned fallbackSeconds() const;

18
libp2p/Session.cpp

@ -46,6 +46,7 @@ Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr<Peer> const& _n, Pe
m_info(_info), m_info(_info),
m_ping(chrono::steady_clock::time_point::max()) m_ping(chrono::steady_clock::time_point::max())
{ {
m_peer->m_lastDisconnect = NoDisconnect;
m_lastReceived = m_connect = chrono::steady_clock::now(); m_lastReceived = m_connect = chrono::steady_clock::now();
} }
@ -76,12 +77,14 @@ NodeId Session::id() const
return m_peer ? m_peer->id : NodeId(); return m_peer ? m_peer->id : NodeId();
} }
void Session::addRating(unsigned _r) void Session::addRating(int _r)
{ {
if (m_peer) if (m_peer)
{ {
m_peer->m_rating += _r; m_peer->m_rating += _r;
m_peer->m_score += _r; m_peer->m_score += _r;
if (_r >= 0)
m_peer->noteSessionGood();
} }
} }
@ -358,16 +361,11 @@ void Session::drop(DisconnectReason _reason)
} }
catch (...) {} catch (...) {}
if (m_peer) m_peer->m_lastDisconnect = _reason;
if (_reason == BadProtocol)
{ {
if (_reason != m_peer->m_lastDisconnect || _reason == NoDisconnect || _reason == ClientQuit || _reason == DisconnectRequested) m_peer->m_rating /= 2;
m_peer->m_failedAttempts = 0; m_peer->m_score /= 2;
m_peer->m_lastDisconnect = _reason;
if (_reason == BadProtocol)
{
m_peer->m_rating /= 2;
m_peer->m_score /= 2;
}
} }
m_dropped = true; m_dropped = true;
} }

2
libp2p/Session.h

@ -74,7 +74,7 @@ public:
void sealAndSend(RLPStream& _s); void sealAndSend(RLPStream& _s);
int rating() const; int rating() const;
void addRating(unsigned _r); void addRating(int _r);
void addNote(std::string const& _k, std::string const& _v) { m_info.notes[_k] = _v; } void addNote(std::string const& _k, std::string const& _v) { m_info.notes[_k] = _v; }

2
libtestutils/StateLoader.cpp

@ -26,7 +26,7 @@ using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::test; using namespace dev::test;
StateLoader::StateLoader(Json::Value const& _json) : m_state(Address(), OverlayDB(), BaseState::Empty) StateLoader::StateLoader(Json::Value const& _json)
{ {
for (string const& name: _json.getMemberNames()) for (string const& name: _json.getMemberNames())
{ {

14
libweb3jsonrpc/CMakeLists.txt

@ -22,9 +22,9 @@ file(GLOB HEADERS "*.h")
if (ETH_STATIC) if (ETH_STATIC)
add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS})
else() else ()
add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS}) add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS})
endif() endif ()
target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
@ -33,11 +33,13 @@ target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES})
target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} secp256k1)
target_link_libraries(${EXECUTABLE} solidity)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) if (SOLIDITY)
target_link_libraries(${EXECUTABLE} solidity)
endif ()
if (SERPENT)
target_link_libraries(${EXECUTABLE} serpent) target_link_libraries(${EXECUTABLE} serpent)
endif() endif ()
if (ETH_JSON_RPC_STUB) if (ETH_JSON_RPC_STUB)
add_custom_target(jsonrpcstub) add_custom_target(jsonrpcstub)
@ -51,7 +53,7 @@ if (ETH_JSON_RPC_STUB)
-P "${ETH_SCRIPTS_DIR}/jsonrpcstub.cmake" -P "${ETH_SCRIPTS_DIR}/jsonrpcstub.cmake"
) )
add_dependencies(${EXECUTABLE} jsonrpcstub) add_dependencies(${EXECUTABLE} jsonrpcstub)
endif() endif ()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

5
libweb3jsonrpc/WebThreeStubServer.cpp

@ -44,6 +44,11 @@ WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn,
ldb::DB::Open(o, path, &m_db); ldb::DB::Open(o, path, &m_db);
} }
std::string WebThreeStubServer::web3_clientVersion()
{
return m_web3.clientVersion();
}
dev::eth::Interface* WebThreeStubServer::client() dev::eth::Interface* WebThreeStubServer::client()
{ {
return m_web3.ethereum(); return m_web3.ethereum();

4
libweb3jsonrpc/WebThreeStubServer.h

@ -42,7 +42,9 @@ class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThr
{ {
public: public:
WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts); WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts);
virtual std::string web3_clientVersion();
private: private:
virtual dev::eth::Interface* client() override; virtual dev::eth::Interface* client() override;
virtual std::shared_ptr<dev::shh::Interface> face() override; virtual std::shared_ptr<dev::shh::Interface> face() override;

21
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -26,9 +26,11 @@
#include <jsonrpccpp/common/exception.h> #include <jsonrpccpp/common/exception.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
#if ETH_SOLIDITY
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
#endif
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
@ -36,7 +38,7 @@
#include <libethcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libwhisper/Message.h> #include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.h> #include <libwhisper/WhisperHost.h>
#ifndef _MSC_VER #if ETH_SERPENT
#include <libserpent/funcs.h> #include <libserpent/funcs.h>
#endif #endif
#include "WebThreeStubServerBase.h" #include "WebThreeStubServerBase.h"
@ -59,7 +61,7 @@ static Json::Value toJson(dev::eth::BlockInfo const& _bi)
Json::Value res; Json::Value res;
if (_bi) if (_bi)
{ {
res["hash"] = toJS(_bi.hash); res["hash"] = toJS(_bi.hash());
res["parentHash"] = toJS(_bi.parentHash); res["parentHash"] = toJS(_bi.parentHash);
res["sha3Uncles"] = toJS(_bi.sha3Uncles); res["sha3Uncles"] = toJS(_bi.sha3Uncles);
res["miner"] = toJS(_bi.coinbaseAddress); res["miner"] = toJS(_bi.coinbaseAddress);
@ -296,6 +298,11 @@ bool WebThreeStubServerBase::net_listening()
return network()->isNetworkStarted(); return network()->isNetworkStarted();
} }
string WebThreeStubServerBase::eth_protocolVersion()
{
return toJS(eth::c_protocolVersion);
}
string WebThreeStubServerBase::eth_coinbase() string WebThreeStubServerBase::eth_coinbase()
{ {
return toJS(client()->address()); return toJS(client()->address());
@ -615,8 +622,10 @@ Json::Value WebThreeStubServerBase::eth_getCompilers()
{ {
Json::Value ret(Json::arrayValue); Json::Value ret(Json::arrayValue);
ret.append("lll"); ret.append("lll");
#if SOLIDITY
ret.append("solidity"); ret.append("solidity");
#ifndef _MSC_VER #endif
#if SERPENT
ret.append("serpent"); ret.append("serpent");
#endif #endif
return ret; return ret;
@ -637,7 +646,8 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _code)
{ {
// TODO throw here jsonrpc errors // TODO throw here jsonrpc errors
string res; string res;
#ifndef _MSC_VER (void)_code;
#if SERPENT
try try
{ {
res = toJS(dev::asBytes(::compile(_code))); res = toJS(dev::asBytes(::compile(_code)));
@ -657,7 +667,9 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _code)
string WebThreeStubServerBase::eth_compileSolidity(string const& _code) string WebThreeStubServerBase::eth_compileSolidity(string const& _code)
{ {
// TOOD throw here jsonrpc errors // TOOD throw here jsonrpc errors
(void)_code;
string res; string res;
#if SOLIDITY
dev::solidity::CompilerStack compiler; dev::solidity::CompilerStack compiler;
try try
{ {
@ -673,6 +685,7 @@ string WebThreeStubServerBase::eth_compileSolidity(string const& _code)
{ {
cwarn << "Uncought solidity compilation exception"; cwarn << "Uncought solidity compilation exception";
} }
#endif
return res; return res;
} }

1
libweb3jsonrpc/WebThreeStubServerBase.h

@ -77,6 +77,7 @@ public:
virtual std::string net_peerCount(); virtual std::string net_peerCount();
virtual bool net_listening(); virtual bool net_listening();
virtual std::string eth_protocolVersion();
virtual std::string eth_coinbase(); virtual std::string eth_coinbase();
virtual bool eth_mining(); virtual bool eth_mining();
virtual std::string eth_gasPrice(); virtual std::string eth_gasPrice();

7
libweb3jsonrpc/abstractwebthreestubserver.h

@ -17,6 +17,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
this->bindAndAddMethod(jsonrpc::Procedure("net_version", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::net_versionI); this->bindAndAddMethod(jsonrpc::Procedure("net_version", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::net_versionI);
this->bindAndAddMethod(jsonrpc::Procedure("net_peerCount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::net_peerCountI); this->bindAndAddMethod(jsonrpc::Procedure("net_peerCount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::net_peerCountI);
this->bindAndAddMethod(jsonrpc::Procedure("net_listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::net_listeningI); this->bindAndAddMethod(jsonrpc::Procedure("net_listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::net_listeningI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_protocolVersion", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_protocolVersionI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_coinbaseI); this->bindAndAddMethod(jsonrpc::Procedure("eth_coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_coinbaseI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_mining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_miningI); this->bindAndAddMethod(jsonrpc::Procedure("eth_mining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_miningI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_gasPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_gasPriceI); this->bindAndAddMethod(jsonrpc::Procedure("eth_gasPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_gasPriceI);
@ -92,6 +93,11 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
(void)request; (void)request;
response = this->net_listening(); response = this->net_listening();
} }
inline virtual void eth_protocolVersionI(const Json::Value &request, Json::Value &response)
{
(void)request;
response = this->eth_protocolVersion();
}
inline virtual void eth_coinbaseI(const Json::Value &request, Json::Value &response) inline virtual void eth_coinbaseI(const Json::Value &request, Json::Value &response)
{ {
(void)request; (void)request;
@ -302,6 +308,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
virtual std::string net_version() = 0; virtual std::string net_version() = 0;
virtual std::string net_peerCount() = 0; virtual std::string net_peerCount() = 0;
virtual bool net_listening() = 0; virtual bool net_listening() = 0;
virtual std::string eth_protocolVersion() = 0;
virtual std::string eth_coinbase() = 0; virtual std::string eth_coinbase() = 0;
virtual bool eth_mining() = 0; virtual bool eth_mining() = 0;
virtual std::string eth_gasPrice() = 0; virtual std::string eth_gasPrice() = 0;

1
libweb3jsonrpc/spec.json

@ -6,6 +6,7 @@
{ "name": "net_peerCount", "params": [], "order": [], "returns" : "" }, { "name": "net_peerCount", "params": [], "order": [], "returns" : "" },
{ "name": "net_listening", "params": [], "order": [], "returns" : false }, { "name": "net_listening", "params": [], "order": [], "returns" : false },
{ "name": "eth_protocolVersion", "params": [], "order": [], "returns" : "" },
{ "name": "eth_coinbase", "params": [], "order": [], "returns" : "" }, { "name": "eth_coinbase", "params": [], "order": [], "returns" : "" },
{ "name": "eth_mining", "params": [], "order": [], "returns" : false }, { "name": "eth_mining", "params": [], "order": [], "returns" : false },
{ "name": "eth_gasPrice", "params": [], "order": [], "returns" : "" }, { "name": "eth_gasPrice", "params": [], "order": [], "returns" : "" },

2
libwebthree/WebThree.h

@ -127,6 +127,8 @@ public:
// Misc stuff: // Misc stuff:
std::string const& clientVersion() const { return m_clientVersion; }
void setClientVersion(std::string const& _name) { m_clientVersion = _name; } void setClientVersion(std::string const& _name) { m_clientVersion = _name; }
// Network stuff: // Network stuff:

2
libwhisper/WhisperHost.cpp

@ -162,7 +162,7 @@ void WhisperHost::uninstallWatch(unsigned _i)
void WhisperHost::doWork() void WhisperHost::doWork()
{ {
for (auto& i: peerSessions()) for (auto i: peerSessions())
i.first->cap<WhisperPeer>().get()->sendMessages(); i.first->cap<WhisperPeer>().get()->sendMessages();
cleanup(); cleanup();
} }

49
mix/CMakeLists.txt

@ -29,7 +29,9 @@ else()
qt5_add_resources(UI_RESOURCES noweb.qrc) qt5_add_resources(UI_RESOURCES noweb.qrc)
endif() endif()
add_definitions(-DQT_QML_DEBUG) if (CMAKE_BUILD_TYPE EQUAL "DEBUG")
add_definitions(-DQT_QML_DEBUG)
endif()
# eth_add_executable is defined in cmake/EthExecutableHelper.cmake # eth_add_executable is defined in cmake/EthExecutableHelper.cmake
eth_add_executable(${EXECUTABLE} eth_add_executable(${EXECUTABLE}
@ -37,33 +39,15 @@ eth_add_executable(${EXECUTABLE}
UI_RESOURCES ${UI_RESOURCES} UI_RESOURCES ${UI_RESOURCES}
) )
target_link_libraries(${EXECUTABLE} Qt5::Core)
target_link_libraries(${EXECUTABLE} Qt5::Gui)
target_link_libraries(${EXECUTABLE} Qt5::Widgets)
target_link_libraries(${EXECUTABLE} Qt5::Network)
target_link_libraries(${EXECUTABLE} Qt5::Quick)
target_link_libraries(${EXECUTABLE} Qt5::Qml)
target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} ethereum)
target_link_libraries(${EXECUTABLE} evm)
target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} secp256k1)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
target_link_libraries(${EXECUTABLE} serpent)
endif()
target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} solidity)
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} jsqrc)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
set(LIBRARIES "Qt5::Core;Qt5::Gui;Qt5::Widgets;Qt5::Network;Qt5::Quick;Qt5::Qml;webthree;ethereum;evm;ethcore;devcrypto;solidity;evmcore;devcore;jsqrc;web3jsonrpc")
if (${ETH_HAVE_WEBENGINE}) if (${ETH_HAVE_WEBENGINE})
add_definitions(-DETH_HAVE_WEBENGINE) add_definitions(-DETH_HAVE_WEBENGINE)
target_link_libraries(${EXECUTABLE} Qt5::WebEngine) list(APPEND LIBRARIES "Qt5::WebEngine")
endif() endif()
target_link_libraries(${EXECUTABLE} ${LIBRARIES})
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake # eth_install_executable is defined in cmake/EthExecutableHelper.cmake
eth_install_executable(${EXECUTABLE} eth_install_executable(${EXECUTABLE}
QMLDIR ${CMAKE_CURRENT_SOURCE_DIR}/qml QMLDIR ${CMAKE_CURRENT_SOURCE_DIR}/qml
@ -71,5 +55,22 @@ eth_install_executable(${EXECUTABLE}
#add qml asnd stdc files to project tree in Qt creator #add qml asnd stdc files to project tree in Qt creator
file(GLOB_RECURSE QMLFILES "qml/*.*") file(GLOB_RECURSE QMLFILES "qml/*.*")
file(GLOB_RECURSE TESTFILES "test/qml/*.*")
file(GLOB_RECURSE SOLFILES "stdc/*.*") file(GLOB_RECURSE SOLFILES "stdc/*.*")
add_custom_target(mix_qml SOURCES ${QMLFILES} ${SOLFILES}) add_custom_target(mix_qml SOURCES ${QMLFILES} ${SOLFILES} ${TESTFILES})
#test target
find_package(Qt5QuickTest REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST_EXECUTABLE mix_test)
list(APPEND LIBRARIES "Qt5::QuickTest")
list(APPEND LIBRARIES "Qt5::Test")
list(REMOVE_ITEM SRC_LIST "./main.cpp")
aux_source_directory(test SRC_LIST)
file(GLOB HEADERS "test/*.h")
add_executable(${TEST_EXECUTABLE} ${UI_RESOURCES} ${SRC_LIST} ${HEADERS})
target_link_libraries(${TEST_EXECUTABLE} ${LIBRARIES})
set_target_properties(${TEST_EXECUTABLE} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)

7
mix/ClientModel.cpp

@ -90,6 +90,7 @@ ClientModel::ClientModel():
ClientModel::~ClientModel() ClientModel::~ClientModel()
{ {
m_runFuture.waitForFinished();
} }
QString ClientModel::apiCall(QString const& _message) QString ClientModel::apiCall(QString const& _message)
@ -113,7 +114,7 @@ void ClientModel::mine()
m_mining = true; m_mining = true;
emit miningStarted(); emit miningStarted();
emit miningStateChanged(); emit miningStateChanged();
QtConcurrent::run([=]() m_runFuture = QtConcurrent::run([=]()
{ {
try try
{ {
@ -206,7 +207,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
emit runStateChanged(); emit runStateChanged();
//run sequence //run sequence
QtConcurrent::run([=]() m_runFuture = QtConcurrent::run([=]()
{ {
try try
{ {
@ -468,7 +469,7 @@ RecordLogEntry* ClientModel::lastBlock() const
strGas << blockInfo.gasUsed; strGas << blockInfo.gasUsed;
std::stringstream strNumber; std::stringstream strNumber;
strNumber << blockInfo.number; strNumber << blockInfo.number;
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash.ref()))), tr("Gas Used: ") + QString::fromStdString(strGas.str()), QString(), QString(), false, RecordLogEntry::RecordType::Block); RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash().ref()))), tr("Gas Used: ") + QString::fromStdString(strGas.str()), QString(), QString(), false, RecordLogEntry::RecordType::Block);
QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership);
return record; return record;
} }

2
mix/ClientModel.h

@ -27,6 +27,7 @@
#include <map> #include <map>
#include <QString> #include <QString>
#include <QVariantMap> #include <QVariantMap>
#include <QFuture>
#include "MachineStates.h" #include "MachineStates.h"
namespace dev namespace dev
@ -203,6 +204,7 @@ private:
std::atomic<bool> m_running; std::atomic<bool> m_running;
std::atomic<bool> m_mining; std::atomic<bool> m_mining;
QFuture<void> m_runFuture;
std::unique_ptr<MixClient> m_client; std::unique_ptr<MixClient> m_client;
std::unique_ptr<RpcConnector> m_rpcConnector; std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server; std::unique_ptr<Web3Server> m_web3Server;

44
mix/MixApplication.cpp

@ -23,6 +23,7 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QUrl> #include <QUrl>
#include <QIcon> #include <QIcon>
#include <QFont>
#ifdef ETH_HAVE_WEBENGINE #ifdef ETH_HAVE_WEBENGINE
#include <QtWebEngine/QtWebEngine> #include <QtWebEngine/QtWebEngine>
#endif #endif
@ -37,21 +38,49 @@
using namespace dev::mix; using namespace dev::mix;
ApplicationService::ApplicationService()
{
#ifdef ETH_HAVE_WEBENGINE
QtWebEngine::initialize();
#endif
QFont f;
m_systemPointSize = f.pointSize();
}
MixApplication::MixApplication(int& _argc, char* _argv[]): MixApplication::MixApplication(int& _argc, char* _argv[]):
QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()) QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine())
{ {
m_engine->load(QUrl("qrc:/qml/Application.qml"));
if (!m_engine->rootObjects().empty())
{
QWindow *window = qobject_cast<QWindow*>(m_engine->rootObjects().at(0));
if (window)
window->setIcon(QIcon(":/res/mix_256x256x32.png"));
}
}
void MixApplication::initialize()
{
#if __linux
//work around ubuntu appmenu-qt5 bug
//https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1323853
putenv((char*)"QT_QPA_PLATFORMTHEME=");
putenv((char*)"QSG_RENDER_LOOP=threaded");
#endif
#if (defined(_WIN32) || defined(_WIN64))
if (!getenv("OPENSSL_CONF"))
putenv((char*)"OPENSSL_CONF=c:\\");
#endif
setOrganizationName(tr("Ethereum")); setOrganizationName(tr("Ethereum"));
setOrganizationDomain(tr("ethereum.org")); setOrganizationDomain(tr("ethereum.org"));
setApplicationName(tr("Mix")); setApplicationName(tr("Mix"));
setApplicationVersion("0.1"); setApplicationVersion("0.1");
#ifdef ETH_HAVE_WEBENGINE
QtWebEngine::initialize();
#endif
QFont f;
m_engine->rootContext()->setContextProperty("systemPointSize", f.pointSize());
qmlRegisterType<CodeModel>("org.ethereum.qml.CodeModel", 1, 0, "CodeModel"); qmlRegisterType<CodeModel>("org.ethereum.qml.CodeModel", 1, 0, "CodeModel");
qmlRegisterType<ClientModel>("org.ethereum.qml.ClientModel", 1, 0, "ClientModel"); qmlRegisterType<ClientModel>("org.ethereum.qml.ClientModel", 1, 0, "ClientModel");
qmlRegisterType<ApplicationService>("org.ethereum.qml.ApplicationService", 1, 0, "ApplicationService");
qmlRegisterType<FileIo>("org.ethereum.qml.FileIo", 1, 0, "FileIo"); qmlRegisterType<FileIo>("org.ethereum.qml.FileIo", 1, 0, "FileIo");
qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther"); qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther");
qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt"); qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt");
@ -63,10 +92,7 @@ MixApplication::MixApplication(int& _argc, char* _argv[]):
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer"); qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
qRegisterMetaType<CodeModel*>("CodeModel*"); qRegisterMetaType<CodeModel*>("CodeModel*");
qRegisterMetaType<ClientModel*>("ClientModel*"); qRegisterMetaType<ClientModel*>("ClientModel*");
qRegisterMetaType<ClientModel*>("ClientModel*");
m_engine->load(QUrl("qrc:/qml/main.qml"));
QWindow *window = qobject_cast<QWindow*>(m_engine->rootObjects().at(0));
window->setIcon(QIcon(":/res/mix_256x256x32.png"));
} }
MixApplication::~MixApplication() MixApplication::~MixApplication()

21
mix/MixApplication.h

@ -33,12 +33,33 @@ namespace dev
namespace mix namespace mix
{ {
class ApplicationService: public QObject
{
Q_OBJECT
Q_PROPERTY(int systemPointSize READ systemPointSize CONSTANT)
Q_PROPERTY(bool haveWebEngine READ haveWebEngine CONSTANT)
public:
ApplicationService();
int systemPointSize() const { return m_systemPointSize; }
#ifdef ETH_HAVE_WEBENGINE
bool haveWebEngine() const { return true; }
#else
bool haveWebEngine() const { return false; }
#endif
private:
int m_systemPointSize = 0;
};
class MixApplication: public QApplication class MixApplication: public QApplication
{ {
Q_OBJECT Q_OBJECT
public: public:
MixApplication(int& _argc, char* _argv[]); MixApplication(int& _argc, char* _argv[]);
static void initialize();
virtual ~MixApplication(); virtual ~MixApplication();
QQmlApplicationEngine* engine() { return m_engine.get(); } QQmlApplicationEngine* engine() { return m_engine.get(); }

2
mix/MixClient.cpp

@ -93,7 +93,7 @@ void MixClient::resetState(std::map<Secret, u256> _accounts)
h256 stateRoot = accountState.root(); h256 stateRoot = accountState.root();
m_bc.reset(); m_bc.reset();
m_bc.reset(new MixBlockChain(m_dbPath, stateRoot)); m_bc.reset(new MixBlockChain(m_dbPath, stateRoot));
m_state = eth::State(genesisState.begin()->first , m_stateDB, BaseState::Empty); m_state = eth::State(m_stateDB, BaseState::PreExisting, genesisState.begin()->first);
m_state.sync(bc()); m_state.sync(bc());
m_startState = m_state; m_startState = m_state;
WriteGuard lx(x_executions); WriteGuard lx(x_executions);

41
mix/QBasicNodeDefinition.cpp

@ -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 QBasicNodeDefinition.cpp
* @author Yann yann@ethdev.com
* @date 2014
*/
#include "QBasicNodeDefinition.h"
#include <libsolidity/AST.h>
namespace dev
{
namespace mix
{
QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d):
QObject(_parent), m_name(QString::fromStdString(_d->getName()))
{
}
QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, std::string const& _name):
QObject(_parent), m_name(QString::fromStdString(_name))
{
}
}
}

12
mix/QBasicNodeDefinition.h

@ -21,11 +21,17 @@
#pragma once #pragma once
#include <string>
#include <QObject> #include <QObject>
#include <libsolidity/AST.h>
namespace dev namespace dev
{ {
namespace solidity
{
class Declaration;
}
namespace mix namespace mix
{ {
@ -37,8 +43,8 @@ class QBasicNodeDefinition: public QObject
public: public:
QBasicNodeDefinition(QObject* _parent = nullptr): QObject(_parent) {} QBasicNodeDefinition(QObject* _parent = nullptr): QObject(_parent) {}
~QBasicNodeDefinition() {} ~QBasicNodeDefinition() {}
QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d): QObject(_parent), m_name(QString::fromStdString(_d->getName())) {} QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d);
QBasicNodeDefinition(QObject* _parent, std::string const& _name): QObject(_parent), m_name(QString::fromStdString(_name)) {} QBasicNodeDefinition(QObject* _parent, std::string const& _name);
/// Get the name of the node. /// Get the name of the node.
QString name() const { return m_name; } QString name() const { return m_name; }

1
mix/QVariableDeclaration.cpp

@ -21,6 +21,7 @@
*/ */
#include "QVariableDeclaration.h" #include "QVariableDeclaration.h"
#include <libsolidity/AST.h>
#include "CodeModel.h" #include "CodeModel.h"
namespace dev namespace dev

5
mix/QVariableDeclaration.h

@ -26,14 +26,15 @@
#pragma once #pragma once
namespace dev
{
namespace solidity namespace solidity
{ {
class Type; class Type;
class VariableDeclaration; class VariableDeclaration;
} }
namespace dev
{
namespace mix namespace mix
{ {

37
mix/QVariableDefinition.cpp

@ -0,0 +1,37 @@
/*
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 QVariableDefinition.cpp
* @author Yann yann@ethdev.com
* @date 2014
*/
#include "QVariableDefinition.h"
#include <libdevcore/CommonJS.h>
namespace dev
{
namespace mix
{
QString QVariableDefinition::encodeValueAsString()
{
return QString::fromStdString(dev::toHex(encodeValue()));
}
}
}

8
mix/QVariableDefinition.h

@ -21,14 +21,14 @@
#pragma once #pragma once
#include <QAbstractListModel> #include <QObject>
#include "QBigInt.h" #include <libdevcore/Common.h>
#include "QVariableDeclaration.h"
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
class QVariableDeclaration;
class QVariableDefinition: public QObject class QVariableDefinition: public QObject
{ {
@ -54,7 +54,7 @@ public:
/// Decode the return value @a _rawValue. /// Decode the return value @a _rawValue.
virtual void decodeValue(dev::bytes const& _rawValue) = 0; virtual void decodeValue(dev::bytes const& _rawValue) = 0;
/// returns String representation of the encoded value. /// returns String representation of the encoded value.
Q_INVOKABLE QString encodeValueAsString() { return QString::fromStdString(dev::toHex(encodeValue())); } Q_INVOKABLE QString encodeValueAsString();
protected: protected:
QString m_value; QString m_value;

18
mix/main.cpp

@ -22,27 +22,17 @@
#include <iostream> #include <iostream>
#include <stdlib.h> #include <stdlib.h>
#include <boost/exception/exception.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include "MixApplication.h" #include "MixApplication.h"
#include "Exceptions.h"
using namespace dev::mix; using namespace dev::mix;
int main(int _argc, char* _argv[]) int main(int _argc, char* _argv[])
{ {
#ifdef ETH_HAVE_WEBENGINE
Q_INIT_RESOURCE(js);
#endif
#if __linux
//work around ubuntu appmenu-qt5 bug
//https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1323853
putenv((char*)"QT_QPA_PLATFORMTHEME=");
putenv((char*)"QSG_RENDER_LOOP=threaded");
#endif
#if (defined(_WIN32) || defined(_WIN64))
if (!getenv("OPENSSL_CONF"))
putenv((char*)"OPENSSL_CONF=c:\\");
#endif
try try
{ {
MixApplication::initialize();
MixApplication app(_argc, _argv); MixApplication app(_argc, _argv);
return app.exec(); return app.exec();
} }

7
mix/qml.qrc

@ -1,6 +1,7 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>qml/AlertMessageDialog.qml</file> <file>qml/AlertMessageDialog.qml</file>
<file>qml/Application.qml</file>
<file>qml/BasicMessage.qml</file> <file>qml/BasicMessage.qml</file>
<file>qml/BigIntValue.qml</file> <file>qml/BigIntValue.qml</file>
<file>qml/CallStack.qml</file> <file>qml/CallStack.qml</file>
@ -47,7 +48,8 @@
<file>qml/StatusPaneStyle.qml</file> <file>qml/StatusPaneStyle.qml</file>
<file>qml/StepActionImage.qml</file> <file>qml/StepActionImage.qml</file>
<file>qml/StorageView.qml</file> <file>qml/StorageView.qml</file>
<file>qml/StructView.qml</file> <file>qml/StatesComboBox.qml</file>
<file>qml/StructView.qml</file>
<file>qml/Style.qml</file> <file>qml/Style.qml</file>
<file>qml/TabStyle.qml</file> <file>qml/TabStyle.qml</file>
<file>qml/TransactionDialog.qml</file> <file>qml/TransactionDialog.qml</file>
@ -59,8 +61,5 @@
<file>qml/js/ProjectModel.js</file> <file>qml/js/ProjectModel.js</file>
<file>qml/js/QEtherHelper.js</file> <file>qml/js/QEtherHelper.js</file>
<file>qml/js/TransactionHelper.js</file> <file>qml/js/TransactionHelper.js</file>
<file>qml/main.qml</file>
<file>qml/qmldir</file>
<file>qml/StatesComboBox.qml</file>
</qresource> </qresource>
</RCC> </RCC>

15
mix/qml/main.qml → mix/qml/Application.qml

@ -11,6 +11,7 @@ import org.ethereum.qml.CodeModel 1.0
import org.ethereum.qml.ClientModel 1.0 import org.ethereum.qml.ClientModel 1.0
import org.ethereum.qml.FileIo 1.0 import org.ethereum.qml.FileIo 1.0
import org.ethereum.qml.Clipboard 1.0 import org.ethereum.qml.Clipboard 1.0
import org.ethereum.qml.ApplicationService 1.0
ApplicationWindow { ApplicationWindow {
@ -22,6 +23,16 @@ ApplicationWindow {
minimumWidth: 400 minimumWidth: 400
minimumHeight: 300 minimumHeight: 300
title: qsTr("Mix") title: qsTr("Mix")
property alias systemPointSize: appService.systemPointSize;
property alias mainContent: mainContent;
property alias codeModel: codeModel;
property alias clientModel: clientModel;
property alias projectModel: projectModel;
property alias appService: appService;
ApplicationService {
id: appService
}
CodeModel { CodeModel {
id: codeModel id: codeModel
@ -44,6 +55,10 @@ ApplicationWindow {
id: clipboard id: clipboard
} }
Style {
id: appStyle
}
Connections { Connections {
target: mainApplication target: mainApplication
onClosing: onClosing:

5
mix/qml/CallStack.qml

@ -2,7 +2,6 @@ import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import "."
DebugInfoList DebugInfoList
{ {
@ -37,7 +36,7 @@ DebugInfoList
anchors.leftMargin: 5 anchors.leftMargin: 5
color: "#4a4a4a" color: "#4a4a4a"
text: styleData.row; text: styleData.row;
font.pointSize: DebuggerPaneStyle.general.basicFontSize font.pointSize: dbgStyle.general.basicFontSize
width: parent.width - 5 width: parent.width - 5
elide: Text.ElideRight elide: Text.ElideRight
} }
@ -58,7 +57,7 @@ DebugInfoList
color: "#4a4a4a" color: "#4a4a4a"
text: styleData.value; text: styleData.value;
elide: Text.ElideRight elide: Text.ElideRight
font.pointSize: DebuggerPaneStyle.general.basicFontSize font.pointSize: dbgStyle.general.basicFontSize
} }
} }
} }

51
mix/qml/CodeEditor.qml

@ -24,31 +24,36 @@ Item {
id: contentView id: contentView
width: parent.width width: parent.width
height: parent.height * 0.7 height: parent.height * 0.7
Rectangle {
id: lineColumn CodeEditorStyle {
property int rowHeight: codeEditor.font.pixelSize + 3 id: style
color: "#202020" }
width: 50
height: parent.height Rectangle {
Column { id: lineColumn
y: -codeEditor.flickableItem.contentY + 4 property int rowHeight: codeEditor.font.pixelSize + 3
width: parent.width color: "#202020"
Repeater { width: 50
model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) height: parent.height
delegate: Text { Column {
id: text y: -codeEditor.flickableItem.contentY + 4
color: codeEditor.textColor width: parent.width
font: codeEditor.font Repeater {
width: lineColumn.width - 4 model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight))
horizontalAlignment: Text.AlignRight delegate: Text {
verticalAlignment: Text.AlignVCenter id: text
height: lineColumn.rowHeight color: codeEditor.textColor
renderType: Text.NativeRendering font: codeEditor.font
text: index + 1 width: lineColumn.width - 4
} horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
height: lineColumn.rowHeight
renderType: Text.NativeRendering
text: index + 1
} }
} }
} }
}
TextArea { TextArea {
id: codeEditor id: codeEditor
@ -66,7 +71,7 @@ Item {
height: parent.height height: parent.height
font.family: "Monospace" font.family: "Monospace"
font.pointSize: CodeEditorStyle.general.basicFontSize font.pointSize: style.general.basicFontSize
width: parent.width width: parent.width
tabChangesFocus: false tabChangesFocus: false

1
mix/qml/CodeEditorStyle.qml

@ -1,4 +1,3 @@
pragma Singleton
import QtQuick 2.0 import QtQuick 2.0
QtObject { QtObject {

45
mix/qml/CodeEditorView.qml

@ -7,13 +7,15 @@ import QtQuick.Dialogs 1.1
Item { Item {
id: codeEditorView id: codeEditorView
property string currentDocumentId: "" property string currentDocumentId: ""
property int openDocCount: 0
signal documentEdit(string documentId) signal documentEdit(string documentId)
signal breakpointsChanged(string documentId) signal breakpointsChanged(string documentId)
signal isCleanChanged(var isClean, string documentId) signal isCleanChanged(var isClean, string documentId)
signal loadComplete
function getDocumentText(documentId) { function getDocumentText(documentId) {
for (var i = 0; i < editorListModel.count; i++) { for (var i = 0; i < openDocCount; i++) {
if (editorListModel.get(i).documentId === documentId) { if (editorListModel.get(i).documentId === documentId) {
return editors.itemAt(i).item.getText(); return editors.itemAt(i).item.getText();
} }
@ -22,7 +24,7 @@ Item {
} }
function isDocumentOpen(documentId) { function isDocumentOpen(documentId) {
for (var i = 0; i < editorListModel.count; i++) for (var i = 0; i < openDocCount; i++)
if (editorListModel.get(i).documentId === documentId && if (editorListModel.get(i).documentId === documentId &&
editors.itemAt(i).item) editors.itemAt(i).item)
return true; return true;
@ -35,15 +37,27 @@ Item {
} }
function loadDocument(document) { function loadDocument(document) {
for (var i = 0; i < editorListModel.count; i++) for (var i = 0; i < openDocCount; i++)
if (editorListModel.get(i).documentId === document.documentId) if (editorListModel.get(i).documentId === document.documentId)
return; //already open return; //already open
editorListModel.append(document); if (editorListModel.count <= openDocCount)
editorListModel.append(document);
else
{
editorListModel.set(openDocCount, document);
editors.itemAt(openDocCount).visible = true;
doLoadDocument(editors.itemAt(openDocCount).item, editorListModel.get(openDocCount))
}
openDocCount++;
} }
function doLoadDocument(editor, document) { function doLoadDocument(editor, document) {
var data = fileIo.readFile(document.path); var data = fileIo.readFile(document.path);
editor.onLoadComplete.connect(function() {
loadComplete();
});
editor.onEditorTextChanged.connect(function() { editor.onEditorTextChanged.connect(function() {
documentEdit(document.documentId); documentEdit(document.documentId);
if (document.isContract) if (document.isContract)
@ -60,7 +74,7 @@ Item {
} }
function getEditor(documentId) { function getEditor(documentId) {
for (var i = 0; i < editorListModel.count; i++) for (var i = 0; i < openDocCount; i++)
{ {
if (editorListModel.get(i).documentId === documentId) if (editorListModel.get(i).documentId === documentId)
return editors.itemAt(i).item; return editors.itemAt(i).item;
@ -91,7 +105,7 @@ Item {
} }
function editingContract() { function editingContract() {
for (var i = 0; i < editorListModel.count; i++) for (var i = 0; i < openDocCount; i++)
if (editorListModel.get(i).documentId === currentDocumentId) if (editorListModel.get(i).documentId === currentDocumentId)
return editorListModel.get(i).isContract; return editorListModel.get(i).isContract;
return false; return false;
@ -99,7 +113,7 @@ Item {
function getBreakpoints() { function getBreakpoints() {
var bpMap = {}; var bpMap = {};
for (var i = 0; i < editorListModel.count; i++) { for (var i = 0; i < openDocCount; i++) {
var documentId = editorListModel.get(i).documentId; var documentId = editorListModel.get(i).documentId;
var editor = editors.itemAt(i).item; var editor = editors.itemAt(i).item;
if (editor) { if (editor) {
@ -130,7 +144,7 @@ Item {
} }
onProjectSaving: { onProjectSaving: {
for (var i = 0; i < editorListModel.count; i++) for (var i = 0; i < openDocCount; i++)
{ {
var doc = editorListModel.get(i); var doc = editorListModel.get(i);
var editor = editors.itemAt(i).item; var editor = editors.itemAt(i).item;
@ -142,7 +156,7 @@ Item {
onProjectSaved: { onProjectSaved: {
if (projectModel.appIsClosing || projectModel.projectIsClosing) if (projectModel.appIsClosing || projectModel.projectIsClosing)
return; return;
for (var i = 0; i < editorListModel.count; i++) for (var i = 0; i < openDocCount; i++)
{ {
var doc = editorListModel.get(i); var doc = editorListModel.get(i);
resetEditStatus(doc.documentId); resetEditStatus(doc.documentId);
@ -152,8 +166,9 @@ Item {
onProjectClosed: { onProjectClosed: {
for (var i = 0; i < editorListModel.count; i++) for (var i = 0; i < editorListModel.count; i++)
editors.itemAt(i).visible = false; editors.itemAt(i).visible = false;
editorListModel.clear(); //editorListModel.clear();
currentDocumentId = ""; currentDocumentId = "";
openDocCount = 0;
} }
onDocumentSaved: { onDocumentSaved: {
@ -177,6 +192,11 @@ Item {
} }
} }
CodeEditorStyle
{
id: style;
}
MessageDialog MessageDialog
{ {
id: messageDialog id: messageDialog
@ -194,12 +214,15 @@ Item {
Repeater { Repeater {
id: editors id: editors
model: editorListModel model: editorListModel
onItemRemoved: {
item.item.unloaded = true;
}
delegate: Loader { delegate: Loader {
id: loader id: loader
active: false active: false
asynchronous: true asynchronous: true
anchors.fill: parent anchors.fill: parent
source: "CodeEditor.qml" source: appService.haveWebEngine ? "WebCodeEditor.qml" : "CodeEditor.qml"
visible: (index >= 0 && index < editorListModel.count && currentDocumentId === editorListModel.get(index).documentId) visible: (index >= 0 && index < editorListModel.count && currentDocumentId === editorListModel.get(index).documentId)
property bool changed: false property bool changed: false
onVisibleChanged: { onVisibleChanged: {

2
mix/qml/CommonSeparator.qml

@ -4,6 +4,6 @@ import "."
Rectangle Rectangle
{ {
height: 1 height: 1
color: Style.generic.layout.separatorColor color: appStyle.generic.layout.separatorColor
} }

12
mix/qml/Debugger.qml

@ -65,6 +65,10 @@ Rectangle {
Debugger.setBreakpoints(bp); Debugger.setBreakpoints(bp);
} }
DebuggerPaneStyle {
id: dbgStyle
}
Connections { Connections {
target: clientModel target: clientModel
onDebugDataReady: { onDebugDataReady: {
@ -449,7 +453,7 @@ Rectangle {
color: "#b2b3ae" color: "#b2b3ae"
text: styleData.value.split(' ')[0] text: styleData.value.split(' ')[0]
font.family: "monospace" font.family: "monospace"
font.pointSize: DebuggerPaneStyle.general.basicFontSize font.pointSize: dbgStyle.general.basicFontSize
wrapMode: Text.NoWrap wrapMode: Text.NoWrap
id: id id: id
} }
@ -459,7 +463,7 @@ Rectangle {
color: styleData.selected ? "white" : "black" color: styleData.selected ? "white" : "black"
font.family: "monospace" font.family: "monospace"
text: styleData.value.replace(styleData.value.split(' ')[0], '') text: styleData.value.replace(styleData.value.split(' ')[0], '')
font.pointSize: DebuggerPaneStyle.general.basicFontSize font.pointSize: dbgStyle.general.basicFontSize
} }
} }
} }
@ -532,7 +536,7 @@ Rectangle {
font.family: "monospace" font.family: "monospace"
color: "#4a4a4a" color: "#4a4a4a"
text: styleData.row; text: styleData.row;
font.pointSize: DebuggerPaneStyle.general.basicFontSize font.pointSize: dbgStyle.general.basicFontSize
} }
} }
@ -550,7 +554,7 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a" color: "#4a4a4a"
text: styleData.value text: styleData.value
font.pointSize: DebuggerPaneStyle.general.basicFontSize font.pointSize: dbgStyle.general.basicFontSize
} }
} }
} }

1
mix/qml/DebuggerPaneStyle.qml

@ -1,4 +1,3 @@
pragma Singleton
import QtQuick 2.0 import QtQuick 2.0
QtObject { QtObject {

1
mix/qml/DefaultLabel.qml

@ -1,6 +1,5 @@
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import "."
Label { Label {
text: text text: text

4
mix/qml/DeploymentDialog.qml

@ -31,7 +31,7 @@ Window {
property string currentAccount property string currentAccount
property alias gasToUse: gasToUseInput.text property alias gasToUse: gasToUseInput.text
color: Style.generic.layout.backgroundColor color: appStyle.generic.layout.backgroundColor
function close() function close()
{ {
@ -329,7 +329,7 @@ Window {
anchors.verticalCenter: parent.verticalCenter; anchors.verticalCenter: parent.verticalCenter;
anchors.left: applicationUrlEth.right anchors.left: applicationUrlEth.right
font.italic: true font.italic: true
font.pointSize: Style.absoluteSize(-1) font.pointSize: appStyle.absoluteSize(-1)
} }
} }
} }

30
mix/qml/FilesSection.qml

@ -18,21 +18,21 @@ Rectangle
property string sectionName; property string sectionName;
property variant selManager; property variant selManager;
property int index; property int index;
color: index % 2 === 0 ? "transparent" : ProjectFilesStyle.title.background color: index % 2 === 0 ? "transparent" : projectFilesStyle.title.background
function hiddenHeightTopLevel() function hiddenHeightTopLevel()
{ {
return section.state === "hidden" ? ProjectFilesStyle.documentsList.height : ProjectFilesStyle.documentsList.fileNameHeight * model.count + ProjectFilesStyle.documentsList.height; return section.state === "hidden" ? projectFilesStyle.documentsList.height : projectFilesStyle.documentsList.fileNameHeight * model.count + projectFilesStyle.documentsList.height;
} }
function hiddenHeightRepeater() function hiddenHeightRepeater()
{ {
return section.state === "hidden" ? 0 : ProjectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count; return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count;
} }
function hiddenHeightElement() function hiddenHeightElement()
{ {
return section.state === "hidden" ? 0 : ProjectFilesStyle.documentsList.fileNameHeight; return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight;
} }
function getDocumentIndex(documentId) function getDocumentIndex(documentId)
@ -68,7 +68,7 @@ Rectangle
{ {
anchors.top: parent.top anchors.top: parent.top
id: rowCol id: rowCol
height: ProjectFilesStyle.documentsList.height height: projectFilesStyle.documentsList.height
Layout.fillWidth: true Layout.fillWidth: true
@ -88,15 +88,15 @@ Rectangle
id: section id: section
text: sectionName text: sectionName
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: ProjectFilesStyle.general.leftMargin anchors.leftMargin: projectFilesStyle.general.leftMargin
color: ProjectFilesStyle.documentsList.sectionColor color: projectFilesStyle.documentsList.sectionColor
font.family: boldFont.name font.family: boldFont.name
font.pointSize: ProjectFilesStyle.documentsList.sectionFontSize font.pointSize: projectFilesStyle.documentsList.sectionFontSize
states: [ states: [
State { State {
name: "hidden" name: "hidden"
PropertyChanges { target: filesList; visible: false; } PropertyChanges { target: filesList; visible: false; }
PropertyChanges { target: rowCol; Layout.minimumHeight: ProjectFilesStyle.documentsList.height; Layout.maximumHeight: ProjectFilesStyle.documentsList.height; height: ProjectFilesStyle.documentsList.height; } PropertyChanges { target: rowCol; Layout.minimumHeight: projectFilesStyle.documentsList.height; Layout.maximumHeight: projectFilesStyle.documentsList.height; height: projectFilesStyle.documentsList.height; }
PropertyChanges { target: imgArrow; source: "qrc:/qml/img/closedtriangleindicator_filesproject.png" } PropertyChanges { target: imgArrow; source: "qrc:/qml/img/closedtriangleindicator_filesproject.png" }
} }
] ]
@ -138,7 +138,7 @@ Rectangle
Layout.preferredHeight: wrapperItem.hiddenHeightElement() Layout.preferredHeight: wrapperItem.hiddenHeightElement()
Layout.maximumHeight: wrapperItem.hiddenHeightElement() Layout.maximumHeight: wrapperItem.hiddenHeightElement()
height: wrapperItem.hiddenHeightElement() height: wrapperItem.hiddenHeightElement()
color: isSelected ? ProjectFilesStyle.documentsList.highlightColor : "transparent" color: isSelected ? projectFilesStyle.documentsList.highlightColor : "transparent"
property bool isSelected property bool isSelected
property bool renameMode property bool renameMode
@ -147,15 +147,15 @@ Rectangle
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.fill: parent anchors.fill: parent
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: ProjectFilesStyle.general.leftMargin + 2 anchors.leftMargin: projectFilesStyle.general.leftMargin + 2
Text { Text {
id: nameText id: nameText
height: parent.height height: parent.height
visible: !renameMode visible: !renameMode
color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color
text: name; text: name;
font.family: fileNameFont.name font.family: fileNameFont.name
font.pointSize: ProjectFilesStyle.documentsList.fontSize font.pointSize: projectFilesStyle.documentsList.fontSize
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
Connections Connections
@ -182,7 +182,7 @@ Rectangle
DefaultLabel { DefaultLabel {
id: editStatusLabel id: editStatusLabel
visible: false visible: false
color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
text: "*" text: "*"
width: 10 width: 10
@ -196,7 +196,7 @@ Rectangle
visible: renameMode visible: renameMode
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: ProjectFilesStyle.general.leftMargin anchors.leftMargin: projectFilesStyle.general.leftMargin
MouseArea { MouseArea {
id: textMouseArea id: textMouseArea
anchors.fill: parent anchors.fill: parent

4
mix/qml/ItemDelegateDataDump.qml

@ -28,7 +28,7 @@ Rectangle {
font.bold: true font.bold: true
color: "#4a4a4a" color: "#4a4a4a"
text: modelData[0] text: modelData[0]
font.pointSize: DebuggerPaneStyle.general.dataDumpFontSize; font.pointSize: dbgStyle.general.dataDumpFontSize;
} }
} }
@ -47,7 +47,7 @@ Rectangle {
anchors.leftMargin: 4 anchors.leftMargin: 4
color: "#4a4a4a" color: "#4a4a4a"
text: modelData[1] text: modelData[1]
font.pointSize: DebuggerPaneStyle.general.dataDumpFontSize font.pointSize: dbgStyle.general.dataDumpFontSize
} }
} }
} }

115
mix/qml/LogsPane.qml

@ -3,7 +3,6 @@ import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3 import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.SortFilterProxyModel 1.0 import org.ethereum.qml.SortFilterProxyModel 1.0
import "."
Rectangle Rectangle
{ {
@ -28,6 +27,10 @@ Rectangle
} }
} }
LogsPaneStyle {
id: logStyle
}
anchors.fill: parent anchors.fill: parent
radius: 10 radius: 10
color: "transparent" color: "transparent"
@ -99,15 +102,15 @@ Rectangle
Rectangle Rectangle
{ {
width: LogsPaneStyle.generic.layout.dateWidth + LogsPaneStyle.generic.layout.contentWidth + LogsPaneStyle.generic.layout.typeWidth width: logStyle.generic.layout.dateWidth + logStyle.generic.layout.contentWidth + logStyle.generic.layout.typeWidth
height: 30 height: 30
color: color:
{ {
var cl; var cl;
if (level === "warning" || level === "error") if (level === "warning" || level === "error")
cl = LogsPaneStyle.generic.layout.errorColor; cl = logStyle.generic.layout.errorColor;
else else
cl = index % 2 === 0 ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor; cl = index % 2 === 0 ? "transparent" : logStyle.generic.layout.logAlternateColor;
if (index === 0) if (index === 0)
logsRepeater.frontColor = cl; logsRepeater.frontColor = cl;
return cl; return cl;
@ -137,9 +140,9 @@ Rectangle
DefaultLabel { DefaultLabel {
text: date; text: date;
font.family: LogsPaneStyle.generic.layout.logLabelFont font.family: logStyle.generic.layout.logLabelFont
width: LogsPaneStyle.generic.layout.dateWidth width: logStyle.generic.layout.dateWidth
font.pointSize: Style.absoluteSize(-1) font.pointSize: appStyle.absoluteSize(-1)
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 15 anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -150,9 +153,9 @@ Rectangle
DefaultLabel { DefaultLabel {
text: type; text: type;
font.family: LogsPaneStyle.generic.layout.logLabelFont font.family: logStyle.generic.layout.logLabelFont
width: LogsPaneStyle.generic.layout.typeWidth width: logStyle.generic.layout.typeWidth
font.pointSize: Style.absoluteSize(-1) font.pointSize: appStyle.absoluteSize(-1)
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 100 anchors.leftMargin: 100
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -164,9 +167,9 @@ Rectangle
Text { Text {
id: logContent id: logContent
text: content; text: content;
font.family: LogsPaneStyle.generic.layout.logLabelFont font.family: logStyle.generic.layout.logLabelFont
width: LogsPaneStyle.generic.layout.contentWidth width: logStyle.generic.layout.contentWidth
font.pointSize: Style.absoluteSize(-1) font.pointSize: appStyle.absoluteSize(-1)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight elide: Text.ElideRight
anchors.left: parent.left anchors.left: parent.left
@ -194,8 +197,8 @@ Rectangle
id: itemDelegate id: itemDelegate
DefaultLabel { DefaultLabel {
text: styleData.value; text: styleData.value;
font.family: LogsPaneStyle.generic.layout.logLabelFont font.family: logStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-1) font.pointSize: appStyle.absoluteSize(-1)
color: { color: {
if (proxyModel.get(styleData.row).level === "error") if (proxyModel.get(styleData.row).level === "error")
return "red"; return "red";
@ -214,16 +217,16 @@ Rectangle
GradientStop { position: 0.0; color: "#f1f1f1" } GradientStop { position: 0.0; color: "#f1f1f1" }
GradientStop { position: 1.0; color: "#d9d7da" } GradientStop { position: 1.0; color: "#d9d7da" }
} }
Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight Layout.preferredHeight: logStyle.generic.layout.headerHeight
height: LogsPaneStyle.generic.layout.headerHeight height: logStyle.generic.layout.headerHeight
width: logsPane.width width: logsPane.width
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
Row Row
{ {
id: rowAction id: rowAction
anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin anchors.leftMargin: logStyle.generic.layout.leftMargin
anchors.left: parent.left anchors.left: parent.left
spacing: LogsPaneStyle.generic.layout.headerButtonSpacing spacing: logStyle.generic.layout.headerButtonSpacing
height: parent.height height: parent.height
Rectangle Rectangle
{ {
@ -233,9 +236,9 @@ Rectangle
DefaultLabel DefaultLabel
{ {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: LogsPaneStyle.generic.layout.logLabelColor color: logStyle.generic.layout.logLabelColor
font.pointSize: Style.absoluteSize(-3) font.pointSize: appStyle.absoluteSize(-3)
font.family: LogsPaneStyle.generic.layout.logLabelFont font.family: logStyle.generic.layout.logLabelFont
text: qsTr("Show:") text: qsTr("Show:")
} }
} }
@ -244,20 +247,20 @@ Rectangle
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 1; width: 1;
height: parent.height height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 color: logStyle.generic.layout.buttonSeparatorColor1
} }
Rectangle { Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 2; width: 2;
height: parent.height height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 color: logStyle.generic.layout.buttonSeparatorColor2
} }
ToolButton { ToolButton {
id: javascriptButton id: javascriptButton
checkable: true checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight height: logStyle.generic.layout.headerButtonHeight
width: 20 width: 20
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
checked: true checked: true
@ -270,16 +273,16 @@ Rectangle
label: label:
Item { Item {
DefaultLabel { DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont font.family: logStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3) font.pointSize: appStyle.absoluteSize(-3)
color: LogsPaneStyle.generic.layout.logLabelColor color: logStyle.generic.layout.logLabelColor
anchors.centerIn: parent anchors.centerIn: parent
text: qsTr("JS") text: qsTr("JS")
} }
} }
background: background:
Rectangle { Rectangle {
color: javascriptButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent" color: javascriptButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
} }
} }
} }
@ -288,20 +291,20 @@ Rectangle
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 1; width: 1;
height: parent.height height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 color: logStyle.generic.layout.buttonSeparatorColor1
} }
Rectangle { Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 2; width: 2;
height: parent.height height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 color: logStyle.generic.layout.buttonSeparatorColor2
} }
ToolButton { ToolButton {
id: runButton id: runButton
checkable: true checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight height: logStyle.generic.layout.headerButtonHeight
width: 30 width: 30
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
checked: true checked: true
@ -314,16 +317,16 @@ Rectangle
label: label:
Item { Item {
DefaultLabel { DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont font.family: logStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3) font.pointSize: appStyle.absoluteSize(-3)
color: LogsPaneStyle.generic.layout.logLabelColor color: logStyle.generic.layout.logLabelColor
anchors.centerIn: parent anchors.centerIn: parent
text: qsTr("Run") text: qsTr("Run")
} }
} }
background: background:
Rectangle { Rectangle {
color: runButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent" color: runButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
} }
} }
} }
@ -332,20 +335,20 @@ Rectangle
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 1; width: 1;
height: parent.height height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 color: logStyle.generic.layout.buttonSeparatorColor1
} }
Rectangle { Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 2; width: 2;
height: parent.height height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 color: logStyle.generic.layout.buttonSeparatorColor2
} }
ToolButton { ToolButton {
id: stateButton id: stateButton
checkable: true checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 35 width: 35
checked: true checked: true
@ -358,16 +361,16 @@ Rectangle
label: label:
Item { Item {
DefaultLabel { DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont font.family: logStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3) font.pointSize: appStyle.absoluteSize(-3)
color: LogsPaneStyle.generic.layout.logLabelColor color: logStyle.generic.layout.logLabelColor
anchors.centerIn: parent anchors.centerIn: parent
text: qsTr("State") text: qsTr("State")
} }
} }
background: background:
Rectangle { Rectangle {
color: stateButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent" color: stateButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
} }
} }
} }
@ -376,14 +379,14 @@ Rectangle
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 1; width: 1;
height: parent.height height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 color: logStyle.generic.layout.buttonSeparatorColor1
} }
Rectangle { Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 2; width: 2;
height: parent.height height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 color: logStyle.generic.layout.buttonSeparatorColor2
} }
} }
@ -395,7 +398,7 @@ Rectangle
spacing: 10 spacing: 10
Rectangle Rectangle
{ {
height: LogsPaneStyle.generic.layout.headerButtonHeight height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: "transparent" color: "transparent"
width: 20 width: 20
@ -410,8 +413,8 @@ Rectangle
ButtonStyle { ButtonStyle {
background: background:
Rectangle { Rectangle {
height: LogsPaneStyle.generic.layout.headerButtonHeight height: logStyle.generic.layout.headerButtonHeight
implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight implicitHeight: logStyle.generic.layout.headerButtonHeight
color: "transparent" color: "transparent"
} }
} }
@ -438,7 +441,7 @@ Rectangle
Rectangle Rectangle
{ {
height: LogsPaneStyle.generic.layout.headerButtonHeight height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: "transparent" color: "transparent"
width: 20 width: 20
@ -453,8 +456,8 @@ Rectangle
ButtonStyle { ButtonStyle {
background: background:
Rectangle { Rectangle {
height: LogsPaneStyle.generic.layout.headerButtonHeight height: logStyle.generic.layout.headerButtonHeight
implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight implicitHeight: logStyle.generic.layout.headerButtonHeight
color: "transparent" color: "transparent"
} }
} }
@ -510,8 +513,8 @@ Rectangle
width: 100 width: 100
anchors.left: searchImg.right anchors.left: searchImg.right
anchors.leftMargin: -7 anchors.leftMargin: -7
font.family: LogsPaneStyle.generic.layout.logLabelFont font.family: logStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3) font.pointSize: appStyle.absoluteSize(-3)
font.italic: true font.italic: true
text: qsTr(" - Search - ") text: qsTr(" - Search - ")
onFocusChanged: onFocusChanged:
@ -540,7 +543,7 @@ Rectangle
Rectangle Rectangle
{ {
height: LogsPaneStyle.generic.layout.headerButtonHeight height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: "transparent" color: "transparent"
width: 20 width: 20
@ -555,8 +558,8 @@ Rectangle
ButtonStyle { ButtonStyle {
background: background:
Rectangle { Rectangle {
height: LogsPaneStyle.generic.layout.headerButtonHeight height: logStyle.generic.layout.headerButtonHeight
implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight implicitHeight: logStyle.generic.layout.headerButtonHeight
color: "transparent" color: "transparent"
} }
} }

1
mix/qml/LogsPaneStyle.qml

@ -1,4 +1,3 @@
pragma Singleton
import QtQuick 2.0 import QtQuick 2.0
QtObject { QtObject {

1
mix/qml/MainContent.qml

@ -22,6 +22,7 @@ Rectangle {
property alias rightViewVisible: rightView.visible property alias rightViewVisible: rightView.visible
property alias webViewVisible: webPreview.visible property alias webViewVisible: webPreview.visible
property alias webView: webPreview
property alias projectViewVisible: projectList.visible property alias projectViewVisible: projectList.visible
property alias runOnProjectLoad: mainSettings.runOnProjectLoad property alias runOnProjectLoad: mainSettings.runOnProjectLoad
property alias rightPane: rightView property alias rightPane: rightView

1
mix/qml/NewProjectDialog.qml

@ -15,6 +15,7 @@ Window {
property alias projectTitle: titleField.text property alias projectTitle: titleField.text
readonly property string projectPath: "file://" + pathField.text readonly property string projectPath: "file://" + pathField.text
property alias pathFieldText: pathField.text
signal accepted signal accepted
function open() { function open() {

1
mix/qml/ProjectFilesStyle.qml

@ -1,4 +1,3 @@
pragma Singleton
import QtQuick 2.0 import QtQuick 2.0
QtObject { QtObject {

21
mix/qml/ProjectList.qml

@ -8,6 +8,11 @@ import "."
Item { Item {
property bool renameMode: false; property bool renameMode: false;
ProjectFilesStyle {
id: projectFilesStyle
}
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
id: filesCol id: filesCol
@ -20,8 +25,8 @@ Item {
Rectangle Rectangle
{ {
color: ProjectFilesStyle.title.background color: projectFilesStyle.title.background
height: ProjectFilesStyle.title.height height: projectFilesStyle.title.height
Layout.fillWidth: true Layout.fillWidth: true
Image { Image {
id: projectIcon id: projectIcon
@ -37,14 +42,14 @@ Item {
Text Text
{ {
id: projectTitle id: projectTitle
color: ProjectFilesStyle.title.color color: projectFilesStyle.title.color
text: projectModel.projectTitle text: projectModel.projectTitle
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: !projectModel.isEmpty; visible: !projectModel.isEmpty;
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: ProjectFilesStyle.general.leftMargin anchors.leftMargin: projectFilesStyle.general.leftMargin
font.family: srcSansProLight.name font.family: srcSansProLight.name
font.pointSize: ProjectFilesStyle.title.fontSize font.pointSize: projectFilesStyle.title.fontSize
font.weight: Font.Light font.weight: Font.Light
} }
@ -54,7 +59,7 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 15 anchors.rightMargin: 15
font.family: srcSansProLight.name font.family: srcSansProLight.name
font.pointSize: ProjectFilesStyle.title.fontSize font.pointSize: projectFilesStyle.title.fontSize
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
font.weight: Font.Light font.weight: Font.Light
} }
@ -64,14 +69,14 @@ Item {
{ {
Layout.fillWidth: true Layout.fillWidth: true
height: 3 height: 3
color: ProjectFilesStyle.documentsList.background color: projectFilesStyle.documentsList.background
} }
Rectangle Rectangle
{ {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
color: ProjectFilesStyle.documentsList.background color: projectFilesStyle.documentsList.background
ColumnLayout ColumnLayout
{ {

2
mix/qml/ProjectModel.qml

@ -43,8 +43,10 @@ Item {
property string deploymentDir property string deploymentDir
property var listModel: projectListModel property var listModel: projectListModel
property var stateListModel: projectStateListModel.model property var stateListModel: projectStateListModel.model
property alias stateDialog: projectStateListModel.stateDialog
property CodeEditorView codeEditor: null property CodeEditorView codeEditor: null
property var unsavedFiles: [] property var unsavedFiles: []
property alias newProjectDialog: newProjectDialog
//interface //interface
function saveAll() { ProjectModelCode.saveAll(); } function saveAll() { ProjectModelCode.saveAll(); }

3
mix/qml/Splitter.qml

@ -9,6 +9,3 @@ SplitView
color: "#cccccc" color: "#cccccc"
} }
} }

16
mix/qml/StateDialog.qml

@ -17,10 +17,12 @@ Window {
height: 480 height: 480
title: qsTr("Edit State") title: qsTr("Edit State")
visible: false visible: false
color: StateDialogStyle.generic.backgroundColor color: stateDialogStyle.generic.backgroundColor
property alias stateTitle: titleField.text property alias stateTitle: titleField.text
property alias isDefault: defaultCheckBox.checked property alias isDefault: defaultCheckBox.checked
property alias model: transactionsModel
property alias transactionDialog: transactionDialog
property int stateIndex property int stateIndex
property var stateTransactions: [] property var stateTransactions: []
property var stateAccounts: [] property var stateAccounts: []
@ -56,6 +58,11 @@ Window {
forceActiveFocus(); forceActiveFocus();
} }
function acceptAndClose() {
close();
accepted();
}
function close() { function close() {
visible = false; visible = false;
} }
@ -71,6 +78,10 @@ Window {
return item; return item;
} }
StateDialogStyle {
id: stateDialogStyle
}
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 10 anchors.margins: 10
@ -292,8 +303,7 @@ Window {
Button { Button {
text: qsTr("OK"); text: qsTr("OK");
onClicked: { onClicked: {
close(); acceptAndClose();
accepted();
} }
} }
Button { Button {

1
mix/qml/StateDialogStyle.qml

@ -1,4 +1,3 @@
pragma Singleton
import QtQuick 2.0 import QtQuick 2.0
QtObject { QtObject {

1
mix/qml/StateListModel.qml

@ -12,6 +12,7 @@ Item {
property alias model: stateListModel property alias model: stateListModel
property var stateList: [] property var stateList: []
property alias stateDialog: stateDialog
property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project
function fromPlainStateItem(s) { function fromPlainStateItem(s) {

6
mix/qml/StatusPane.qml

@ -54,6 +54,10 @@ Rectangle {
currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "error" } currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "error" }
} }
StatusPaneStyle {
id: statusPaneStyle
}
Connections { Connections {
target: webPreview target: webPreview
onJavaScriptMessage: onJavaScriptMessage:
@ -123,7 +127,7 @@ Rectangle {
Text { Text {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: Style.absoluteSize(-1) font.pointSize: appStyle.absoluteSize(-1)
height: 15 height: 15
font.family: "sans serif" font.family: "sans serif"
objectName: "status" objectName: "status"

1
mix/qml/StatusPaneStyle.qml

@ -1,4 +1,3 @@
pragma Singleton
import QtQuick 2.0 import QtQuick 2.0
QtObject { QtObject {

4
mix/qml/StorageView.qml

@ -29,7 +29,7 @@ DebugInfoList
anchors.leftMargin: 5 anchors.leftMargin: 5
color: "#4a4a4a" color: "#4a4a4a"
text: styleData.value.split('\t')[0]; text: styleData.value.split('\t')[0];
font.pointSize: DebuggerPaneStyle.general.basicFontSize font.pointSize: dbgStyle.general.basicFontSize
width: parent.width - 5 width: parent.width - 5
elide: Text.ElideRight elide: Text.ElideRight
} }
@ -52,7 +52,7 @@ DebugInfoList
color: "#4a4a4a" color: "#4a4a4a"
text: styleData.value.split('\t')[1]; text: styleData.value.split('\t')[1];
elide: Text.ElideRight elide: Text.ElideRight
font.pointSize: DebuggerPaneStyle.general.basicFontSize font.pointSize: dbgStyle.general.basicFontSize
} }
} }
} }

1
mix/qml/Style.qml

@ -1,4 +1,3 @@
pragma Singleton
import QtQuick 2.0 import QtQuick 2.0
QtObject { QtObject {

45
mix/qml/TransactionDialog.qml

@ -13,7 +13,7 @@ Window {
width: 520 width: 520
height: 500; height: 500;
visible: false visible: false
color: StateDialogStyle.generic.backgroundColor color: transactionDialogStyle.generic.backgroundColor
title: qsTr("Edit Transaction") title: qsTr("Edit Transaction")
property int transactionIndex property int transactionIndex
property alias gas: gasValueEdit.gasValue; property alias gas: gasValueEdit.gasValue;
@ -59,16 +59,7 @@ Window {
contractComboBox.currentIndex = contractIndex; contractComboBox.currentIndex = contractIndex;
loadFunctions(contractComboBox.currentValue()); loadFunctions(contractComboBox.currentValue());
selectFunction(functionId);
var functionIndex = -1;
for (var f = 0; f < functionsModel.count; f++)
if (functionsModel.get(f).text === item.functionId)
functionIndex = f;
if (functionIndex == -1 && functionsModel.count > 0)
functionIndex = 0; //@todo suggest unused function
functionComboBox.currentIndex = functionIndex;
paramsModel = []; paramsModel = [];
if (functionId !== contractComboBox.currentValue()) if (functionId !== contractComboBox.currentValue())
@ -87,9 +78,6 @@ Window {
visible = true; visible = true;
valueField.focus = true; valueField.focus = true;
modalTransactionDialog.height = (paramsModel.length > 0 ? 500 : 300);
paramLabel.visible = paramsModel.length > 0;
paramScroll.visible = paramsModel.length > 0;
} }
function loadFunctions(contractId) function loadFunctions(contractId)
@ -107,6 +95,19 @@ Window {
} }
function selectFunction(functionId)
{
var functionIndex = -1;
for (var f = 0; f < functionsModel.count; f++)
if (functionsModel.get(f).text === functionId)
functionIndex = f;
if (functionIndex == -1 && functionsModel.count > 0)
functionIndex = 0; //@todo suggest unused function
functionComboBox.currentIndex = functionIndex;
}
function loadParameter(parameter) function loadParameter(parameter)
{ {
var type = parameter.type; var type = parameter.type;
@ -136,6 +137,15 @@ Window {
typeLoader.members = [] typeLoader.members = []
typeLoader.value = paramValues; typeLoader.value = paramValues;
typeLoader.members = paramsModel; typeLoader.members = paramsModel;
paramLabel.visible = paramsModel.length > 0;
paramScroll.visible = paramsModel.length > 0;
modalTransactionDialog.height = (paramsModel.length > 0 ? 500 : 300);
}
function acceptAndClose()
{
close();
accepted();
} }
function close() function close()
@ -169,6 +179,10 @@ Window {
return item; return item;
} }
StateDialogStyle {
id: transactionDialogStyle
}
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 10 anchors.margins: 10
@ -365,8 +379,7 @@ Window {
Button { Button {
text: qsTr("OK"); text: qsTr("OK");
onClicked: { onClicked: {
close(); acceptAndClose();
accepted();
} }
} }
Button { Button {

19
mix/qml/WebCodeEditor.qml

@ -4,23 +4,27 @@ import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import QtWebEngine 1.0 import QtWebEngine 1.0
import QtWebEngine.experimental 1.0 import QtWebEngine.experimental 1.0
import org.ethereum.qml.Clipboard 1.0
import "js/ErrorLocationFormater.js" as ErrorLocationFormater import "js/ErrorLocationFormater.js" as ErrorLocationFormater
Item { Item {
signal editorTextChanged
signal breakpointsChanged signal breakpointsChanged
signal editorTextChanged
signal loadComplete
property bool isClean: true property bool isClean: true
property string currentText: "" property string currentText: ""
property string currentMode: "" property string currentMode: ""
property bool initialized: false property bool initialized: false
property bool unloaded: false
property var currentBreakpoints: []; property var currentBreakpoints: [];
function setText(text, mode) { function setText(text, mode) {
currentText = text; currentText = text;
currentMode = mode; if (mode !== undefined)
currentMode = mode;
if (initialized && editorBrowser) { if (initialized && editorBrowser) {
editorBrowser.runJavaScript("setTextBase64(\"" + Qt.btoa(text) + "\")"); editorBrowser.runJavaScript("setTextBase64(\"" + Qt.btoa(text) + "\")");
editorBrowser.runJavaScript("setMode(\"" + mode + "\")"); editorBrowser.runJavaScript("setMode(\"" + currentMode + "\")");
} }
setFocus(); setFocus();
} }
@ -65,6 +69,11 @@ Item {
editorBrowser.runJavaScript("changeGeneration()", function(result) {}); editorBrowser.runJavaScript("changeGeneration()", function(result) {});
} }
Clipboard
{
id: clipboard
}
Connections { Connections {
target: clipboard target: clipboard
onClipboardChanged: syncClipboard() onClipboardChanged: syncClipboard()
@ -133,7 +142,7 @@ Item {
if (!editorBrowser) if (!editorBrowser)
return; return;
editorBrowser.runJavaScript("getTextChanged()", function(result) { editorBrowser.runJavaScript("getTextChanged()", function(result) {
if (result === true) { if (result === true && editorBrowser) {
editorBrowser.runJavaScript("getText()" , function(textValue) { editorBrowser.runJavaScript("getText()" , function(textValue) {
currentText = textValue; currentText = textValue;
editorTextChanged(); editorTextChanged();
@ -141,7 +150,7 @@ Item {
} }
}); });
editorBrowser.runJavaScript("getBreakpointsChanged()", function(result) { editorBrowser.runJavaScript("getBreakpointsChanged()", function(result) {
if (result === true) { if (result === true && editorBrowser) {
editorBrowser.runJavaScript("getBreakpoints()" , function(bp) { editorBrowser.runJavaScript("getBreakpoints()" , function(bp) {
if (currentBreakpoints !== bp) { if (currentBreakpoints !== bp) {
currentBreakpoints = bp; currentBreakpoints = bp;

21
mix/qml/WebPreview.qml

@ -12,6 +12,7 @@ Item {
id: webPreview id: webPreview
property string pendingPageUrl: "" property string pendingPageUrl: ""
property bool initialized: false property bool initialized: false
property alias urlInput: urlInput
signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content) signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content)
function setPreviewUrl(url) { function setPreviewUrl(url) {
@ -60,6 +61,10 @@ Item {
setPreviewUrl(urlInput.text); setPreviewUrl(urlInput.text);
} }
WebPreviewStyle {
id: webPreviewStyle
}
Connections { Connections {
target: mainApplication target: mainApplication
onLoaded: { onLoaded: {
@ -183,7 +188,7 @@ Item {
Rectangle Rectangle
{ {
anchors.leftMargin: 4 anchors.leftMargin: 4
color: WebPreviewStyle.general.headerBackgroundColor color: webPreviewStyle.general.headerBackgroundColor
Layout.preferredWidth: parent.width Layout.preferredWidth: parent.width
Layout.preferredHeight: 32 Layout.preferredHeight: 32
Row { Row {
@ -230,7 +235,7 @@ Item {
{ {
width: 1 width: 1
height: parent.height - 10 height: parent.height - 10
color: WebPreviewStyle.general.separatorColor color: webPreviewStyle.general.separatorColor
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@ -251,7 +256,7 @@ Item {
{ {
width: 1 width: 1
height: parent.height - 10 height: parent.height - 10
color: WebPreviewStyle.general.separatorColor color: webPreviewStyle.general.separatorColor
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@ -285,7 +290,7 @@ Item {
{ {
Layout.preferredHeight: 1 Layout.preferredHeight: 1
Layout.preferredWidth: parent.width Layout.preferredWidth: parent.width
color: WebPreviewStyle.general.separatorColor color: webPreviewStyle.general.separatorColor
} }
Splitter Splitter
@ -355,9 +360,9 @@ Item {
id: expressionInput id: expressionInput
width: parent.width - 15 width: parent.width - 15
height: 20 height: 20
font.family: WebPreviewStyle.general.fontName font.family: webPreviewStyle.general.fontName
font.italic: true font.italic: true
font.pointSize: Style.absoluteSize(-3) font.pointSize: appStyle.absoluteSize(-3)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
property var history: [] property var history: []
@ -417,8 +422,8 @@ Item {
id: resultTextArea id: resultTextArea
width: expressionPanel.width width: expressionPanel.width
wrapMode: Text.Wrap wrapMode: Text.Wrap
font.family: WebPreviewStyle.general.fontName font.family: webPreviewStyle.general.fontName
font.pointSize: Style.absoluteSize(-3) font.pointSize: appStyle.absoluteSize(-3)
backgroundVisible: true backgroundVisible: true
style: TextAreaStyle { style: TextAreaStyle {
backgroundColor: "#f0f0f0" backgroundColor: "#f0f0f0"

1
mix/qml/WebPreviewStyle.qml

@ -1,4 +1,3 @@
pragma Singleton
import QtQuick 2.0 import QtQuick 2.0
QtObject { QtObject {

8
mix/qml/qmldir

@ -1,8 +0,0 @@
singleton Style 1.0 Style.qml
singleton StateDialogStyle 1.0 StateDialogStyle.qml
singleton ProjectFilesStyle 1.0 ProjectFilesStyle.qml
singleton DebuggerPaneStyle 1.0 DebuggerPaneStyle.qml
singleton StateStyle 1.0 StateStyle.qml
singleton StatusPaneStyle 1.0 StatusPaneStyle.qml
singleton WebPreviewStyle 1.0 WebPreviewStyle.qml
singleton LogsPaneStyle 1.0 LogsPaneStyle.qml

6
mix/test.qrc

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>test/TestMain.qml</file>
<file>test/TestTransactionDebug.qml</file>
</qresource>
</RCC>

50
mix/test/TestMain.cpp

@ -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 main.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <iostream>
#include <stdlib.h>
#include <quicktest.h>
#include <QtQml>
#include "MixApplication.h"
#include "Exceptions.h"
#include "TestService.h"
using namespace dev::mix;
int main(int _argc, char* _argv[])
{
try
{
MixApplication::initialize();
qmlRegisterType<TestService>("org.ethereum.qml.TestService", 1, 0, "TestService");
return quick_test_main(_argc, _argv, "mix", _argv[1]);
}
catch (boost::exception const& _e)
{
std::cerr << boost::diagnostic_information(_e);
}
catch (std::exception const& _e)
{
std::cerr << _e.what();
}
}

194
mix/test/TestService.cpp

@ -0,0 +1,194 @@
/*
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 TestService.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include "TestService.h"
#include <QtTest/QSignalSpy>
#include <QElapsedTimer>
#include <QQuickItem>
#include <QQuickWindow>
#include <QtTest/QTest>
#include <QtTest/qtestkeyboard.h>
namespace dev
{
namespace mix
{
enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDoubleClick, MouseMove };
static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt::MouseButton _button, Qt::KeyboardModifiers _stateKey, QPointF _pos, int _delay = -1)
{
if (_delay == -1 || _delay < 30)
_delay = 30;
if (_delay > 0)
QTest::qWait(_delay);
if (_action == MouseClick)
{
mouseEvent(MousePress, _window, _item, _button, _stateKey, _pos);
mouseEvent(MouseRelease, _window, _item, _button, _stateKey, _pos);
return;
}
QPoint pos = _pos.toPoint();
QQuickItem* sgitem = qobject_cast<QQuickItem*>(_item);
if (sgitem)
pos = sgitem->mapToScene(_pos).toPoint();
_stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask);
QMouseEvent me(QEvent::User, QPoint(), Qt::LeftButton, _button, _stateKey);
switch (_action)
{
case MousePress:
me = QMouseEvent(QEvent::MouseButtonPress, pos, _window->mapToGlobal(pos), _button, _button, _stateKey);
break;
case MouseRelease:
me = QMouseEvent(QEvent::MouseButtonRelease, pos, _window->mapToGlobal(pos), _button, 0, _stateKey);
break;
case MouseDoubleClick:
me = QMouseEvent(QEvent::MouseButtonDblClick, pos, _window->mapToGlobal(pos), _button, _button, _stateKey);
break;
case MouseMove:
// with move event the _button is NoButton, but 'buttons' holds the currently pressed buttons
me = QMouseEvent(QEvent::MouseMove, pos, _window->mapToGlobal(pos), Qt::NoButton, _button, _stateKey);
break;
default:
break;
}
QSpontaneKeyEvent::setSpontaneous(&me);
if (!qApp->notify(_window, &me))
{
static const char* mouseActionNames[] = { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove" };
QString warning = QString::fromLatin1("Mouse event \"%1\" not accepted by receiving window");
QWARN(warning.arg(QString::fromLatin1(mouseActionNames[static_cast<int>(_action)])).toLatin1().data());
}
}
bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeout)
{
QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str());
QMetaObject const* mo = _item->metaObject();
QStringList methods;
for (int i = mo->methodOffset(); i < mo->methodCount(); ++i)
if (mo->method(i).methodType() == QMetaMethod::Signal)
methods << QString::fromLatin1(mo->method(i).methodSignature());
QElapsedTimer timer;
timer.start();
while (!spy.size())
{
int remaining = _timeout - int(timer.elapsed());
if (remaining <= 0)
break;
QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
QTest::qSleep(10);
}
return spy.size();
}
bool TestService::keyPress(QObject* _item, int _key, int _modifiers, int _delay)
{
QWindow* window = eventWindow(_item);
QTest::keyPress(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay);
return true;
}
bool TestService::keyRelease(QObject* _item, int _key, int _modifiers, int _delay)
{
QWindow* window = eventWindow(_item);
QTest::keyRelease(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay);
return true;
}
bool TestService::keyClick(QObject* _item, int _key, int _modifiers, int _delay)
{
QWindow* window = eventWindow(_item);
QTest::keyClick(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay);
return true;
}
bool TestService::keyPressChar(QObject* _item, QString const& _character, int _modifiers, int _delay)
{
QWindow* window = eventWindow(_item);
QTest::keyPress(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay);
return true;
}
bool TestService::keyReleaseChar(QObject* _item, QString const& _character, int _modifiers, int _delay)
{
QWindow* window = eventWindow(_item);
QTest::keyRelease(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay);
return true;
}
bool TestService::keyClickChar(QObject* _item, QString const& _character, int _modifiers, int _delay)
{
QWindow* window = eventWindow(_item);
QTest::keyClick(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay);
return true;
}
bool TestService::mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay)
{
QWindow* window = qobject_cast<QWindow*>(_item);
if (!window)
window = eventWindow(_item);
mouseEvent(MouseClick, window, _item, Qt::MouseButton(_button), Qt::KeyboardModifiers(_modifiers), QPointF(_x, _y), _delay);
return true;
}
void TestService::setTargetWindow(QObject* _window)
{
QQuickWindow* window = qobject_cast<QQuickWindow*>(_window);
if (window)
m_targetWindow = window;
window->requestActivate();
}
QWindow* TestService::eventWindow(QObject* _item)
{
QQuickItem* item = qobject_cast<QQuickItem*>(_item);
if (item && item->window())
return item->window();
QQuickWindow* window = qobject_cast<QQuickWindow*>(_item);
if (!window)
window = qobject_cast<QQuickWindow*>(m_targetWindow);
if (window)
{
window->requestActivate();
return window;
}
item = qobject_cast<QQuickItem*>(m_targetWindow);
if (item)
return item->window();
return 0;
}
}
}

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

Loading…
Cancel
Save