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
# Normally, set(...CACHE...) creates cache variables, but does not modify them.
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(PARANOIA OFF CACHE BOOL "Additional run-time checks")
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(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(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")
endfunction()
@ -53,8 +59,8 @@ function(configureProject)
add_definitions(-DETH_SOLIDITY)
endif()
if (HEADLESS OR JUSTTESTS)
add_definitions(-DETH_HEADLESS)
if (GUI)
add_definitions(-DETH_GUI)
endif()
endfunction()
@ -133,9 +139,128 @@ configureProject()
# Force chromium.
set (ETH_HAVE_WEBENGINE 1)
message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}")
message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}; FATDB: ${FATDB}; CHROMIUM: ${ETH_HAVE_WEBENGINE}; USENPM: ${USENPM}; ETHASHCL: ${ETHASHCL}")
# Normalise build options
# 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".
set(TARGET_PLATFORM CACHE STRING "linux")
@ -143,6 +268,32 @@ if ("x${TARGET_PLATFORM}" STREQUAL "x")
set(TARGET_PLATFORM "linux")
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")
set(CMAKE_THREAD_LIBS_INIT pthread)
endif ()
@ -166,21 +317,24 @@ add_subdirectory(libdevcore)
add_subdirectory(libevmcore)
add_subdirectory(liblll)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
if (SERPENT)
add_subdirectory(libserpent)
add_subdirectory(sc)
endif ()
add_subdirectory(libsolidity)
if (SOLIDITY)
add_subdirectory(libsolidity)
endif ()
if (NOT JUSTTESTS)
if (TOOLS)
add_subdirectory(lllc)
add_subdirectory(solc)
if (SOLIDITY)
add_subdirectory(solc)
endif ()
endif()
if (JSONRPC)
add_subdirectory(libweb3jsonrpc)
add_subdirectory(ethrpctest)
endif()
add_subdirectory(secp256k1)
@ -196,12 +350,17 @@ endif ()
add_subdirectory(libethcore)
add_subdirectory(libevm)
add_subdirectory(libethereum)
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(abi)
@ -211,25 +370,26 @@ if (NOT JUSTTESTS)
add_subdirectory(exp)
endif ()
# TODO check msvc
if(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(neth)
endif ()
if (NOT HEADLESS)
endif()
add_subdirectory(libnatspec)
add_subdirectory(libjsqrc)
if (NCURSES)
add_subdirectory(neth)
endif ()
if (ETH_HAVE_WEBENGINE)
add_subdirectory(alethzero)
# add_subdirectory(third) // reenable once not qtwebkit.
endif()
if (GUI)
add_subdirectory(mix)
add_subdirectory(libnatspec)
add_subdirectory(libjsqrc)
if (ETH_HAVE_WEBENGINE)
add_subdirectory(alethzero)
# add_subdirectory(third) // reenable once not qtwebkit.
endif()
if (SOLIDITY)
add_subdirectory(mix)
endif ()
endif()
#unset(TARGET_PLATFORM CACHE)

5
alethzero/Connect.cpp

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

3
alethzero/Connect.ui

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

6
alethzero/MainWin.cpp

@ -148,7 +148,7 @@ Main::Main(QWidget *parent) :
cerr << "State root: " << CanonBlockChain::genesis().stateRoot << endl;
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 Hex: " << toHex(block) << 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)
{
auto ret = ethereum()->installWatch(_tf);
auto ret = ethereum()->installWatch(_tf, Reaping::Manual);
m_handlers[ret] = _f;
_f(LocalisedLogEntries());
return ret;
@ -1491,7 +1491,7 @@ void Main::on_blocks_currentItemChanged()
{
BlockInfo uncle = BlockInfo::fromHeader(u.data());
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 << "Number: <b>" << uncle.number << "</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_MINSIZEREL "-Os -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)
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(
COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
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
<< " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl
#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
<< " -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
@ -129,7 +129,7 @@ void help()
<< " -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,--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
<< " -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
@ -274,9 +274,9 @@ int main(int argc, char** argv)
return -1;
}
}
else if (arg == "-K" || arg == "--kill-blockchain")
else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill")
killChain = WithExisting::Kill;
else if (arg == "-B" || arg == "--rebuild-blockchain")
else if (arg == "-B" || arg == "--rebuild")
killChain = WithExisting::Verify;
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i];

1
evmjit/libevmjit/BasicBlock.cpp

@ -49,6 +49,7 @@ void BasicBlock::LocalStack::push(llvm::Value* _value)
assert(_value->getType() == Type::Word);
m_bblock.m_currentStack.push_back(_value);
m_bblock.m_tosOffset += 1;
m_maxSize = std::max(m_maxSize, m_bblock.m_currentStack.size());
}
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.
void swap(size_t _index);
size_t getMaxSize() const { return m_maxSize; }
int getDiff() const { return m_bblock.m_tosOffset; }
private:
LocalStack(BasicBlock& _owner);
LocalStack(LocalStack const&) = delete;
@ -49,6 +52,7 @@ public:
private:
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);

31
evmjit/libevmjit/Cache.cpp

@ -4,6 +4,7 @@
#include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Instructions.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/Support/Path.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_os_ostream.h>
@ -59,6 +60,36 @@ void Cache::clear()
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)
{
if (g_mode != CacheMode::on && g_mode != CacheMode::read)

12
evmjit/libevmjit/Cache.h

@ -1,9 +1,15 @@
#pragma once
#include <memory>
#include <unordered_map>
#include <llvm/ExecutionEngine/ObjectCache.h>
namespace llvm
{
class ExecutionEngine;
}
namespace dev
{
namespace eth
@ -18,7 +24,8 @@ enum class CacheMode
off,
read,
write,
clear
clear,
preload
};
class ObjectCache : public llvm::ObjectCache
@ -43,6 +50,9 @@ public:
/// Clears cache storage
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.
if (!_basicBlock.llvm()->getTerminator())
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::write, "w", "Write only. No objects are loaded from cache."),
clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."),
clEnumValN(CacheMode::preload, "p", "Preload all cached objects."),
clEnumValEnd)};
cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}};
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};
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;
static std::unordered_map<std::string, uint64_t> funcCache;
static std::unique_ptr<llvm::ExecutionEngine> ee;
if (!ee)
{
@ -138,6 +146,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
return ReturnCode::LLVMConfigError;
module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module
ee->setObjectCache(objectCache);
if (preloadCache)
Cache::preload(*ee, funcCache);
}
static StatsCollector statsCollector;
@ -146,10 +157,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
m_runtime.init(_data, _env);
EntryFuncPtr entryFuncPtr = nullptr;
static std::unordered_map<std::string, EntryFuncPtr> funcCache;
auto it = funcCache.find(mainFuncName);
if (it != funcCache.end())
entryFuncPtr = it->second;
entryFuncPtr = (EntryFuncPtr) it->second;
if (!entryFuncPtr)
{
@ -177,7 +187,8 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError;
funcCache[mainFuncName] = entryFuncPtr;
if (it == funcCache.end())
funcCache[mainFuncName] = (uint64_t) entryFuncPtr;
listener->stateChanged(ExecState::Execution);
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::CallDataSize: return "callDataSize";
case RuntimeData::Gas: return "gas";
case RuntimeData::Number: return "number";
case RuntimeData::Number: return "number";
case RuntimeData::Timestamp: return "timestamp";
}
}
@ -101,6 +101,48 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
assert(m_memPtr->getType() == Array::getType()->getPointerTo());
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env");
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()

5
evmjit/libevmjit/RuntimeManager.h

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

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using 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()
@ -57,8 +57,7 @@ void BlockInfo::setEmpty()
extraData.clear();
mixHash = h256();
nonce = Nonce();
m_seedHash = h256();
hash = headerHash(WithNonce);
m_hash = m_seedHash = h256();
}
h256 const& BlockInfo::seedHash() const
@ -69,10 +68,17 @@ h256 const& BlockInfo::seedHash() const
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;
ret.populateFromHeader(RLP(_block), _s);
ret.populateFromHeader(RLP(_header), _s, _h);
return ret;
}
@ -97,9 +103,11 @@ h256 BlockInfo::headerHash(bytesConstRef _block)
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;
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 header = root[0];
if (!header.isList())
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())
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)
{
m_hash = m_seedHash = h256();
stateRoot = _parent.stateRoot;
parentHash = _parent.hash;
parentHash = _parent.hash();
number = _parent.number + 1;
gasLimit = selectGasLimit(_parent);
gasUsed = 0;
@ -230,7 +239,7 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const
// Check timestamp is after previous timestamp.
if (parentHash)
{
if (parentHash != _parent.hash)
if (parentHash != _parent.hash())
BOOST_THROW_EXCEPTION(InvalidParentHash());
if (timestamp <= _parent.timestamp)

21
libethcore/BlockInfo.h

@ -68,7 +68,6 @@ struct BlockInfo
{
public:
// 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 sha3Uncles;
Address coinbaseAddress;
@ -86,14 +85,14 @@ public:
Nonce nonce;
BlockInfo();
explicit BlockInfo(bytes const& _block, Strictness _s = CheckEverything): BlockInfo(&_block, _s) {}
explicit BlockInfo(bytesConstRef _block, Strictness _s = CheckEverything);
explicit BlockInfo(bytes const& _block, Strictness _s = IgnoreNonce, h256 const& _h = h256()): BlockInfo(&_block, _s, _h) {}
explicit BlockInfo(bytesConstRef _block, Strictness _s = IgnoreNonce, h256 const& _h = h256());
static h256 headerHash(bytes const& _block) { return headerHash(&_block); }
static h256 headerHash(bytesConstRef _block);
static BlockInfo fromHeader(bytes const& _block, Strictness _s = CheckEverything) { return fromHeader(bytesConstRef(&_block), _s); }
static BlockInfo fromHeader(bytesConstRef _block, Strictness _s = CheckEverything);
static BlockInfo fromHeader(bytes const& _header, Strictness _s = IgnoreNonce, h256 const& _h = h256()) { return fromHeader(bytesConstRef(&_header), _s, _h); }
static BlockInfo fromHeader(bytesConstRef _header, Strictness _s = IgnoreNonce, h256 const& _h = h256());
explicit operator bool() const { return timestamp != Invalid256; }
@ -119,9 +118,11 @@ public:
void setEmpty();
void populateFromHeader(RLP const& _header, Strictness _s = CheckEverything);
void populate(bytesConstRef _block, Strictness _s = CheckEverything);
void populate(bytes const& _block, Strictness _s = CheckEverything) { populate(&_block, _s); }
void noteDirty() const { m_hash = m_seedHash= h256(); }
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 verifyParent(BlockInfo const& _parent) const;
void populateFromParent(BlockInfo const& parent);
@ -129,6 +130,7 @@ public:
u256 calculateDifficulty(BlockInfo const& _parent) const;
u256 selectGasLimit(BlockInfo const& _parent) const;
h256 const& seedHash() const;
h256 const& hash() const;
/// sha3 of the header only.
h256 headerHash(IncludeNonce _n) const;
@ -136,11 +138,12 @@ public:
private:
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)
{
_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.gasUsed << " " << _bi.timestamp << " " << _bi.mixHash << " " << _bi.nonce << " (" << _bi.seedHash() << ")";
return _out;

2
libethcore/Common.cpp

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

10
libethcore/Common.h

@ -85,5 +85,15 @@ enum class RelativeBlock: BlockNumber
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()
{
while (!m_caches.empty())
killCache(m_caches.begin()->first);
while (!m_lights.empty())
killCache(m_lights.begin()->first);
}
void Ethasher::killCache(h256 const& _s)
{
RecursiveGuard l(x_this);
if (m_caches.count(_s))
if (m_lights.count(_s))
{
ethash_delete_light(m_caches.at(_s));
m_caches.erase(_s);
ethash_delete_light(m_lights.at(_s));
m_lights.erase(_s);
}
}
void const* Ethasher::cache(BlockInfo const& _header)
void const* Ethasher::light(BlockInfo const& _header)
{
RecursiveGuard l(x_this);
if (_header.number > c_ethashEpochLength * 2048)
@ -67,41 +67,52 @@ void const* Ethasher::cache(BlockInfo const& _header)
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);
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)
{
RecursiveGuard l(x_this);
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();
m_fulls.erase(m_fulls.begin());
}
}*/
try {
boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {}
std::string memoFile = getDataDir("ethash") + "/full";
auto info = rlpList(c_ethashRevision, _header.seedHash());
if (boost::filesystem::exists(memoFile) && contents(memoFile + ".info") != info)
boost::filesystem::remove(memoFile);
std::string oldMemoFile = getDataDir("ethash") + "/full";
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);
if (!m_fulls[_header.seedHash()])
{
ethash_params p = params((unsigned)_header.number);
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);
writeFile(memoFile, m_fulls[_header.seedHash()]);
writeFile(memoFile + ".info", info);
}
}
return m_fulls[_header.seedHash()];
@ -162,7 +173,10 @@ Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce)
{
auto p = Ethasher::params(_header);
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);
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 FullType = void const*;
LightType cache(BlockInfo const& _header);
LightType light(BlockInfo const& _header);
bytesConstRef full(BlockInfo const& _header);
static ethash_params params(BlockInfo const& _header);
static ethash_params params(unsigned _n);
@ -99,7 +99,7 @@ private:
static Ethasher* s_this;
RecursiveMutex x_this;
std::map<h256, LightType> m_caches;
std::map<h256, LightType> m_lights;
std::map<h256, bytesRef> m_fulls;
};

330
libethereum/BlockChain.cpp

@ -19,10 +19,13 @@
* @date 2014
*/
#include <leveldb/db.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 <test/JsonSpiritHeaders.h>
#include <libdevcore/Common.h>
@ -33,6 +36,7 @@
#include <libethcore/Exceptions.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/BlockInfo.h>
#include <libethcore/Ethasher.h>
#include <liblll/Compiler.h>
#include "GenesisInfo.h"
#include "State.h"
@ -43,6 +47,7 @@ using namespace dev::eth;
namespace js = json_spirit;
#define ETH_CATCH 1
#define ETH_TIMED_IMPORTS 0
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;
}
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
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
}
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
static const chrono::system_clock::duration c_collectionDuration = chrono::seconds(15);
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.
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));
}
#if ETH_PARANOIA
checkConsistency();
#endif
// TODO: Implement ability to rebuild details map from DB.
std::string l;
@ -174,13 +196,21 @@ void BlockChain::close()
m_blocks.clear();
}
#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
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();
// Keep extras DB around, but under a temp name
delete m_extrasDB;
m_extrasDB = nullptr;
IGNORE_EXCEPTIONS(boost::filesystem::remove_all(_path + "/details.old"));
boost::filesystem::rename(_path + "/details", _path + "/details.old");
ldb::DB* oldExtrasDB;
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);
// 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.
m_details.clear();
@ -201,11 +231,30 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
m_lastLastHashes.clear();
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
{
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 (...)
{
@ -217,6 +266,10 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
_progress(d, originalNumber);
}
#if ETH_PROFILING_GPERF
ProfilerStop();
#endif
delete oldExtrasDB;
boost::filesystem::remove_all(_path + "/details.old");
}
@ -253,12 +306,12 @@ LastHashes BlockChain::lastHashes(unsigned _n) const
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);
vector<bytes> blocks;
_bq.drain(blocks);
_bq.drain(blocks, _max);
h256s ret;
for (auto const& block: blocks)
@ -266,10 +319,7 @@ h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max
try
{
for (auto h: import(block, _stateDB))
if (!_max--)
break;
else
ret.push_back(h);
ret.push_back(h);
}
catch (UnknownParent)
{
@ -284,15 +334,15 @@ h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max
catch (...)
{}
}
_bq.doneDrain();
return ret;
bool yetMore = _bq.doneDrain();
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
{
return import(_block, _stateDB);
return import(_block, _stateDB, _force);
}
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.
BlockInfo bi;
@ -329,7 +391,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
auto newHash = BlockInfo::headerHash(_block);
// Check block doesn't already exist first!
if (isKnown(newHash))
if (isKnown(newHash) && !_force)
{
clog(BlockChainNote) << newHash << ": Not new.";
BOOST_THROW_EXCEPTION(AlreadyHaveBlock());
@ -362,6 +424,11 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
clog(BlockChainNote) << "Attempting import of " << newHash.abridged() << "...";
#if ETH_TIMED_IMPORTS
preliminaryChecks = t.elapsed();
t.restart();
#endif
u256 td;
#if ETH_CATCH
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.
// 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);
BlockLogBlooms blb;
BlockReceipts br;
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);
td = pd.totalDifficulty + tdIncrease;
#if ETH_TIMED_IMPORTS
enactment = t.elapsed();
t.restart();
#endif
#if ETH_PARANOIA
checkConsistency();
#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[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);
m_logBlooms[newHash] = blb;
@ -437,25 +479,27 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
m_receipts[newHash] = br;
}
#if ETH_TIMED_IMPORTS
collation = t.elapsed();
t.restart();
#endif
{
ReadGuard l1(x_blocksBlooms);
ReadGuard l2(x_details);
ReadGuard l3(x_blockHashes);
ReadGuard l4(x_receipts);
ReadGuard l5(x_logBlooms);
ReadGuard l6(x_transactionAddresses);
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(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, 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
checkConsistency();
#endif
@ -471,7 +515,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
{
clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(_e);
_e << errinfo_comment("Malformed block ");
clog(BlockChainWarn) << "Block: " << bi.hash;
clog(BlockChainWarn) << "Block: " << bi.hash();
clog(BlockChainWarn) << bi;
clog(BlockChainWarn) << "Block parent: " << bi.parentHash;
clog(BlockChainWarn) << BlockInfo(block(bi.parentHash));
@ -493,16 +537,78 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
h256 last = currentHash();
if (td > details(last).totalDifficulty)
{
ret = treeRoute(last, newHash);
h256 common;
unsigned commonIndex;
tie(ret, common, commonIndex) = treeRoute(last, newHash);
{
WriteGuard l(x_lastBlockHash);
m_lastBlockHash = newHash;
}
noteCanonChanged();
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);
noteCanonChanged();
StructuredLogger::chainNewHead(
bi.headerHash(WithoutNonce).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 << ")";
}
#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;
}
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();
if (!_from || !_to)
return h256s();
return make_tuple(h256s(), h256(), 0);
h256s ret;
h256s back;
unsigned fn = details(_from).number;
@ -553,20 +712,19 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
assert(to);
from = details(from).parent;
to = details(to).parent;
if (_pre)
if (_pre && (from != to || _common))
ret.push_back(from);
if (_post)
if (_post && (from != to || (!_pre && _common)))
back.push_back(to);
fn--;
tn--;
// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged();
}
if (o_common)
*o_common = from;
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);
return ret;
return make_tuple(ret, from, i);
}
void BlockChain::noteUsed(h256 const& _h, unsigned _extra) const
@ -828,3 +986,33 @@ bytes BlockChain::block(h256 const& _hash) const
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();
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 TransactionHashes = h256s;
@ -99,26 +100,27 @@ public:
void process();
/// 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.
/// @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
/// @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).
bool isKnown(h256 const& _hash) const;
/// 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() const { return BlockInfo(block()); }
BlockInfo info(h256 const& _hash) const { return BlockInfo(block(_hash), IgnoreNonce, _hash); }
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.
bytes block(h256 const& _hash) const;
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.
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(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]; }
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;
@ -194,21 +197,31 @@ public:
/// 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)>());
/** @returns the hash 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.
/** @returns a tuple of:
* - 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),
* then:
* @code
* treeRoute(3a, 2b) == { 3a, 2a, 1a, 1b, 2b }; // *o_common == g
* treeRoute(2a, 1a) == { 2a, 1a }; // *o_common == 1a
* treeRoute(1a, 2a) == { 1a, 2a }; // *o_common == 1a
* treeRoute(1b, 2a) == { 1b, 1a, 2a }; // *o_common == g
* treeRoute(3a, 2b, false) == make_tuple({ 3a, 2a, 1a, 1b, 2b }, g, 3);
* treeRoute(2a, 1a, false) == make_tuple({ 2a, 1a }, 1a, 1)
* treeRoute(1a, 2a, false) == make_tuple({ 1a, 2a }, 1a, 0)
* 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
*/
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
{
@ -257,6 +270,30 @@ private:
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();
/// 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));
}
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);
if (m_drainingSet.empty())
{
swap(o_out, m_ready);
swap(m_drainingSet, m_readySet);
o_out.resize(min<unsigned>(_max, m_ready.size()));
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 <libethcore/Common.h>
#include <libdevcore/Guards.h>
#include <libethcore/Common.h>
namespace dev
{
@ -37,16 +38,6 @@ class BlockChain;
struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; };
#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.
* 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.
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.
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.
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).
void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); }

2
libethereum/CanonBlockChain.cpp

@ -92,6 +92,6 @@ bytes CanonBlockChain::createGenesisBlock()
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
{
public:
CanonBlockChain(WithExisting _we = WithExisting::Trust): CanonBlockChain(std::string(), _we) {}
CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust);
CanonBlockChain(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()): CanonBlockChain(std::string(), _we, _pc) {}
CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback());
~CanonBlockChain() {}
/// @returns the genesis block header.

33
libethereum/Client.cpp

@ -90,7 +90,7 @@ void BasicGasPricer::update(BlockChain const& _bc)
{
auto bb = _bc.block(p);
RLP r(bb);
BlockReceipts brs(_bc.receipts(bi.hash));
BlockReceipts brs(_bc.receipts(bi.hash()));
for (unsigned i = 0; i < r[1].size(); ++i)
{
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):
Worker("eth"),
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_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(Address(), m_stateDB),
m_postMine(Address(), m_stateDB)
m_preMine(m_stateDB, BaseState::CanonGenesis),
m_postMine(m_stateDB)
{
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):
Worker("eth"),
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_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(Address(), m_stateDB),
m_postMine(Address(), m_stateDB)
m_preMine(m_stateDB),
m_postMine(m_stateDB)
{
m_gp->update(m_bc);
@ -221,8 +221,8 @@ void Client::killChain()
}
m_bc.reopen(Defaults::dbPath(), WithExisting::Kill);
m_preMine = State(Address(), m_stateDB);
m_postMine = State(Address(), m_stateDB);
m_preMine = State(m_stateDB);
m_postMine = State(m_stateDB);
if (auto h = m_host.lock())
h->reset();
@ -313,7 +313,7 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed)
auto m = i.second.filter.matches(tr);
if (m.size())
{
auto transactionHash = transaction(d.hash, j).sha3();
auto transactionHash = transaction(d.hash(), j).sha3();
// filter catches them
for (LogEntry const& l: m)
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.
bool stillGotWork = false;
cworkin << "WORK";
h256Set changeds;
@ -496,7 +498,10 @@ void Client::doWork()
cwork << "BQ ==> CHAIN ==> STATE";
OverlayDB db = m_stateDB;
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())
{
for (auto i: newBlocks)
@ -544,7 +549,9 @@ void Client::doWork()
noteChanged(changeds);
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))
{
// watches garbage collection
@ -601,7 +608,7 @@ void Client::inject(bytesConstRef _rlp)
{
startWorking();
m_tq.attemptImport(_rlp);
m_tq.import(_rlp);
}
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));
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());
cnote << "New transaction " << t;
@ -56,7 +56,7 @@ Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes con
u256 n = postMine().transactionsFrom(toAddress(_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());
cnote << "New transaction " << t;
@ -182,7 +182,7 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
if (_f.matches(receipt.bloom()))
{
auto info = bc().info(h);
auto th = transaction(info.hash, i).sha3();
auto th = transaction(info.hash(), i).sha3();
LogEntries le = _f.matches(receipt);
if (le.size())
{
@ -265,7 +265,8 @@ LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const
// cwatch << "peekWatch" << _watchId;
auto& w = m_watches.at(_watchId);
// 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;
}
@ -278,8 +279,9 @@ LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId)
auto& w = m_watches.at(_watchId);
// cwatch << "lastPoll updated to " << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count();
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;
}

27
libethereum/EthereumPeer.cpp

@ -326,15 +326,27 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case TransactionsPacket:
{
clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)";
addRating(_r.itemCount());
Guard l(x_knownTransactions);
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
auto h = sha3(_r[i].data());
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.
addRating(0);
break;
case ImportResult::Success:
addRating(100);
host()->m_transactionsSent.insert(h);
break;
default:;
}
}
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)
s << p;
sealAndSend(s);
addRating(0);
break;
}
case BlockHashesPacket:
@ -370,6 +383,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
addRating(1);
auto h = _r[i].toHash<h256>();
if (host()->m_chain.isKnown(h))
{
@ -398,6 +412,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
++n;
}
}
addRating(0);
RLPStream s;
prep(s, BlocksPacket, n).appendRaw(rlp, n);
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.";
if (m_asking == Asking::Blocks)
transition(Asking::Blocks);
{
if (!got)
transition(Asking::Blocks);
else
transition(Asking::Nothing);
}
break;
}
case NewBlockPacket:
@ -497,6 +517,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
setNeedsSyncing(h, _r[1].toInt<u256>());
break;
}
Guard l(x_knownBlocks);
m_knownBlocks.insert(h);
}

66
libethereum/State.cpp

@ -43,6 +43,7 @@ using namespace dev;
using namespace dev::eth;
#define ctrace clog(StateTrace)
#define ETH_TIMED_ENACTMENTS 0
static const u256 c_blockReward = 1500 * finney;
@ -77,23 +78,24 @@ OverlayDB State::openDB(std::string _path, WithExisting _we)
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_state(&m_db),
m_ourAddress(_coinbaseAddress),
m_blockReward(c_blockReward)
{
// Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly.
m_state.init();
if (_bs != BaseState::PreExisting)
// 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)
{
dev::eth::commit(genesisState(), m_db, m_state);
m_db.commit();
paranoia("after DB commit of normal construction.", true);
paranoia("after DB commit of Genesis construction.", true);
m_previousBlock = CanonBlockChain::genesis();
}
else
@ -320,7 +322,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi)
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...
{
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.
}
@ -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)
{
#if ETH_TIMED_ENACTMENTS
boost::timer t;
double populateVerify;
double populateGrand;
double syncReset;
double enactment;
#endif
// Check family:
BlockInfo biParent(_bc.block(_bi.parentHash));
_bi.verifyParent(biParent);
#if ETH_TIMED_ENACTMENTS
populateVerify = t.elapsed();
t.restart();
#endif
BlockInfo biGrandParent;
if (biParent.number)
biGrandParent.populate(_bc.block(biParent.parentHash));
#if ETH_TIMED_ENACTMENTS
populateGrand = t.elapsed();
t.restart();
#endif
sync(_bc, _bi.parentHash);
resetCurrent();
#if ETH_TIMED_ENACTMENTS
syncReset = t.elapsed();
t.restart();
#endif
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
@ -504,18 +538,18 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
{
// m_currentBlock is assumed to be prepopulated and reset.
#if !ETH_RELEASE
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(rootHash() == m_previousBlock.stateRoot);
#endif
if (m_currentBlock.parentHash != m_previousBlock.hash)
if (m_currentBlock.parentHash != m_previousBlock.hash())
BOOST_THROW_EXCEPTION(InvalidParentHash());
// Populate m_currentBlock with the correct values.
m_currentBlock.populate(_block, _checkNonce ? CheckEverything : IgnoreNonce);
m_currentBlock = bi;
m_currentBlock.verifyInternals(_block);
// cnote << "playback begins:" << m_state.root();
@ -565,7 +599,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
cwarn << TransactionReceipt(&b);
}
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)
{
auto b = rs.receipts[j].rlp();
@ -807,7 +841,7 @@ void State::commitToMine(BlockChain const& _bc)
m_currentBlock.gasUsed = gasUsed();
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)
@ -857,10 +891,10 @@ void State::completeMine()
ret.appendRaw(m_currentTxs);
ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes);
m_currentBlock.hash = sha3(RLP(m_currentBytes)[0].data());
cnote << "Mined " << m_currentBlock.hash.abridged() << "(parent: " << m_currentBlock.parentHash.abridged() << ")";
m_currentBlock.noteDirty();
cnote << "Mined " << m_currentBlock.hash().abridged() << "(parent: " << m_currentBlock.parentHash.abridged() << ")";
StructuredLogger::minedNewBlock(
m_currentBlock.hash.abridged(),
m_currentBlock.hash().abridged(),
m_currentBlock.nonce.abridged(),
"", //TODO: chain head hash here ??
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 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
{
@ -103,8 +108,15 @@ class State
friend class Executive;
public:
/// Construct state object.
State(Address _coinbaseAddress = Address(), OverlayDB const& _db = OverlayDB(), BaseState _bs = BaseState::CanonGenesis);
/// Default constructor; creates with a blank database prepopulated with the genesis block.
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.
State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash);
@ -335,6 +347,7 @@ private:
/// Debugging only. Good for checking the Trie is in shape.
void paranoia(std::string const& _when, bool _enforceRefs = false) const;
OverlayDB m_db; ///< Our overlay for the state tree.
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.

12
libethereum/TransactionQueue.cpp

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

8
libethereum/TransactionQueue.h

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

2
libp2p/Capability.cpp

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

2
libp2p/Capability.h

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

31
libp2p/Host.cpp

@ -182,10 +182,8 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
}
else
p = m_peers[_id];
p->m_lastDisconnect = NoDisconnect;
if (p->isOffline())
p->m_lastConnected = std::chrono::system_clock::now();
p->m_failedAttempts = 0;
p->endpoint.tcp.address(_endpoint.address());
auto protocolVersion = _rlp[0].toInt<unsigned>();
@ -219,16 +217,19 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
ps->disconnect(DuplicatePeer);
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;
}
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();
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));
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)
{
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_lastAttempted = std::chrono::system_clock::now();
_p->m_failedAttempts++;
}
else
{
@ -499,9 +502,7 @@ void Host::connect(std::shared_ptr<Peer> const& _p)
Guard l(x_connecting);
m_connecting.push_back(handshake);
}
// preempt setting failedAttempts; this value is cleared upon success
_p->m_failedAttempts++;
handshake->start();
}

1
libp2p/Peer.cpp

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

3
libp2p/Peer.h

@ -75,6 +75,9 @@ public:
/// Reason peer was previously disconnected.
DisconnectReason lastDisconnect() const { return m_lastDisconnect; }
/// Peer session is noted as useful.
void noteSessionGood() { m_failedAttempts = 0; }
protected:
/// Returns number of seconds to wait until attempting connection, based on attempted connection history.
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_ping(chrono::steady_clock::time_point::max())
{
m_peer->m_lastDisconnect = NoDisconnect;
m_lastReceived = m_connect = chrono::steady_clock::now();
}
@ -76,12 +77,14 @@ NodeId Session::id() const
return m_peer ? m_peer->id : NodeId();
}
void Session::addRating(unsigned _r)
void Session::addRating(int _r)
{
if (m_peer)
{
m_peer->m_rating += _r;
m_peer->m_score += _r;
if (_r >= 0)
m_peer->noteSessionGood();
}
}
@ -358,16 +361,11 @@ void Session::drop(DisconnectReason _reason)
}
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_failedAttempts = 0;
m_peer->m_lastDisconnect = _reason;
if (_reason == BadProtocol)
{
m_peer->m_rating /= 2;
m_peer->m_score /= 2;
}
m_peer->m_rating /= 2;
m_peer->m_score /= 2;
}
m_dropped = true;
}

2
libp2p/Session.h

@ -74,7 +74,7 @@ public:
void sealAndSend(RLPStream& _s);
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; }

2
libtestutils/StateLoader.cpp

@ -26,7 +26,7 @@ using namespace dev;
using namespace dev::eth;
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())
{

14
libweb3jsonrpc/CMakeLists.txt

@ -22,9 +22,9 @@ file(GLOB HEADERS "*.h")
if (ETH_STATIC)
add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS})
else()
else ()
add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS})
endif()
endif ()
target_link_libraries(${EXECUTABLE} ${LEVELDB_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} 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)
endif()
endif ()
if (ETH_JSON_RPC_STUB)
add_custom_target(jsonrpcstub)
@ -51,7 +53,7 @@ if (ETH_JSON_RPC_STUB)
-P "${ETH_SCRIPTS_DIR}/jsonrpcstub.cmake"
)
add_dependencies(${EXECUTABLE} jsonrpcstub)
endif()
endif ()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
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);
}
std::string WebThreeStubServer::web3_clientVersion()
{
return m_web3.clientVersion();
}
dev::eth::Interface* WebThreeStubServer::client()
{
return m_web3.ethereum();

4
libweb3jsonrpc/WebThreeStubServer.h

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

21
libweb3jsonrpc/WebThreeStubServerBase.cpp

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

1
libweb3jsonrpc/WebThreeStubServerBase.h

@ -77,6 +77,7 @@ public:
virtual std::string net_peerCount();
virtual bool net_listening();
virtual std::string eth_protocolVersion();
virtual std::string eth_coinbase();
virtual bool eth_mining();
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_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("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_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);
@ -92,6 +93,11 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
(void)request;
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)
{
(void)request;
@ -302,6 +308,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
virtual std::string net_version() = 0;
virtual std::string net_peerCount() = 0;
virtual bool net_listening() = 0;
virtual std::string eth_protocolVersion() = 0;
virtual std::string eth_coinbase() = 0;
virtual bool eth_mining() = 0;
virtual std::string eth_gasPrice() = 0;

1
libweb3jsonrpc/spec.json

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

2
libwebthree/WebThree.h

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

2
libwhisper/WhisperHost.cpp

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

49
mix/CMakeLists.txt

@ -29,7 +29,9 @@ else()
qt5_add_resources(UI_RESOURCES noweb.qrc)
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(${EXECUTABLE}
@ -37,33 +39,15 @@ eth_add_executable(${EXECUTABLE}
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})
add_definitions(-DETH_HAVE_WEBENGINE)
target_link_libraries(${EXECUTABLE} Qt5::WebEngine)
list(APPEND LIBRARIES "Qt5::WebEngine")
endif()
target_link_libraries(${EXECUTABLE} ${LIBRARIES})
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake
eth_install_executable(${EXECUTABLE}
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
file(GLOB_RECURSE QMLFILES "qml/*.*")
file(GLOB_RECURSE TESTFILES "test/qml/*.*")
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()
{
m_runFuture.waitForFinished();
}
QString ClientModel::apiCall(QString const& _message)
@ -113,7 +114,7 @@ void ClientModel::mine()
m_mining = true;
emit miningStarted();
emit miningStateChanged();
QtConcurrent::run([=]()
m_runFuture = QtConcurrent::run([=]()
{
try
{
@ -206,7 +207,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
emit runStateChanged();
//run sequence
QtConcurrent::run([=]()
m_runFuture = QtConcurrent::run([=]()
{
try
{
@ -468,7 +469,7 @@ RecordLogEntry* ClientModel::lastBlock() const
strGas << blockInfo.gasUsed;
std::stringstream strNumber;
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);
return record;
}

2
mix/ClientModel.h

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

44
mix/MixApplication.cpp

@ -23,6 +23,7 @@
#include <QQmlApplicationEngine>
#include <QUrl>
#include <QIcon>
#include <QFont>
#ifdef ETH_HAVE_WEBENGINE
#include <QtWebEngine/QtWebEngine>
#endif
@ -37,21 +38,49 @@
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[]):
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"));
setOrganizationDomain(tr("ethereum.org"));
setApplicationName(tr("Mix"));
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<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<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther");
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");
qRegisterMetaType<CodeModel*>("CodeModel*");
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"));
qRegisterMetaType<ClientModel*>("ClientModel*");
}
MixApplication::~MixApplication()

21
mix/MixApplication.h

@ -33,12 +33,33 @@ namespace dev
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
{
Q_OBJECT
public:
MixApplication(int& _argc, char* _argv[]);
static void initialize();
virtual ~MixApplication();
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();
m_bc.reset();
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_startState = m_state;
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
#include <string>
#include <QObject>
#include <libsolidity/AST.h>
namespace dev
{
namespace solidity
{
class Declaration;
}
namespace mix
{
@ -37,8 +43,8 @@ class QBasicNodeDefinition: public QObject
public:
QBasicNodeDefinition(QObject* _parent = nullptr): QObject(_parent) {}
~QBasicNodeDefinition() {}
QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d): QObject(_parent), m_name(QString::fromStdString(_d->getName())) {}
QBasicNodeDefinition(QObject* _parent, std::string const& _name): QObject(_parent), m_name(QString::fromStdString(_name)) {}
QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d);
QBasicNodeDefinition(QObject* _parent, std::string const& _name);
/// Get the name of the node.
QString name() const { return m_name; }

1
mix/QVariableDeclaration.cpp

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

5
mix/QVariableDeclaration.h

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

18
mix/main.cpp

@ -22,27 +22,17 @@
#include <iostream>
#include <stdlib.h>
#include <boost/exception/exception.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include "MixApplication.h"
#include "Exceptions.h"
using namespace dev::mix;
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
{
MixApplication::initialize();
MixApplication app(_argc, _argv);
return app.exec();
}

7
mix/qml.qrc

@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/">
<file>qml/AlertMessageDialog.qml</file>
<file>qml/Application.qml</file>
<file>qml/BasicMessage.qml</file>
<file>qml/BigIntValue.qml</file>
<file>qml/CallStack.qml</file>
@ -47,7 +48,8 @@
<file>qml/StatusPaneStyle.qml</file>
<file>qml/StepActionImage.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/TabStyle.qml</file>
<file>qml/TransactionDialog.qml</file>
@ -59,8 +61,5 @@
<file>qml/js/ProjectModel.js</file>
<file>qml/js/QEtherHelper.js</file>
<file>qml/js/TransactionHelper.js</file>
<file>qml/main.qml</file>
<file>qml/qmldir</file>
<file>qml/StatesComboBox.qml</file>
</qresource>
</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.FileIo 1.0
import org.ethereum.qml.Clipboard 1.0
import org.ethereum.qml.ApplicationService 1.0
ApplicationWindow {
@ -22,6 +23,16 @@ ApplicationWindow {
minimumWidth: 400
minimumHeight: 300
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 {
id: codeModel
@ -44,6 +55,10 @@ ApplicationWindow {
id: clipboard
}
Style {
id: appStyle
}
Connections {
target: mainApplication
onClosing:

5
mix/qml/CallStack.qml

@ -2,7 +2,6 @@ import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import "."
DebugInfoList
{
@ -37,7 +36,7 @@ DebugInfoList
anchors.leftMargin: 5
color: "#4a4a4a"
text: styleData.row;
font.pointSize: DebuggerPaneStyle.general.basicFontSize
font.pointSize: dbgStyle.general.basicFontSize
width: parent.width - 5
elide: Text.ElideRight
}
@ -58,7 +57,7 @@ DebugInfoList
color: "#4a4a4a"
text: styleData.value;
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
width: parent.width
height: parent.height * 0.7
Rectangle {
id: lineColumn
property int rowHeight: codeEditor.font.pixelSize + 3
color: "#202020"
width: 50
height: parent.height
Column {
y: -codeEditor.flickableItem.contentY + 4
width: parent.width
Repeater {
model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight))
delegate: Text {
id: text
color: codeEditor.textColor
font: codeEditor.font
width: lineColumn.width - 4
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
height: lineColumn.rowHeight
renderType: Text.NativeRendering
text: index + 1
}
CodeEditorStyle {
id: style
}
Rectangle {
id: lineColumn
property int rowHeight: codeEditor.font.pixelSize + 3
color: "#202020"
width: 50
height: parent.height
Column {
y: -codeEditor.flickableItem.contentY + 4
width: parent.width
Repeater {
model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight))
delegate: Text {
id: text
color: codeEditor.textColor
font: codeEditor.font
width: lineColumn.width - 4
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
height: lineColumn.rowHeight
renderType: Text.NativeRendering
text: index + 1
}
}
}
}
TextArea {
id: codeEditor
@ -66,7 +71,7 @@ Item {
height: parent.height
font.family: "Monospace"
font.pointSize: CodeEditorStyle.general.basicFontSize
font.pointSize: style.general.basicFontSize
width: parent.width
tabChangesFocus: false

1
mix/qml/CodeEditorStyle.qml

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

45
mix/qml/CodeEditorView.qml

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

2
mix/qml/CommonSeparator.qml

@ -4,6 +4,6 @@ import "."
Rectangle
{
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);
}
DebuggerPaneStyle {
id: dbgStyle
}
Connections {
target: clientModel
onDebugDataReady: {
@ -449,7 +453,7 @@ Rectangle {
color: "#b2b3ae"
text: styleData.value.split(' ')[0]
font.family: "monospace"
font.pointSize: DebuggerPaneStyle.general.basicFontSize
font.pointSize: dbgStyle.general.basicFontSize
wrapMode: Text.NoWrap
id: id
}
@ -459,7 +463,7 @@ Rectangle {
color: styleData.selected ? "white" : "black"
font.family: "monospace"
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"
color: "#4a4a4a"
text: styleData.row;
font.pointSize: DebuggerPaneStyle.general.basicFontSize
font.pointSize: dbgStyle.general.basicFontSize
}
}
@ -550,7 +554,7 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
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
QtObject {

1
mix/qml/DefaultLabel.qml

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

4
mix/qml/DeploymentDialog.qml

@ -31,7 +31,7 @@ Window {
property string currentAccount
property alias gasToUse: gasToUseInput.text
color: Style.generic.layout.backgroundColor
color: appStyle.generic.layout.backgroundColor
function close()
{
@ -329,7 +329,7 @@ Window {
anchors.verticalCenter: parent.verticalCenter;
anchors.left: applicationUrlEth.right
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 variant selManager;
property int index;
color: index % 2 === 0 ? "transparent" : ProjectFilesStyle.title.background
color: index % 2 === 0 ? "transparent" : projectFilesStyle.title.background
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()
{
return section.state === "hidden" ? 0 : ProjectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count;
return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count;
}
function hiddenHeightElement()
{
return section.state === "hidden" ? 0 : ProjectFilesStyle.documentsList.fileNameHeight;
return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight;
}
function getDocumentIndex(documentId)
@ -68,7 +68,7 @@ Rectangle
{
anchors.top: parent.top
id: rowCol
height: ProjectFilesStyle.documentsList.height
height: projectFilesStyle.documentsList.height
Layout.fillWidth: true
@ -88,15 +88,15 @@ Rectangle
id: section
text: sectionName
anchors.left: parent.left
anchors.leftMargin: ProjectFilesStyle.general.leftMargin
color: ProjectFilesStyle.documentsList.sectionColor
anchors.leftMargin: projectFilesStyle.general.leftMargin
color: projectFilesStyle.documentsList.sectionColor
font.family: boldFont.name
font.pointSize: ProjectFilesStyle.documentsList.sectionFontSize
font.pointSize: projectFilesStyle.documentsList.sectionFontSize
states: [
State {
name: "hidden"
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" }
}
]
@ -138,7 +138,7 @@ Rectangle
Layout.preferredHeight: wrapperItem.hiddenHeightElement()
Layout.maximumHeight: wrapperItem.hiddenHeightElement()
height: wrapperItem.hiddenHeightElement()
color: isSelected ? ProjectFilesStyle.documentsList.highlightColor : "transparent"
color: isSelected ? projectFilesStyle.documentsList.highlightColor : "transparent"
property bool isSelected
property bool renameMode
@ -147,15 +147,15 @@ Rectangle
anchors.verticalCenter: parent.verticalCenter
anchors.fill: parent
anchors.left: parent.left
anchors.leftMargin: ProjectFilesStyle.general.leftMargin + 2
anchors.leftMargin: projectFilesStyle.general.leftMargin + 2
Text {
id: nameText
height: parent.height
visible: !renameMode
color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color
color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color
text: name;
font.family: fileNameFont.name
font.pointSize: ProjectFilesStyle.documentsList.fontSize
font.pointSize: projectFilesStyle.documentsList.fontSize
verticalAlignment: Text.AlignVCenter
Connections
@ -182,7 +182,7 @@ Rectangle
DefaultLabel {
id: editStatusLabel
visible: false
color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color
color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color
verticalAlignment: Text.AlignVCenter
text: "*"
width: 10
@ -196,7 +196,7 @@ Rectangle
visible: renameMode
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: ProjectFilesStyle.general.leftMargin
anchors.leftMargin: projectFilesStyle.general.leftMargin
MouseArea {
id: textMouseArea
anchors.fill: parent

4
mix/qml/ItemDelegateDataDump.qml

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

1
mix/qml/LogsPaneStyle.qml

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

1
mix/qml/MainContent.qml

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

1
mix/qml/NewProjectDialog.qml

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

1
mix/qml/ProjectFilesStyle.qml

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

21
mix/qml/ProjectList.qml

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

2
mix/qml/ProjectModel.qml

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

3
mix/qml/Splitter.qml

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

16
mix/qml/StateDialog.qml

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

1
mix/qml/StateDialogStyle.qml

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

1
mix/qml/StateListModel.qml

@ -12,6 +12,7 @@ Item {
property alias model: stateListModel
property var stateList: []
property alias stateDialog: stateDialog
property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project
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" }
}
StatusPaneStyle {
id: statusPaneStyle
}
Connections {
target: webPreview
onJavaScriptMessage:
@ -123,7 +127,7 @@ Rectangle {
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: Style.absoluteSize(-1)
font.pointSize: appStyle.absoluteSize(-1)
height: 15
font.family: "sans serif"
objectName: "status"

1
mix/qml/StatusPaneStyle.qml

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

4
mix/qml/StorageView.qml

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

45
mix/qml/TransactionDialog.qml

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

19
mix/qml/WebCodeEditor.qml

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

21
mix/qml/WebPreview.qml

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

1
mix/qml/WebPreviewStyle.qml

@ -1,4 +1,3 @@
pragma Singleton
import QtQuick 2.0
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