Browse Source

Merge branch 'develop' of https://github.com/ethereum/cpp-ethereum into az_load_rlp

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

23
CMakeLists.txt

@ -30,19 +30,11 @@ endfunction()
# propagates CMake configuration options to the compiler
function(configureProject)
if (PARANOIA)
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_definitions(-DETH_PARANOIA)
else ()
message(FATAL_ERROR "Paranoia requires debug.")
endif ()
add_definitions(-DETH_PARANOIA)
endif ()
if (VMTRACE)
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_definitions(-DETH_VMTRACE)
else ()
message(FATAL_ERROR "VM tracing requires debug.")
endif ()
add_definitions(-DETH_VMTRACE)
endif ()
if (ETHASHCL)
@ -123,6 +115,17 @@ endfunction()
set(CMAKE_AUTOMOC ON)
cmake_policy(SET CMP0015 NEW)
# Clear invalid option
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
if (PARANOIA)
message("Paranoia requires debug - disabling for release build.")
set(PARANOIA OFF)
endif ()
if (VMTRACE)
message("VM Tracing requires debug - disabling for release build.")
set (VMTRACE OFF)
endif ()
endif ()
createDefaultCacheConfig()
configureProject()

6
abi/main.cpp

@ -436,7 +436,7 @@ struct ABIMethod
if (_params[pi].second != Format::Open)
throw ExpectedOpen();
++pi;
int l = a.dims[addr.size()];
int l = a.dims[a.dims.size() - 1 - addr.size()];
if (l == -1)
{
// read ahead in params and discover the arity.
@ -486,7 +486,7 @@ struct ABIMethod
put();
else
{
int l = a.dims[addr.size()];
int l = a.dims[a.dims.size() - 1 - addr.size()];
if (l == -1)
{
l = fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32));
@ -525,7 +525,7 @@ struct ABIMethod
{
out << "[";
addr.push_back(0);
int l = a.dims[addr.size() - 1];
int l = a.dims[a.dims.size() - 1 - (addr.size() - 1)];
if (l == -1)
l = catDims[d++];
for (addr.back() = 0; addr.back() < l; ++addr.back())

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

@ -168,7 +168,7 @@ Main::Main(QWidget *parent) :
QSettings s("ethereum", "alethzero");
m_networkConfig = s.value("peers").toByteArray();
bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size());
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), false, {"eth", "shh"}, p2p::NetworkPreferences(), network));
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth", "shh"}, p2p::NetworkPreferences(), network));
m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads));
m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this));
@ -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;
@ -1582,7 +1582,7 @@ void Main::on_debugCurrent_triggered()
unsigned txi = item->data(Qt::UserRole + 1).toInt();
bytes t = ethereum()->blockChain().transaction(h, txi);
State s(ethereum()->state(txi, h));
Executive e(s, ethereum()->blockChain(), PendingBlock);
Executive e(s, ethereum()->blockChain());
Debugger dw(this, this);
dw.populate(e, Transaction(t, CheckSignature::Sender));
dw.exec();

13
eth/main.cpp

@ -117,8 +117,8 @@ void help()
<< " -h,--help Show this help message and exit." << endl
<< " -i,--interactive Enter interactive mode (default: non-interactive)." << endl
#if ETH_JSONRPC
<< " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl
<< " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl
<< " -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
<< " --listen-ip <port> Listen on the given port for incoming connections (default: 30303)." << endl
@ -129,6 +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,--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
@ -168,7 +169,7 @@ void version()
}
Address c_config = Address("ccdeac59d35627b7de09332e819d5159e7bb7250");
string pretty(h160 _a, dev::eth::State _st)
string pretty(h160 _a, dev::eth::State const& _st)
{
string ns;
h256 n;
@ -216,7 +217,7 @@ int main(int argc, char** argv)
bool bootstrap = false;
bool upnp = true;
bool forceMining = false;
bool killChain = false;
WithExisting killChain = WithExisting::Trust;
bool jit = false;
bool structuredLogging = false;
string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S";
@ -274,7 +275,9 @@ int main(int argc, char** argv)
}
}
else if (arg == "-K" || arg == "--kill-blockchain")
killChain = true;
killChain = WithExisting::Kill;
else if (arg == "-B" || arg == "--rebuild-blockchain")
killChain = WithExisting::Verify;
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i];
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)

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.1";
char const* Version = "0.9.3";
}

16
libdevcore/Common.h

@ -134,4 +134,20 @@ private:
std::function<void(void)> m_f;
};
enum class WithExisting: int
{
Trust = 0,
Verify,
Kill
};
}
namespace std {
inline dev::WithExisting max(dev::WithExisting _a, dev::WithExisting _b)
{
return static_cast<dev::WithExisting>(max(static_cast<int>(_a), static_cast<int>(_b)));
}
}

30
libethcore/BlockInfo.h

@ -80,7 +80,7 @@ public:
u256 number;
u256 gasLimit;
u256 gasUsed;
u256 timestamp;
u256 timestamp = Invalid256;
bytes extraData;
h256 mixHash;
Nonce nonce;
@ -100,20 +100,20 @@ public:
bool operator==(BlockInfo const& _cmp) const
{
return parentHash == _cmp.parentHash &&
sha3Uncles == _cmp.sha3Uncles &&
coinbaseAddress == _cmp.coinbaseAddress &&
stateRoot == _cmp.stateRoot &&
transactionsRoot == _cmp.transactionsRoot &&
receiptsRoot == _cmp.receiptsRoot &&
logBloom == _cmp.logBloom &&
difficulty == _cmp.difficulty &&
number == _cmp.number &&
gasLimit == _cmp.gasLimit &&
gasUsed == _cmp.gasUsed &&
timestamp == _cmp.timestamp &&
extraData == _cmp.extraData &&
mixHash == _cmp.mixHash &&
nonce == _cmp.nonce;
sha3Uncles == _cmp.sha3Uncles &&
coinbaseAddress == _cmp.coinbaseAddress &&
stateRoot == _cmp.stateRoot &&
transactionsRoot == _cmp.transactionsRoot &&
receiptsRoot == _cmp.receiptsRoot &&
logBloom == _cmp.logBloom &&
difficulty == _cmp.difficulty &&
number == _cmp.number &&
gasLimit == _cmp.gasLimit &&
gasUsed == _cmp.gasUsed &&
timestamp == _cmp.timestamp &&
extraData == _cmp.extraData &&
mixHash == _cmp.mixHash &&
nonce == _cmp.nonce;
}
bool operator!=(BlockInfo const& _cmp) const { return !operator==(_cmp); }

3
libethcore/Common.cpp

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

3
libethcore/Common.h

@ -35,6 +35,9 @@ namespace eth
/// Current protocol version.
extern const unsigned c_protocolVersion;
/// Current minor protocol version.
extern const unsigned c_minorProtocolVersion;
/// Current database version.
extern const unsigned c_databaseVersion;

334
libethereum/BlockChain.cpp

@ -19,10 +19,10 @@
* @date 2014
*/
#include <leveldb/db.h>
#include "BlockChain.h"
#include <leveldb/db.h>
#include <boost/timer.hpp>
#include <boost/filesystem.hpp>
#include <test/JsonSpiritHeaders.h>
#include <libdevcore/Common.h>
@ -43,6 +43,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)
{
@ -98,7 +99,7 @@ static const unsigned c_minCacheSize = 1024 * 1024 * 32;
#endif
BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, bool _killExisting)
BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, WithExisting _we, ProgressCallback const& _p)
{
// initialise deathrow.
m_cacheUsage.resize(c_collectionQueueSize);
@ -108,7 +109,9 @@ BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, bool _kill
m_genesisBlock = _genesisBlock;
m_genesisHash = sha3(RLP(m_genesisBlock)[0].data());
open(_path, _killExisting);
open(_path, _we);
if (_we == WithExisting::Verify)
rebuild(_path, _p);
}
BlockChain::~BlockChain()
@ -116,24 +119,23 @@ BlockChain::~BlockChain()
close();
}
void BlockChain::open(std::string _path, bool _killExisting)
void BlockChain::open(std::string const& _path, WithExisting _we)
{
if (_path.empty())
_path = Defaults::get()->m_dbPath;
boost::filesystem::create_directories(_path);
if (_killExisting)
std::string path = _path.empty() ? Defaults::get()->m_dbPath : _path;
boost::filesystem::create_directories(path);
if (_we == WithExisting::Kill)
{
boost::filesystem::remove_all(_path + "/blocks");
boost::filesystem::remove_all(_path + "/details");
boost::filesystem::remove_all(path + "/blocks");
boost::filesystem::remove_all(path + "/details");
}
ldb::Options o;
o.create_if_missing = true;
ldb::DB::Open(o, _path + "/blocks", &m_blocksDB);
ldb::DB::Open(o, _path + "/details", &m_extrasDB);
ldb::DB::Open(o, path + "/blocks", &m_blocksDB);
ldb::DB::Open(o, path + "/details", &m_extrasDB);
if (!m_blocksDB || !m_extrasDB)
{
if (boost::filesystem::space(_path + "/blocks").available < 1024)
if (boost::filesystem::space(path + "/blocks").available < 1024)
{
cwarn << "Not enough available space found on hard drive. Please free some up and then re-run. Bailing.";
BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace());
@ -158,7 +160,6 @@ void BlockChain::open(std::string _path, bool _killExisting)
// TODO: Implement ability to rebuild details map from DB.
std::string l;
m_extrasDB->Get(m_readOptions, ldb::Slice("best"), &l);
m_lastBlockHash = l.empty() ? m_genesisHash : *(h256*)l.data();
cnote << "Opened blockchain DB. Latest: " << currentHash();
@ -174,6 +175,66 @@ 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)
{
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;
o.create_if_missing = true;
ldb::DB::Open(o, _path + "/details.old", &oldExtrasDB);
ldb::DB::Open(o, _path + "/details", &m_extrasDB);
// Open a fresh state DB
State s(Address(), State::openDB(_path, WithExisting::Kill), BaseState::CanonGenesis);
// Clear all memos ready for replay.
m_details.clear();
m_logBlooms.clear();
m_receipts.clear();
m_transactionAddresses.clear();
m_blockHashes.clear();
m_blocksBlooms.clear();
m_lastLastHashes.clear();
m_lastBlockHash = genesisHash();
h256 lastHash = genesisHash();
for (unsigned d = 1; d < originalNumber; ++d)
{
try
{
bytes b = block(queryExtras<BlockHash, ExtraBlockHash>(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value);
BlockInfo bi(b);
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 (...)
{
// Failed to import - stop here.
break;
}
if (_progress)
_progress(d, originalNumber);
}
delete oldExtrasDB;
boost::filesystem::remove_all(_path + "/details.old");
}
template <class T, class V>
bool contains(T const& _t, V const& _v)
{
@ -211,7 +272,7 @@ h256s 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)
@ -219,10 +280,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)
{
@ -241,11 +299,11 @@ h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max
return ret;
}
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 (...)
{
@ -254,8 +312,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;
@ -282,7 +352,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());
@ -315,6 +385,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
@ -324,6 +399,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
// Get total difficulty increase and update state, checking it.
State s(bi.coinbaseAddress, _db);
auto tdIncrease = s.enactOn(&_block, bi, *this);
BlockLogBlooms blb;
BlockReceipts br;
for (unsigned i = 0; i < s.pending().size(); ++i)
@ -334,6 +410,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
@ -350,37 +431,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;
@ -390,36 +440,48 @@ 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
}
#if ETH_CATCH
catch (Exception const& _e)
catch (InvalidNonce const& _e)
{
clog(BlockChainNote) << " Malformed block: " << diagnostic_information(_e);
_e << errinfo_comment("Malformed block ");
throw;
}
catch (Exception const& _e)
{
clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(_e);
_e << errinfo_comment("Malformed block ");
clog(BlockChainWarn) << "Block: " << bi.hash;
clog(BlockChainWarn) << bi;
clog(BlockChainWarn) << "Block parent: " << bi.parentHash;
clog(BlockChainWarn) << BlockInfo(block(bi.parentHash));
throw;
}
#endif
StructuredLogger::chainReceivedNewBlock(
@ -436,16 +498,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(),
@ -457,19 +581,72 @@ 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();
// 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;
unsigned tn = details(_to).number;
cdebug << "treeRoute" << fn << "..." << tn;
// cdebug << "treeRoute" << fn << "..." << tn;
h256 from = _from;
while (fn > tn)
{
@ -477,7 +654,7 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
ret.push_back(from);
from = details(from).parent;
fn--;
cdebug << "from:" << fn << _from.abridged();
// cdebug << "from:" << fn << _from.abridged();
}
h256 to = _to;
while (fn < tn)
@ -486,7 +663,7 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
back.push_back(to);
to = details(to).parent;
tn--;
cdebug << "to:" << tn << _to.abridged();
// cdebug << "to:" << tn << _to.abridged();
}
while (from != to)
{
@ -496,20 +673,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

49
libethereum/BlockChain.h

@ -58,6 +58,7 @@ struct FutureTime: virtual Exception {};
struct BlockChainChat: public LogChannel { static const char* name() { return "-B-"; } static const int verbosity = 7; };
struct BlockChainNote: public LogChannel { static const char* name() { return "=B="; } static const int verbosity = 4; };
struct BlockChainWarn: public LogChannel { static const char* name() { return "=B="; } static const int verbosity = 1; };
// TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::map<Address, Account> const& genesisState();
@ -77,6 +78,8 @@ enum {
ExtraBlocksBlooms
};
using ProgressCallback = std::function<void(unsigned, unsigned)>;
/**
* @brief Implements the blockchain database. All data this gives is disk-backed.
* @threadsafe
@ -85,10 +88,11 @@ enum {
class BlockChain
{
public:
BlockChain(bytes const& _genesisBlock, std::string _path, bool _killExisting);
BlockChain(bytes const& _genesisBlock, std::string _path, WithExisting _we, ProgressCallback const& _p = ProgressCallback());
~BlockChain();
void reopen(std::string _path, bool _killExisting = false) { close(); open(_path, _killExisting); }
/// Attempt a database re-open.
void reopen(std::string const& _path, WithExisting _we = WithExisting::Trust) { close(); open(_path, _we); }
/// (Potentially) renders invalid existing bytesConstRef returned by lastBlock.
/// To be called from main loop every 100ms or so.
@ -99,11 +103,11 @@ public:
/// 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;
@ -159,6 +163,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;
@ -186,21 +191,35 @@ public:
/// togther with all their quoted uncles.
h256Set allUnclesFrom(h256 const& _parent) const;
/** @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.
/// Run through database and verify all blocks by reevaluating.
/// 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 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
{
@ -222,10 +241,10 @@ public:
private:
static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); }
void open(std::string _path, bool _killExisting = false);
void open(std::string const& _path, WithExisting _we = WithExisting::Trust);
void close();
template<class T, unsigned N> T queryExtras(h256 const& _h, std::map<h256, T>& _m, boost::shared_mutex& _x, T const& _n) const
template<class T, unsigned N> T queryExtras(h256 const& _h, std::map<h256, T>& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const
{
{
ReadGuard l(_x);
@ -235,7 +254,7 @@ private:
}
std::string s;
m_extrasDB->Get(m_readOptions, toSlice(_h, N), &s);
(_extrasDB ? _extrasDB : m_extrasDB)->Get(m_readOptions, toSlice(_h, N), &s);
if (s.empty())
{
// cout << "Not found in DB: " << _h << endl;

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

4
libethereum/BlockQueue.h

@ -61,9 +61,9 @@ 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(); }

2
libethereum/CanonBlockChain.cpp

@ -92,6 +92,6 @@ bytes CanonBlockChain::createGenesisBlock()
return block.out();
}
CanonBlockChain::CanonBlockChain(std::string _path, bool _killExisting): BlockChain(CanonBlockChain::createGenesisBlock(), _path, _killExisting)
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(bool _killExisting = false): CanonBlockChain(std::string(), _killExisting) {}
CanonBlockChain(std::string _path, bool _killExisting = false);
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.

43
libethereum/Client.cpp

@ -38,14 +38,30 @@ using namespace p2p;
VersionChecker::VersionChecker(string const& _dbPath):
m_path(_dbPath.size() ? _dbPath : Defaults::dbPath())
{
auto protocolContents = contents(m_path + "/protocol");
auto databaseContents = contents(m_path + "/database");
m_ok = RLP(protocolContents).toInt<unsigned>(RLP::LaisezFaire) == eth::c_protocolVersion && RLP(databaseContents).toInt<unsigned>(RLP::LaisezFaire) == c_databaseVersion;
bytes statusBytes = contents(m_path + "/status");
RLP status(statusBytes);
try
{
auto protocolVersion = (unsigned)status[0];
auto minorProtocolVersion = (unsigned)status[1];
auto databaseVersion = (unsigned)status[2];
m_action =
protocolVersion != eth::c_protocolVersion || databaseVersion != c_databaseVersion ?
WithExisting::Kill
: minorProtocolVersion != eth::c_minorProtocolVersion ?
WithExisting::Verify
:
WithExisting::Trust;
}
catch (...)
{
m_action = WithExisting::Kill;
}
}
void VersionChecker::setOk()
{
if (!m_ok)
if (m_action != WithExisting::Trust)
{
try
{
@ -55,8 +71,7 @@ void VersionChecker::setOk()
{
cwarn << "Unhandled exception! Failed to create directory: " << m_path << "\n" << boost::current_exception_diagnostic_information();
}
writeFile(m_path + "/protocol", rlp(eth::c_protocolVersion));
writeFile(m_path + "/database", rlp(c_databaseVersion));
writeFile(m_path + "/status", rlpList(eth::c_protocolVersion, eth::c_minorProtocolVersion, c_databaseVersion));
}
}
@ -102,12 +117,12 @@ void BasicGasPricer::update(BlockChain const& _bc)
}
}
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean, u256 _networkId, int _miners):
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners):
Worker("eth"),
m_vc(_dbPath),
m_bc(_dbPath, !m_vc.ok() || _forceClean),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "..." << endl; }),
m_gp(new TrivialGasPricer),
m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)),
m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(Address(), m_stateDB),
m_postMine(Address(), m_stateDB)
{
@ -127,12 +142,12 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean,
startWorking();
}
Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string const& _dbPath, bool _forceClean, u256 _networkId, int _miners):
Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners):
Worker("eth"),
m_vc(_dbPath),
m_bc(_dbPath, !m_vc.ok() || _forceClean),
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, !m_vc.ok() || _forceClean)),
m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(Address(), m_stateDB),
m_postMine(Address(), m_stateDB)
{
@ -202,9 +217,9 @@ void Client::killChain()
{
WriteGuard l(x_stateDB);
m_stateDB = OverlayDB();
m_stateDB = State::openDB(Defaults::dbPath(), true);
m_stateDB = State::openDB(Defaults::dbPath(), WithExisting::Kill);
}
m_bc.reopen(Defaults::dbPath(), true);
m_bc.reopen(Defaults::dbPath(), WithExisting::Kill);
m_preMine = State(Address(), m_stateDB);
m_postMine = State(Address(), m_stateDB);

8
libethereum/Client.h

@ -65,10 +65,10 @@ public:
VersionChecker(std::string const& _dbPath);
void setOk();
bool ok() const { return m_ok; }
WithExisting action() const { return m_action; }
private:
bool m_ok;
WithExisting m_action;
std::string m_path;
};
@ -126,7 +126,7 @@ public:
explicit Client(
p2p::Host* _host,
std::string const& _dbPath = std::string(),
bool _forceClean = false,
WithExisting _forceAction = WithExisting::Trust,
u256 _networkId = 0,
int _miners = -1
);
@ -135,7 +135,7 @@ public:
p2p::Host* _host,
std::shared_ptr<GasPricer> _gpForAdoption, // pass it in with new.
std::string const& _dbPath = std::string(),
bool _forceClean = false,
WithExisting _forceAction = WithExisting::Trust,
u256 _networkId = 0,
int _miners = -1
);

8
libethereum/ClientBase.cpp

@ -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;
}

4
libethereum/Executive.h

@ -61,9 +61,9 @@ class Executive
{
public:
/// Basic constructor.
Executive(State& _s, LastHashes const& _lh, unsigned _level): m_s(_s), m_lastHashes(_lh), m_depth(_level) {}
Executive(State& _s, LastHashes const& _lh, unsigned _level = 0): m_s(_s), m_lastHashes(_lh), m_depth(_level) {}
/// Basic constructor.
Executive(State& _s, BlockChain const& _bc, unsigned _level);
Executive(State& _s, BlockChain const& _bc, unsigned _level = 0);
/// Basic destructor.
~Executive() = default;

39
libethereum/State.cpp

@ -43,16 +43,17 @@ using namespace dev;
using namespace dev::eth;
#define ctrace clog(StateTrace)
#define ETH_TIMED_ENACTMENTS 0
static const u256 c_blockReward = 1500 * finney;
OverlayDB State::openDB(std::string _path, bool _killExisting)
OverlayDB State::openDB(std::string _path, WithExisting _we)
{
if (_path.empty())
_path = Defaults::get()->m_dbPath;
boost::filesystem::create_directory(_path);
if (_killExisting)
if (_we == WithExisting::Kill)
boost::filesystem::remove_all(_path + "/state");
ldb::Options o;
@ -353,16 +354,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

4
libethereum/State.h

@ -123,8 +123,8 @@ public:
Address address() const { return m_ourAddress; }
/// Open a DB - useful for passing into the constructor & keeping for other states that are necessary.
static OverlayDB openDB(std::string _path, bool _killExisting = false);
static OverlayDB openDB(bool _killExisting = false) { return openDB(std::string(), _killExisting); }
static OverlayDB openDB(std::string _path, WithExisting _we = WithExisting::Trust);
static OverlayDB openDB(WithExisting _we = WithExisting::Trust) { return openDB(std::string(), _we); }
OverlayDB const& db() const { return m_db; }
/// @returns the set containing all addresses currently in use in Ethereum.

2
libevm/VM.h

@ -56,7 +56,7 @@ public:
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d >= c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 curPC() const { return m_curPC; }

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;

14
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();
}
@ -358,16 +359,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
libtestutils/BlockChainLoader.cpp

@ -35,7 +35,7 @@ BlockChainLoader::BlockChainLoader(Json::Value const& _json)
m_state = sl.state();
// load genesisBlock
m_bc.reset(new BlockChain(fromHex(_json["genesisRLP"].asString()), m_dir.path(), true));
m_bc.reset(new BlockChain(fromHex(_json["genesisRLP"].asString()), m_dir.path(), WithExisting::Kill));
// load blocks
for (auto const& block: _json["blocks"])

5
libtestutils/CMakeLists.txt

@ -32,7 +32,10 @@ endif()
target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} ethereum)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
if (JSONRPC)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
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;

85
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -57,58 +57,70 @@ const unsigned dev::SensibleHttpPort = 8080;
static Json::Value toJson(dev::eth::BlockInfo const& _bi)
{
Json::Value res;
res["hash"] = toJS(_bi.hash);
res["parentHash"] = toJS(_bi.parentHash);
res["sha3Uncles"] = toJS(_bi.sha3Uncles);
res["miner"] = toJS(_bi.coinbaseAddress);
res["stateRoot"] = toJS(_bi.stateRoot);
res["transactionsRoot"] = toJS(_bi.transactionsRoot);
res["difficulty"] = toJS(_bi.difficulty);
res["number"] = toJS(_bi.number);
res["gasUsed"] = toJS(_bi.gasUsed);
res["gasLimit"] = toJS(_bi.gasLimit);
res["timestamp"] = toJS(_bi.timestamp);
res["extraData"] = toJS(_bi.extraData);
res["nonce"] = toJS(_bi.nonce);
res["logsBloom"] = toJS(_bi.logBloom);
if (_bi)
{
res["hash"] = toJS(_bi.hash);
res["parentHash"] = toJS(_bi.parentHash);
res["sha3Uncles"] = toJS(_bi.sha3Uncles);
res["miner"] = toJS(_bi.coinbaseAddress);
res["stateRoot"] = toJS(_bi.stateRoot);
res["transactionsRoot"] = toJS(_bi.transactionsRoot);
res["difficulty"] = toJS(_bi.difficulty);
res["number"] = toJS(_bi.number);
res["gasUsed"] = toJS(_bi.gasUsed);
res["gasLimit"] = toJS(_bi.gasLimit);
res["timestamp"] = toJS(_bi.timestamp);
res["extraData"] = toJS(_bi.extraData);
res["nonce"] = toJS(_bi.nonce);
res["logsBloom"] = toJS(_bi.logBloom);
}
return res;
}
static Json::Value toJson(dev::eth::Transaction const& _t)
{
Json::Value res;
res["hash"] = toJS(_t.sha3());
res["input"] = toJS(_t.data());
res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.receiveAddress());
res["from"] = toJS(_t.safeSender());
res["gas"] = toJS(_t.gas());
res["gasPrice"] = toJS(_t.gasPrice());
res["nonce"] = toJS(_t.nonce());
res["value"] = toJS(_t.value());
if (_t)
{
res["hash"] = toJS(_t.sha3());
res["input"] = toJS(_t.data());
res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.receiveAddress());
res["from"] = toJS(_t.safeSender());
res["gas"] = toJS(_t.gas());
res["gasPrice"] = toJS(_t.gasPrice());
res["nonce"] = toJS(_t.nonce());
res["value"] = toJS(_t.value());
}
return res;
}
static Json::Value toJson(dev::eth::BlockInfo const& _bi, UncleHashes const& _us, Transactions const& _ts)
{
Json::Value res = toJson(_bi);
res["uncles"] = Json::Value(Json::arrayValue);
for (h256 h: _us)
res["uncles"].append(toJS(h));
res["transactions"] = Json::Value(Json::arrayValue);
for (Transaction const& t: _ts)
res["transactions"].append(toJson(t));
if (_bi)
{
res["uncles"] = Json::Value(Json::arrayValue);
for (h256 h: _us)
res["uncles"].append(toJS(h));
res["transactions"] = Json::Value(Json::arrayValue);
for (Transaction const& t: _ts)
res["transactions"].append(toJson(t));
}
return res;
}
static Json::Value toJson(dev::eth::BlockInfo const& _bi, UncleHashes const& _us, TransactionHashes const& _ts)
{
Json::Value res = toJson(_bi);
res["uncles"] = Json::Value(Json::arrayValue);
for (h256 h: _us)
res["uncles"].append(toJS(h));
res["transactions"] = Json::Value(Json::arrayValue);
for (h256 const& t: _ts)
res["transactions"].append(toJS(t));
if (_bi)
{
res["uncles"] = Json::Value(Json::arrayValue);
for (h256 h: _us)
res["uncles"].append(toJS(h));
res["transactions"] = Json::Value(Json::arrayValue);
for (h256 const& t: _ts)
res["transactions"].append(toJS(t));
}
return res;
}
@ -284,6 +296,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());

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" : "" },

4
libwebthree/WebThree.cpp

@ -39,7 +39,7 @@ using namespace dev::shh;
WebThreeDirect::WebThreeDirect(
std::string const& _clientVersion,
std::string const& _dbPath,
bool _forceClean,
WithExisting _we,
std::set<std::string> const& _interfaces,
NetworkPreferences const& _n,
bytesConstRef _network, int _miners
@ -50,7 +50,7 @@ WebThreeDirect::WebThreeDirect(
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
if (_interfaces.count("eth"))
m_ethereum.reset(new eth::Client(&m_net, _dbPath, _forceClean, 0, _miners));
m_ethereum.reset(new eth::Client(&m_net, _dbPath, _we, 0, _miners));
if (_interfaces.count("shh"))
m_whisper = m_net.registerCapability<WhisperHost>(new WhisperHost);

10
libwebthree/WebThree.h

@ -25,17 +25,13 @@
#include <mutex>
#include <list>
#include <atomic>
// Make sure boost/asio.hpp is included before windows.h.
#include <boost/asio.hpp>
#include <boost/asio.hpp> // Make sure boost/asio.hpp is included before windows.h.
#include <boost/utility.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Exceptions.h>
#include <libp2p/Host.h>
#include <libwhisper/WhisperHost.h>
#include <libethereum/Client.h>
@ -113,7 +109,7 @@ public:
WebThreeDirect(
std::string const& _clientVersion,
std::string const& _dbPath,
bool _forceClean = false,
WithExisting _we = WithExisting::Trust,
std::set<std::string> const& _interfaces = {"eth", "shh"},
p2p::NetworkPreferences const& _n = p2p::NetworkPreferences(),
bytesConstRef _network = bytesConstRef(),
@ -131,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)

5
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
{
@ -212,7 +213,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
emit runStateChanged();
//run sequence
QtConcurrent::run([=]()
m_runFuture = QtConcurrent::run([=]()
{
try
{

2
mix/ClientModel.h

@ -27,6 +27,7 @@
#include <map>
#include <QString>
#include <QVariantMap>
#include <QFuture>
#include "MachineStates.h"
namespace dev
@ -206,6 +207,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.h

@ -37,7 +37,7 @@ namespace mix
class MixBlockChain: public dev::eth::BlockChain
{
public:
MixBlockChain(std::string const& _path, h256 _stateRoot): BlockChain(createGenesisBlock(_stateRoot), _path, true) {}
MixBlockChain(std::string const& _path, h256 _stateRoot): BlockChain(createGenesisBlock(_stateRoot), _path, WithExisting::Kill) {}
static bytes createGenesisBlock(h256 _stateRoot);
};

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

@ -32,7 +32,7 @@ Window {
property string currentAccount
property alias gasToUse: gasToUseInput.text
color: Style.generic.layout.backgroundColor
color: appStyle.generic.layout.backgroundColor
function close()
{
@ -330,7 +330,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;
}
}
}

59
mix/test/TestService.h

@ -0,0 +1,59 @@
/*
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.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <QObject>
class QWindow;
namespace dev
{
namespace mix
{
class TestService: public QObject
{
Q_OBJECT
Q_PROPERTY(QObject* targetWindow READ targetWindow WRITE setTargetWindow)
public:
QObject* targetWindow() const { return m_targetWindow; }
void setTargetWindow(QObject* _window);
public slots:
bool waitForSignal(QObject* _item, QString _signalName, int _timeout);
bool keyPress(QObject* _item, int _key, int _modifiers, int _delay);
bool keyRelease(QObject* _item, int _key, int _modifiers, int _delay);
bool keyClick(QObject* _item, int _key, int _modifiers, int _delay);
bool keyPressChar(QObject* _item, QString const& _character, int _modifiers, int _delay);
bool keyReleaseChar(QObject* _item, QString const& _character, int _modifiers, int _delay);
bool keyClickChar(QObject* _item, QString const& _character, int _modifiers, int _delay);
bool mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay);
private:
QWindow* eventWindow(QObject* _item);
QObject* m_targetWindow;
};
}
}

142
mix/test/qml/TestMain.qml

@ -0,0 +1,142 @@
import QtQuick 2.2
import QtTest 1.1
import org.ethereum.qml.TestService 1.0
import "../../qml"
TestCase
{
id: tc
TestService
{
id: ts
targetWindow: mainApplication
function typeString(str, el)
{
if (el === undefined)
el = mainApplication;
for (var c in str)
{
ts.keyPressChar(el, str[c], Qt.NoModifier, 0);
ts.keyReleaseChar(el, str[c], Qt.NoModifier, 0);
}
}
}
function newProject()
{
waitForRendering(mainApplication.mainContent, 10000);
mainApplication.projectModel.createProject();
var projectDlg = mainApplication.projectModel.newProjectDialog;
wait(30);
projectDlg.projectTitle = "TestProject";
projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path
projectDlg.acceptAndClose();
wait(30);
}
function editContract(c)
{
mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText(c);
ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000))
fail("not compiled");
}
function clickElement(el, x, y)
{
ts.mouseClick(el, x, y, Qt.LeftButton, Qt.NoModifier, 10)
}
function test_defaultTransactionSequence()
{
newProject();
editContract(
"contract Contract {\r" +
" function Contract() {\r" +
" uint x = 69;\r" +
" uint y = 5;\r" +
" for (uint i = 0; i < y; ++i) {\r" +
" x += 42;\r" +
" z += x;\r" +
" }\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("not run");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3);
}
function test_transactionWithParameter()
{
newProject();
editContract(
"contract Contract {\r" +
" function setZ(uint256 x) {\r" +
" z = x;\r" +
" }\r" +
" function getZ() returns(uint256) {\r" +
" return z;\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.addTransaction();
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
transactionDialog.selectFunction("setZ");
clickElement(transactionDialog, 140, 300);
ts.typeString("442", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
transactionDialog.selectFunction("getZ");
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
wait(1);
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("not run");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 5);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(442)");
}
function test_constructorParameters()
{
newProject();
editContract(
"contract Contract {\r" +
" function Contract(uint256 x) {\r" +
" z = x;\r" +
" }\r" +
" function getZ() returns(uint256) {\r" +
" return z;\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.editTransaction(2);
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
clickElement(transactionDialog, 140, 300);
ts.typeString("442", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
transactionDialog.selectFunction("getZ");
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
wait(1);
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("not run");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 4);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "returned", "(442)");
}
Application
{
id: mainApplication
}
}

84
mix/web.qrc

@ -1,44 +1,44 @@
<RCC>
<qresource prefix="/">
<file>qml/WebPreview.qml</file>
<file>qml/html/WebContainer.html</file>
<file>qml/html/cm/active-line.js</file>
<file>qml/html/codeeditor.html</file>
<file>qml/html/cm/codemirror.css</file>
<file>qml/html/cm/codemirror.js</file>
<file>qml/html/cm/javascript.js</file>
<file>qml/html/cm/matchbrackets.js</file>
<file alias="qml/CodeEditor.qml">qml/WebCodeEditor.qml</file>
<file>qml/html/codeeditor.js</file>
<file>qml/html/cm/fullscreen.css</file>
<file>qml/html/cm/fullscreen.js</file>
<file>qml/html/cm/solarized.css</file>
<file>qml/html/cm/xml.js</file>
<file>qml/html/cm/htmlmixed.js</file>
<file>qml/html/cm/css.js</file>
<file>qml/html/cm/solidity.js</file>
<file>qml/html/cm/dialog.css</file>
<file>qml/html/cm/dialog.js</file>
<file>qml/html/cm/search.js</file>
<file>qml/html/cm/searchcursor.js</file>
<file>qml/html/cm/anyword-hint.js</file>
<file>qml/html/cm/show-hint.js</file>
<file>qml/html/cm/show-hint.css</file>
<file>qml/html/cm/closebrackets.js</file>
<file>qml/html/cm/solidityToken.js</file>
<file>qml/html/cm/javascript-hint.js</file>
<file>qml/html/cm/errorannotation.js</file>
<file>qml/html/cm/tern.js</file>
<file>qml/html/cm/ecma5spec.js</file>
<file>qml/html/cm/comment.js</file>
<file>qml/html/cm/def.js</file>
<file>qml/html/cm/doc_comment.js</file>
<file>qml/html/cm/infer.js</file>
<file>qml/html/cm/signal.js</file>
<file>qml/html/cm/ternserver.js</file>
<file>qml/html/cm/acorn.js</file>
<file>qml/html/cm/acorn_loose.js</file>
<file>qml/html/cm/walk.js</file>
<file>qml/html/cm/mark-selection.js</file>
</qresource>
<qresource prefix="/">
<file>qml/WebCodeEditor.qml</file>
<file>qml/WebPreview.qml</file>
<file>qml/html/WebContainer.html</file>
<file>qml/html/cm/acorn.js</file>
<file>qml/html/cm/acorn_loose.js</file>
<file>qml/html/cm/active-line.js</file>
<file>qml/html/cm/anyword-hint.js</file>
<file>qml/html/cm/closebrackets.js</file>
<file>qml/html/cm/codemirror.css</file>
<file>qml/html/cm/codemirror.js</file>
<file>qml/html/cm/comment.js</file>
<file>qml/html/cm/css.js</file>
<file>qml/html/cm/def.js</file>
<file>qml/html/cm/dialog.css</file>
<file>qml/html/cm/dialog.js</file>
<file>qml/html/cm/doc_comment.js</file>
<file>qml/html/cm/ecma5spec.js</file>
<file>qml/html/cm/errorannotation.js</file>
<file>qml/html/cm/fullscreen.css</file>
<file>qml/html/cm/fullscreen.js</file>
<file>qml/html/cm/htmlmixed.js</file>
<file>qml/html/cm/infer.js</file>
<file>qml/html/cm/javascript-hint.js</file>
<file>qml/html/cm/javascript.js</file>
<file>qml/html/cm/mark-selection.js</file>
<file>qml/html/cm/matchbrackets.js</file>
<file>qml/html/cm/search.js</file>
<file>qml/html/cm/searchcursor.js</file>
<file>qml/html/cm/show-hint.css</file>
<file>qml/html/cm/show-hint.js</file>
<file>qml/html/cm/signal.js</file>
<file>qml/html/cm/solarized.css</file>
<file>qml/html/cm/solidity.js</file>
<file>qml/html/cm/solidityToken.js</file>
<file>qml/html/cm/tern.js</file>
<file>qml/html/cm/ternserver.js</file>
<file>qml/html/cm/walk.js</file>
<file>qml/html/cm/xml.js</file>
<file>qml/html/codeeditor.html</file>
<file>qml/html/codeeditor.js</file>
</qresource>
</RCC>

2
neth/main.cpp

@ -550,7 +550,7 @@ int main(int argc, char** argv)
dev::WebThreeDirect web3(
clientImplString,
dbPath,
killChain,
killChain ? WithExisting::Kill : WithExisting::Trust,
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs,
&nodesState,

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

Loading…
Cancel
Save