Browse Source

Removed Mix

cl-refactor
arkpar 10 years ago
parent
commit
9fa014b940
  1. 4
      CMakeLists.txt
  2. 1
      mix/.gitignore
  3. 97
      mix/CMakeLists.txt
  4. 986
      mix/ClientModel.cpp
  5. 298
      mix/ClientModel.h
  6. 40
      mix/Clipboard.cpp
  7. 52
      mix/Clipboard.h
  8. 171
      mix/CodeHighlighter.cpp
  9. 108
      mix/CodeHighlighter.h
  10. 698
      mix/CodeModel.cpp
  11. 308
      mix/CodeModel.h
  12. 383
      mix/ContractCallDataEncoder.cpp
  13. 91
      mix/ContractCallDataEncoder.h
  14. 185
      mix/DebuggingStateWrapper.cpp
  15. 204
      mix/DebuggingStateWrapper.h
  16. 31
      mix/Exceptions.cpp
  17. 52
      mix/Exceptions.h
  18. 234
      mix/FileIo.cpp
  19. 86
      mix/FileIo.h
  20. 192
      mix/HttpServer.cpp
  21. 131
      mix/HttpServer.h
  22. 59
      mix/InverseMouseArea.cpp
  23. 57
      mix/InverseMouseArea.h
  24. 96
      mix/MachineStates.h
  25. 116
      mix/MixApplication.cpp
  26. 72
      mix/MixApplication.h
  27. 384
      mix/MixClient.cpp
  28. 135
      mix/MixClient.h
  29. 41
      mix/QBasicNodeDefinition.cpp
  30. 59
      mix/QBasicNodeDefinition.h
  31. 96
      mix/QBigInt.cpp
  32. 109
      mix/QBigInt.h
  33. 57
      mix/QContractDefinition.cpp
  34. 65
      mix/QContractDefinition.h
  35. 55
      mix/QEther.cpp
  36. 92
      mix/QEther.h
  37. 67
      mix/QFunctionDefinition.cpp
  38. 71
      mix/QFunctionDefinition.h
  39. 72
      mix/QVariableDeclaration.cpp
  40. 103
      mix/QVariableDeclaration.h
  41. 37
      mix/QVariableDefinition.cpp
  42. 71
      mix/QVariableDefinition.h
  43. 74
      mix/SolidityType.h
  44. 156
      mix/SortFilterProxyModel.cpp
  45. 97
      mix/SortFilterProxyModel.h
  46. 166
      mix/Web3Server.cpp
  47. 70
      mix/Web3Server.h
  48. 47
      mix/main.cpp
  49. 6
      mix/noweb.qrc
  50. 5
      mix/osx.qrc
  51. 81
      mix/qml.qrc
  52. 30
      mix/qml/AlertMessageDialog.qml
  53. 438
      mix/qml/Application.qml
  54. 22
      mix/qml/BasicMessage.qml
  55. 13
      mix/qml/BigIntValue.qml
  56. 378
      mix/qml/Block.qml
  57. 664
      mix/qml/BlockChain.qml
  58. 73
      mix/qml/CallStack.qml
  59. 89
      mix/qml/CodeEditor.qml
  60. 13
      mix/qml/CodeEditorStyle.qml
  61. 387
      mix/qml/CodeEditorView.qml
  62. 9
      mix/qml/CommonSeparator.qml
  63. 33
      mix/qml/DebugBasicInfo.qml
  64. 152
      mix/qml/DebugInfoList.qml
  65. 671
      mix/qml/Debugger.qml
  66. 16
      mix/qml/DebuggerPaneStyle.qml
  67. 10
      mix/qml/DefaultLabel.qml
  68. 6
      mix/qml/DefaultTextField.qml
  69. 540
      mix/qml/DeployContractStep.qml
  70. 187
      mix/qml/DeploymentDialog.qml
  71. 292
      mix/qml/DeploymentDialogSteps.qml
  72. 250
      mix/qml/DeploymentWorker.qml
  73. 99
      mix/qml/Ether.qml
  74. 15
      mix/qml/EtherValue.qml
  75. 302
      mix/qml/FilesSection.qml
  76. 61
      mix/qml/ItemDelegateDataDump.qml
  77. 133
      mix/qml/KeyValuePanel.qml
  78. 648
      mix/qml/LogsPane.qml
  79. 30
      mix/qml/LogsPaneStyle.qml
  80. 298
      mix/qml/MacFileDialog.qml
  81. 246
      mix/qml/MainContent.qml
  82. 30
      mix/qml/ModalDialog.qml
  83. 123
      mix/qml/NewProjectDialog.qml
  84. 212
      mix/qml/PackagingStep.qml
  85. 32
      mix/qml/ProjectFilesStyle.qml
  86. 225
      mix/qml/ProjectList.qml
  87. 230
      mix/qml/ProjectModel.qml
  88. 226
      mix/qml/QAddressView.qml
  89. 109
      mix/qml/QBoolTypeView.qml
  90. 5
      mix/qml/QFileDialog.qml
  91. 27
      mix/qml/QHashTypeView.qml
  92. 26
      mix/qml/QIntTypeView.qml
  93. 15
      mix/qml/QRealTypeView.qml
  94. 31
      mix/qml/QStringTypeView.qml
  95. 7
      mix/qml/QVariableDeclaration.qml
  96. 13
      mix/qml/QVariableDefinition.qml
  97. 352
      mix/qml/RegisteringStep.qml
  98. 130
      mix/qml/ScenarioButton.qml
  99. 128
      mix/qml/ScenarioExecution.qml
  100. 386
      mix/qml/ScenarioLoader.qml

4
CMakeLists.txt

@ -509,10 +509,6 @@ if (GUI)
# add_subdirectory(third) // reenable once not qtwebkit.
endif()
if (SOLIDITY)
add_subdirectory(mix)
endif ()
endif()
if (APPLE AND GUI)

1
mix/.gitignore

@ -1 +0,0 @@
*.pro

97
mix/CMakeLists.txt

@ -1,97 +0,0 @@
cmake_policy(SET CMP0015 NEW)
# let cmake autolink dependencies on windows
cmake_policy(SET CMP0020 NEW)
# this policy was introduced in cmake 3.0
# remove if, once 3.0 will be used on unix
if (${CMAKE_MAJOR_VERSION} GREATER 2)
cmake_policy(SET CMP0043 OLD)
endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
include_directories(BEFORE ..)
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "3.6") AND NOT APPLE)
# Supress warnings for qt headers for clang+ccache
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override")
endif ()
#TODO: remove once qt 5.5.1 is out
if (APPLE)
qt5_add_resources(UI_RESOURCES osx.qrc)
endif()
find_package (Qt5WebEngine)
if (APPLE AND (NOT "${Qt5Core_VERSION_STRING}" VERSION_LESS "5.5"))
# TODO: remove indirect dependencies once macdeployqt is fixed
find_package (Qt5WebEngineCore)
find_package (Qt5DBus)
find_package (Qt5PrintSupport)
endif()
qt5_add_resources(UI_RESOURCES res.qrc qml.qrc)
file(GLOB HEADERS "*.h")
set(EXECUTABLE mix)
if ("${Qt5WebEngine_VERSION_STRING}" VERSION_GREATER "5.3.0")
set (ETH_HAVE_WEBENGINE TRUE)
qt5_add_resources(UI_RESOURCES web.qrc)
else()
qt5_add_resources(UI_RESOURCES noweb.qrc)
endif()
if (CMAKE_BUILD_TYPE MATCHES Debug)
add_definitions(-DQT_QML_DEBUG)
endif()
# eth_add_executable is defined in cmake/EthExecutableHelper.cmake
eth_add_executable(${EXECUTABLE}
ICON mix
UI_RESOURCES ${UI_RESOURCES}
)
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)
list(APPEND LIBRARIES "Qt5::WebEngine")
endif()
if (APPLE AND (NOT "${Qt5Core_VERSION_STRING}" VERSION_LESS "5.5"))
list(APPEND LIBRARIES "Qt5::WebEngineCore")
list(APPEND LIBRARIES "Qt5::DBus")
list(APPEND LIBRARIES "Qt5::PrintSupport")
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
)
#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} ${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)

986
mix/ClientModel.cpp

@ -1,986 +0,0 @@
/*
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 ClientModel.cpp
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
// Make sure boost/asio.hpp is included before windows.h.
#include <boost/asio.hpp>
#include "ClientModel.h"
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <QStandardPaths>
#include <jsonrpccpp/server.h>
#include <libethcore/CommonJS.h>
#include <libethereum/Transaction.h>
#include <libdevcore/FixedHash.h>
#include "DebuggingStateWrapper.h"
#include "Exceptions.h"
#include "QContractDefinition.h"
#include "QVariableDeclaration.h"
#include "QVariableDefinition.h"
#include "ContractCallDataEncoder.h"
#include "CodeModel.h"
#include "QEther.h"
#include "Web3Server.h"
#include "MixClient.h"
using namespace dev;
using namespace dev::eth;
using namespace std;
namespace dev
{
namespace mix
{
class RpcConnector: public jsonrpc::AbstractServerConnector
{
public:
virtual bool StartListening() override { return true; }
virtual bool StopListening() override { return true; }
virtual bool SendResponse(string const& _response, void*) override
{
m_response = QString::fromStdString(_response);
return true;
}
QString response() const { return m_response; }
private:
QString m_response;
};
ClientModel::ClientModel():
m_running(false), m_rpcConnector(new RpcConnector())
{
qRegisterMetaType<QBigInt*>("QBigInt*");
qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*");
qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>");
qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
qRegisterMetaType<QSolidityType*>("QSolidityType*");
qRegisterMetaType<QMachineState*>("QMachineState");
qRegisterMetaType<QInstruction*>("QInstruction");
qRegisterMetaType<QCode*>("QCode");
qRegisterMetaType<QCallData*>("QCallData");
qRegisterMetaType<RecordLogEntry*>("RecordLogEntry*");
}
ClientModel::~ClientModel()
{
m_runFuture.waitForFinished();
}
void ClientModel::init(QString _dbpath)
{
m_dbpath = _dbpath;
if (m_dbpath.isEmpty())
m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
else
m_client.reset(new MixClient(m_dbpath.toStdString()));
m_ethAccounts = make_shared<FixedAccountHolder>([=](){return m_client.get();}, std::vector<KeyPair>());
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_ethAccounts, std::vector<KeyPair>(), m_client.get()));
connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection);
}
QString ClientModel::apiCall(QString const& _message)
{
try
{
m_rpcConnector->OnRequest(_message.toStdString(), nullptr);
return m_rpcConnector->response();
}
catch (...)
{
cerr << boost::current_exception_diagnostic_information();
return QString();
}
}
void ClientModel::mine()
{
if (m_mining)
BOOST_THROW_EXCEPTION(ExecutionStateException());
m_mining = true;
emit miningStarted();
emit miningStateChanged();
m_runFuture = QtConcurrent::run([=]()
{
try
{
m_client->mine();
newBlock();
m_mining = false;
emit miningComplete();
}
catch (...)
{
m_mining = false;
cerr << boost::current_exception_diagnostic_information();
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
}
emit miningStateChanged();
});
}
QString ClientModel::newSecret()
{
KeyPair a = KeyPair::create();
return QString::fromStdString(dev::toHex(a.secret().ref()));
}
QString ClientModel::address(QString const& _secret)
{
return QString::fromStdString(dev::toHex(KeyPair(Secret(_secret.toStdString())).address().ref()));
}
QString ClientModel::toHex(QString const& _int)
{
return QString::fromStdString(dev::toHex(dev::u256(_int.toStdString())));
}
QString ClientModel::encodeAbiString(QString _string)
{
ContractCallDataEncoder encoder;
return QString::fromStdString(dev::toHex(encoder.encodeBytes(_string)));
}
QString ClientModel::encodeStringParam(QString const& _param)
{
ContractCallDataEncoder encoder;
return QString::fromStdString(dev::toHex(encoder.encodeStringParam(_param, 32)));
}
QStringList ClientModel::encodeParams(QVariant const& _param, QString const& _contract, QString const& _function)
{
QStringList ret;
CompiledContract const& compilerRes = m_codeModel->contract(_contract);
QList<QVariableDeclaration*> paramsList;
shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract();
if (_contract == _function)
paramsList = contractDef->constructor()->parametersList();
else
for (QFunctionDefinition* tf: contractDef->functionsList())
if (tf->name() == _function)
{
paramsList = tf->parametersList();
break;
}
if (paramsList.length() > 0)
for (QVariableDeclaration* var: paramsList)
{
ContractCallDataEncoder encoder;
QSolidityType const* type = var->type();
QVariant value = _param.toMap().value(var->name());
encoder.encode(value, type->type());
ret.push_back(QString::fromStdString(dev::toHex(encoder.encodedData())));
}
return ret;
}
QVariantMap ClientModel::contractAddresses() const
{
QVariantMap res;
for (auto const& c: m_contractAddresses)
res.insert(c.first.first, QString::fromStdString(toJS(c.second)));
return res;
}
QVariantList ClientModel::gasCosts() const
{
QVariantList res;
for (auto const& c: m_gasCosts)
res.append(QVariant::fromValue(static_cast<int>(c)));
return res;
}
void ClientModel::addAccount(QString const& _secret)
{
KeyPair key(Secret(_secret.toStdString()));
m_accountsSecret.push_back(key);
Address address = key.address();
m_accounts[address] = Account(u256(0), Account::NormalCreation);
m_ethAccounts->setAccounts(m_accountsSecret);
}
QString ClientModel::resolveAddress(QString const& _secret)
{
KeyPair key(Secret(_secret.toStdString()));
return "0x" + QString::fromStdString(key.address().hex());
}
void ClientModel::setupScenario(QVariantMap _scenario)
{
onStateReset();
WriteGuard(x_queueTransactions);
m_running = true;
QVariantList blocks = _scenario.value("blocks").toList();
QVariantList stateAccounts = _scenario.value("accounts").toList();
QVariantList stateContracts = _scenario.value("contracts").toList();
m_accounts.clear();
m_accountsSecret.clear();
for (auto const& b: stateAccounts)
{
QVariantMap account = b.toMap();
Address address = {};
if (account.contains("secret"))
{
KeyPair key(Secret(account.value("secret").toString().toStdString()));
m_accountsSecret.push_back(key);
address = key.address();
}
else if (account.contains("address"))
address = Address(fromHex(account.value("address").toString().toStdString()));
if (!address)
continue;
m_accounts[address] = Account(qvariant_cast<QEther*>(account.value("balance"))->toU256Wei(), Account::NormalCreation);
}
m_ethAccounts->setAccounts(m_accountsSecret);
for (auto const& c: stateContracts)
{
QVariantMap contract = c.toMap();
Address address = Address(fromHex(contract.value("address").toString().toStdString()));
Account account(qvariant_cast<QEther*>(contract.value("balance"))->toU256Wei(), Account::ContractConception);
bytes code = fromHex(contract.value("code").toString().toStdString());
account.setCode(std::move(code));
QVariantMap storageMap = contract.value("storage").toMap();
for(auto s = storageMap.cbegin(); s != storageMap.cend(); ++s)
account.setStorage(fromBigEndian<u256>(fromHex(s.key().toStdString())), fromBigEndian<u256>(fromHex(s.value().toString().toStdString())));
m_accounts[address] = account;
}
bool trToExecute = false;
for (auto const& b: blocks)
{
QVariantList transactions = b.toMap().value("transactions").toList();
m_queueTransactions.push_back(transactions);
trToExecute = transactions.size() > 0;
}
m_client->resetState(m_accounts, Secret(_scenario.value("miner").toMap().value("secret").toString().toStdString()));
if (m_queueTransactions.count() > 0 && trToExecute)
{
setupExecutionChain();
processNextTransactions();
}
else
m_running = false;
}
void ClientModel::setupExecutionChain()
{
connect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions, Qt::QueuedConnection);
connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection);
connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection);
}
void ClientModel::stopExecution()
{
disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions);
disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock);
disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution);
m_running = false;
}
void ClientModel::finalizeBlock()
{
m_queueTransactions.pop_front();// pop last execution group. The last block is never mined (pending block)
if (m_queueTransactions.size() > 0)
mine();
else
{
stopExecution();
emit runComplete();
}
}
TransactionSettings ClientModel::transaction(QVariant const& _tr) const
{
QVariantMap transaction = _tr.toMap();
QString contractId = transaction.value("contractId").toString();
QString functionId = transaction.value("functionId").toString();
bool gasAuto = transaction.value("gasAuto").toBool();
u256 gas = 0;
if (transaction.value("gas").data())
gas = boost::get<u256>(qvariant_cast<QBigInt*>(transaction.value("gas"))->internalValue());
else
gasAuto = true;
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
QString sender = transaction.value("sender").toString();
bool isContractCreation = transaction.value("isContractCreation").toBool();
bool isFunctionCall = transaction.value("isFunctionCall").toBool();
if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later
contractId = m_codeModel->contracts().keys()[0];
Secret f = Secret(sender.toStdString());
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, f, isContractCreation, isFunctionCall);
transactionSettings.parameterValues = transaction.value("parameters").toMap();
if (contractId == functionId || functionId == "Constructor")
transactionSettings.functionId.clear();
return transactionSettings;
}
void ClientModel::processNextTransactions()
{
WriteGuard(x_queueTransactions);
vector<TransactionSettings> transactionSequence;
for (auto const& t: m_queueTransactions.front())
{
TransactionSettings transactionSettings = transaction(t);
transactionSequence.push_back(transactionSettings);
}
executeSequence(transactionSequence);
}
void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence)
{
if (m_running)
{
qWarning() << "Waiting for current execution to complete";
m_runFuture.waitForFinished();
}
emit runStarted();
//run sequence
m_runFuture = QtConcurrent::run([=]()
{
try
{
m_gasCosts.clear();
for (TransactionSettings const& transaction: _sequence)
{
std::pair<QString, int> ctrInstance = resolvePair(transaction.contractId);
QString address = resolveToken(ctrInstance);
if (!transaction.isFunctionCall)
{
callAddress(Address(address.toStdString()), bytes(), transaction);
onNewTransaction();
continue;
}
ContractCallDataEncoder encoder;
//encode data
CompiledContract const& compilerRes = m_codeModel->contract(ctrInstance.first);
QFunctionDefinition const* f = nullptr;
bytes contractCode = compilerRes.bytes();
shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract();
if (transaction.functionId.isEmpty())
f = contractDef->constructor();
else
for (QFunctionDefinition const* tf: contractDef->functionsList())
if (tf->name() == transaction.functionId)
{
f = tf;
break;
}
if (!f)
emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code."));
if (!transaction.functionId.isEmpty())
encoder.encode(f);
for (QVariableDeclaration const* p: f->parametersList())
{
QSolidityType const* type = p->type();
QVariant value = transaction.parameterValues.value(p->name());
if (type->type().type == SolidityType::Type::Address)
{
if (type->array())
{
QJsonArray jsonDoc = QJsonDocument::fromJson(value.toString().toUtf8()).array();
int k = 0;
for (QJsonValue const& item: jsonDoc)
{
if (item.toString().startsWith("<"))
{
std::pair<QString, int> ctrParamInstance = resolvePair(item.toString());
jsonDoc.replace(k, resolveToken(ctrParamInstance));
}
k++;
}
QJsonDocument doc(jsonDoc);
value = QVariant(doc.toJson(QJsonDocument::Compact));
}
else if (value.toString().startsWith("<"))
{
std::pair<QString, int> ctrParamInstance = resolvePair(value.toString());
value = QVariant(resolveToken(ctrParamInstance));
}
}
encoder.encode(value, type->type());
}
if (transaction.functionId.isEmpty() || transaction.functionId == ctrInstance.first)
{
bytes param = encoder.encodedData();
contractCode.insert(contractCode.end(), param.begin(), param.end());
Address newAddress = deployContract(contractCode, transaction);
std::pair<QString, int> contractToken = retrieveToken(transaction.contractId);
m_contractAddresses[contractToken] = newAddress;
m_contractNames[newAddress] = contractToken.first;
contractAddressesChanged();
gasCostsChanged();
}
else
{
auto contractAddressIter = m_contractAddresses.find(ctrInstance);
if (contractAddressIter == m_contractAddresses.end())
{
emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId);
Address fakeAddress = Address::random();
std::pair<QString, int> contractToken = resolvePair(transaction.contractId);
m_contractNames[fakeAddress] = contractToken.first;
callAddress(fakeAddress, encoder.encodedData(), transaction); //Transact to a random fake address to that transaction is added to the list anyway
}
else
callAddress(contractAddressIter->second, encoder.encodedData(), transaction);
}
m_gasCosts.append(m_client->lastExecution().gasUsed);
onNewTransaction();
TransactionException exception = m_client->lastExecution().excepted;
if (exception != TransactionException::None)
break;
}
emit runComplete();
}
catch(boost::exception const&)
{
cerr << boost::current_exception_diagnostic_information();
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
}
catch(exception const& e)
{
cerr << boost::current_exception_diagnostic_information();
emit runFailed(e.what());
}
emit runStateChanged();
});
}
void ClientModel::executeTr(QVariantMap _tr)
{
WriteGuard(x_queueTransactions);
QVariantList trs;
trs.push_back(_tr);
m_queueTransactions.push_back(trs);
if (!m_running)
{
m_running = true;
setupExecutionChain();
processNextTransactions();
}
}
std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId)
{
std::pair<QString, int> ret = std::make_pair(_contractId, 0);
if (_contractId.startsWith("<") && _contractId.endsWith(">"))
{
QStringList values = ret.first.remove("<").remove(">").split(" - ");
ret = std::make_pair(values[0], values[1].toUInt());
}
if (_contractId.startsWith("0x"))
ret = std::make_pair(_contractId, -2);
return ret;
}
QString ClientModel::resolveToken(std::pair<QString, int> const& _value)
{
if (_value.second == -2) //-2: first contains a real address
return _value.first;
else if (m_contractAddresses.size() > 0)
return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref()));
else
return _value.first;
}
std::pair<QString, int> ClientModel::retrieveToken(QString const& _value)
{
std::pair<QString, int> ret;
ret.first = _value;
ret.second = m_contractAddresses.size();
return ret;
}
void ClientModel::showDebugger()
{
ExecutionResult last = m_client->lastExecution();
showDebuggerForTransaction(last);
}
void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
{
//we need to wrap states in a QObject before sending to QML.
QDebugData* debugData = new QDebugData();
QQmlEngine::setObjectOwnership(debugData, QQmlEngine::JavaScriptOwnership);
QList<QCode*> codes;
QList<QHash<int, int>> codeMaps;
QList<AssemblyItems> codeItems;
QList<CompiledContract const*> contracts;
for (MachineCode const& code: _t.executionCode)
{
QHash<int, int> codeMap;
codes.push_back(QMachineState::getHumanReadableCode(debugData, code.address, code.code, codeMap));
codeMaps.push_back(move(codeMap));
//try to resolve contract for source level debugging
auto nameIter = m_contractNames.find(code.address);
CompiledContract const* compilerRes = nullptr;
if (nameIter != m_contractNames.end() && (compilerRes = m_codeModel->tryGetContract(nameIter->second))) //returned object is guaranteed to live till the end of event handler in main thread
{
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes->assemblyItems() : compilerRes->constructorAssemblyItems();
codes.back()->setDocument(compilerRes->documentId());
codeItems.push_back(move(assemblyItems));
contracts.push_back(compilerRes);
}
else
{
codeItems.push_back(AssemblyItems());
contracts.push_back(nullptr);
}
}
QList<QCallData*> data;
for (bytes const& d: _t.transactionData)
data.push_back(QMachineState::getDebugCallData(debugData, d));
QVariantList states;
QVariantList solCallStack;
map<int, QVariableDeclaration*> solLocals; //<stack pos, decl>
map<QString, QVariableDeclaration*> storageDeclarations; //<name, decl>
unsigned prevInstructionIndex = 0;
for (MachineState const& s: _t.machineStates)
{
int instructionIndex = codeMaps[s.codeIndex][static_cast<unsigned>(s.curPC)];
QSolState* solState = nullptr;
if (!codeItems[s.codeIndex].empty() && contracts[s.codeIndex])
{
CompiledContract const* contract = contracts[s.codeIndex];
AssemblyItem const& instruction = codeItems[s.codeIndex][instructionIndex];
if (instruction.type() == eth::Push && !instruction.data())
{
//register new local variable initialization
auto localIter = contract->locals().find(LocationPair(instruction.getLocation().start, instruction.getLocation().end));
if (localIter != contract->locals().end())
solLocals[s.stack.size()] = new QVariableDeclaration(debugData, localIter.value().name.toStdString(), localIter.value().type);
}
if (instruction.type() == eth::Tag)
{
//track calls into functions
AssemblyItem const& prevInstruction = codeItems[s.codeIndex][prevInstructionIndex];
QString functionName = m_codeModel->resolveFunctionName(instruction.getLocation());
if (!functionName.isEmpty() && ((prevInstruction.getJumpType() == AssemblyItem::JumpType::IntoFunction) || solCallStack.empty()))
solCallStack.push_front(QVariant::fromValue(functionName));
else if (prevInstruction.getJumpType() == AssemblyItem::JumpType::OutOfFunction && !solCallStack.empty())
{
solCallStack.pop_front();
solLocals.clear();
}
}
//format solidity context values
QVariantMap locals;
QVariantList localDeclarations;
QVariantMap localValues;
for (auto l: solLocals)
if (l.first < (int)s.stack.size())
{
if (l.second->type()->name().startsWith("mapping"))
break; //mapping type not yet managed
localDeclarations.push_back(QVariant::fromValue(l.second));
localValues[l.second->name()] = formatValue(l.second->type()->type(), s.stack[l.first]);
}
locals["variables"] = localDeclarations;
locals["values"] = localValues;
QVariantMap storage;
QVariantList storageDeclarationList;
QVariantMap storageValues;
for (auto st: s.storage)
if (st.first < numeric_limits<unsigned>::max())
{
auto storageIter = contract->storage().find(static_cast<unsigned>(st.first));
if (storageIter != contract->storage().end())
{
QVariableDeclaration* storageDec = nullptr;
for (SolidityDeclaration const& codeDec : storageIter.value())
{
if (codeDec.type.name.startsWith("mapping"))
continue; //mapping type not yet managed
auto decIter = storageDeclarations.find(codeDec.name);
if (decIter != storageDeclarations.end())
storageDec = decIter->second;
else
{
storageDec = new QVariableDeclaration(debugData, codeDec.name.toStdString(), codeDec.type);
storageDeclarations[storageDec->name()] = storageDec;
}
storageDeclarationList.push_back(QVariant::fromValue(storageDec));
storageValues[storageDec->name()] = formatStorageValue(storageDec->type()->type(), s.storage, codeDec.offset, codeDec.slot);
}
}
}
storage["variables"] = storageDeclarationList;
storage["values"] = storageValues;
prevInstructionIndex = instructionIndex;
// filter out locations that match whole function or contract
SourceLocation location = instruction.getLocation();
QString source;
if (location.sourceName)
source = QString::fromUtf8(location.sourceName->c_str());
if (m_codeModel->isContractOrFunctionLocation(location))
location = dev::SourceLocation(-1, -1, location.sourceName);
solState = new QSolState(debugData, move(storage), move(solCallStack), move(locals), location.start, location.end, source);
}
states.append(QVariant::fromValue(new QMachineState(debugData, instructionIndex, s, codes[s.codeIndex], data[s.dataIndex], solState)));
}
debugData->setStates(move(states));
debugDataReady(debugData);
}
QVariant ClientModel::formatValue(SolidityType const& _type, u256 const& _value)
{
ContractCallDataEncoder decoder;
bytes val = toBigEndian(_value);
QVariant res = decoder.decode(_type, val);
return res;
}
QVariant ClientModel::formatStorageValue(SolidityType const& _type, unordered_map<u256, u256> const& _storage, unsigned _offset, u256 const& _slot)
{
u256 slot = _slot;
QVariantList values;
ContractCallDataEncoder decoder;
u256 count = 1;
if (_type.dynamicSize)
{
count = _storage.at(slot);
slot = fromBigEndian<u256>(sha3(toBigEndian(slot)).asBytes());
}
else if (_type.array)
count = _type.count;
unsigned offset = _offset;
while (count--)
{
auto slotIter = _storage.find(slot);
u256 slotValue = slotIter != _storage.end() ? slotIter->second : u256();
bytes slotBytes = toBigEndian(slotValue);
auto start = slotBytes.end() - _type.size - offset;
bytes val(32 - _type.size); //prepend with zeroes
if (_type.type == SolidityType::SignedInteger && (*start & 0x80)) //extend sign
std::fill(val.begin(), val.end(), 0xff);
val.insert(val.end(), start, start + _type.size);
values.append(decoder.decode(_type, val));
offset += _type.size;
if ((offset + _type.size) > 32)
{
slot++;
offset = 0;
}
}
if (!_type.array)
return values[0];
return QVariant::fromValue(values);
}
void ClientModel::emptyRecord()
{
debugDataReady(new QDebugData());
}
void ClientModel::debugRecord(unsigned _index)
{
ExecutionResult e = m_client->execution(_index);
showDebuggerForTransaction(e);
}
Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
{
eth::TransactionSkeleton ts;
ts.creation = true;
ts.value = _ctrTransaction.value;
ts.data = _code;
ts.gas = _ctrTransaction.gas;
ts.gasPrice = _ctrTransaction.gasPrice;
ts.from = toAddress(_ctrTransaction.sender);
return m_client->submitTransaction(ts, _ctrTransaction.sender, _ctrTransaction.gasAuto).second;
}
void ClientModel::callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
{
eth::TransactionSkeleton ts;
ts.creation = false;
ts.value = _tr.value;
ts.to = _contract;
ts.data = _data;
ts.gas = _tr.gas;
ts.gasPrice = _tr.gasPrice;
ts.from = toAddress(_tr.sender);
m_client->submitTransaction(ts, _tr.sender, _tr.gasAuto);
}
RecordLogEntry* ClientModel::lastBlock() const
{
eth::BlockInfo blockInfo = m_client->blockInfo();
stringstream strGas;
strGas << blockInfo.gasUsed();
stringstream strNumber;
strNumber << blockInfo.number();
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantMap(), QVariantList());
QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership);
return record;
}
void ClientModel::onStateReset()
{
m_contractAddresses.clear();
m_contractNames.clear();
m_stdContractAddresses.clear();
m_stdContractNames.clear();
m_queueTransactions.clear();
emit stateCleared();
}
void ClientModel::onNewTransaction()
{
ExecutionResult const& tr = m_client->lastExecution();
switch (tr.excepted)
{
case TransactionException::None:
break;
case TransactionException::NotEnoughCash:
emit runFailed("Insufficient balance");
break;
case TransactionException::OutOfGasIntrinsic:
case TransactionException::OutOfGasBase:
case TransactionException::OutOfGas:
emit runFailed("Not enough gas");
break;
case TransactionException::BlockGasLimitReached:
emit runFailed("Block gas limit reached");
break;
case TransactionException::BadJumpDestination:
emit runFailed("Solidity exception (bad jump)");
break;
case TransactionException::OutOfStack:
emit runFailed("Out of stack");
break;
case TransactionException::StackUnderflow:
emit runFailed("Stack underflow");
//these should not happen in mix
case TransactionException::Unknown:
case TransactionException::BadInstruction:
case TransactionException::InvalidSignature:
case TransactionException::InvalidNonce:
case TransactionException::InvalidFormat:
case TransactionException::BadRLP:
emit runFailed("Internal execution error");
break;
}
unsigned block = m_client->number() + 1;
unsigned recordIndex = tr.executonIndex;
QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex);
QString address = QString::fromStdString(toJS(tr.address));
QString value = QString::fromStdString(toString(tr.value));
QString contract = address;
QString function;
QString returned;
QString gasUsed;
bool creation = (bool)tr.contractAddress;
if (!tr.isCall())
gasUsed = QString::fromStdString(toString(tr.gasUsed));
//TODO: handle value transfer
FixedHash<4> functionHash;
bool abi = false;
if (creation)
{
//contract creation
function = QObject::tr("Constructor");
address = QObject::tr("(Create contract)");
}
else
{
//transaction/call
if (tr.inputParameters.size() >= 4)
{
functionHash = FixedHash<4>(tr.inputParameters.data(), FixedHash<4>::ConstructFromPointer);
function = QString::fromStdString(toJS(functionHash));
abi = true;
}
else
function = QObject::tr("<none>");
}
if (creation)
returned = QString::fromStdString(toJS(tr.contractAddress));
Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress;
auto contractAddressIter = m_contractNames.find(contractAddress);
QVariantMap inputParameters;
QVariantMap returnParameters;
QVariantList logs;
if (contractAddressIter != m_contractNames.end())
{
ContractCallDataEncoder encoder;
CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second);
const QContractDefinition* def = compilerRes.contract();
contract = def->name();
if (creation)
function = contract;
if (abi)
{
QFunctionDefinition const* funcDef = def->getFunction(functionHash);
if (funcDef)
{
function = funcDef->name();
QStringList returnValues = encoder.decode(funcDef->returnParameters(), tr.result.output);
returned += "(";
returned += returnValues.join(", ");
returned += ")";
QStringList returnParams = encoder.decode(funcDef->returnParameters(), tr.result.output);
for (int k = 0; k < returnParams.length(); ++k)
returnParameters.insert(funcDef->returnParameters().at(k)->name(), returnParams.at(k));
bytes data = tr.inputParameters;
data.erase(data.begin(), data.begin() + 4);
QStringList parameters = encoder.decode(funcDef->parametersList(), data);
for (int k = 0; k < parameters.length(); ++k)
inputParameters.insert(funcDef->parametersList().at(k)->name(), parameters.at(k));
}
}
// Fill generated logs and decode parameters
for (auto const& log: tr.logs)
{
QVariantMap l;
l.insert("address", QString::fromStdString(log.address.hex()));
std::ostringstream s;
s << log.data;
l.insert("data", QString::fromStdString(s.str()));
std::ostringstream streamTopic;
streamTopic << log.topics;
l.insert("topic", QString::fromStdString(streamTopic.str()));
auto const& sign = log.topics.front(); // first hash supposed to be the event signature. To check
auto dataIterator = log.data.begin();
int topicDataIndex = 1;
for (auto const& event: def->eventsList())
{
if (sign == event->fullHash())
{
QVariantList paramsList;
l.insert("name", event->name());
for (auto const& e: event->parametersList())
{
bytes data;
QString param;
if (!e->isIndexed())
{
data = bytes(dataIterator, dataIterator + 32);
dataIterator = dataIterator + 32;
}
else
{
data = log.topics.at(topicDataIndex).asBytes();
topicDataIndex++;
}
param = encoder.decode(e, data);
QVariantMap p;
p.insert("indexed", e->isIndexed());
p.insert("value", param);
p.insert("name", e->name());
paramsList.push_back(p);
}
l.insert("param", paramsList);
break;
}
}
logs.push_back(l);
}
}
QString sender;
for (auto const& secret: m_accountsSecret)
{
if (secret.address() == tr.sender)
{
sender = QString::fromStdString(dev::toHex(secret.secret().ref()));
break;
}
}
QString label;
if (function != QObject::tr("<none>"))
label = contract + "." + function + "()";
else
label = contract;
if (!creation)
for (auto const& ctr: m_contractAddresses)
{
if (ctr.second == tr.address)
{
contract = "<" + ctr.first.first + " - " + QString::number(ctr.first.second) + ">";
break;
}
}
RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction,
gasUsed, sender, label, inputParameters, returnParameters, logs);
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newRecord(log);
// retrieving all accounts balance
QVariantMap state;
QVariantMap accountBalances;
for (auto const& ctr : m_contractAddresses)
{
u256 wei = m_client->balanceAt(ctr.second, PendingBlock);
accountBalances.insert("0x" + QString::fromStdString(ctr.second.hex()), QEther(wei, QEther::Wei).format());
}
for (auto const& account : m_accounts)
{
u256 wei = m_client->balanceAt(account.first, PendingBlock);
accountBalances.insert("0x" + QString::fromStdString(account.first.hex()), QEther(wei, QEther::Wei).format());
}
state.insert("accounts", accountBalances);
emit newState(recordIndex, state);
}
}
}

298
mix/ClientModel.h

@ -1,298 +0,0 @@
/*
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 ClientModel.h
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <atomic>
#include <map>
#include <QString>
#include <QQmlListProperty>
#include <QVariantMap>
#include <QFuture>
#include <QVariableDeclaration.h>
#include <libethereum/Account.h>
#include "MachineStates.h"
#include "QEther.h"
namespace dev
{
namespace eth { class FixedAccountHolder; }
namespace mix
{
class Web3Server;
class RpcConnector;
class QEther;
class QDebugData;
class MixClient;
class QVariableDefinition;
class CodeModel;
struct SolidityType;
/// Backend transaction config class
struct TransactionSettings
{
TransactionSettings() {}
TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret const& _sender, bool _isContractCreation, bool _isFunctionCall):
contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender), isContractCreation(_isContractCreation), isFunctionCall(_isFunctionCall) {}
TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl):
contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCreation(true), isFunctionCall(true) {}
/// Contract name
QString contractId;
/// Contract function name
QString functionId;
/// Transaction value
u256 value;
/// Gas
u256 gas;
/// Calculate gas automatically
bool gasAuto = true;
/// Gas price
u256 gasPrice;
/// Mapping from contract function parameter name to value
QVariantMap parameterValues;
/// Standard contract url
QString stdContractUrl;
/// Sender
Secret sender;
/// Tr deploys a contract
bool isContractCreation;
/// Tr call a ctr function
bool isFunctionCall;
};
/// UI Transaction log record
class RecordLogEntry: public QObject
{
Q_OBJECT
Q_ENUMS(RecordType)
/// Recording index
Q_PROPERTY(unsigned recordIndex MEMBER m_recordIndex CONSTANT)
/// Human readable transaction bloack and transaction index
Q_PROPERTY(QString transactionIndex MEMBER m_transactionIndex CONSTANT)
/// Contract name if any
Q_PROPERTY(QString contract MEMBER m_contract CONSTANT)
/// Function name if any
Q_PROPERTY(QString function MEMBER m_function CONSTANT)
/// Transaction value
Q_PROPERTY(QString value MEMBER m_value CONSTANT)
/// Receiving address
Q_PROPERTY(QString address MEMBER m_address CONSTANT)
/// Returned value or transaction address in case of creation
Q_PROPERTY(QString returned MEMBER m_returned CONSTANT)
/// true if call, false if transaction
Q_PROPERTY(bool call MEMBER m_call CONSTANT)
/// @returns record type
Q_PROPERTY(RecordType type MEMBER m_type CONSTANT)
/// Gas used
Q_PROPERTY(QString gasUsed MEMBER m_gasUsed CONSTANT)
/// Sender
Q_PROPERTY(QString sender MEMBER m_sender CONSTANT)
/// label
Q_PROPERTY(QString label MEMBER m_label CONSTANT)
/// input parameters
Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT)
/// return parameters
Q_PROPERTY(QVariantMap returnParameters MEMBER m_returnParameters CONSTANT)
/// logs
Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT)
public:
enum RecordType
{
Transaction,
Block
};
RecordLogEntry():
m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {}
RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed,
QString _sender, QString _label, QVariantMap _inputParameters, QVariantMap _returnParameters, QVariantList _logs):
m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed),
m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_returnParameters(_returnParameters), m_logs(_logs) {}
private:
unsigned m_recordIndex;
QString m_transactionIndex;
QString m_contract;
QString m_function;
QString m_value;
QString m_address;
QString m_returned;
bool m_call;
RecordType m_type;
QString m_gasUsed;
QString m_sender;
QString m_label;
QVariantMap m_inputParameters;
QVariantMap m_returnParameters;
QVariantList m_logs;
};
/**
* @brief Ethereum state control
*/
class ClientModel: public QObject
{
Q_OBJECT
public:
ClientModel();
~ClientModel();
/// @returns true if currently executing contract code
Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged)
/// @returns true if currently mining
Q_PROPERTY(bool mining MEMBER m_mining NOTIFY miningStateChanged)
/// @returns deployed contracts addresses
Q_PROPERTY(QVariantMap contractAddresses READ contractAddresses NOTIFY contractAddressesChanged)
/// @returns deployed contracts gas costs
Q_PROPERTY(QVariantList gasCosts READ gasCosts NOTIFY gasCostsChanged)
/// @returns the last block
Q_PROPERTY(RecordLogEntry* lastBlock READ lastBlock CONSTANT)
/// ethereum.js RPC request entry point
/// @param _message RPC request in Json format
/// @returns RPC response in Json format
Q_INVOKABLE QString apiCall(QString const& _message);
/// Simulate mining. Creates a new block
Q_INVOKABLE void mine();
/// Get/set code model. Should be set from qml
Q_PROPERTY(CodeModel* codeModel MEMBER m_codeModel)
/// Encode parameters
Q_INVOKABLE QStringList encodeParams(QVariant const& _param, QString const& _contract, QString const& _function);
/// Encode parameter
Q_INVOKABLE QString encodeStringParam(QString const& _param);
/// To Hex number
Q_INVOKABLE QString toHex(QString const& _int);
/// Add new account to the model
Q_INVOKABLE void addAccount(QString const& _secret);
/// Return the address associated with the current secret
Q_INVOKABLE QString resolveAddress(QString const& _secret);
/// Compute required gas for a list of transactions @arg _tr
QBigInt computeRequiredGas(QVariantList _tr);
/// init eth client
Q_INVOKABLE void init(QString _dbpath);
public slots:
/// Setup scenario, run transaction sequence, show debugger for the last transaction
/// @param _state JS object with state configuration
void setupScenario(QVariantMap _scenario);
/// Execute the given @param _tr on the current state
void executeTr(QVariantMap _tr);
/// Show the debugger for a specified record
Q_INVOKABLE void debugRecord(unsigned _index);
/// Show the debugger for an empty record
Q_INVOKABLE void emptyRecord();
/// Generate new secret
Q_INVOKABLE QString newSecret();
/// retrieve the address of @arg _secret
Q_INVOKABLE QString address(QString const& _secret);
/// Encode a string to ABI parameter. Returns a hex string
Q_INVOKABLE QString encodeAbiString(QString _string);
private slots:
/// Update UI with machine states result. Display a modal dialog.
void showDebugger();
signals:
/// Transaction execution started
void runStarted();
/// Transaction execution completed successfully
void runComplete();
/// Mining has started
void miningStarted();
/// Mined a new block
void miningComplete();
/// Mining stopped or started
void miningStateChanged();
/// Transaction execution completed with error
/// @param _message Error message
void runFailed(QString const& _message);
/// Contract address changed
void contractAddressesChanged();
/// Gas costs updated
void gasCostsChanged();
/// Execution state changed
void newBlock();
/// Execution state changed
void runStateChanged();
/// Show debugger window request
void debugDataReady(QObject* _debugData);
/// ethereum.js RPC response ready
/// @param _message RPC response in Json format
void apiResponse(QString const& _message);
/// New transaction log entry
void newRecord(RecordLogEntry* _r);
/// State (transaction log) cleared
void stateCleared();
/// new state has been processed
void newState(unsigned _record, QVariantMap _accounts);
private:
RecordLogEntry* lastBlock() const;
QVariantMap contractAddresses() const;
QVariantList gasCosts() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence);
Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
void callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
void onNewTransaction();
void onStateReset();
void showDebuggerForTransaction(ExecutionResult const& _t);
QVariant formatValue(SolidityType const& _type, dev::u256 const& _value);
QString resolveToken(std::pair<QString, int> const& _value);
std::pair<QString, int> retrieveToken(QString const& _value);
std::pair<QString, int> resolvePair(QString const& _contractId);
QVariant formatStorageValue(SolidityType const& _type, std::unordered_map<dev::u256, dev::u256> const& _storage, unsigned _offset, dev::u256 const& _slot);
void processNextTransactions();
void finalizeBlock();
void stopExecution();
void setupExecutionChain();
TransactionSettings transaction(QVariant const& _tr) const;
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;
std::shared_ptr<eth::FixedAccountHolder> m_ethAccounts;
std::unordered_map<Address, eth::Account> m_accounts;
std::vector<KeyPair> m_accountsSecret;
QList<u256> m_gasCosts;
std::map<std::pair<QString, int>, Address> m_contractAddresses;
std::map<Address, QString> m_contractNames;
std::map<QString, Address> m_stdContractAddresses;
std::map<Address, QString> m_stdContractNames;
CodeModel* m_codeModel = nullptr;
QList<QVariantList> m_queueTransactions;
mutable boost::shared_mutex x_queueTransactions;
QString m_dbpath;
};
}
}
Q_DECLARE_METATYPE(dev::mix::RecordLogEntry*)

40
mix/Clipboard.cpp

@ -1,40 +0,0 @@
/*
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 Clipboard.cpp
* @author Yann yann@ethdev.com
* @date 2015
*/
#include "Clipboard.h"
#include <QApplication>
#include <QClipboard>
using namespace dev::mix;
Clipboard::Clipboard()
{
connect(QApplication::clipboard(), &QClipboard::dataChanged, [this] { emit clipboardChanged();});
}
QString Clipboard::text() const
{
QClipboard *clipboard = QApplication::clipboard();
return clipboard->text();
}
void Clipboard::setText(QString _text)
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(_text);
}

52
mix/Clipboard.h

@ -1,52 +0,0 @@
/*
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 Clipboard.h
* @author Yann yann@ethdev.com
* @date 2015
*/
#pragma once
#include <QObject>
namespace dev
{
namespace mix
{
/**
* @brief Provides access to system clipboard
*/
class Clipboard: public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY clipboardChanged)
public:
Clipboard();
/// Copy text to clipboard
void setText(QString _text);
/// Get text from clipboard
QString text() const;
signals:
void clipboardChanged();
};
}
}

171
mix/CodeHighlighter.cpp

@ -1,171 +0,0 @@
/*
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 CodeHighlighter.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include <algorithm>
#include <QRegularExpression>
#include <QTextDocument>
#include <QTextBlock>
#include <QTextLayout>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/AST.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h>
#include "CodeHighlighter.h"
using namespace dev::mix;
CodeHighlighterSettings::CodeHighlighterSettings()
{
backgroundColor = QColor(0x00, 0x2b, 0x36);
foregroundColor = QColor(0xee, 0xe8, 0xd5);
formats[Keyword].setForeground(QColor(0x93, 0xa1, 0xa1));
formats[Comment].setForeground(QColor(0x85, 0x99, 0x00));
formats[StringLiteral].setForeground(QColor(0xdc, 0x32, 0x2f));
formats[NumLiteral].setForeground(foregroundColor);
formats[Import].setForeground(QColor(0x6c, 0x71, 0xc4));
formats[CompilationError].setUnderlineColor(Qt::red);
formats[CompilationError].setUnderlineStyle(QTextCharFormat::SingleUnderline);
}
namespace
{
using namespace dev::solidity;
class HighlightVisitor: public ASTConstVisitor
{
public:
HighlightVisitor(CodeHighlighter::Formats* _formats) { m_formats = _formats; }
private:
CodeHighlighter::Formats* m_formats;
virtual bool visit(ImportDirective const& _node)
{
m_formats->push_back(CodeHighlighter::FormatRange(CodeHighlighterSettings::Import, _node.getLocation()));
return true;
}
};
}
CodeHighlighter::FormatRange::FormatRange(CodeHighlighterSettings::Token _t, dev::SourceLocation const& _location):
token(_t), start(_location.start), length(_location.end - _location.start)
{}
void CodeHighlighter::processSource(std::string const& _source)
{
processComments(_source);
solidity::CharStream stream(_source);
solidity::Scanner scanner(stream);
solidity::Token::Value token = scanner.getCurrentToken();
while (token != Token::EOS)
{
if ((token >= Token::Break && token < Token::TypesEnd) ||
token == Token::In || token == Token::Delete || token == Token::NullLiteral || token == Token::TrueLiteral || token == Token::FalseLiteral)
m_formats.push_back(FormatRange(CodeHighlighterSettings::Keyword, scanner.getCurrentLocation()));
else if (token == Token::StringLiteral)
m_formats.push_back(FormatRange(CodeHighlighterSettings::StringLiteral, scanner.getCurrentLocation()));
else if (token == Token::CommentLiteral)
m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, scanner.getCurrentLocation()));
else if (token == Token::Number)
m_formats.push_back(FormatRange(CodeHighlighterSettings::NumLiteral, scanner.getCurrentLocation()));
token = scanner.next();
}
std::sort(m_formats.begin(), m_formats.end());
}
void CodeHighlighter::processAST(dev::solidity::ASTNode const& _ast)
{
HighlightVisitor visitor(&m_formats);
_ast.accept(visitor);
std::sort(m_formats.begin(), m_formats.end());
}
void CodeHighlighter::processError(dev::Exception const& _exception)
{
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
if (location)
m_formats.push_back(FormatRange(CodeHighlighterSettings::CompilationError, *location));
}
void CodeHighlighter::processComments(std::string const& _source)
{
unsigned i = 0;
size_t size = _source.size();
if (size == 0)
return;
while (i < size - 1)
{
if (_source[i] == '/' && _source[i + 1] == '/')
{
//add single line comment
int start = i;
i += 2;
while (i < size && _source[i] != '\n')
++i;
m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, start, i - start));
}
else if (_source[i] == '/' && _source[i + 1] == '*')
{
//add multiline comment
int start = i;
i += 2;
while ((_source[i] != '/' || _source[i - 1] != '*') && i < size)
++i;
m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, start, i - start + 1));
}
++i;
}
}
void CodeHighlighter::updateFormatting(QTextDocument* _document, CodeHighlighterSettings const& _settings)
{
QTextBlock block = _document->firstBlock();
QList<QTextLayout::FormatRange> ranges;
Formats::const_iterator format = m_formats.begin();
while (true)
{
while ((format == m_formats.end() || (block.position() + block.length() <= format->start)) && block.isValid())
{
auto layout = block.layout();
layout->clearAdditionalFormats();
layout->setAdditionalFormats(ranges);
_document->markContentsDirty(block.position(), block.length());
block = block.next();
ranges.clear();
}
if (!block.isValid())
break;
int intersectionStart = std::max(format->start, block.position());
int intersectionLength = std::min(format->start + format->length, block.position() + block.length()) - intersectionStart;
if (intersectionLength > 0)
{
QTextLayout::FormatRange range;
range.format = _settings.formats[format->token];
range.start = format->start - block.position();
range.length = format->length;
ranges.append(range);
}
++format;
}
}

108
mix/CodeHighlighter.h

@ -1,108 +0,0 @@
/*
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 CodeHighlighter.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <vector>
#include <QString>
#include <QTextCharFormat>
class QTextDocument;
namespace dev
{
struct Exception;
struct SourceLocation;
namespace solidity
{
class ASTNode;
}
namespace mix
{
/// Code highligting settings
class CodeHighlighterSettings
{
public:
enum Token
{
Import,
Keyword,
Comment,
StringLiteral,
NumLiteral,
CompilationError,
Size, //this must be kept last
};
CodeHighlighterSettings();
///Format for each token
QTextCharFormat formats[Size];
///Background color
QColor backgroundColor;
///Foreground color
QColor foregroundColor;
};
/// Code highlighting engine class
class CodeHighlighter
{
public:
/// Formatting range
struct FormatRange
{
FormatRange(CodeHighlighterSettings::Token _t, int _start, int _length): token(_t), start(_start), length(_length) {}
FormatRange(CodeHighlighterSettings::Token _t, SourceLocation const& _location);
bool operator<(FormatRange const& _other) const { return start < _other.start || (start == _other.start && length < _other.length); }
CodeHighlighterSettings::Token token;
int start;
int length;
};
using Formats = std::vector<FormatRange>; // Sorted by start position
public:
/// Collect highligting information by lexing the source
void processSource(std::string const& _source);
/// Collect additional highligting information from AST
void processAST(solidity::ASTNode const& _ast);
/// Collect highlighting information from compilation exception
void processError(dev::Exception const& _exception);
/// Apply formatting for a text document
/// @todo Remove this once editor is reworked
void updateFormatting(QTextDocument* _document, CodeHighlighterSettings const& _settings);
private:
/// Collect highligting information by paring for comments
/// @todo Support this in solidity?
void processComments(std::string const& _source);
private:
Formats m_formats;
};
}
}

698
mix/CodeModel.cpp

@ -1,698 +0,0 @@
/*
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 CodeModel.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <sstream>
#include <memory>
#include <QDebug>
#include <QApplication>
#include <QtQml>
#include <libdevcore/Common.h>
#include <libevmasm/SourceLocation.h>
#include <libsolidity/AST.h>
#include <libsolidity/Types.h>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libsolidity/InterfaceHandler.h>
#include <libsolidity/GasEstimator.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libevmcore/Instruction.h>
#include <libethcore/CommonJS.h>
#include "QContractDefinition.h"
#include "QFunctionDefinition.h"
#include "QVariableDeclaration.h"
#include "CodeHighlighter.h"
#include "FileIo.h"
#include "CodeModel.h"
using namespace dev::mix;
const std::set<std::string> c_predefinedContracts =
{ "Config", "Coin", "CoinReg", "coin", "service", "owned", "mortal", "NameReg", "named", "std", "configUser" };
namespace
{
using namespace dev::eth;
using namespace dev::solidity;
class CollectLocalsVisitor: public ASTConstVisitor
{
public:
CollectLocalsVisitor(QHash<LocationPair, SolidityDeclaration>* _locals):
m_locals(_locals), m_functionScope(false) {}
private:
LocationPair nodeLocation(ASTNode const& _node)
{
return LocationPair(_node.getLocation().start, _node.getLocation().end);
}
virtual bool visit(FunctionDefinition const&) override
{
m_functionScope = true;
return true;
}
virtual void endVisit(FunctionDefinition const&) override
{
m_functionScope = false;
}
virtual bool visit(VariableDeclaration const& _node) override
{
SolidityDeclaration decl;
decl.type = CodeModel::nodeType(_node.getType().get());
decl.name = QString::fromStdString(_node.getName());
decl.slot = 0;
decl.offset = 0;
if (m_functionScope)
m_locals->insert(nodeLocation(_node), decl);
return true;
}
private:
QHash<LocationPair, SolidityDeclaration>* m_locals;
bool m_functionScope;
};
class CollectLocationsVisitor: public ASTConstVisitor
{
public:
CollectLocationsVisitor(SourceMap* _sourceMap):
m_sourceMap(_sourceMap) {}
private:
LocationPair nodeLocation(ASTNode const& _node)
{
return LocationPair(_node.getLocation().start, _node.getLocation().end);
}
virtual bool visit(FunctionDefinition const& _node) override
{
m_sourceMap->functions.insert(nodeLocation(_node), QString::fromStdString(_node.getName()));
return true;
}
virtual bool visit(ContractDefinition const& _node) override
{
m_sourceMap->contracts.insert(nodeLocation(_node), QString::fromStdString(_node.getName()));
return true;
}
private:
SourceMap* m_sourceMap;
};
QHash<unsigned, SolidityDeclarations> collectStorage(dev::solidity::ContractDefinition const& _contract)
{
QHash<unsigned, SolidityDeclarations> result;
dev::solidity::ContractType contractType(_contract);
for (auto v : contractType.getStateVariables())
{
dev::solidity::VariableDeclaration const* declaration = std::get<0>(v);
dev::u256 slot = std::get<1>(v);
unsigned offset = std::get<2>(v);
result[static_cast<unsigned>(slot)].push_back(SolidityDeclaration { QString::fromStdString(declaration->getName()), CodeModel::nodeType(declaration->getType().get()), slot, offset });
}
return result;
}
} //namespace
void BackgroundWorker::queueCodeChange(int _jobId)
{
m_model->runCompilationJob(_jobId);
}
CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler, QString const& _contractName, QString const& _source):
QObject(nullptr),
m_sourceHash(qHash(_source))
{
std::string name = _contractName.toStdString();
ContractDefinition const& contractDefinition = _compiler.getContractDefinition(name);
m_contract.reset(new QContractDefinition(nullptr, &contractDefinition));
QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership);
m_contract->moveToThread(QApplication::instance()->thread());
m_bytes = _compiler.getBytecode(_contractName.toStdString());
dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(interfaceHandler.getABIInterface(contractDefinition));
if (m_contractInterface.isEmpty())
m_contractInterface = "[]";
if (contractDefinition.getLocation().sourceName.get())
m_documentId = QString::fromStdString(*contractDefinition.getLocation().sourceName);
CollectLocalsVisitor visitor(&m_locals);
m_storage = collectStorage(contractDefinition);
contractDefinition.accept(visitor);
m_assemblyItems = *_compiler.getRuntimeAssemblyItems(name);
m_constructorAssemblyItems = *_compiler.getAssemblyItems(name);
}
QString CompiledContract::codeHex() const
{
return QString::fromStdString(toJS(m_bytes));
}
CodeModel::CodeModel():
m_compiling(false),
m_codeHighlighterSettings(new CodeHighlighterSettings()),
m_backgroundWorker(this),
m_backgroundJobId(0)
{
m_backgroundThread.start();
m_backgroundWorker.moveToThread(&m_backgroundThread);
connect(this, &CodeModel::scheduleCompilationJob, &m_backgroundWorker, &BackgroundWorker::queueCodeChange, Qt::QueuedConnection);
qRegisterMetaType<CompiledContract*>("CompiledContract*");
qRegisterMetaType<QContractDefinition*>("QContractDefinition*");
qRegisterMetaType<QFunctionDefinition*>("QFunctionDefinition*");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
qmlRegisterType<QFunctionDefinition>("org.ethereum.qml", 1, 0, "QFunctionDefinition");
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml", 1, 0, "QVariableDeclaration");
}
CodeModel::~CodeModel()
{
stop();
disconnect(this);
releaseContracts();
if (m_gasCostsMaps)
delete m_gasCostsMaps;
}
void CodeModel::stop()
{
///@todo: cancel bg job
m_backgroundThread.exit();
m_backgroundThread.wait();
}
void CodeModel::reset(QVariantMap const& _documents)
{
///@todo: cancel bg job
Guard l(x_contractMap);
releaseContracts();
Guard pl(x_pendingContracts);
m_pendingContracts.clear();
for (QVariantMap::const_iterator d = _documents.cbegin(); d != _documents.cend(); ++d)
m_pendingContracts[d.key()] = d.value().toString();
// launch the background thread
m_compiling = true;
emit stateChanged();
emit scheduleCompilationJob(++m_backgroundJobId);
}
void CodeModel::unregisterContractSrc(QString const& _documentId)
{
{
Guard pl(x_pendingContracts);
m_pendingContracts.erase(_documentId);
}
// launch the background thread
m_compiling = true;
emit stateChanged();
emit scheduleCompilationJob(++m_backgroundJobId);
}
void CodeModel::registerCodeChange(QString const& _documentId, QString const& _code)
{
{
Guard pl(x_pendingContracts);
m_pendingContracts[_documentId] = _code;
}
// launch the background thread
m_compiling = true;
emit stateChanged();
emit scheduleCompilationJob(++m_backgroundJobId);
}
QVariantMap CodeModel::contracts() const
{
QVariantMap result;
Guard l(x_contractMap);
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c)
result.insert(c.key(), QVariant::fromValue(c.value()));
return result;
}
CompiledContract* CodeModel::contractByDocumentId(QString const& _documentId) const
{
Guard l(x_contractMap);
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c)
if (c.value()->m_documentId == _documentId)
return c.value();
return nullptr;
}
CompiledContract const& CodeModel::contract(QString const& _name) const
{
Guard l(x_contractMap);
CompiledContract* res = m_contractMap.value(_name);
if (res == nullptr)
BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Contract not found: " + _name.toStdString()));
return *res;
}
CompiledContract const* CodeModel::tryGetContract(QString const& _name) const
{
Guard l(x_contractMap);
CompiledContract* res = m_contractMap.value(_name);
return res;
}
void CodeModel::releaseContracts()
{
for (ContractMap::iterator c = m_contractMap.begin(); c != m_contractMap.end(); ++c)
c.value()->deleteLater();
m_contractMap.clear();
m_sourceMaps.clear();
}
void CodeModel::runCompilationJob(int _jobId)
{
if (_jobId != m_backgroundJobId)
return; //obsolete job
solidity::CompilerStack cs(true);
try
{
cs.addSource("configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xf025d81196b72fba60a1d4dddad12eeb8360d828;}})");
std::vector<std::string> sourceNames;
{
Guard l(x_pendingContracts);
for (auto const& c: m_pendingContracts)
{
cs.addSource(c.first.toStdString(), c.second.toStdString());
sourceNames.push_back(c.first.toStdString());
}
}
cs.compile(m_optimizeCode);
gasEstimation(cs);
collectContracts(cs, sourceNames);
}
catch (dev::Exception const& _exception)
{
std::stringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", cs);
QString message = QString::fromStdString(error.str());
QVariantMap firstLocation;
QVariantList secondLocations;
if (SourceLocation const* first = boost::get_error_info<solidity::errinfo_sourceLocation>(_exception))
firstLocation = resolveCompilationErrorLocation(cs, *first);
if (SecondarySourceLocation const* second = boost::get_error_info<solidity::errinfo_secondarySourceLocation>(_exception))
{
for (auto const& c: second->infos)
secondLocations.push_back(resolveCompilationErrorLocation(cs, c.second));
}
compilationError(message, firstLocation, secondLocations);
}
m_compiling = false;
emit stateChanged();
}
QVariantMap CodeModel::resolveCompilationErrorLocation(CompilerStack const& _compiler, SourceLocation const& _location)
{
std::tuple<int, int, int, int> pos = _compiler.positionFromSourceLocation(_location);
QVariantMap startError;
startError.insert("line", std::get<0>(pos) > 1 ? (std::get<0>(pos) - 1) : 1);
startError.insert("column", std::get<1>(pos) > 1 ? (std::get<1>(pos) - 1) : 1);
QVariantMap endError;
endError.insert("line", std::get<2>(pos) > 1 ? (std::get<2>(pos) - 1) : 1);
endError.insert("column", std::get<3>(pos) > 1 ? (std::get<3>(pos) - 1) : 1);
QVariantMap error;
error.insert("start", startError);
error.insert("end", endError);
QString sourceName;
if (_location.sourceName)
sourceName = QString::fromStdString(*_location.sourceName);
error.insert("source", sourceName);
if (!sourceName.isEmpty())
if (CompiledContract* contract = contractByDocumentId(sourceName))
sourceName = contract->contract()->name(); //substitute the location to match our contract names
error.insert("contractName", sourceName);
return error;
}
void CodeModel::gasEstimation(solidity::CompilerStack const& _cs)
{
if (m_gasCostsMaps)
m_gasCostsMaps->deleteLater();
m_gasCostsMaps = new GasMapWrapper;
for (std::string n: _cs.getContractNames())
{
ContractDefinition const& contractDefinition = _cs.getContractDefinition(n);
QString sourceName = QString::fromStdString(*contractDefinition.getLocation().sourceName);
if (!m_gasCostsMaps->contains(sourceName))
m_gasCostsMaps->insert(sourceName, QVariantList());
if (!contractDefinition.isFullyImplemented())
continue;
dev::solidity::SourceUnit const& sourceUnit = _cs.getAST(*contractDefinition.getLocation().sourceName);
AssemblyItems const* items = _cs.getRuntimeAssemblyItems(n);
std::map<ASTNode const*, GasMeter::GasConsumption> gasCosts = GasEstimator::breakToStatementLevel(GasEstimator::structuralEstimation(*items, std::vector<ASTNode const*>({&sourceUnit})), {&sourceUnit});
auto gasToString = [](GasMeter::GasConsumption const& _gas)
{
if (_gas.isInfinite)
return QString("0");
else
return QString::fromStdString(toString(_gas.value));
};
// Structural gas costs (per opcode)
for (auto gasItem = gasCosts.begin(); gasItem != gasCosts.end(); ++gasItem)
{
SourceLocation const& location = gasItem->first->getLocation();
GasMeter::GasConsumption cost = gasItem->second;
m_gasCostsMaps->push(sourceName, location.start, location.end, gasToString(cost), cost.isInfinite, GasMap::type::Statement);
}
eth::AssemblyItems const& runtimeAssembly = *_cs.getRuntimeAssemblyItems(n);
QString contractName = QString::fromStdString(contractDefinition.getName());
// Functional gas costs (per function, but also for accessors)
for (auto it: contractDefinition.getInterfaceFunctions())
{
if (!it.second->hasDeclaration())
continue;
SourceLocation loc = it.second->getDeclaration().getLocation();
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(runtimeAssembly, it.second->externalSignature());
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function,
contractName, QString::fromStdString(it.second->getDeclaration().getName()));
}
if (auto const* fallback = contractDefinition.getFallbackFunction())
{
SourceLocation loc = fallback->getLocation();
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(runtimeAssembly, "INVALID");
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function,
contractName, "fallback");
}
for (auto const& it: contractDefinition.getDefinedFunctions())
{
if (it->isPartOfExternalInterface() || it->isConstructor())
continue;
SourceLocation loc = it->getLocation();
size_t entry = _cs.getFunctionEntryPoint(n, *it);
GasEstimator::GasConsumption cost = GasEstimator::GasConsumption::infinite();
if (entry > 0)
cost = GasEstimator::functionalEstimation(runtimeAssembly, entry, *it);
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function,
contractName, QString::fromStdString(it->getName()));
}
if (auto const* constructor = contractDefinition.getConstructor())
{
SourceLocation loc = constructor->getLocation();
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(*_cs.getAssemblyItems(n));
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Constructor,
contractName, contractName);
}
}
}
QVariantList CodeModel::gasCostByDocumentId(QString const& _documentId) const
{
if (m_gasCostsMaps)
return m_gasCostsMaps->gasCostsByDocId(_documentId);
else
return QVariantList();
}
QVariantList CodeModel::gasCostBy(QString const& _contractName, QString const& _functionName) const
{
if (m_gasCostsMaps)
return m_gasCostsMaps->gasCostsBy(_contractName, _functionName);
else
return QVariantList();
}
void CodeModel::collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames)
{
Guard pl(x_pendingContracts);
Guard l(x_contractMap);
ContractMap result;
SourceMaps sourceMaps;
for (std::string const& sourceName: _sourceNames)
{
dev::solidity::SourceUnit const& source = _cs.getAST(sourceName);
SourceMap sourceMap;
CollectLocationsVisitor collector(&sourceMap);
source.accept(collector);
sourceMaps.insert(QString::fromStdString(sourceName), std::move(sourceMap));
}
for (std::string n: _cs.getContractNames())
{
if (c_predefinedContracts.count(n) != 0)
continue;
QString name = QString::fromStdString(n);
ContractDefinition const& contractDefinition = _cs.getContractDefinition(n);
if (!contractDefinition.isFullyImplemented())
continue;
QString sourceName = QString::fromStdString(*contractDefinition.getLocation().sourceName);
auto sourceIter = m_pendingContracts.find(sourceName);
QString source = sourceIter != m_pendingContracts.end() ? sourceIter->second : QString();
CompiledContract* contract = new CompiledContract(_cs, name, source);
QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership);
result[name] = contract;
CompiledContract* prevContract = nullptr;
// find previous contract by name
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c)
if (c.value()->contract()->name() == contract->contract()->name())
prevContract = c.value();
// if not found, try by documentId
if (!prevContract)
{
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c)
if (c.value()->documentId() == contract->documentId())
{
//make sure there are no other contracts in the same source, otherwise it is not a rename
if (!std::any_of(result.begin(),result.end(), [=](ContractMap::const_iterator::value_type _v) { return _v != contract && _v->documentId() == contract->documentId(); }))
prevContract = c.value();
}
}
if (prevContract != nullptr && prevContract->contractInterface() != result[name]->contractInterface())
emit contractInterfaceChanged(name);
if (prevContract == nullptr)
emit newContractCompiled(name);
else if (prevContract->contract()->name() != name)
emit contractRenamed(contract->documentId(), prevContract->contract()->name(), name);
}
releaseContracts();
m_contractMap.swap(result);
m_sourceMaps.swap(sourceMaps);
emit codeChanged();
emit compilationComplete();
}
bool CodeModel::hasContract() const
{
Guard l(x_contractMap);
return m_contractMap.size() != 0;
}
dev::bytes const& CodeModel::getStdContractCode(const QString& _contractName, const QString& _url)
{
auto cached = m_compiledContracts.find(_contractName);
if (cached != m_compiledContracts.end())
return cached->second;
FileIo fileIo;
std::string source = fileIo.readFile(_url).toStdString();
solidity::CompilerStack cs(false);
cs.setSource(source);
cs.compile(false);
for (std::string const& name: cs.getContractNames())
{
dev::bytes code = cs.getBytecode(name);
m_compiledContracts.insert(std::make_pair(QString::fromStdString(name), std::move(code)));
}
return m_compiledContracts.at(_contractName);
}
void CodeModel::retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type const* _type)
{
if (_type->getCategory() == Type::Category::Array)
{
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(_type);
_wrapperType.baseType = std::make_shared<dev::mix::SolidityType const>(nodeType(arrayType->getBaseType().get()));
}
}
SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
{
SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString(true)), std::vector<SolidityDeclaration>(), std::vector<QString>(), nullptr };
if (!_type)
return r;
switch (_type->getCategory())
{
case Type::Category::Integer:
{
IntegerType const* it = dynamic_cast<IntegerType const*>(_type);
r.size = it->getNumBits() / 8;
r.type = it->isAddress() ? SolidityType::Type::Address : it->isSigned() ? SolidityType::Type::SignedInteger : SolidityType::Type::UnsignedInteger;
}
break;
case Type::Category::Bool:
r.type = SolidityType::Type::Bool;
break;
case Type::Category::FixedBytes:
{
FixedBytesType const* b = dynamic_cast<FixedBytesType const*>(_type);
r.type = SolidityType::Type::Bytes;
r.size = static_cast<unsigned>(b->numBytes());
}
break;
case Type::Category::Contract:
r.type = SolidityType::Type::Address;
break;
case Type::Category::Array:
{
ArrayType const* array = dynamic_cast<ArrayType const*>(_type);
if (array->isString())
r.type = SolidityType::Type::String;
else if (array->isByteArray())
r.type = SolidityType::Type::Bytes;
else
{
SolidityType elementType = nodeType(array->getBaseType().get());
elementType.name = r.name;
r = elementType;
}
r.count = static_cast<unsigned>(array->getLength());
r.dynamicSize = _type->isDynamicallySized();
r.array = true;
retrieveSubType(r, _type);
}
break;
case Type::Category::Enum:
{
r.type = SolidityType::Type::Enum;
EnumType const* e = dynamic_cast<EnumType const*>(_type);
for(auto const& enumValue: e->getEnumDefinition().getMembers())
r.enumNames.push_back(QString::fromStdString(enumValue->getName()));
}
break;
case Type::Category::Struct:
{
r.type = SolidityType::Type::Struct;
StructType const* s = dynamic_cast<StructType const*>(_type);
for(auto const& structMember: s->getMembers())
{
auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.name);
r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.name), nodeType(structMember.type.get()), slotAndOffset.first, slotAndOffset.second });
}
}
break;
case Type::Category::Function:
case Type::Category::IntegerConstant:
case Type::Category::StringLiteral:
case Type::Category::Magic:
case Type::Category::Mapping:
case Type::Category::Modifier:
case Type::Category::Real:
case Type::Category::TypeType:
case Type::Category::Void:
default:
break;
}
return r;
}
bool CodeModel::isContractOrFunctionLocation(dev::SourceLocation const& _location)
{
if (!_location.sourceName)
return false;
Guard l(x_contractMap);
auto sourceMapIter = m_sourceMaps.find(QString::fromStdString(*_location.sourceName));
if (sourceMapIter != m_sourceMaps.cend())
{
LocationPair location(_location.start, _location.end);
return sourceMapIter.value().contracts.contains(location) || sourceMapIter.value().functions.contains(location);
}
return false;
}
QString CodeModel::resolveFunctionName(dev::SourceLocation const& _location)
{
if (!_location.sourceName)
return QString();
Guard l(x_contractMap);
auto sourceMapIter = m_sourceMaps.find(QString::fromStdString(*_location.sourceName));
if (sourceMapIter != m_sourceMaps.cend())
{
LocationPair location(_location.start, _location.end);
auto functionNameIter = sourceMapIter.value().functions.find(location);
if (functionNameIter != sourceMapIter.value().functions.cend())
return functionNameIter.value();
}
return QString();
}
void CodeModel::setOptimizeCode(bool _value)
{
m_optimizeCode = _value;
emit scheduleCompilationJob(++m_backgroundJobId);
}
void GasMapWrapper::push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type, QString _contractName, QString _functionName)
{
GasMap* gas = new GasMap(_start, _end, _value, _isInfinite, _type, _contractName, _functionName, this);
m_gasMaps.find(_source).value().push_back(QVariant::fromValue(gas));
}
bool GasMapWrapper::contains(QString _key)
{
return m_gasMaps.contains(_key);
}
void GasMapWrapper::insert(QString _source, QVariantList _variantList)
{
m_gasMaps.insert(_source, _variantList);
}
QVariantList GasMapWrapper::gasCostsByDocId(QString _source)
{
auto gasIter = m_gasMaps.find(_source);
if (gasIter != m_gasMaps.end())
return gasIter.value();
else
return QVariantList();
}
QVariantList GasMapWrapper::gasCostsBy(QString _contractName, QString _functionName)
{
QVariantList gasMap;
for (auto const& map: m_gasMaps)
{
for (auto const& gas: map)
{
if (gas.value<GasMap*>()->contractName() == _contractName && (_functionName.isEmpty() || gas.value<GasMap*>()->functionName() == _functionName))
gasMap.push_back(gas);
}
}
return gasMap;
}

308
mix/CodeModel.h

@ -1,308 +0,0 @@
/*
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 CodeModel.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <memory>
#include <atomic>
#include <map>
#include <QObject>
#include <QThread>
#include <QHash>
#include <QMetaEnum>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libevmcore/Params.h>
#include <libevmasm/Assembly.h>
#include <libdevcore/SHA3.h>
#include "SolidityType.h"
#include "QBigInt.h"
class QTextDocument;
namespace dev
{
namespace solidity
{
class CompilerStack;
class Type;
}
namespace mix
{
class CodeModel;
class CodeHighlighter;
class CodeHighlighterSettings;
class QContractDefinition;
//utility class to perform tasks in background thread
class BackgroundWorker: public QObject
{
Q_OBJECT
public:
BackgroundWorker(CodeModel* _model): QObject(), m_model(_model) {}
public slots:
void queueCodeChange(int _jobId);
private:
CodeModel* m_model;
};
using LocationPair = QPair<int, int>;
///Compilation result model. Contains all the compiled contract data required by UI
class CompiledContract: public QObject
{
Q_OBJECT
Q_PROPERTY(QContractDefinition* contract READ contract)
Q_PROPERTY(QString contractInterface READ contractInterface CONSTANT)
Q_PROPERTY(QString codeHex READ codeHex CONSTANT)
Q_PROPERTY(QString documentId READ documentId CONSTANT)
public:
/// Successful compilation result constructor
CompiledContract(solidity::CompilerStack const& _compiler, QString const& _contractName, QString const& _source);
/// @returns contract definition for QML property
QContractDefinition* contract() const { return m_contract.get(); }
/// @returns contract definition
std::shared_ptr<QContractDefinition> sharedContract() const { return m_contract; }
/// @returns contract bytecode
dev::bytes const& bytes() const { return m_bytes; }
/// @returns contract bytecode as hex string
QString codeHex() const;
/// @returns contract definition in JSON format
QString contractInterface() const { return m_contractInterface; }
/// @return assebly item locations
eth::AssemblyItems const& assemblyItems() const { return m_assemblyItems; }
eth::AssemblyItems const& constructorAssemblyItems() const { return m_constructorAssemblyItems; }
/// @returns contract source Id
QString documentId() const { return m_documentId; }
QHash<LocationPair, SolidityDeclaration> const& locals() const { return m_locals; }
QHash<unsigned, SolidityDeclarations> const& storage() const { return m_storage; }
private:
uint m_sourceHash;
std::shared_ptr<QContractDefinition> m_contract;
QString m_compilerMessage; ///< @todo: use some structure here
dev::bytes m_bytes;
QString m_contractInterface;
QString m_documentId;
eth::AssemblyItems m_assemblyItems;
eth::AssemblyItems m_constructorAssemblyItems;
QHash<LocationPair, SolidityDeclaration> m_locals;
QHash<unsigned, SolidityDeclarations> m_storage;
friend class CodeModel;
};
using ContractMap = QMap<QString, CompiledContract*>; //needs to be sorted
/// Source map
using LocationMap = QHash<LocationPair, QString>;
struct SourceMap
{
LocationMap contracts;
LocationMap functions;
};
using SourceMaps = QMap<QString, SourceMap>; //by source id
using GasCostsMaps = QMap<QString, QVariantList>; //gas cost by contract name
class GasMap: public QObject
{
Q_OBJECT
Q_ENUMS(type)
Q_PROPERTY(int start MEMBER m_start CONSTANT)
Q_PROPERTY(int end MEMBER m_end CONSTANT)
Q_PROPERTY(QString gas MEMBER m_gas CONSTANT)
Q_PROPERTY(bool isInfinite MEMBER m_isInfinite CONSTANT)
Q_PROPERTY(QString codeBlockType READ codeBlockType CONSTANT)
Q_PROPERTY(QString contractName MEMBER m_contractName CONSTANT)
Q_PROPERTY(QString functionName MEMBER m_functionName CONSTANT)
public:
enum type
{
Statement,
Function,
Constructor
};
GasMap(int _start, int _end, QString _gas, bool _isInfinite, type _type, QString _contractName, QString _functionName, QObject* _parent): QObject(_parent),
m_start(_start), m_end(_end), m_gas(_gas), m_isInfinite(_isInfinite), m_type(_type), m_contractName(_contractName), m_functionName(_functionName) {}
QString contractName() { return m_contractName; }
QString functionName() { return m_functionName; }
private:
int m_start;
int m_end;
QString m_gas;
bool m_isInfinite;
type m_type;
QString m_contractName;
QString m_functionName;
QString codeBlockType() const
{
QMetaEnum units = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("type"));
if (m_type)
{
const char* key = units.valueToKey(m_type);
return QString(key).toLower();
}
return QString("");
}
};
class GasMapWrapper: public QObject
{
Q_OBJECT
Q_PROPERTY(GasCostsMaps gasMaps MEMBER m_gasMaps CONSTANT)
public:
GasMapWrapper(QObject* _parent = nullptr): QObject(_parent){}
void push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type, QString _contractName = "", QString _functionName = "");
bool contains(QString _key);
void insert(QString _source, QVariantList _variantList);
QVariantList gasCostsByDocId(QString _source);
QVariantList gasCostsBy(QString _contractName, QString _functionName = "");
private:
GasCostsMaps m_gasMaps;
};
/// Code compilation model. Compiles contracts in background an provides compiled contract data
class CodeModel: public QObject
{
Q_OBJECT
public:
CodeModel();
~CodeModel();
Q_PROPERTY(QVariantMap contracts READ contracts NOTIFY codeChanged)
Q_PROPERTY(bool compiling READ isCompiling NOTIFY stateChanged)
Q_PROPERTY(bool hasContract READ hasContract NOTIFY codeChanged)
Q_PROPERTY(bool optimizeCode MEMBER m_optimizeCode WRITE setOptimizeCode)
Q_PROPERTY(int callStipend READ callStipend)
Q_PROPERTY(int txGas READ txGas)
/// @returns latest compilation results for contracts
QVariantMap contracts() const;
/// @returns compilation status
bool isCompiling() const { return m_compiling; }
/// @returns true there is a contract which has at least one function
bool hasContract() const;
/// Get contract code by url. Contract is compiled on first access and cached
dev::bytes const& getStdContractCode(QString const& _contractName, QString const& _url);
/// Get contract by name
/// Throws if not found
CompiledContract const& contract(QString const& _name) const;
/// Get contract by name
/// @returns nullptr if not found
Q_INVOKABLE CompiledContract const* tryGetContract(QString const& _name) const;
/// Find a contract by document id
/// @returns CompiledContract object or null if not found
Q_INVOKABLE CompiledContract* contractByDocumentId(QString const& _documentId) const;
/// Reset code model
Q_INVOKABLE void reset() { reset(QVariantMap()); }
/// Delete a contract source
Q_INVOKABLE void unregisterContractSrc(QString const& _documentId);
/// Convert solidity type info to mix type
static SolidityType nodeType(dev::solidity::Type const* _type);
/// Retrieve subtype
static void retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type const* _type);
/// Check if given location belongs to contract or function
bool isContractOrFunctionLocation(dev::SourceLocation const& _location);
/// Get funciton name by location
QString resolveFunctionName(dev::SourceLocation const& _location);
/// Gas estimation for compiled sources
void gasEstimation(solidity::CompilerStack const& _cs);
/// Gas cost by doc id
Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const;
/// Gas cost by @arg contractName @arg functionName
Q_INVOKABLE QVariantList gasCostBy(QString const& _contractName, QString const& _functionName) const;
/// Set optimize code
Q_INVOKABLE void setOptimizeCode(bool _value);
/// sha3
Q_INVOKABLE QString sha3(QString _source) { return QString::fromStdString(dev::sha3(_source.toStdString()).hex()); }
int txGas() { return static_cast<int>(dev::eth::c_txGas); }
int callStipend() { return static_cast<int>(dev::eth::c_callStipend); }
signals:
/// Emited on compilation state change
void stateChanged();
/// Emitted on compilation complete
void compilationComplete();
/// Emitted on compilation error
void compilationError(QString _error, QVariantMap _firstErrorLoc, QVariantList _secondErrorLoc);
/// Internal signal used to transfer compilation job to background thread
void scheduleCompilationJob(int _jobId);
/// Emitted if there are any changes in the code model
void codeChanged();
/// Emitted if there are any changes in the contract interface
void contractInterfaceChanged(QString _documentId);
/// Emitted if there is a new contract compiled for the first time
void newContractCompiled(QString _documentId);
/// Emitted if a contract name has been changed
void contractRenamed(QString _documentId, QString _oldName, QString _newName);
public slots:
/// Update code model on source code change
void registerCodeChange(QString const& _documentId, QString const& _code);
/// Reset code model for a new project
void reset(QVariantMap const& _documents);
private:
void runCompilationJob(int _jobId);
void stop();
void releaseContracts();
void collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames);
QVariantMap resolveCompilationErrorLocation(dev::solidity::CompilerStack const& _cs, dev::SourceLocation const& _location);
std::atomic<bool> m_compiling;
mutable dev::Mutex x_contractMap;
ContractMap m_contractMap;
SourceMaps m_sourceMaps;
GasMapWrapper* m_gasCostsMaps = 0;
std::unique_ptr<CodeHighlighterSettings> m_codeHighlighterSettings;
QThread m_backgroundThread;
BackgroundWorker m_backgroundWorker;
int m_backgroundJobId = 0; //protects from starting obsolete compilation job
std::map<QString, dev::bytes> m_compiledContracts; //by name
dev::Mutex x_pendingContracts;
std::map<QString, QString> m_pendingContracts; //name to source
bool m_optimizeCode = false;
friend class BackgroundWorker;
};
}
}

383
mix/ContractCallDataEncoder.cpp

@ -1,383 +0,0 @@
/*
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 ContractCallDataEncoder.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <vector>
#include <QtCore/qmath.h>
#include <QMap>
#include <QStringList>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <libethcore/CommonJS.h>
#include <libsolidity/AST.h>
#include "QVariableDeclaration.h"
#include "QVariableDefinition.h"
#include "QFunctionDefinition.h"
#include "ContractCallDataEncoder.h"
using namespace std;
using namespace dev;
using namespace dev::solidity;
using namespace dev::mix;
bytes ContractCallDataEncoder::encodedData()
{
bytes r(m_encodedData);
size_t headerSize = m_encodedData.size() & ~0x1fUL; //ignore any prefix that is not 32-byte aligned
//apply offsets
for (auto const& p: m_dynamicOffsetMap)
{
vector_ref<byte> offsetRef(m_dynamicData.data() + p.first, 32);
toBigEndian(p.second + headerSize, offsetRef); //add header size minus signature hash
}
for (auto const& p: m_staticOffsetMap)
{
vector_ref<byte> offsetRef(r.data() + p.first, 32);
toBigEndian(p.second + headerSize, offsetRef); //add header size minus signature hash
}
if (m_dynamicData.size() > 0)
r.insert(r.end(), m_dynamicData.begin(), m_dynamicData.end());
return r;
}
void ContractCallDataEncoder::encode(QFunctionDefinition const* _function)
{
bytes hash = _function->hash().asBytes();
m_encodedData.insert(m_encodedData.end(), hash.begin(), hash.end());
}
void ContractCallDataEncoder::encodeArray(QJsonArray const& _array, SolidityType const& _type, bytes& _content)
{
size_t offsetStart = _content.size();
if (_type.dynamicSize)
{
bytes count = bytes(32);
toBigEndian((u256)_array.size(), count);
_content += count; //reserved space for count
}
int k = 0;
for (QJsonValue const& c: _array)
{
if (c.isArray())
{
if (_type.baseType->dynamicSize)
m_dynamicOffsetMap.push_back(std::make_pair(m_dynamicData.size() + offsetStart + 32 + k * 32, m_dynamicData.size() + _content.size()));
encodeArray(c.toArray(), *_type.baseType, _content);
}
else
{
// encode single item
if (c.isDouble())
encodeSingleItem(QString::number(c.toDouble()), _type, _content);
else if (c.isString())
encodeSingleItem(c.toString(), _type, _content);
}
k++;
}
}
void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& _type)
{
if (_type.dynamicSize && (_type.type == SolidityType::Type::Bytes || _type.type == SolidityType::Type::String))
{
bytes empty(32);
size_t sizePos = m_dynamicData.size();
m_dynamicData += empty; //reserve space for count
encodeSingleItem(_data.toString(), _type, m_dynamicData);
vector_ref<byte> sizeRef(m_dynamicData.data() + sizePos, 32);
toBigEndian(static_cast<unsigned>(_data.toString().size()), sizeRef);
m_staticOffsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos));
m_encodedData += empty; //reserve space for offset
}
else if (_type.array)
{
bytes content;
size_t size = m_encodedData.size();
if (_type.dynamicSize)
{
m_encodedData += bytes(32); // reserve space for offset
m_staticOffsetMap.push_back(std::make_pair(size, m_dynamicData.size()));
}
QJsonDocument jsonDoc = QJsonDocument::fromJson(_data.toString().toUtf8());
encodeArray(jsonDoc.array(), _type, content);
if (!_type.dynamicSize)
m_encodedData.insert(m_encodedData.end(), content.begin(), content.end());
else
m_dynamicData.insert(m_dynamicData.end(), content.begin(), content.end());
}
else
encodeSingleItem(_data.toString(), _type, m_encodedData);
}
unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest)
{
if (_type.type == SolidityType::Type::Struct)
BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Struct parameters are not supported yet"));
unsigned const alignSize = 32;
QString src = _data;
bytes result;
if ((src.startsWith("\"") && src.endsWith("\"")) || (src.startsWith("\'") && src.endsWith("\'")))
src = src.remove(src.length() - 1, 1).remove(0, 1);
if (src.startsWith("0x"))
{
result = fromHex(src.toStdString().substr(2));
if (_type.type != SolidityType::Type::Bytes)
result = padded(result, alignSize);
}
else
{
try
{
bigint i(src.toStdString());
result = bytes(alignSize);
toBigEndian((u256)i, result);
}
catch (std::exception const&)
{
// manage input as a string.
result = encodeStringParam(src, alignSize);
}
}
size_t dataSize = _type.dynamicSize ? result.size() : alignSize;
if (result.size() % alignSize != 0)
result.resize((result.size() & ~(alignSize - 1)) + alignSize);
_dest.insert(_dest.end(), result.begin(), result.end());
return dataSize;
}
bigint ContractCallDataEncoder::decodeInt(dev::bytes const& _rawValue)
{
dev::u256 un = dev::fromBigEndian<dev::u256>(_rawValue);
if (un >> 255)
return (-s256(~un + 1));
return un;
}
QString ContractCallDataEncoder::toString(dev::bigint const& _int)
{
std::stringstream str;
str << std::dec << _int;
return QString::fromStdString(str.str());
}
dev::bytes ContractCallDataEncoder::encodeBool(QString const& _str)
{
bytes b(1);
b[0] = _str == "1" || _str.toLower() == "true " ? 1 : 0;
return padded(b, 32);
}
bool ContractCallDataEncoder::decodeBool(dev::bytes const& _rawValue)
{
byte ret = _rawValue.at(_rawValue.size() - 1);
return (ret != 0);
}
QString ContractCallDataEncoder::toString(bool _b)
{
return _b ? "true" : "false";
}
dev::bytes ContractCallDataEncoder::encodeStringParam(QString const& _str, unsigned alignSize)
{
bytes result;
QByteArray bytesAr = _str.toLocal8Bit();
result = bytes(bytesAr.begin(), bytesAr.end());
return paddedRight(result, alignSize);
}
dev::bytes ContractCallDataEncoder::encodeBytes(QString const& _str)
{
QByteArray bytesAr = _str.toLocal8Bit();
bytes r = bytes(bytesAr.begin(), bytesAr.end());
return padded(r, 32);
}
dev::bytes ContractCallDataEncoder::decodeBytes(dev::bytes const& _rawValue)
{
return _rawValue;
}
QString ContractCallDataEncoder::toString(dev::bytes const& _b)
{
return QString::fromStdString(dev::toJS(_b));
}
QString ContractCallDataEncoder::toChar(dev::bytes const& _b)
{
QString str;
asString(_b, str);
return str;
}
QJsonValue ContractCallDataEncoder::decodeArrayContent(SolidityType const& _type, bytes const& _value, int& pos)
{
if (_type.baseType->array)
{
QJsonArray sub = decodeArray(*_type.baseType, _value, pos);
return sub;
}
else
{
bytesConstRef value(_value.data() + pos, 32);
bytes rawParam(32);
value.populate(&rawParam);
QVariant i = decode(*_type.baseType, rawParam);
pos = pos + 32;
return i.toString();
}
}
QJsonArray ContractCallDataEncoder::decodeArray(SolidityType const& _type, bytes const& _value, int& pos)
{
QJsonArray array;
bytesConstRef value(&_value);
int count = 0;
bigint offset = pos;
int valuePosition = pos;
if (!_type.dynamicSize)
count = _type.count;
else
{
bytesConstRef value(_value.data() + pos, 32); // offset
bytes rawParam(32);
value.populate(&rawParam);
offset = decodeInt(rawParam);
valuePosition = static_cast<int>(offset) + 32;
pos += 32;
value = bytesConstRef(_value.data() + static_cast<int>(offset), 32); // count
value.populate(&rawParam);
count = static_cast<int>(decodeInt(rawParam));
}
if (_type.type == QSolidityType::Type::Bytes || _type.type == QSolidityType::Type::String)
{
bytesConstRef value(_value.data() + (static_cast<int>(offset) + 32), 32);
bytes rawParam(count);
value.populate(&rawParam);
if (_type.type == QSolidityType::Type::Bytes)
array.append(toString(decodeBytes(rawParam)));
else
array.append(toChar(decodeBytes(rawParam)));
}
else
{
for (int k = 0; k < count; ++k)
{
if (_type.dynamicSize)
array.append(decodeArrayContent(_type, _value, valuePosition));
else
array.append(decodeArrayContent(_type, _value, pos));
}
}
return array;
}
QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const& _value)
{
bytesConstRef value(&_value);
bytes rawParam(32);
value.populate(&rawParam);
QSolidityType::Type type = _type.type;
if (type == QSolidityType::Type::SignedInteger || type == QSolidityType::Type::UnsignedInteger)
return QVariant::fromValue(toString(decodeInt(rawParam)));
else if (type == QSolidityType::Type::Bool)
return QVariant::fromValue(toString(decodeBool(rawParam)));
else if (type == QSolidityType::Type::Bytes || type == QSolidityType::Type::Hash)
return QVariant::fromValue(toString(decodeBytes(rawParam)));
else if (type == QSolidityType::Type::String)
return QVariant::fromValue(toChar(decodeBytes(rawParam)));
else if (type == QSolidityType::Type::Struct)
return QVariant::fromValue(QString("struct")); //TODO
else if (type == QSolidityType::Type::Address)
return QVariant::fromValue(toString(decodeBytes(unpadLeft(rawParam))));
else if (type == QSolidityType::Type::Enum)
return QVariant::fromValue(decodeEnum(rawParam));
else
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Parameter declaration not found"));
}
QString ContractCallDataEncoder::decodeEnum(bytes _value)
{
return toString(decodeInt(_value));
}
QString ContractCallDataEncoder::decode(QVariableDeclaration* const& _param, bytes _value)
{
SolidityType const& type = _param->type()->type();
return decode(type, _value).toString();
}
QStringList ContractCallDataEncoder::decode(QList<QVariableDeclaration*> const& _returnParameters, bytes _value)
{
bytes _v = _value;
QStringList r;
int readPosition = 0;
for (int k = 0; k <_returnParameters.length(); k++)
{
QVariableDeclaration* dec = static_cast<QVariableDeclaration*>(_returnParameters.at(k));
SolidityType const& type = dec->type()->type();
if (type.array)
{
QJsonArray array = decodeArray(type, _v, readPosition);
if (type.type == SolidityType::String && array.count() <= 1)
{
if (array.count() == 1)
r.append(array[0].toString());
else
r.append(QString());
}
else
{
QJsonDocument jsonDoc = QJsonDocument::fromVariant(array.toVariantList());
r.append(jsonDoc.toJson(QJsonDocument::Compact));
}
}
else
{
bytesConstRef value(_value.data() + readPosition, 32);
bytes rawParam(32);
value.populate(&rawParam);
r.append(decode(type, rawParam).toString());
readPosition += 32;
}
}
return r;
}
bool ContractCallDataEncoder::asString(dev::bytes const& _b, QString& _str)
{
dev::bytes bunPad = unpadded(_b);
for (unsigned i = 0; i < bunPad.size(); i++)
{
if (bunPad.at(i) < 9 || bunPad.at(i) > 127)
return false;
else
_str += QString::fromStdString(dev::toJS(bunPad.at(i))).replace("0x", "");
}
return true;
}

91
mix/ContractCallDataEncoder.h

@ -1,91 +0,0 @@
/*
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 ContractCallDataEncoder.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include "QVariableDeclaration.h"
#include "QVariableDefinition.h"
namespace dev
{
namespace mix
{
class QFunctionDefinition;
class QVariableDeclaration;
class QVariableDefinition;
class QSolidityType;
/**
* @brief Encode/Decode data to be sent to a transaction or to be displayed in a view.
*/
class ContractCallDataEncoder
{
public:
ContractCallDataEncoder() {}
/// Encode hash of the function to call.
void encode(QFunctionDefinition const* _function);
/// Encode data for corresponding type
void encode(QVariant const& _data, SolidityType const& _type);
/// Decode variable in order to be sent to QML view.
QStringList decode(QList<QVariableDeclaration*> const& _dec, bytes _value);
/// Decode @param _parameter
QString decode(QVariableDeclaration* const& _param, bytes _value);
/// Decode single variable
QVariant decode(SolidityType const& _type, bytes const& _value);
/// Get all encoded data encoded by encode function.
bytes encodedData();
/// Encode a string to bytes (in order to be used as funtion param)
dev::bytes encodeStringParam(QString const& _str, unsigned _alignSize);
/// Encode a string to ABI bytes
dev::bytes encodeBytes(QString const& _str);
/// Decode bytes from ABI
dev::bytes decodeBytes(dev::bytes const& _rawValue);
/// Decode array
QJsonArray decodeArray(SolidityType const& _type, bytes const& _value, int& pos);
/// Decode array items
QJsonValue decodeArrayContent(SolidityType const& _type, bytes const& _value, int& pos);
/// Decode enum
QString decodeEnum(bytes _value);
private:
unsigned encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest);
bigint decodeInt(dev::bytes const& _rawValue);
dev::bytes encodeInt(QString const& _str);
QString toString(dev::bigint const& _int);
dev::bytes encodeBool(QString const& _str);
bool decodeBool(dev::bytes const& _rawValue);
QString toString(bool _b);
QString toString(dev::bytes const& _b);
bool asString(dev::bytes const& _b, QString& _str);
void encodeArray(QJsonArray const& _array, SolidityType const& _type, bytes& _content);
QString toChar(dev::bytes const& _b);
private:
bytes m_encodedData;
bytes m_dynamicData;
std::vector<std::pair<size_t, size_t>> m_dynamicOffsetMap;
std::vector<std::pair<size_t, size_t>> m_staticOffsetMap;
};
}
}

185
mix/DebuggingStateWrapper.cpp

@ -1,185 +0,0 @@
/*
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 DebuggingStateWrapper.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Used to translate c++ type (u256, bytes, ...) into friendly value (to be used by QML).
*/
#include <tuple>
#include <QDebug>
#include <QPointer>
#include <QQmlEngine>
#include <QVariantList>
#include <libevmcore/Instruction.h>
#include <libethcore/CommonJS.h>
#include <libdevcrypto/Common.h>
#include <libevmcore/Instruction.h>
#include <libdevcore/Common.h>
#include "DebuggingStateWrapper.h"
#include "QBigInt.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
namespace
{
static QVariantList memDumpToList(bytes const& _bytes, unsigned _width, bool _includeAddress = false)
{
QVariantList dumpList;
for (unsigned i = 0; i < _bytes.size(); i += _width)
{
std::stringstream ret;
if (_includeAddress)
{
ret << std::setfill('0') << std::setw(6) << std::hex << i << " ";
ret << " ";
}
for (unsigned j = i; j < i + _width; ++j)
if (j < _bytes.size())
if (_bytes[j] >= 32 && _bytes[j] < 127)
ret << (char)_bytes[j];
else
ret << '?';
else
ret << ' ';
QString strPart = QString::fromStdString(ret.str());
ret.clear();
ret.str(std::string());
for (unsigned j = i; j < i + _width && j < _bytes.size(); ++j)
ret << std::setfill('0') << std::setw(2) << std::hex << (unsigned)_bytes[j] << " ";
QString hexPart = QString::fromStdString(ret.str());
QStringList line = { strPart, hexPart };
dumpList.push_back(line);
}
return dumpList;
}
}
QCode* QMachineState::getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code, QHash<int, int>& o_codeMap)
{
QVariantList codeStr;
for (unsigned i = 0; i <= _code.size(); ++i)
{
byte b = i < _code.size() ? _code[i] : 0;
try
{
QString s = QString::fromStdString(instructionInfo((Instruction)b).name);
std::ostringstream out;
out << std::hex << std::setw(4) << std::setfill('0') << i;
int offset = i;
if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32)
{
unsigned bc = getPushNumber((Instruction)b);
s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&_code[i + 1], bc)));
i += bc;
}
o_codeMap[offset] = codeStr.size();
codeStr.append(QVariant::fromValue(new QInstruction(_owner, QString::fromStdString(out.str()) + " " + s)));
}
catch (...)
{
qDebug() << QString("Unhandled exception!") << endl <<
QString::fromStdString(boost::current_exception_diagnostic_information());
break; // probably hit data segment
}
}
return new QCode(_owner, QString::fromStdString(toString(_address)), std::move(codeStr));
}
QBigInt* QMachineState::gasCost()
{
return new QBigInt(m_state.gasCost);
}
QBigInt* QMachineState::gas()
{
return new QBigInt(m_state.gas);
}
QBigInt* QMachineState::newMemSize()
{
return new QBigInt(m_state.newMemSize);
}
QStringList QMachineState::debugStack()
{
QStringList stack;
for (std::vector<u256>::reverse_iterator i = m_state.stack.rbegin(); i != m_state.stack.rend(); ++i)
stack.append(QString::fromStdString(prettyU256(*i)));
return stack;
}
QStringList QMachineState::debugStorage()
{
QStringList storage;
for (auto const& i: m_state.storage)
{
std::stringstream s;
s << "@" << prettyU256(i.first) << "\t" << prettyU256(i.second);
storage.append(QString::fromStdString(s.str()));
}
return storage;
}
QVariantList QMachineState::debugMemory()
{
return memDumpToList(m_state.memory, 16, true);
}
QCallData* QMachineState::getDebugCallData(QObject* _owner, bytes const& _data)
{
return new QCallData(_owner, memDumpToList(_data, 16));
}
QVariantList QMachineState::levels()
{
QVariantList levelList;
for (unsigned l: m_state.levels)
levelList.push_back(l);
return levelList;
}
QString QMachineState::instruction()
{
return QString::fromStdString(dev::eth::instructionInfo(m_state.inst).name);
}
QString QMachineState::endOfDebug()
{
if (m_state.gasCost > m_state.gas)
return QObject::tr("OUT-OF-GAS");
else if (m_state.inst == Instruction::RETURN && m_state.stack.size() >= 2)
{
unsigned from = (unsigned)m_state.stack.back();
unsigned size = (unsigned)m_state.stack[m_state.stack.size() - 2];
unsigned o = 0;
bytes out(size, 0);
for (; o < size && from + o < m_state.memory.size(); ++o)
out[o] = m_state.memory[from + o];
return QObject::tr("RETURN") + " " + QString::fromStdString(dev::memDump(out, 16, false));
}
else if (m_state.inst == Instruction::STOP)
return QObject::tr("STOP");
else if (m_state.inst == Instruction::SUICIDE && m_state.stack.size() >= 1)
return QObject::tr("SUICIDE") + " 0x" + QString::fromStdString(toString(right160(m_state.stack.back())));
else
return QObject::tr("EXCEPTION");
}

204
mix/DebuggingStateWrapper.h

@ -1,204 +0,0 @@
/*
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 DebuggingStateWrapper.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
// Make sure boost/asio.hpp is included before windows.h.
#include <boost/asio.hpp>
#include <QStringList>
#include <QHash>
#include <libdevcore/Common.h>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include "MachineStates.h"
#include "QVariableDefinition.h"
#include "QBigInt.h"
namespace dev
{
namespace mix
{
/**
* @brief Contains the line nb of the assembly code and the corresponding index in the code bytes array.
*/
class QInstruction: public QObject
{
Q_OBJECT
Q_PROPERTY(QString line MEMBER m_line CONSTANT)
public:
QInstruction(QObject* _owner, QString _line): QObject(_owner), m_line(_line) {}
private:
QString m_line;
};
/**
* @brief Solidity state
*/
class QSolState: public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantMap storage MEMBER m_storage CONSTANT)
Q_PROPERTY(QVariantList callStack MEMBER m_callStack CONSTANT)
Q_PROPERTY(QVariantMap locals MEMBER m_locals CONSTANT)
Q_PROPERTY(int start MEMBER m_start CONSTANT)
Q_PROPERTY(int end MEMBER m_end CONSTANT)
Q_PROPERTY(QString sourceName MEMBER m_sourceName CONSTANT)
public:
QSolState(QObject* _parent, QVariantMap&& _storage, QVariantList&& _callStack, QVariantMap&& _locals, int _start, int _end, QString _sourceName):
QObject(_parent), m_storage(std::move(_storage)), m_callStack(std::move(_callStack)), m_locals(std::move(_locals)), m_start(_start), m_end(_end), m_sourceName(_sourceName)
{ }
private:
QVariantMap m_storage;
QVariantList m_callStack;
QVariantMap m_locals;
int m_start;
int m_end;
QString m_sourceName;
};
/**
* @brief Shared container for lines
*/
class QCode: public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList instructions MEMBER m_instructions CONSTANT)
Q_PROPERTY(QString address MEMBER m_address CONSTANT)
Q_PROPERTY(QString documentId MEMBER m_document CONSTANT)
public:
QCode(QObject* _owner, QString const& _address, QVariantList&& _instrunctions): QObject(_owner), m_instructions(std::move(_instrunctions)), m_address(_address) {}
void setDocument(QString const& _documentId) { m_document = _documentId; }
private:
QVariantList m_instructions;
QString m_address;
QString m_document;
};
/**
* @brief Shared container for call data
*/
class QCallData: public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList items MEMBER m_items CONSTANT)
public:
QCallData(QObject* _owner, QVariantList&& _items): QObject(_owner), m_items(std::move(_items)) {}
private:
QVariantList m_items;
};
/**
* @brief Shared container for machine states
*/
class QDebugData: public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList states MEMBER m_states CONSTANT)
public:
QDebugData() { }
void setStates(QVariantList&& _states) { m_states = std::move(_states); }
private:
QVariantList m_states;
};
/**
* @brief Wrap MachineState in QObject
*/
class QMachineState: public QObject
{
Q_OBJECT
Q_PROPERTY(int step READ step CONSTANT)
Q_PROPERTY(int curPC READ curPC CONSTANT)
Q_PROPERTY(int instructionIndex MEMBER m_instructionIndex CONSTANT)
Q_PROPERTY(QBigInt* gasCost READ gasCost CONSTANT)
Q_PROPERTY(QBigInt* gas READ gas CONSTANT)
Q_PROPERTY(QString instruction READ instruction CONSTANT)
Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT)
Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT)
Q_PROPERTY(QVariantList debugMemory READ debugMemory CONSTANT)
Q_PROPERTY(QObject* code MEMBER m_code CONSTANT)
Q_PROPERTY(QObject* callData MEMBER m_callData CONSTANT)
Q_PROPERTY(QString endOfDebug READ endOfDebug CONSTANT)
Q_PROPERTY(QBigInt* newMemSize READ newMemSize CONSTANT)
Q_PROPERTY(QVariantList levels READ levels CONSTANT)
Q_PROPERTY(unsigned codeIndex READ codeIndex CONSTANT)
Q_PROPERTY(unsigned dataIndex READ dataIndex CONSTANT)
Q_PROPERTY(QObject* solidity MEMBER m_solState CONSTANT)
public:
QMachineState(QObject* _owner, int _instructionIndex, MachineState const& _state, QCode* _code, QCallData* _callData, QSolState* _solState):
QObject(_owner), m_instructionIndex(_instructionIndex), m_state(_state), m_code(_code), m_callData(_callData), m_solState(_solState) { }
/// Get the step of this machine states.
int step() { return (int)m_state.steps; }
/// Get the proccessed code index.
int curPC() { return (int)m_state.curPC; }
/// Get the code id
unsigned codeIndex() { return m_state.codeIndex; }
/// Get the call data id
unsigned dataIndex() { return m_state.dataIndex; }
/// Get gas cost.
QBigInt* gasCost();
/// Get gas used.
QBigInt* gas();
/// Get stack.
QStringList debugStack();
/// Get storage.
QStringList debugStorage();
/// Get memory.
QVariantList debugMemory();
/// Get end of debug information.
QString endOfDebug();
/// Get the new memory size.
QBigInt* newMemSize();
/// Get current instruction
QString instruction();
/// Get all previous steps.
QVariantList levels();
/// Get the current processed machine state.
MachineState const& state() const { return m_state; }
/// Convert all machine states in human readable code.
static QCode* getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code, QHash<int, int>& o_codeMap);
/// Convert call data into human readable form
static QCallData* getDebugCallData(QObject* _owner, bytes const& _data);
private:
int m_instructionIndex;
MachineState m_state;
QCode* m_code;
QCallData* m_callData;
QSolState* m_solState;
};
}
}

31
mix/Exceptions.cpp

@ -1,31 +0,0 @@
/*
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 Exceptions.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include <ostream>
#include <QQmlError>
#include "Exceptions.h"
std::ostream& operator<<(std::ostream& _out, QQmlError const& _error)
{
_out << _error.toString().toStdString();
return _out;
}

52
mix/Exceptions.h

@ -1,52 +0,0 @@
/*
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 Exceptions.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <iosfwd>
#include <libdevcore/Exceptions.h>
class QTextDocument;
class QQmlError;
namespace dev
{
namespace mix
{
struct QmlLoadException: virtual Exception {};
struct FileIoException: virtual Exception {};
struct InvalidBlockException: virtual Exception {};
struct FunctionNotFoundException: virtual Exception {};
struct ExecutionStateException: virtual Exception {};
struct ParameterChangedException: virtual Exception {};
struct OutOfGasException: virtual Exception {};
using QmlErrorInfo = boost::error_info<struct tagQmlError, QQmlError>;
using FileError = boost::error_info<struct tagFileError, std::string>;
using BlockIndex = boost::error_info<struct tagBlockIndex, unsigned>;
using FunctionName = boost::error_info<struct tagFunctionName, std::string>;
}
}
std::ostream& operator<<(std::ostream& _out, QQmlError const& _error);

234
mix/FileIo.cpp

@ -1,234 +0,0 @@
/*
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 FileIo.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include <QFileSystemWatcher>
#include <QDebug>
#include <QDesktopServices>
#include <QMimeDatabase>
#include <QDirIterator>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#include <QUrl>
#include <json/json.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
#include "FileIo.h"
using namespace dev;
using namespace dev::crypto;
using namespace dev::mix;
FileIo::FileIo(): m_watcher(new QFileSystemWatcher(this))
{
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &FileIo::fileChanged);
}
void FileIo::openFileBrowser(QString const& _dir)
{
QDesktopServices::openUrl(QUrl(_dir));
}
QString FileIo::pathFromUrl(QString const& _url)
{
QUrl url(_url);
QString path(url.path());
if (url.scheme() == "qrc")
path = ":" + path;
#ifdef WIN32
if (url.scheme() == "file")
{
if (path.startsWith("/"))
path = path.right(path.length() - 1);
if (!url.host().isEmpty())
path = url.host() + ":/" + path;
}
#endif
return path;
}
void FileIo::makeDir(QString const& _url)
{
QDir dirPath(pathFromUrl(_url));
if (dirPath.exists())
dirPath.removeRecursively();
dirPath.mkpath(dirPath.path());
}
void FileIo::deleteDir(QString const& _url)
{
QDir dirPath(pathFromUrl(_url));
if (dirPath.exists())
dirPath.removeRecursively();
}
QString FileIo::readFile(QString const& _url)
{
QFile file(pathFromUrl(_url));
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream stream(&file);
QString data = stream.readAll();
return data;
}
else
error(tr("Error reading file %1").arg(_url));
return QString();
}
void FileIo::writeFile(QString const& _url, QString const& _data)
{
QString path = pathFromUrl(_url);
m_watcher->removePath(path);
QFile file(path);
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
{
QTextStream stream(&file);
stream << _data;
}
else
error(tr("Error writing file %1").arg(_url));
file.close();
m_watcher->addPath(path);
}
void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl)
{
if (QUrl(_sourceUrl).scheme() == "qrc")
{
writeFile(_destUrl, readFile(_sourceUrl));
return;
}
if (!QFile::copy(pathFromUrl(_sourceUrl), pathFromUrl(_destUrl)))
error(tr("Error copying file %1 to %2").arg(_sourceUrl).arg(_destUrl));
}
QString FileIo::getHomePath() const
{
return QDir::homePath();
}
void FileIo::moveFile(QString const& _sourceUrl, QString const& _destUrl)
{
if (!QFile::rename(pathFromUrl(_sourceUrl), pathFromUrl(_destUrl)))
error(tr("Error moving file %1 to %2").arg(_sourceUrl).arg(_destUrl));
}
bool FileIo::fileExists(QString const& _url)
{
QFile file(pathFromUrl(_url));
return file.exists();
}
QStringList FileIo::makePackage(QString const& _deploymentFolder)
{
Json::Value manifest;
Json::Value entries(Json::arrayValue);
QDir deployDir = QDir(pathFromUrl(_deploymentFolder));
dev::RLPStream rlpStr;
int k = 1;
std::vector<bytes> files;
QMimeDatabase mimeDb;
for (auto item: deployDir.entryInfoList(QDir::Files))
{
QFile qFile(item.filePath());
if (qFile.open(QIODevice::ReadOnly))
{
k++;
QFileInfo fileInfo = QFileInfo(qFile.fileName());
Json::Value jsonValue;
std::string path = fileInfo.fileName() == "index.html" ? "/" : fileInfo.fileName().toStdString();
jsonValue["path"] = path; //TODO: Manage relative sub folder
jsonValue["file"] = "/" + fileInfo.fileName().toStdString();
jsonValue["contentType"] = mimeDb.mimeTypeForFile(qFile.fileName()).name().toStdString();
QByteArray a = qFile.readAll();
bytes data = bytes(a.begin(), a.end());
files.push_back(data);
jsonValue["hash"] = toHex(dev::sha3(data).ref());
entries.append(jsonValue);
}
qFile.close();
}
rlpStr.appendList(k);
manifest["entries"] = entries;
std::stringstream jsonStr;
jsonStr << manifest;
QByteArray b = QString::fromStdString(jsonStr.str()).toUtf8();
rlpStr.append(bytesConstRef((const unsigned char*)b.data(), b.size()));
for (unsigned int k = 0; k < files.size(); k++)
rlpStr.append(files.at(k));
bytes dapp = rlpStr.out();
dev::h256 dappHash = dev::sha3(dapp);
//encrypt
KeyPair key((Secret(dappHash)));
Secp256k1PP enc;
enc.encrypt(key.pub(), dapp);
QUrl url(_deploymentFolder + "package.dapp");
QFile compressed(url.path());
QByteArray qFileBytes((char*)dapp.data(), static_cast<int>(dapp.size()));
if (compressed.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
compressed.write(qFileBytes);
compressed.flush();
}
else
error(tr("Error creating package.dapp"));
compressed.close();
QStringList ret;
ret.append(QString::fromStdString(toHex(dappHash.ref())));
ret.append(qFileBytes.toBase64());
ret.append(url.toString());
return ret;
}
void FileIo::watchFileChanged(QString const& _path)
{
m_watcher->addPath(pathFromUrl(_path));
}
void FileIo::stopWatching(QString const& _path)
{
m_watcher->removePath(pathFromUrl(_path));
}
void FileIo::deleteFile(QString const& _path)
{
QFile file(pathFromUrl(_path));
file.remove();
}
QUrl FileIo::pathFolder(QString const& _path)
{
QFileInfo info(_path);
if (info.exists() && info.isDir())
return QUrl::fromLocalFile(_path);
return QUrl::fromLocalFile(QFileInfo(_path).absolutePath());
}

86
mix/FileIo.h

@ -1,86 +0,0 @@
/*
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 FileIo.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <libdevcore/CommonData.h>
#include <QObject>
class QFileSystemWatcher;
namespace dev
{
namespace mix
{
///File services for QML
class FileIo: public QObject
{
Q_OBJECT
Q_PROPERTY(QString homePath READ getHomePath CONSTANT)
signals:
/// Signalled in case of IO error
void error(QString const& _errorText);
/// Signnalled when a file is changed.
void fileChanged(QString const& _filePath);
public:
FileIo();
/// Create a directory if it does not exist. Signals on failure.
Q_INVOKABLE void makeDir(QString const& _url);
/// Read file contents to a string. Signals on failure.
Q_INVOKABLE QString readFile(QString const& _url);
/// Write contents to a file. Signals on failure.
Q_INVOKABLE void writeFile(QString const& _url, QString const& _data);
/// Copy a file from _sourcePath to _destPath. Signals on failure.
Q_INVOKABLE void copyFile(QString const& _sourceUrl, QString const& _destUrl);
/// Move (rename) a file from _sourcePath to _destPath. Signals on failure.
Q_INVOKABLE void moveFile(QString const& _sourceUrl, QString const& _destUrl);
/// Check if file exists
Q_INVOKABLE bool fileExists(QString const& _url);
/// Compress a folder, @returns sha3 of the compressed file.
Q_INVOKABLE QStringList makePackage(QString const& _deploymentFolder);
/// Open a file browser.
Q_INVOKABLE void openFileBrowser(QString const& _dir);
/// Listen for files change in @arg _path.
Q_INVOKABLE void watchFileChanged(QString const& _path);
/// Stop Listenning for files change in @arg _path.
Q_INVOKABLE void stopWatching(QString const& _path);
/// Delete a file
Q_INVOKABLE void deleteFile(QString const& _path);
/// delete a directory
Q_INVOKABLE void deleteDir(QString const& _url);
//TODO: remove once qt 5.5.1 is out
Q_INVOKABLE QString urlToPath(QUrl const& _url) { return _url.toLocalFile(); }
Q_INVOKABLE QUrl pathToUrl(QString const& _path) { return QUrl::fromLocalFile(_path); }
Q_INVOKABLE QUrl pathFolder(QString const& _path);
private:
QString getHomePath() const;
QString pathFromUrl(QString const& _url);
QFileSystemWatcher* m_watcher;
};
}
}

192
mix/HttpServer.cpp

@ -1,192 +0,0 @@
/*
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 HttpServer.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include <memory>
#include <QTcpSocket>
#include "HttpServer.h"
using namespace dev::mix;
HttpServer::HttpServer()
: m_port(0) , m_listen(false) , m_accept(true) , m_componentCompleted(true)
{
}
HttpServer::~HttpServer()
{
setListen(false);
}
void HttpServer::classBegin()
{
m_componentCompleted = false;
}
void HttpServer::componentComplete()
{
init();
m_componentCompleted = true;
}
QUrl HttpServer::url() const
{
QUrl url;
url.setPort(m_port);
url.setHost("localhost");
url.setScheme("http");
return url;
}
void HttpServer::setPort(int _port)
{
if (_port == m_port)
return;
m_port = _port;
emit portChanged(_port);
emit urlChanged(url());
if (m_componentCompleted && this->isListening())
updateListening();
}
QString HttpServer::errorString() const
{
return QTcpServer::errorString();
}
void HttpServer::setListen(bool _listen)
{
if (_listen == m_listen)
return;
m_listen = _listen;
emit listenChanged(_listen);
if (m_componentCompleted)
updateListening();
}
void HttpServer::setAccept(bool _accept)
{
if (_accept == m_accept)
return;
m_accept = _accept;
emit acceptChanged(_accept);
}
void HttpServer::init()
{
updateListening();
}
void HttpServer::updateListening()
{
if (this->isListening())
this->close();
if (!m_listen)
return;
if (!QTcpServer::listen(QHostAddress::LocalHost, m_port))
{
errorStringChanged();
return;
}
if (m_port != QTcpServer::serverPort())
{
m_port = QTcpServer::serverPort();
emit portChanged(m_port);
emit urlChanged(url());
}
}
void HttpServer::incomingConnection(qintptr _socket)
{
if (!m_accept)
return;
QTcpSocket* s = new QTcpSocket(this);
connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
connect(s, SIGNAL(disconnected()), this, SLOT(discardClient()));
s->setSocketDescriptor(_socket);
}
void HttpServer::readClient()
{
if (!m_accept)
return;
QTcpSocket* socket = (QTcpSocket*)sender();
try
{
if (socket->canReadLine())
{
QString hdr = QString(socket->readLine());
QVariantMap headers;
if (hdr.startsWith("POST") || hdr.startsWith("GET"))
{
QUrl url(hdr.split(' ')[1]);
QString l;
do
{
l = socket->readLine();
//collect headers
int colon = l.indexOf(':');
if (colon > 0)
headers[l.left(colon).trimmed().toLower()] = l.right(l.length() - colon - 1).trimmed();
}
while (!(l.isEmpty() || l == "\r" || l == "\r\n"));
QString content = socket->readAll();
std::unique_ptr<HttpRequest> request(new HttpRequest(this, std::move(url), std::move(content), std::move(headers)));
clientConnected(request.get());
QTextStream os(socket);
os.setAutoDetectUnicode(true);
QString q;
///@todo: allow setting response content-type, charset, etc
os << "HTTP/1.0 200 Ok\r\n";
if (!request->m_responseContentType.isEmpty())
os << "Content-Type: " << request->m_responseContentType << "; ";
os << "charset=\"utf-8\"\r\n\r\n";
os << request->m_response;
}
}
}
catch(...)
{
delete socket;
throw;
}
socket->close();
if (socket->state() == QTcpSocket::UnconnectedState)
delete socket;
}
void HttpServer::discardClient()
{
QTcpSocket* socket = (QTcpSocket*)sender();
socket->deleteLater();
}

131
mix/HttpServer.h

@ -1,131 +0,0 @@
/*
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 HttpServer.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <QObject>
#include <QTcpServer>
#include <QUrl>
#include <QVariantMap>
#include <QQmlParserStatus>
namespace dev
{
namespace mix
{
/// Simple http server for serving jsonrpc requests
class HttpRequest: public QObject
{
Q_OBJECT
/// Request url
Q_PROPERTY(QUrl url MEMBER m_url CONSTANT)
/// Request body contents
Q_PROPERTY(QString content MEMBER m_content CONSTANT)
/// Request HTTP headers
Q_PROPERTY(QVariantMap headers MEMBER m_headers CONSTANT)
private:
HttpRequest(QObject* _parent, QUrl&& _url, QString&& _content, QVariantMap&& _headers):
QObject(_parent), m_url(std::move(_url)), m_content(std::move(_content)), m_headers(std::move(_headers))
{
}
public:
/// Set response for a request
/// @param _response Response body. If no response is set, server returns status 200 with empty body
Q_INVOKABLE void setResponse(QString const& _response) { m_response = _response; }
/// Set response content type
/// @param _contentType Response content type string. text/plain by default
Q_INVOKABLE void setResponseContentType(QString const& _contentType) { m_responseContentType = _contentType ; }
private:
QUrl m_url;
QString m_content;
QString m_response;
QString m_responseContentType;
QVariantMap m_headers;
friend class HttpServer;
};
class HttpServer: public QTcpServer, public QQmlParserStatus
{
Q_OBJECT
Q_DISABLE_COPY(HttpServer)
Q_INTERFACES(QQmlParserStatus)
/// Server url
Q_PROPERTY(QUrl url READ url NOTIFY urlChanged)
/// Server port
Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged)
/// Listen for connections
Q_PROPERTY(bool listen READ listen WRITE setListen NOTIFY listenChanged)
/// Accept new connections
Q_PROPERTY(bool accept READ accept WRITE setAccept NOTIFY acceptChanged)
/// Error string if any
Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
public:
explicit HttpServer();
virtual ~HttpServer();
QUrl url() const;
int port() const { return m_port; }
void setPort(int _port);
bool listen() const { return m_listen; }
void setListen(bool _listen);
bool accept() const { return m_accept; }
void setAccept(bool _accept);
QString errorString() const;
protected:
virtual void classBegin() override;
virtual void componentComplete() override;
virtual void incomingConnection(qintptr _socket) override;
signals:
void clientConnected(HttpRequest* _request);
void errorStringChanged();
void urlChanged(QUrl const& _url);
void portChanged(int _port);
void listenChanged(bool _listen);
void acceptChanged(bool _accept);
private:
void init();
void updateListening();
void newConnection();
void serverError();
private slots:
void readClient();
void discardClient();
private:
int m_port;
bool m_listen;
bool m_accept;
bool m_componentCompleted;
};
}
}

59
mix/InverseMouseArea.cpp

@ -1,59 +0,0 @@
/*
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 InverseMouseArea.cpp
* @author Yann yann@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include <QQuickWindow>
#include <QDebug>
#include <QQuickItem>
#include <QGraphicsSceneMouseEvent>
#include "InverseMouseArea.h"
using namespace dev::mix;
void InverseMouseArea::itemChange(ItemChange _c, const ItemChangeData& _v)
{
Q_UNUSED(_v);
if (this->m_active && _c == ItemSceneChange && window())
window()->installEventFilter(this);
}
bool InverseMouseArea::eventFilter(QObject* _obj, QEvent* _ev)
{
Q_UNUSED(_obj);
if (this->m_active && _ev->type() == QEvent::MouseButtonPress && !this->contains(static_cast<QMouseEvent*>(_ev)->pos()))
emit clickedOutside(QPointF(static_cast<QMouseEvent*>(_ev)->pos()));
return false;
}
bool InverseMouseArea::contains(const QPointF& _point) const
{
if (!this->m_active)
return false;
QPointF global = this->parentItem()->mapToItem(0, QPointF(0, 0));
return QRectF(global.x(), global.y(), this->parentItem()->width(), this->parentItem()->height()).contains(_point);
}
void InverseMouseArea::setActive(bool _v)
{
m_active = _v;
if (m_active && window())
window()->installEventFilter(this);
}

57
mix/InverseMouseArea.h

@ -1,57 +0,0 @@
/*
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 InverseMouseArea.h
* @author Yann yann@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <QQuickWindow>
#include <QQuickItem>
namespace dev
{
namespace mix
{
class InverseMouseArea: public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool active MEMBER m_active WRITE setActive)
public:
InverseMouseArea(QQuickItem* _parent = 0): QQuickItem(_parent) {}
~InverseMouseArea() { if (window()) { window()->removeEventFilter(this); } }
void setActive(bool _v);
protected:
void itemChange(ItemChange _c, const ItemChangeData& _v) override;
bool eventFilter(QObject* _obj, QEvent *_ev) override;
bool contains(const QPointF& _point) const override;
private:
bool m_active;
signals:
void clickedOutside(QPointF _point);
};
}
}

96
mix/MachineStates.h

@ -1,96 +0,0 @@
/*
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 MixClient.h
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <vector>
#include <map>
#include <stdint.h>
#include <libdevcore/Common.h>
#include <libdevcrypto/Common.h>
#include <libevmcore/Instruction.h>
#include <libethereum/Transaction.h>
#include <libethereum/TransactionReceipt.h>
namespace dev
{
namespace mix
{
/**
* @brief Store information about a machine state.
*/
struct MachineState
{
uint64_t steps;
dev::u256 curPC;
dev::eth::Instruction inst;
dev::bigint newMemSize;
dev::u256 gas;
dev::u256s stack;
dev::bytes memory;
dev::bigint gasCost;
std::unordered_map<dev::u256, dev::u256> storage;
std::vector<unsigned> levels;
unsigned codeIndex;
unsigned dataIndex;
};
/**
* @brief Executed conract code info
*/
struct MachineCode
{
dev::Address address;
bytes code;
};
/**
* @brief Store information about a machine states.
*/
struct ExecutionResult
{
ExecutionResult(): transactionIndex(std::numeric_limits<unsigned>::max()) {}
std::vector<MachineState> machineStates;
std::vector<bytes> transactionData;
std::vector<MachineCode> executionCode;
dev::eth::ExecutionResult result;
dev::Address address;
dev::Address sender;
dev::Address contractAddress;
dev::u256 value;
dev::u256 gasUsed;
unsigned transactionIndex;
unsigned executonIndex = 0;
bytes inputParameters;
eth::LocalisedLogEntries logs;
eth::TransactionException excepted = eth::TransactionException::Unknown;
bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); }
bool isConstructor() const { return !isCall() && !address; }
};
using ExecutionResults = std::vector<ExecutionResult>;
}
}

116
mix/MixApplication.cpp

@ -1,116 +0,0 @@
/*
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 MixApplication.cpp
* @author Yann yann@ethdev.com
* @date 2014
*/
#include "MixApplication.h"
#include <boost/exception/diagnostic_information.hpp>
#include <QQmlApplicationEngine>
#include <QUrl>
#include <QIcon>
#include <QFont>
#ifdef ETH_HAVE_WEBENGINE
#include <QtWebEngine/QtWebEngine>
#endif
#include "CodeModel.h"
#include "ClientModel.h"
#include "FileIo.h"
#include "QEther.h"
#include "QVariableDeclaration.h"
#include "SortFilterProxyModel.h"
#include "Clipboard.h"
#include "HttpServer.h"
#include "InverseMouseArea.h"
extern int qInitResources_js();
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())
{
setWindowIcon(QIcon(":/res/mix_256x256x32.png"));
m_engine->load(QUrl("qrc:/qml/Application.qml"));
}
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
#ifdef ETH_HAVE_WEBENGINE
qInitResources_js();
#endif
setOrganizationName(tr("Ethereum"));
setOrganizationDomain(tr("ethereum.org"));
setApplicationName(tr("Mix"));
setApplicationVersion("0.1");
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");
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration");
qmlRegisterType<RecordLogEntry>("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry");
qmlRegisterType<SortFilterProxyModel>("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel");
qmlRegisterType<QSolidityType>("org.ethereum.qml.QSolidityType", 1, 0, "QSolidityType");
qmlRegisterType<Clipboard>("org.ethereum.qml.Clipboard", 1, 0, "Clipboard");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
qmlRegisterType<InverseMouseArea>("org.ethereum.qml.InverseMouseArea", 1, 0, "InverseMouseArea");
qRegisterMetaType<CodeModel*>("CodeModel*");
qRegisterMetaType<ClientModel*>("ClientModel*");
}
MixApplication::~MixApplication()
{
}
bool MixApplication::notify(QObject * receiver, QEvent * event)
{
try
{
return QApplication::notify(receiver, event);
}
catch (...)
{
std::cerr << boost::current_exception_diagnostic_information();
}
return false;
}

72
mix/MixApplication.h

@ -1,72 +0,0 @@
/*
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 MixApplication.h
* @author Yann yann@ethdev.com
* @date 2014
* This class will be use instead of QApplication to launch the application. the method 'notify' allows to catch all exceptions.
* Not use for now: TODO.
*/
#pragma once
#include <memory>
#include <QApplication>
class QQmlApplicationEngine;
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(); }
bool notify(QObject* _receiver, QEvent* _event) override;
private:
std::unique_ptr<QQmlApplicationEngine> m_engine;
};
}
}

384
mix/MixClient.cpp

@ -1,384 +0,0 @@
/*
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 MixClient.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include "MixClient.h"
#include <vector>
#include <utility>
#include <libdevcore/Exceptions.h>
#include <libethcore/Params.h>
#include <libethcore/BasicAuthority.h>
#include <libethereum/CanonBlockChain.h>
#include <libethereum/Transaction.h>
#include <libethereum/Executive.h>
#include <libethereum/ExtVM.h>
#include <libethereum/BlockChain.h>
#include <libevm/VM.h>
#include "Exceptions.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace dev
{
namespace mix
{
u256 const c_mixGenesisDifficulty = 131072; //TODO: make it lower for Mix somehow
namespace
{
}
MixBlockChain::MixBlockChain(std::string const& _path, h256 _stateRoot):
FullBlockChain<NoProof>(createGenesisBlock(_stateRoot), std::unordered_map<Address, Account>(), _path, WithExisting::Kill)
{
}
bytes MixBlockChain::createGenesisBlock(h256 _stateRoot)
{
RLPStream block(3);
block.appendList(13)
<< h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie
<< LogBloom() << c_mixGenesisDifficulty << 0 << 3141592 << 0 << (unsigned)0
<< std::string();
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
}
MixClient::MixClient(std::string const& _dbPath):
m_dbPath(_dbPath)
{
resetState(std::unordered_map<Address, Account>());
}
MixClient::~MixClient()
{
}
void MixClient::resetState(std::unordered_map<Address, Account> const& _accounts, Secret const& _miner)
{
WriteGuard l(x_state);
Guard fl(x_filtersWatches);
m_filters.clear();
for (auto& i: m_specialFilters)
i.second.clear();
m_watches.clear();
m_stateDB = OverlayDB();
SecureTrieDB<Address, MemoryDB> accountState(&m_stateDB);
accountState.init();
dev::eth::commit(_accounts, accountState);
h256 stateRoot = accountState.root();
m_bc.reset();
m_bc.reset(new MixBlockChain(m_dbPath, stateRoot));
Block b(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address());
b.sync(bc());
m_preMine = b;
m_postMine = b;
WriteGuard lx(x_executions);
m_executions.clear();
}
Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secret const& _secret)
{
Transaction ret;
if (_secret)
{
if (_t.isCreation())
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret);
else
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret);
}
else
{
if (_t.isCreation())
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce());
else
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce());
ret.forceSender(_t.safeSender());
}
return ret;
}
// TODO: prototype changed - will need rejigging.
ExecutionResult MixClient::debugTransaction(Transaction const& _t, State const& _state, EnvInfo const& _envInfo, bool _call)
{
State execState = _state;
execState.addBalance(_t.sender(), _t.gas() * _t.gasPrice()); //give it enough balance for gas estimation
eth::ExecutionResult er;
Executive execution(execState, _envInfo);
execution.setResultRecipient(er);
ExecutionResult d;
d.address = _t.receiveAddress();
d.sender = _t.sender();
d.value = _t.value();
d.inputParameters = _t.data();
d.executonIndex = m_executions.size();
if (!_call)
d.transactionIndex = m_postMine.pending().size();
try
{
execution.initialize(_t);
execution.execute();
}
catch (Exception const& _e)
{
d.excepted = toTransactionException(_e);
d.transactionData.push_back(_t.data());
return d;
}
std::vector<MachineState> machineStates;
std::vector<unsigned> levels;
std::vector<MachineCode> codes;
std::map<bytes const*, unsigned> codeIndexes;
std::vector<bytes> data;
std::map<bytesConstRef const*, unsigned> dataIndexes;
bytes const* lastCode = nullptr;
bytesConstRef const* lastData = nullptr;
unsigned codeIndex = 0;
unsigned dataIndex = 0;
auto onOp = [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, void* voidVM, void const* voidExt)
{
VM& vm = *static_cast<VM*>(voidVM);
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
if (lastCode == nullptr || lastCode != &ext.code)
{
auto const& iter = codeIndexes.find(&ext.code);
if (iter != codeIndexes.end())
codeIndex = iter->second;
else
{
codeIndex = codes.size();
codes.push_back(MachineCode({ext.myAddress, ext.code}));
codeIndexes[&ext.code] = codeIndex;
}
lastCode = &ext.code;
}
if (lastData == nullptr || lastData != &ext.data)
{
auto const& iter = dataIndexes.find(&ext.data);
if (iter != dataIndexes.end())
dataIndex = iter->second;
else
{
dataIndex = data.size();
data.push_back(ext.data.toBytes());
dataIndexes[&ext.data] = dataIndex;
}
lastData = &ext.data;
}
if (levels.size() < ext.depth)
levels.push_back(machineStates.size() - 1);
else
levels.resize(ext.depth);
machineStates.push_back(MachineState{
steps,
vm.curPC(),
inst,
newMemSize,
static_cast<u256>(gas),
vm.stack(),
vm.memory(),
gasCost,
ext.state().storage(ext.myAddress),
std::move(levels),
codeIndex,
dataIndex
});
};
execution.go(onOp);
execution.finalize();
d.excepted = er.excepted;
d.result = er;
d.machineStates = machineStates;
d.executionCode = std::move(codes);
d.transactionData = std::move(data);
d.gasUsed = er.gasUsed + er.gasRefunded + c_callStipend;
if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
return d;
}
void MixClient::executeTransaction(Transaction const& _t, Block& _block, bool _call, bool _gasAuto, Secret const& _secret)
{
Transaction t = _gasAuto ? replaceGas(_t, m_postMine.gasLimitRemaining()) : _t;
// do debugging run first
EnvInfo envInfo(bc().info(), bc().lastHashes());
ExecutionResult d = debugTransaction(t, _block.state(), envInfo, _call);
// execute on a state
if (!_call && d.excepted == TransactionException::None)
{
u256 useGas = min(d.gasUsed, _block.gasLimitRemaining());
t = _gasAuto ? replaceGas(_t, useGas, _secret) : _t;
eth::ExecutionResult const& er = _block.execute(envInfo.lastHashes(), t);
if (t.isCreation() && _block.state().code(d.contractAddress).empty())
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment"));
d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend;
LocalisedLogEntries logs;
TransactionReceipt const& tr = _block.receipt(_block.pending().size() - 1);
LogEntries le = tr.log();
if (le.size())
for (unsigned j = 0; j < le.size(); ++j)
logs.insert(logs.begin(), LocalisedLogEntry(le[j]));
d.logs = logs;
}
WriteGuard l(x_executions);
m_executions.emplace_back(std::move(d));
}
void MixClient::mine()
{
WriteGuard l(x_state);
m_postMine.commitToSeal(bc());
NoProof::BlockHeader h(m_postMine.info());
RLPStream header;
h.streamRLP(header);
m_postMine.sealBlock(header.out());
bc().import(m_postMine.blockData(), m_postMine.state().db(), (ImportRequirements::Everything & ~ImportRequirements::ValidSeal) != 0);
m_postMine.sync(bc());
m_preMine = m_postMine;
}
ExecutionResult MixClient::lastExecution() const
{
ReadGuard l(x_executions);
return m_executions.empty() ? ExecutionResult() : m_executions.back();
}
ExecutionResult MixClient::execution(unsigned _index) const
{
ReadGuard l(x_executions);
return m_executions.at(_index);
}
Block MixClient::asOf(h256 const& _block) const
{
ReadGuard l(x_state);
Block ret(m_stateDB);
ret.populateFromChain(bc(), _block);
return ret;
}
pair<h256, Address> MixClient::submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret, bool _gasAuto)
{
WriteGuard l(x_state);
TransactionSkeleton ts = _ts;
ts.from = toAddress(_secret);
ts.nonce = m_postMine.transactionsFrom(ts.from);
eth::Transaction t(ts, _secret);
executeTransaction(t, m_postMine, false, _gasAuto, _secret);
return make_pair(t.sha3(), toAddress(ts.from, ts.nonce));
}
dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto, FudgeFactor _ff)
{
(void)_blockNumber;
Block block = asOf(eth::PendingBlock);
u256 n = block.transactionsFrom(_from);
Transaction t(_value, _gasPrice, _gas, _dest, _data, n);
t.forceSender(_from);
if (_ff == FudgeFactor::Lenient)
block.mutableState().addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, block, true, _gasAuto);
return lastExecution().result;
}
dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff)
{
return call(_from, _value, _dest, _data, _gas, _gasPrice, _blockNumber, false, _ff);
}
dev::eth::ExecutionResult MixClient::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff)
{
(void)_blockNumber;
u256 n;
Block temp;
{
ReadGuard lr(x_state);
temp = asOf(eth::PendingBlock);
n = temp.transactionsFrom(_from);
}
Transaction t(_value, _gasPrice, _gas, _data, n);
t.forceSender(_from);
if (_ff == FudgeFactor::Lenient)
temp.mutableState().addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp, true, false);
return lastExecution().result;
}
eth::BlockInfo MixClient::blockInfo() const
{
ReadGuard l(x_state);
return BlockInfo(bc().block());
}
void MixClient::setBeneficiary(Address _us)
{
WriteGuard l(x_state);
m_postMine.setBeneficiary(_us);
}
void MixClient::startMining()
{
//no-op
}
void MixClient::stopMining()
{
//no-op
}
bool MixClient::isMining() const
{
return false;
}
u256 MixClient::hashrate() const
{
return 0;
}
eth::WorkingProgress MixClient::miningProgress() const
{
return eth::WorkingProgress();
}
}
}

135
mix/MixClient.h

@ -1,135 +0,0 @@
/*
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 MixClient.h
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <vector>
#include <string>
#include <libethcore/BasicAuthority.h>
#include <libethereum/ExtVM.h>
#include <libethereum/ClientBase.h>
#include <libethereum/Client.h>
#include "MachineStates.h"
namespace dev
{
namespace eth { class EnvInfo; }
namespace mix
{
class NoProof
{
class BlockHeaderRaw: public dev::eth::BlockInfo
{
public:
static const unsigned SealFields = 0;
protected:
BlockHeaderRaw() = default;
BlockHeaderRaw(BlockInfo const& _bi): BlockInfo(_bi) {}
void populateFromHeader(RLP const& _header, dev::eth::Strictness _s) { (void) _header; (void) _s; }
void populateFromParent(BlockHeaderRaw const& _parent) { (void)_parent; }
void streamRLPFields(RLPStream& _s) const { (void) _s; }
};
public:
static std::string name() { return "NoProof"; }
static unsigned revision() { return 0; }
using BlockHeader = dev::eth::BlockHeaderPolished<BlockHeaderRaw>;
private:
static AddressHash s_authorities;
};
class MixBlockChain: public dev::eth::FullBlockChain<NoProof>
{
public:
MixBlockChain(std::string const& _path, h256 _stateRoot);
static bytes createGenesisBlock(h256 _stateRoot);
};
class MixClient: public dev::eth::ClientBase
{
public:
MixClient(std::string const& _dbPath);
virtual ~MixClient();
/// Reset state to the empty state with given balance.
void resetState(std::unordered_map<dev::Address, dev::eth::Account> const& _accounts, Secret const& _miner = Secret());
void mine();
ExecutionResult lastExecution() const;
ExecutionResult execution(unsigned _index) const;
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override;
dev::eth::ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override;
using ClientBase::submitTransaction;
virtual std::pair<h256, Address> submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret) override { return submitTransaction(_ts, _secret, false); }
std::pair<h256, Address> submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret, bool _gasAuto);
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict);
ExecutionResult debugTransaction(dev::eth::Transaction const& _t, eth:: State const& _state, eth::EnvInfo const& _envInfo, bool _call);
void setBeneficiary(Address _us) override;
void startMining() override;
void stopMining() override;
bool isMining() const override;
u256 hashrate() const override;
eth::WorkingProgress miningProgress() const override;
virtual void flushTransactions() override {}
/// @returns the last mined block information
using Interface::blockInfo; // to remove warning about hiding virtual function
eth::BlockInfo blockInfo() const;
/// return the new address generated by the last tr (if creation). returns empty address if other cases.
Address lastCreatedContractAddr() const;
protected:
/// ClientBase methods
using ClientBase::asOf;
virtual dev::eth::Block asOf(h256 const& _block) const override;
virtual dev::eth::BlockChain& bc() override { return *m_bc; }
virtual dev::eth::BlockChain const& bc() const override { return *m_bc; }
virtual dev::eth::Block preMine() const override { ReadGuard l(x_state); return m_preMine; }
virtual dev::eth::Block postMine() const override { ReadGuard l(x_state); return m_postMine; }
virtual void prepareForTransaction() override {}
private:
void executeTransaction(dev::eth::Transaction const& _t, eth::Block& _block, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret());
dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret());
eth::Block m_preMine;
eth::Block m_postMine;
OverlayDB m_stateDB;
std::unique_ptr<MixBlockChain> m_bc;
mutable boost::shared_mutex x_state;
mutable boost::shared_mutex x_executions;
ExecutionResults m_executions;
std::string m_dbPath;
};
}
}

41
mix/QBasicNodeDefinition.cpp

@ -1,41 +0,0 @@
/*
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())), m_location(_d->getLocation())
{
}
QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, std::string const& _name):
QObject(_parent), m_name(QString::fromStdString(_name))
{
}
}
}

59
mix/QBasicNodeDefinition.h

@ -1,59 +0,0 @@
/*
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.h
* @author Yann yann@ethdev.com
* @date 2014
*/
#pragma once
#include <string>
#include <QObject>
#include <libevmasm/SourceLocation.h>
namespace dev
{
namespace solidity
{
class Declaration;
}
namespace mix
{
class QBasicNodeDefinition: public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name CONSTANT)
public:
QBasicNodeDefinition(QObject* _parent = nullptr): QObject(_parent) {}
~QBasicNodeDefinition() {}
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; }
dev::SourceLocation const& location() { return m_location; }
private:
QString m_name;
dev::SourceLocation m_location;
};
}
}

96
mix/QBigInt.cpp

@ -1,96 +0,0 @@
/*
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 QBigInt.cpp
* @author Yann yann@ethdev.com
* @date 2015
*/
#include <boost/variant/multivisitors.hpp>
#include <boost/variant.hpp>
#include <libethcore/CommonJS.h>
#include "QBigInt.h"
using namespace dev;
using namespace dev::mix;
QString QBigInt::value() const
{
std::ostringstream s;
s << m_internalValue;
return QString::fromStdString(s.str());
}
QBigInt* QBigInt::subtract(QBigInt* const& _value) const
{
BigIntVariant toSubtract = _value->internalValue();
return new QBigInt(boost::apply_visitor(mix::subtract(), m_internalValue, toSubtract));
}
QBigInt* QBigInt::add(QBigInt* const& _value) const
{
BigIntVariant toAdd = _value->internalValue();
return new QBigInt(boost::apply_visitor(mix::add(), m_internalValue, toAdd));
}
QBigInt* QBigInt::multiply(QBigInt* const& _value) const
{
BigIntVariant toMultiply = _value->internalValue();
return new QBigInt(boost::apply_visitor(mix::multiply(), m_internalValue, toMultiply));
}
QBigInt* QBigInt::divide(QBigInt* const& _value) const
{
BigIntVariant toDivide = _value->internalValue();
return new QBigInt(boost::apply_visitor(mix::divide(), m_internalValue, toDivide));
}
QVariantMap QBigInt::checkAgainst(QString const& _type) const
{
QVariantMap ret;
QString type = _type;
QString capacity = type.replace("uint", "").replace("int", "");
if (capacity.isEmpty())
capacity = "256";
bigint range = 1;
for (int k = 0; k < capacity.toInt() / 8; ++k)
range = range * 256;
bigint value = boost::get<bigint>(this->internalValue());
ret.insert("valid", true);
if (_type.startsWith("uint") && value > range - 1)
{
ret.insert("minValue", "0");
std::ostringstream s;
s << range - 1;
ret.insert("maxValue", QString::fromStdString(s.str()));
if (value > range)
ret["valid"] = false;
}
else if (_type.startsWith("int"))
{
range = range / 2;
std::ostringstream s;
s << -range;
ret.insert("minValue", QString::fromStdString(s.str()));
s.str("");
s.clear();
s << range - 1;
ret.insert("maxValue", QString::fromStdString(s.str()));
if (-range > value || value > range - 1)
ret["valid"] = false;
}
return ret;
}

109
mix/QBigInt.h

@ -1,109 +0,0 @@
/*
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 QBigInt.h
* @author Yann yann@ethdev.com
* @date 2015
* Represent a big integer (u256, bigint) to be used in QML.
*/
#pragma once
#include "boost/variant.hpp"
#include "boost/variant/multivisitors.hpp"
#include <QObject>
#include <QQmlEngine>
#include <libethcore/CommonJS.h>
#include <libdevcore/Common.h>
using namespace dev;
namespace dev
{
namespace mix
{
using BigIntVariant = boost::variant<dev::u256, dev::bigint, dev::s256>;
struct add: public boost::static_visitor<BigIntVariant>
{
template<class T1, class T2>
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value + _otherValue; }
};
struct subtract: public boost::static_visitor<BigIntVariant>
{
template<class T1, class T2>
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value - _otherValue; }
};
struct multiply: public boost::static_visitor<BigIntVariant>
{
template<class T1, class T2>
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value * _otherValue; }
};
struct divide: public boost::static_visitor<BigIntVariant>
{
template<class T1, class T2>
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value / _otherValue; }
};
/*
* Represent big integer like big int and u256 in QML.
* The ownership is set by default to Javascript.
*/
class QBigInt: public QObject
{
Q_OBJECT
public:
QBigInt(QObject* _parent = 0): QObject(_parent), m_internalValue(dev::u256(0)) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); }
QBigInt(dev::u256 const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); }
QBigInt(dev::bigint const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); }
QBigInt(BigIntVariant const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value){ QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); }
QBigInt(dev::s256 const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); }
~QBigInt() {}
/// @returns the current used big integer.
BigIntVariant internalValue() const { return m_internalValue; }
/// @returns a string representation of the big integer used. Invokable from QML.
Q_INVOKABLE QString value() const;
/// hex value.
Q_INVOKABLE QString hexValue() const { return QString::fromStdString(dev::toHex(dev::u256(value().toStdString()))); }
/// Set the value of the BigInteger used. Will use u256 type. Invokable from QML.
Q_INVOKABLE void setValue(QString const& _value) { m_internalValue = dev::jsToU256(_value.toStdString()); }
Q_INVOKABLE void setBigInt(QString const& _value) { m_internalValue = bigint(_value.toStdString()); }
void setBigInt(u256 const& _value) { m_internalValue = _value; }
/// Subtract by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* subtract(QBigInt* const& _value) const;
/// Add @a _value to the current big integer. Invokable from QML.
Q_INVOKABLE QBigInt* add(QBigInt* const& _value) const;
/// Multiply by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* multiply(QBigInt* const& _value) const;
/// divide by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* divide(QBigInt* const& _value) const;
/// check if the current value satisfy the given type
Q_INVOKABLE QVariantMap checkAgainst(QString const& _type) const;
protected:
BigIntVariant m_internalValue;
};
}
}

57
mix/QContractDefinition.cpp

@ -1,57 +0,0 @@
/*
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 QContractDefinition.cpp
* @author Yann yann@ethdev.com
* @date 2014
*/
#include <QObject>
#include "QContractDefinition.h"
#include <libsolidity/CompilerStack.h>
#include <libsolidity/AST.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/NameAndTypeResolver.h>
using namespace dev::solidity;
using namespace dev::mix;
QContractDefinition::QContractDefinition(QObject* _parent, dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_parent, _contract)
{
QObject* parent = _parent ? _parent : this;
if (_contract->getConstructor() != nullptr)
m_constructor = new QFunctionDefinition(parent, ContractType(*_contract).getConstructorType());
else
m_constructor = new QFunctionDefinition(parent);
for (auto const& it: _contract->getInterfaceFunctions())
m_functions.append(new QFunctionDefinition(parent, it.second));
for (auto const& it: _contract->getEvents())
m_events.append(new QFunctionDefinition(parent, it));
}
QFunctionDefinition const* QContractDefinition::getFunction(dev::FixedHash<4> _hash) const
{
for (auto const& f: m_functions)
if (f->hash() == _hash)
return f;
return nullptr;
}

65
mix/QContractDefinition.h

@ -1,65 +0,0 @@
/*
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 QContractDefinition.h
* @author Yann yann@ethdev.com
* @date 2014
*/
#pragma once
#include <QObject>
#include <QQmlListProperty>
#include <libsolidity/AST.h>
#include "QFunctionDefinition.h"
#include "QBasicNodeDefinition.h"
namespace dev
{
namespace mix
{
class QContractDefinition: public QBasicNodeDefinition
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> functions READ functions CONSTANT)
Q_PROPERTY(dev::mix::QFunctionDefinition* constructor READ constructor CONSTANT)
Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> events READ events CONSTANT)
public:
QContractDefinition(QObject* _parent, solidity::ContractDefinition const* _contract);
/// Get all the functions of the contract.
QQmlListProperty<QFunctionDefinition> functions() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_functions); }
/// Get the constructor of the contract.
QFunctionDefinition* constructor() const { return m_constructor; }
/// Get all the functions of the contract.
QList<QFunctionDefinition*> const& functionsList() const { return m_functions; }
/// Find function by hash, returns nullptr if not found
QFunctionDefinition const* getFunction(dev::FixedHash<4> _hash) const;
/// Get events
QQmlListProperty<QFunctionDefinition> events() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_events); }
/// Get events
QList<QFunctionDefinition*> const& eventsList() const { return m_events; }
private:
QList<QFunctionDefinition*> m_functions;
QFunctionDefinition* m_constructor;
QList<QFunctionDefinition*> m_events;
};
}
}

55
mix/QEther.cpp

@ -1,55 +0,0 @@
/*
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 QEther.cpp
* @author Yann yann@ethdev.com
* @date 2014
*/
#include <QMetaEnum>
#include "QEther.h"
using namespace dev::mix;
QString QEther::format() const
{
return QString::fromStdString(dev::eth::formatBalance(boost::get<dev::u256>(toWei()->internalValue())));
}
QBigInt* QEther::toWei() const
{
QMetaEnum units = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("EtherUnit"));
const char* key = units.valueToKey(m_currentUnit);
for (std::pair<dev::u256, std::string> rawUnit: dev::eth::units())
{
if (QString::fromStdString(rawUnit.second).toLower() == QString(key).toLower())
return multiply(new QBigInt(rawUnit.first));
}
return new QBigInt(dev::u256(0));
}
void QEther::setUnit(QString const& _unit)
{
QMetaEnum units = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("EtherUnit"));
for (int k = 0; k < units.keyCount(); k++)
{
if (QString(units.key(k)).toLower() == _unit.toLower())
{
m_currentUnit = static_cast<EtherUnit>(units.keysToValue(units.key(k)));
return;
}
}
}

92
mix/QEther.h

@ -1,92 +0,0 @@
/*
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 QEther.h
* @author Yann yann@ethdev.com
* @date 2014
* Represent an amount of Ether in QML (mapped to u256 in c++).
*/
#pragma once
#include <QObject>
#include <libethcore/Common.h>
#include "QBigInt.h"
namespace dev
{
namespace mix
{
class QEther: public QBigInt
{
Q_OBJECT
Q_ENUMS(EtherUnit)
Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(EtherUnit unit READ unit WRITE setUnit NOTIFY unitChanged)
public:
enum EtherUnit
{
Uether,
Vether,
Dether,
Nether,
Yether,
Zether,
Eether,
Pether,
Tether,
Gether,
Mether,
Grand,
Ether,
Finney,
Szabo,
Gwei,
Mwei,
Kwei,
Wei
};
QEther(QObject* _parent = 0): QBigInt(dev::u256(0), _parent), m_currentUnit(EtherUnit::Wei) {}
QEther(dev::u256 _value, EtherUnit _unit, QObject* _parent = 0): QBigInt(_value, _parent), m_currentUnit(_unit) {}
~QEther() {}
/// @returns user-friendly string representation of the amount of ether. Invokable from QML.
Q_INVOKABLE QString format() const;
/// @returns the current amount of Ether in Wei. Invokable from QML.
Q_INVOKABLE QBigInt* toWei() const;
/// @returns the current unit used. Invokable from QML.
Q_INVOKABLE EtherUnit unit() const { return m_currentUnit; }
/// Set the unit to be used. Invokable from QML.
Q_INVOKABLE void setUnit(EtherUnit const& _unit) { m_currentUnit = _unit; }
/// Set the unit to be used. Invokable from QML.
Q_INVOKABLE void setUnit(QString const& _unit);
/// @returns the u256 value of the current amount of Ether in Wei.
dev::u256 toU256Wei() { return boost::get<dev::u256>(toWei()->internalValue()); }
private:
EtherUnit m_currentUnit;
signals:
void valueChanged();
void unitChanged();
};
}
}

67
mix/QFunctionDefinition.cpp

@ -1,67 +0,0 @@
/*
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 QFunctionDefinition.cpp
* @author Yann yann@ethdev.com
* @date 2014
*/
#include <libsolidity/AST.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/Exceptions.h>
#include "QVariableDeclaration.h"
#include "QFunctionDefinition.h"
using namespace dev::solidity;
using namespace dev::mix;
QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalSignature())),
m_fullHash(dev::sha3(_f->externalSignature()))
{
init(_f);
}
QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer<FunctionDefinition> const& _f): QBasicNodeDefinition(_parent, _f.get()), m_hash(dev::sha3(_f->externalSignature())),
m_fullHash(dev::sha3(_f->externalSignature()))
{
for (unsigned i = 0; i < _f->getParameters().size(); ++i)
m_parameters.append(new QVariableDeclaration(parent(), _f->getParameters().at(i)));
for (unsigned i = 0; i < _f->getReturnParameters().size(); ++i)
m_returnParameters.append(new QVariableDeclaration(parent(), _f->getReturnParameters().at(i)));
}
QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer<dev::solidity::EventDefinition> const& _e): QBasicNodeDefinition(_parent, _e.get())
{
for (unsigned i = 0; i < _e->getParameters().size(); ++i)
m_parameters.append(new QVariableDeclaration(parent(), _e->getParameters().at(i)));
FunctionTypePointer _f = std::make_shared<FunctionType>(*_e);
m_hash = (FixedHash<4>)dev::sha3(_f->externalSignature(_e->getName()));
m_fullHash = dev::sha3(_f->externalSignature(_e->getName()));
}
void QFunctionDefinition::init(dev::solidity::FunctionTypePointer _f)
{
auto paramNames = _f->getParameterNames();
auto paramTypes = _f->getParameterTypes();
auto returnNames = _f->getReturnParameterNames();
auto returnTypes = _f->getReturnParameterTypes();
for (unsigned i = 0; i < paramNames.size(); ++i)
m_parameters.append(new QVariableDeclaration(parent(), paramNames[i], paramTypes[i].get()));
for (unsigned i = 0; i < returnNames.size(); ++i)
m_returnParameters.append(new QVariableDeclaration(parent(), returnNames[i], returnTypes[i].get()));
}

71
mix/QFunctionDefinition.h

@ -1,71 +0,0 @@
/*
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 QFunctionDefinition.h
* @author Yann yann@ethdev.com
* @date 2014
*/
#pragma once
#include <QObject>
#include <QQmlListProperty>
#include <libsolidity/AST.h>
#include "QVariableDeclaration.h"
#include "QBasicNodeDefinition.h"
namespace dev
{
namespace mix
{
class QFunctionDefinition: public QBasicNodeDefinition
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<dev::mix::QVariableDeclaration> parameters READ parameters)
public:
QFunctionDefinition(){}
QFunctionDefinition(QObject* _parent): QBasicNodeDefinition(_parent) {}
QFunctionDefinition(QObject* _parent, solidity::FunctionTypePointer const& _f);
QFunctionDefinition(QObject* _parent, solidity::ASTPointer<solidity::EventDefinition> const& _f);
QFunctionDefinition(QObject* _parent, solidity::ASTPointer<solidity::FunctionDefinition> const& _f);
/// Init members
void init(dev::solidity::FunctionTypePointer _f);
/// Get all input parameters of this function.
QList<QVariableDeclaration*> const& parametersList() const { return m_parameters; }
/// Get all input parameters of this function as QML property.
QQmlListProperty<QVariableDeclaration> parameters() const { return QQmlListProperty<QVariableDeclaration>(const_cast<QFunctionDefinition*>(this), const_cast<QFunctionDefinition*>(this)->m_parameters); }
/// Get all return parameters of this function.
QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; }
/// Get the hash of this function declaration on the contract ABI.
FixedHash<4> hash() const { return m_hash; }
/// Get the full hash of this function declaration on the contract ABI.
FixedHash<32> fullHash() const { return m_fullHash; }
/// Get the hash of this function declaration on the contract ABI. returns QString
Q_INVOKABLE QString qhash() const { return QString::fromStdString(m_hash.hex()); }
private:
int m_index;
FixedHash<4> m_hash;
FixedHash<32> m_fullHash;
QList<QVariableDeclaration*> m_parameters;
QList<QVariableDeclaration*> m_returnParameters;
void initQParameters();
};
}
}

72
mix/QVariableDeclaration.cpp

@ -1,72 +0,0 @@
/*
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 QVariableDeclaration.app
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
*/
#include "QVariableDeclaration.h"
#include <libsolidity/AST.h>
#include "CodeModel.h"
using namespace solidity;
namespace dev
{
namespace mix
{
QVariableDeclaration::QVariableDeclaration(QObject* _parent, ASTPointer<VariableDeclaration> const _v):
QBasicNodeDefinition(_parent, _v.get()),
m_type(new QSolidityType(this, CodeModel::nodeType(_v->getType().get()))), m_isIndexed(_v->isIndexed())
{
}
QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed):
QBasicNodeDefinition(_parent, _name),
m_type(new QSolidityType(_parent, _type)), m_isIndexed(_isIndexed)
{
}
QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed):
QBasicNodeDefinition(_parent, _name),
m_type(new QSolidityType(this, CodeModel::nodeType(_type))), m_isIndexed(_isIndexed)
{
}
QSolidityType::QSolidityType(QObject* _parent, SolidityType const& _type):
QObject(_parent),
m_type(_type)
{
}
QVariantList QSolidityType::members() const
{
QVariantList members;
if (m_type.type == Type::Struct)
for (auto const& structMember: m_type.members)
members.push_back(QVariant::fromValue(new QVariableDeclaration(parent(), structMember.name.toStdString(), structMember.type)));
if (m_type.type == Type::Enum)
for (auto const& enumName: m_type.enumNames)
members.push_back(QVariant::fromValue(enumName));
return members;
}
}
}

103
mix/QVariableDeclaration.h

@ -1,103 +0,0 @@
/*
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 QVariableDeclaration.h
* @author Yann yann@ethdev.com
* @date 2014
*/
#include <QDebug>
#include <QVariantList>
#include <libsolidity/AST.h>
#include "QBasicNodeDefinition.h"
#include "SolidityType.h"
#pragma once
namespace dev
{
namespace solidity
{
class Type;
class VariableDeclaration;
}
namespace mix
{
/// UI wrapper around solidity type
class QSolidityType: public QObject
{
Q_OBJECT
Q_PROPERTY(int category READ category CONSTANT) //qml does not support enum properties
Q_PROPERTY(int size READ size CONSTANT)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QVariantList members READ members CONSTANT)
Q_PROPERTY(bool array READ array CONSTANT)
public:
QSolidityType() {}
QSolidityType(QObject* _parent, SolidityType const& _type);
using Type = SolidityType::Type;
enum QmlType //TODO: Q_ENUMS does not support enum forwarding. Keep in sync with SolidityType::Type
{
SignedInteger,
UnsignedInteger,
Hash,
Bool,
Address,
Bytes,
String,
Enum,
Struct
};
Q_ENUMS(QmlType)
SolidityType const& type() const { return m_type; }
Type category() const { return m_type.type; }
int size() const { return m_type.size; }
QString name() const { return m_type.name; }
QVariantList members() const;
bool array() const { return m_type.array; }
private:
SolidityType m_type;
};
/// UI wrapper around declaration (name + type)
class QVariableDeclaration: public QBasicNodeDefinition
{
Q_OBJECT
Q_PROPERTY(QSolidityType* type READ type CONSTANT)
public:
QVariableDeclaration() {}
QVariableDeclaration(QObject* _parent, solidity::ASTPointer<solidity::VariableDeclaration> const _v);
QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed = false);
QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed = false);
QSolidityType* type() const { return m_type; }
void setType(QSolidityType* _type) { m_type = _type; }
bool isIndexed() { return m_isIndexed; }
private:
QSolidityType* m_type;
bool m_isIndexed;
};
}
}

37
mix/QVariableDefinition.cpp

@ -1,37 +0,0 @@
/*
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()));
}
}
}

71
mix/QVariableDefinition.h

@ -1,71 +0,0 @@
/*
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.h
* @author Yann yann@ethdev.com
* @date 2014
*/
#pragma once
#include <QObject>
#include <libdevcore/Common.h>
namespace dev
{
namespace mix
{
class QVariableDeclaration;
class QVariableDefinition: public QObject
{
Q_OBJECT
Q_PROPERTY(QString value READ value CONSTANT)
Q_PROPERTY(QVariableDeclaration* declaration READ declaration CONSTANT)
public:
QVariableDefinition() {}
QVariableDefinition(QVariableDeclaration* _def, QString _value): QObject(), m_value(_value), m_dec(_def) {}
/// Return the associated declaration of this variable definition. Invokable from QML.
Q_INVOKABLE QVariableDeclaration* declaration() const { return m_dec; }
/// Return the variable value.
QString value() const { return m_value; }
/// Set a new value for this instance. Invokable from QML.
Q_INVOKABLE void setValue(QString _value) { m_value = _value; }
/// Set a new Declaration for this instance. Invokable from QML.
Q_INVOKABLE void setDeclaration(QVariableDeclaration* _dec) { m_dec = _dec; }
/// Encode the current value in order to be used as function parameter.
virtual bytes encodeValue() = 0;
/// 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();
protected:
QString m_value;
private:
QVariableDeclaration* m_dec;
};
}
}

74
mix/SolidityType.h

@ -1,74 +0,0 @@
/*
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 SolidityType.h
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <QString>
#include <vector>
#include <libdevcore/Common.h>
namespace dev
{
namespace mix
{
struct SolidityDeclaration;
//Type info extracted from solidity AST
struct SolidityType
{
enum Type //keep in sync with QSolidity::Type
{
SignedInteger,
UnsignedInteger,
Hash, //TODO: remove
Bool,
Address,
Bytes,
String,
Enum,
Struct
};
Type type;
unsigned size; //in bytes,
unsigned count;
bool array;
bool dynamicSize;
QString name;
std::vector<SolidityDeclaration> members; //for struct
std::vector<QString> enumNames; //for enum
std::shared_ptr<SolidityType const> baseType;
};
struct SolidityDeclaration
{
QString name;
SolidityType type;
dev::u256 slot;
unsigned offset;
};
using SolidityDeclarations = std::vector<SolidityDeclaration>;
}
}

156
mix/SortFilterProxyModel.cpp

@ -1,156 +0,0 @@
/*
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/>.
*/
/**
* @author Yann <yann@ethdev.com>
* @date 2015
* Proxy used to filter a QML TableView.
*/
#include "SortFilterProxyModel.h"
#include <QtDebug>
#include <QtQml>
using namespace dev::mix;
SortFilterProxyModel::SortFilterProxyModel(QObject* _parent) : QSortFilterProxyModel(_parent)
{
connect(this, &SortFilterProxyModel::rowsInserted, this, &SortFilterProxyModel::countChanged);
connect(this, &SortFilterProxyModel::rowsRemoved, this, &SortFilterProxyModel::countChanged);
}
int SortFilterProxyModel::count() const
{
return rowCount();
}
QObject* SortFilterProxyModel::source() const
{
return sourceModel();
}
void SortFilterProxyModel::setSource(QObject* _source)
{
setSourceModel(qobject_cast<QAbstractItemModel*>(_source));
}
QByteArray SortFilterProxyModel::sortRole() const
{
return roleNames().value(QSortFilterProxyModel::sortRole());
}
void SortFilterProxyModel::setSortRole(QByteArray const& _role)
{
QSortFilterProxyModel::setSortRole(roleKey(_role));
}
void SortFilterProxyModel::setSortOrder(Qt::SortOrder _order)
{
QSortFilterProxyModel::sort(0, _order);
}
QString SortFilterProxyModel::filterString() const
{
return filterRegExp().pattern();
}
void SortFilterProxyModel::setFilterString(QString const& _filter)
{
setFilterRegExp(QRegExp(_filter, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax())));
}
SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const
{
return static_cast<FilterSyntax>(filterRegExp().patternSyntax());
}
void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax _syntax)
{
setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(_syntax)));
}
QJSValue SortFilterProxyModel::get(int _idx) const
{
QJSEngine *engine = qmlEngine(this);
QJSValue value = engine->newObject();
if (_idx >= 0 && _idx < count())
{
QHash<int, QByteArray> roles = roleNames();
QHashIterator<int, QByteArray> it(roles);
while (it.hasNext())
{
it.next();
value.setProperty(QString::fromUtf8(it.value()), data(index(_idx, 0), it.key()).toString());
}
}
return value;
}
int SortFilterProxyModel::roleKey(QByteArray const& _role) const
{
QHash<int, QByteArray> roles = roleNames();
QHashIterator<int, QByteArray> it(roles);
while (it.hasNext())
{
it.next();
if (it.value() == _role)
return it.key();
}
return -1;
}
QHash<int, QByteArray> SortFilterProxyModel::roleNames() const
{
if (QAbstractItemModel* source = sourceModel())
return source->roleNames();
return QHash<int, QByteArray>();
}
bool SortFilterProxyModel::filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const
{
QAbstractItemModel* model = sourceModel();
QModelIndex sourceIndex = model->index(_sourceRow, 0, _sourceParent);
if (!sourceIndex.isValid())
return true;
QString keyType = model->data(sourceIndex, roleKey(type.toUtf8())).toString();
QString keyContent = model->data(sourceIndex, roleKey(content.toUtf8())).toString();
return keyType.contains(m_filterType) && keyContent.contains(m_filterContent);
}
void SortFilterProxyModel::setFilterType(QString const& _type)
{
m_filterType = QRegExp(_type, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax()));
setFilterRegExp(_type);
}
QString SortFilterProxyModel::filterType() const
{
return m_filterType.pattern();
}
void SortFilterProxyModel::setFilterContent(QString const& _content)
{
m_filterContent = QRegExp(_content, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax()));
setFilterRegExp(_content);
}
QString SortFilterProxyModel::filterContent() const
{
return m_filterContent.pattern();
}

97
mix/SortFilterProxyModel.h

@ -1,97 +0,0 @@
/*
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/>.
*/
/**
* @author Yann <yann@ethdev.com>
* @date 2015
* Proxy used to filter a QML TableView.
*/
#pragma once
#include <QtCore/qsortfilterproxymodel.h>
#include <QtQml/qjsvalue.h>
namespace dev
{
namespace mix
{
class SortFilterProxyModel: public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(QObject* source READ source WRITE setSource)
Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole)
Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder)
Q_PROPERTY(QString filterContent READ filterContent WRITE setFilterContent)
Q_PROPERTY(QString filterType READ filterType WRITE setFilterType)
Q_PROPERTY(QString filterString READ filterString WRITE setFilterString)
Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax)
Q_ENUMS(FilterSyntax)
public:
explicit SortFilterProxyModel(QObject* _parent = 0);
QObject* source() const;
void setSource(QObject* _source);
QByteArray sortRole() const;
void setSortRole(QByteArray const& _role);
void setSortOrder(Qt::SortOrder _order);
QString filterContent() const;
void setFilterContent(QString const& _content);
QString filterType() const;
void setFilterType(QString const& _type);
QString filterString() const;
void setFilterString(QString const& _filter);
enum FilterSyntax {
RegExp,
Wildcard,
FixedString
};
FilterSyntax filterSyntax() const;
void setFilterSyntax(FilterSyntax _syntax);
int count() const;
Q_INVOKABLE QJSValue get(int _index) const;
signals:
void countChanged();
protected:
int roleKey(QByteArray const& _role) const;
QHash<int, QByteArray> roleNames() const;
bool filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const;
private:
QRegExp m_filterType;
QRegExp m_filterContent;
const QString type = "type";
const QString content = "content";
};
}
}

166
mix/Web3Server.cpp

@ -1,166 +0,0 @@
/*
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 Web3Server.h.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <libdevcore/Exceptions.h>
#include <libdevcore/Log.h>
#include <libethereum/Interface.h>
#include <libwebthree/WebThree.h>
#include <libweb3jsonrpc/AccountHolder.h>
#include "Web3Server.h"
using namespace dev::mix;
using namespace dev;
namespace
{
class EmptyNetwork : public dev::WebThreeNetworkFace
{
std::vector<p2p::PeerSessionInfo> peers() override
{
return std::vector<p2p::PeerSessionInfo>();
}
size_t peerCount() const override
{
return 0;
}
void addNode(p2p::NodeId const& _node, bi::tcp::endpoint const& _hostEndpoint) override
{
(void)_node;
(void)_hostEndpoint;
}
void requirePeer(p2p::NodeId const& _node, bi::tcp::endpoint const& _endpoint) override
{
(void)_node;
(void)_endpoint;
}
dev::bytes saveNetwork() override
{
return dev::bytes();
}
void setIdealPeerCount(size_t _n) override
{
(void)_n;
}
bool haveNetwork() const override
{
return false;
}
p2p::NetworkPreferences const& networkPreferences() const override
{
static const p2p::NetworkPreferences c_ret;
return c_ret;
}
void setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers) override
{
(void)_n;
(void)_dropPeers;
}
p2p::NodeInfo nodeInfo() const override { return p2p::NodeInfo(); }
std::string enode() const override { return ""; }
p2p::NodeId id() const override
{
return p2p::NodeId();
}
p2p::Peers nodes() const override
{
return p2p::Peers();
}
void startNetwork() override
{
}
void stopNetwork() override
{
}
bool isNetworkStarted() const override
{
return false;
}
};
}
Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, dev::eth::Interface* _client):
WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts),
m_client(_client),
m_network(new EmptyNetwork())
{
}
Web3Server::~Web3Server()
{
}
std::shared_ptr<dev::shh::Interface> Web3Server::face()
{
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::shh::Interface"));
}
dev::WebThreeNetworkFace* Web3Server::network()
{
return m_network.get();
}
std::string Web3Server::get(std::string const& _name, std::string const& _key)
{
std::string k(_name + "/" + _key);
return m_db[k];
}
void Web3Server::put(std::string const& _name, std::string const& _key, std::string const& _value)
{
std::string k(_name + "/" + _key);
m_db[k] = _value;
}
Json::Value Web3Server::eth_getFilterChanges(std::string const& _filterId)
{
return WebThreeStubServerBase::eth_getFilterChanges(_filterId);
}
std::string Web3Server::eth_sendTransaction(Json::Value const& _json)
{
std::string ret = WebThreeStubServerBase::eth_sendTransaction(_json);
emit newTransaction();
return ret;
}
std::string Web3Server::eth_call(Json::Value const& _json, std::string const& _blockNumber)
{
std::string ret = WebThreeStubServerBase::eth_call(_json, _blockNumber);
emit newTransaction();
return ret;
}

70
mix/Web3Server.h

@ -1,70 +0,0 @@
/*
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 Web3Server.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <map>
#include <string>
#include <QObject>
#include <libweb3jsonrpc/AccountHolder.h>
#include <libweb3jsonrpc/WebThreeStubServerBase.h>
namespace dev
{
namespace mix
{
class Web3Server: public QObject, public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{
Q_OBJECT
public:
Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, dev::eth::Interface* _client);
virtual ~Web3Server();
signals:
void newTransaction();
protected:
virtual Json::Value eth_getFilterChanges(std::string const& _filterId) override;
virtual std::string eth_sendTransaction(Json::Value const& _json) override;
virtual std::string eth_call(Json::Value const& _json, std::string const& _blockNumber) override;
private:
dev::eth::Interface* client() override { return m_client; }
std::shared_ptr<dev::shh::Interface> face() override;
dev::WebThreeNetworkFace* network() override;
dev::WebThreeStubDatabaseFace* db() override { return this; }
std::string get(std::string const& _name, std::string const& _key) override;
void put(std::string const& _name, std::string const& _key, std::string const& _value) override;
private:
dev::eth::Interface* m_client;
std::map<std::string, std::string> m_db;
std::unique_ptr<dev::WebThreeNetworkFace> m_network;
};
}
}

47
mix/main.cpp

@ -1,47 +0,0 @@
/*
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 <boost/exception/exception.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include "MixApplication.h"
using namespace dev::mix;
int main(int _argc, char* _argv[])
{
try
{
MixApplication::initialize();
MixApplication app(_argc, _argv);
return app.exec();
}
catch (boost::exception const& _e)
{
std::cerr << boost::diagnostic_information(_e);
}
catch (std::exception const& _e)
{
std::cerr << _e.what();
}
}

6
mix/noweb.qrc

@ -1,6 +0,0 @@
<RCC>
<qresource prefix="/">
<file alias="qml/WebPreview.qml">qml/WebPreviewStub.qml</file>
<file alias="qml/CodeEditor.qml">qml/CodeEditor.qml</file>
</qresource>
</RCC>

5
mix/osx.qrc

@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/">
<file alias="qml/QFileDialog.qml">qml/MacFileDialog.qml</file>
</qresource>
</RCC>

81
mix/qml.qrc

@ -1,81 +0,0 @@
<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>
<file>qml/CodeEditorStyle.qml</file>
<file>qml/CodeEditorView.qml</file>
<file>qml/CommonSeparator.qml</file>
<file>qml/DebugBasicInfo.qml</file>
<file>qml/DebugInfoList.qml</file>
<file>qml/Debugger.qml</file>
<file>qml/DebuggerPaneStyle.qml</file>
<file>qml/DefaultLabel.qml</file>
<file>qml/DefaultTextField.qml</file>
<file>qml/DeploymentDialog.qml</file>
<file>qml/Ether.qml</file>
<file>qml/EtherValue.qml</file>
<file>qml/FilesSection.qml</file>
<file>qml/ItemDelegateDataDump.qml</file>
<file>qml/LogsPane.qml</file>
<file>qml/LogsPaneStyle.qml</file>
<file>qml/MainContent.qml</file>
<file>qml/ModalDialog.qml</file>
<file>qml/NewProjectDialog.qml</file>
<file>qml/ProjectFilesStyle.qml</file>
<file>qml/ProjectList.qml</file>
<file>qml/ProjectModel.qml</file>
<file>qml/QBoolTypeView.qml</file>
<file>qml/QHashTypeView.qml</file>
<file>qml/QIntTypeView.qml</file>
<file>qml/QRealTypeView.qml</file>
<file>qml/QStringTypeView.qml</file>
<file>qml/QVariableDeclaration.qml</file>
<file>qml/QVariableDefinition.qml</file>
<file>qml/QFileDialog.qml</file>
<file>qml/SourceSansProBold.qml</file>
<file>qml/SourceSansProLight.qml</file>
<file>qml/SourceSansProRegular.qml</file>
<file>qml/Splitter.qml</file>
<file>qml/StateDialog.qml</file>
<file>qml/StateDialogStyle.qml</file>
<file>qml/StateList.qml</file>
<file>qml/StateListModel.qml</file>
<file>qml/StateStyle.qml</file>
<file>qml/StatusPane.qml</file>
<file>qml/StatusPaneStyle.qml</file>
<file>qml/StepActionImage.qml</file>
<file>qml/StorageView.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>
<file>qml/TransactionLog.qml</file>
<file>qml/VariablesView.qml</file>
<file>qml/WebPreviewStyle.qml</file>
<file>qml/js/Debugger.js</file>
<file>qml/js/ErrorLocationFormater.js</file>
<file>qml/js/ProjectModel.js</file>
<file>qml/js/QEtherHelper.js</file>
<file>qml/js/TransactionHelper.js</file>
<file>qml/js/Printer.js</file>
<file>qml/js/ansi2html.js</file>
<file>qml/js/NetworkDeployment.js</file>
<file>qml/js/InputValidator.js</file>
<file>qml/Block.qml</file>
<file>qml/BlockChain.qml</file>
<file>qml/ScenarioExecution.qml</file>
<file>qml/ScenarioLoader.qml</file>
<file>qml/ScenarioButton.qml</file>
<file>qml/Watchers.qml</file>
<file>qml/KeyValuePanel.qml</file>
<file>qml/DeployContractStep.qml</file>
<file>qml/RegisteringStep.qml</file>
<file>qml/DeploymentDialogSteps.qml</file>
<file>qml/PackagingStep.qml</file>
<file>qml/DeploymentWorker.qml</file>
</qresource>
</RCC>

30
mix/qml/AlertMessageDialog.qml

@ -1,30 +0,0 @@
import QtQuick 2.2
import QtQuick.Window 2.0
Window
{
id: alertMessageDialog
title: ""
modality: Qt.WindowModal
height: 150
width: 200
visible: false
Loader
{
focus: true
id: alertMessageDialogContent
objectName: "alertMessageDialogContent"
anchors.fill: parent
}
function open()
{
visible = true
}
function close()
{
visible = false;
alertMessageDialogContent.source = "";
alertMessageDialogContent.sourceComponent = undefined;
alertMessageDialog.destroy();
}
}

438
mix/qml/Application.qml

@ -1,438 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import QtQuick.PrivateWidgets 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
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 {
id: mainApplication
signal loaded;
visible: true
width: 1200
height: 800
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;
property bool trackLastProject: true;
ApplicationService {
id: appService
}
CodeModel {
id: codeModel
}
ClientModel {
id: clientModel
codeModel: codeModel
Component.onCompleted:
{
init("/tmp")
}
}
ProjectModel {
id: projectModel
}
FileIo {
id: fileIo
}
Clipboard {
id: clipboard
}
Style {
id: appStyle
}
Connections {
target: mainApplication
onClosing:
{
mainApplication.close();
close.accepted = false;
}
}
Component.onCompleted: {
loaded();
}
function close() {
projectModel.appIsClosing = true;
if (projectModel.projectPath !== "")
projectModel.closeProject(function() { Qt.quit(); })
else
Qt.quit();
}
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem { action: createProjectAction }
MenuItem { action: openProjectAction }
MenuSeparator {}
MenuItem { action: saveAllFilesAction }
MenuItem { action: saveCurrentDocument }
MenuSeparator {}
MenuItem { action: addExistingFileAction }
MenuItem { action: addNewJsFileAction }
MenuItem { action: addNewHtmlFileAction }
MenuItem { action: addNewCssFileAction }
MenuSeparator {}
MenuItem { action: addNewContractAction }
MenuItem { action: closeProjectAction }
MenuSeparator {}
MenuItem { action: exitAppAction }
}
Menu {
title: qsTr("Deploy")
MenuItem { action: mineAction }
MenuSeparator {}
MenuItem { action: deployViaRpcAction }
MenuSeparator {}
MenuItem { action: toggleRunOnLoadAction }
}
Menu {
title: qsTr("Scenario")
MenuItem { action: editStatesAction }
}
Menu {
title: qsTr("Debug")
MenuItem { action: debugRunAction }
MenuSeparator {}
MenuItem { action: toggleAssemblyDebuggingAction }
}
Menu {
title: qsTr("Tools")
MenuItem { action: gasEstimationAction }
MenuItem { action: optimizeCodeAction }
}
Menu {
title: qsTr("Windows")
MenuItem { action: openNextDocumentAction }
MenuItem { action: openPrevDocumentAction }
MenuSeparator {}
MenuItem { action: toggleProjectNavigatorAction }
MenuItem { action: showHideRightPanelAction }
MenuItem { action: toggleWebPreviewAction }
MenuItem { action: toggleWebPreviewOrientationAction }
}
}
MainContent {
id: mainContent;
anchors.fill: parent
}
ModalDialog {
objectName: "dialog"
id: dialog
}
AlertMessageDialog {
objectName: "alertMessageDialog"
id: messageDialog
}
Settings {
id: mainWindowSettings
property alias mainWidth: mainApplication.width
property alias mainHeight: mainApplication.height
property alias mainX: mainApplication.x
property alias mainY: mainApplication.y
}
Action {
id: exitAppAction
text: qsTr("Exit")
shortcut: "Ctrl+Q"
onTriggered:
{
mainApplication.close();
}
}
Action {
id: mineAction
text: qsTr("New Block")
shortcut: "Ctrl+M"
onTriggered: clientModel.mine();
enabled: codeModel.hasContract && !clientModel.running && !clientModel.mining
}
StateList {
id: stateList
}
Action {
id: editStatesAction
text: qsTr("Edit Scenarii")
shortcut: "Ctrl+Alt+E"
onTriggered: stateList.open();
}
Connections {
target: projectModel.stateListModel
function updateRunLabel()
{
debugRunAction.text = qsTr("Deploy") + " \"" + projectModel.stateListModel.defaultStateName() + "\"";
}
onDefaultStateChanged: updateRunLabel()
onStateListModelReady: updateRunLabel()
}
Action {
id: debugRunAction
text: qsTr("Deploy")
shortcut: "F5"
onTriggered: mainContent.startQuickDebugging()
enabled: codeModel.hasContract && !clientModel.running && projectModel.stateListModel.defaultStateName() !== ""
}
Action {
id: toggleAssemblyDebuggingAction
text: qsTr("Show VM Code")
shortcut: "Ctrl+Alt+V"
onTriggered: mainContent.debuggerPanel.assemblyMode = !mainContent.debuggerPanel.assemblyMode;
checked: mainContent.debuggerPanel.assemblyMode;
checkable: true
enabled: true
}
Action {
id: toggleWebPreviewAction
text: qsTr("Show Web View")
shortcut: "F2"
checkable: true
checked: mainContent.webViewVisible
onTriggered: mainContent.toggleWebPreview();
}
Action {
id: toggleProjectNavigatorAction
text: qsTr("Show Project Navigator")
shortcut: "Alt+0"
checkable: true
checked: mainContent.projectViewVisible
onTriggered: mainContent.toggleProjectView();
}
Action {
id: toggleWebPreviewOrientationAction
text: qsTr("Horizontal Web View")
shortcut: ""
checkable: true
checked: mainContent.webViewHorizontal
onTriggered: mainContent.toggleWebPreviewOrientation();
}
Action {
id: toggleRunOnLoadAction
text: qsTr("Load State on Startup")
shortcut: ""
checkable: true
checked: mainContent.runOnProjectLoad
onTriggered: mainContent.runOnProjectLoad = !mainContent.runOnProjectLoad
}
Action {
id: showHideRightPanelAction
text: qsTr("Show Right View")
shortcut: "F7"
checkable: true
checked: mainContent.rightViewVisible
onTriggered: mainContent.toggleRightView();
}
Action {
id: createProjectAction
text: qsTr("&New Project")
shortcut: "Ctrl+N"
enabled: true;
onTriggered: projectModel.createProject();
}
Action {
id: openProjectAction
text: qsTr("&Open Project")
shortcut: "Ctrl+O"
enabled: true;
onTriggered: openProjectFileDialog.open()
}
QFileDialog {
id: openProjectFileDialog
visible: false
title: qsTr("Open a Project")
selectFolder: true
selectExisting: true
onAccepted: {
var path = openProjectFileDialog.fileUrl.toString();
path += "/";
projectModel.loadProject(path);
}
}
Action {
id: addNewJsFileAction
text: qsTr("New JavaScript File")
shortcut: "Ctrl+Alt+J"
enabled: !projectModel.isEmpty
onTriggered: projectModel.newJsFile();
}
Action {
id: addNewHtmlFileAction
text: qsTr("New HTML File")
shortcut: "Ctrl+Alt+H"
enabled: !projectModel.isEmpty
onTriggered: projectModel.newHtmlFile();
}
Action {
id: addNewCssFileAction
text: qsTr("New CSS File")
shortcut: "Ctrl+Alt+S"
enabled: !projectModel.isEmpty
onTriggered: projectModel.newCssFile();
}
Action {
id: addNewContractAction
text: qsTr("New Contract")
shortcut: "Ctrl+Alt+C"
enabled: !projectModel.isEmpty
onTriggered: projectModel.newContract();
}
Action {
id: addExistingFileAction
text: qsTr("Add Existing File")
shortcut: "Ctrl+Alt+A"
enabled: !projectModel.isEmpty
onTriggered: addExistingFileDialog.open()
}
QFileDialog {
id: addExistingFileDialog
visible: false
title: qsTr("Add a File")
selectFolder: false
selectExisting: true
onAccepted: {
var paths = addExistingFileDialog.fileUrls;
projectModel.addExistingFiles(paths);
}
}
Action {
id: saveAllFilesAction
text: qsTr("Save All")
shortcut: "Ctrl+Shift+A"
enabled: !projectModel.isEmpty
onTriggered: projectModel.saveAll();
}
Action {
id: saveCurrentDocument
text: qsTr("Save Current Document")
shortcut: "Ctrl+S"
enabled: !projectModel.isEmpty
onTriggered: projectModel.saveCurrentDocument();
}
Action {
id: closeProjectAction
text: qsTr("Close Project")
shortcut: "Ctrl+W"
enabled: !projectModel.isEmpty
onTriggered: projectModel.closeProject();
}
Action {
id: openNextDocumentAction
text: qsTr("Next Document")
shortcut: "Ctrl+Tab"
enabled: !projectModel.isEmpty
onTriggered: projectModel.openNextDocument();
}
Action {
id: openPrevDocumentAction
text: qsTr("Previous Document")
shortcut: "Ctrl+Shift+Tab"
enabled: !projectModel.isEmpty
onTriggered: projectModel.openPrevDocument();
}
Action {
id: toggleBreakpointAction
text: qsTr("Toggle Breakpoint")
shortcut: "F9"
enabled: mainContent.codeEditor.editingContract();
onTriggered: mainContent.toggleBreakpoint();
}
Action {
id: deployViaRpcAction
text: qsTr("Deploy to Network")
shortcut: "Ctrl+Shift+D"
enabled: !projectModel.isEmpty && codeModel.hasContract
onTriggered: projectModel.deployProject();
}
Action {
id: goToCompilationError
text: qsTr("Go to compilation error")
shortcut: "F4"
onTriggered:
{
mainContent.codeEditor.goToCompilationError();
}
}
Action {
id: gasEstimationAction
text: qsTr("Display gas estimation")
shortcut: "Ctrl+G"
checkable: true
onTriggered: mainContent.codeEditor.displayGasEstimation(checked);
}
Action {
id: optimizeCodeAction
text: qsTr("Enable optimized compilation")
shortcut: "Ctrl+Shift+O"
checkable: true
onTriggered: codeModel.setOptimizeCode(checked);
}
Settings {
id: appSettings
property alias gasEstimation: gasEstimationAction.checked
property alias optimizeCode: optimizeCodeAction.checked
property string nodeAddress: "http://localhost:8545"
}
}

22
mix/qml/BasicMessage.qml

@ -1,22 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
Rectangle {
anchors.fill: parent
color: "lightgrey"
Label
{
width: parent.width
height: parent.height
horizontalAlignment: "AlignHCenter"
verticalAlignment: "AlignVCenter"
objectName: "messageContent"
id: messageTxt
text: ""
wrapMode: "Wrap"
}
}

13
mix/qml/BigIntValue.qml

@ -1,13 +0,0 @@
/*
* Used to instanciate a QEther obj using Qt.createComponent function.
*/
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
import org.ethereum.qml.QBigInt 1.0
QBigInt
{
id: bigInt
}

378
mix/qml/Block.qml

@ -1,378 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "."
ColumnLayout
{
id: root
property variant transactions
property variant transactionModel
property string status
property int number
property int blockWidth: Layout.preferredWidth - statusWidth - horizontalMargin
property int horizontalMargin: 10
property int trHeight: 35
spacing: 0
property int openedTr: 0
property int blockIndex
property variant scenario
property string labelColor: "#414141"
property string selectedBlockColor: "#accbf2"
property string selectedBlockForeground: "#445e7f"
property int scenarioIndex
signal txSelected(var txIndex)
function calculateHeight()
{
if (transactions)
{
if (index >= 0)
return trHeight + trHeight * transactions.count + openedTr
else
return trHeight
}
else
return trHeight
}
function editTx(txIndex)
{
transactionDialog.stateAccounts = scenario.accounts
transactionDialog.execute = false
transactionDialog.editMode = true
transactionDialog.open(txIndex, blockIndex, transactions.get(txIndex))
}
function select(txIndex)
{
transactionRepeater.itemAt(txIndex).select()
}
onOpenedTrChanged:
{
Layout.preferredHeight = calculateHeight()
height = calculateHeight()
}
DebuggerPaneStyle {
id: dbgStyle
}
Rectangle
{
id: top
Layout.preferredWidth: blockWidth
height: 10
anchors.bottom: rowHeader.top
color: "#DEDCDC"
radius: 15
anchors.left: parent.left
anchors.leftMargin: statusWidth
anchors.bottomMargin: -5
}
RowLayout
{
Layout.preferredHeight: trHeight
Layout.preferredWidth: blockWidth
id: rowHeader
spacing: 0
Rectangle
{
Layout.preferredWidth: blockWidth
Layout.preferredHeight: trHeight
color: "#DEDCDC"
anchors.left: parent.left
anchors.leftMargin: statusWidth
Label {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: horizontalMargin
font.pointSize: dbgStyle.absoluteSize(1)
color: "#adadad"
text:
{
if (number === -2)
return qsTr("GENESIS PARAMETERS")
else if (status === "mined")
return qsTr("BLOCK") + " " + number
else
return qsTr("PENDING TRANSACTIONS")
}
}
Image {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 14
visible: number === -2
source: "qrc:/qml/img/edit_combox.png"
height: 15
fillMode: Image.PreserveAspectFit
MouseArea
{
anchors.fill: parent
onClicked:
{
// load edit block panel
projectModel.stateListModel.editState(scenarioIndex)
}
}
}
}
}
Repeater // List of transactions
{
id: transactionRepeater
model: transactions
RowLayout
{
id: rowTransaction
Layout.preferredHeight: trHeight
spacing: 0
function select()
{
rowContentTr.select()
}
function displayContent()
{
logsText.text = ""
if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count)
{
for (var k = 0; k < transactions.get(index).logs.count; k++)
{
var log = transactions.get(index).logs.get(k)
if (log.name)
logsText.text += log.name + ":\n"
else
logsText.text += "log:\n"
if (log.param)
for (var i = 0; i < log.param.count; i++)
{
var p = log.param.get(i)
logsText.text += p.name + " = " + p.value + " - indexed:" + p.indexed + "\n"
}
else {
logsText.text += "From : " + log.address + "\n"
}
}
logsText.text += "\n\n"
}
rowDetailedContent.visible = !rowDetailedContent.visible
}
Rectangle
{
id: trSaveStatus
Layout.preferredWidth: statusWidth
Layout.preferredHeight: parent.height
color: "transparent"
anchors.top: parent.top
property bool saveStatus
Image {
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: -4
anchors.topMargin: 0
id: saveStatusImage
source: "qrc:/qml/img/recyclediscard@2x.png"
width: statusWidth + 10
fillMode: Image.PreserveAspectFit
}
Component.onCompleted:
{
if (index >= 0)
saveStatus = transactions.get(index).saveStatus
}
onSaveStatusChanged:
{
if (saveStatus)
saveStatusImage.source = "qrc:/qml/img/recyclekeep@2x.png"
else
saveStatusImage.source = "qrc:/qml/img/recyclediscard@2x.png"
if (index >= 0)
transactions.get(index).saveStatus = saveStatus
transactionModel[index].saveStatus = saveStatus
}
MouseArea {
id: statusMouseArea
anchors.fill: parent
onClicked:
{
parent.saveStatus = !parent.saveStatus
}
}
}
Rectangle
{
Layout.preferredWidth: blockWidth
Layout.preferredHeight: trHeight
height: trHeight
color: "#DEDCDC"
id: rowContentTr
anchors.top: parent.top
property bool selected: false
Connections
{
target: blockChainPanel
onTxSelected: {
if (root.blockIndex !== blockIndex || index !== txIndex)
rowContentTr.deselect()
}
}
function select()
{
rowContentTr.selected = true
rowContentTr.color = selectedBlockColor
hash.color = selectedBlockForeground
func.color = selectedBlockForeground
txSelected(index)
}
function deselect()
{
rowContentTr.selected = false
rowContentTr.color = "#DEDCDC"
hash.color = labelColor
func.color = labelColor
}
MouseArea
{
anchors.fill: parent
onClicked: {
if (!rowContentTr.selected)
rowContentTr.select()
else
rowContentTr.deselect()
}
onDoubleClicked:
{
root.editTx(index)
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: trHeight - 10
anchors.verticalCenter: parent.verticalCenter
Rectangle
{
Layout.preferredWidth: fromWidth
anchors.left: parent.left
anchors.leftMargin: horizontalMargin
Text
{
id: hash
width: parent.width - 30
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
maximumLineCount: 1
color: labelColor
font.pointSize: dbgStyle.absoluteSize(1)
font.bold: true
text: {
if (index >= 0)
return clientModel.resolveAddress(transactions.get(index).sender)
else
return ""
}
}
}
Rectangle
{
Layout.preferredWidth: toWidth
Text
{
id: func
text: {
if (index >= 0)
parent.parent.userFrienldyToken(transactions.get(index).label)
else
return ""
}
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
color: labelColor
font.pointSize: dbgStyle.absoluteSize(1)
font.bold: true
maximumLineCount: 1
width: parent.width
}
}
function userFrienldyToken(value)
{
if (value && value.indexOf("<") === 0)
{
if (value.split("> ")[1] === " - ")
return value.split(" - ")[0].replace("<", "")
else
return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()";
}
else
return value
}
}
}
Rectangle
{
width: debugActionWidth
height: trHeight - 10
anchors.right: rowContentTr.right
anchors.top: rowContentTr.top
anchors.rightMargin: 10
color: "transparent"
Image {
id: debugImg
source: "qrc:/qml/img/rightarrowcircle.png"
width: debugActionWidth
fillMode: Image.PreserveAspectFit
anchors.horizontalCenter: parent.horizontalCenter
visible: transactions.get(index).recordIndex !== undefined
}
MouseArea
{
anchors.fill: parent
onClicked:
{
if (transactions.get(index).recordIndex !== undefined)
{
debugTrRequested = [ blockIndex, index ]
clientModel.debugRecord(transactions.get(index).recordIndex);
}
}
}
}
}
}
}

664
mix/qml/BlockChain.qml

@ -1,664 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "js/TransactionHelper.js" as TransactionHelper
import "js/QEtherHelper.js" as QEtherHelper
import "."
ColumnLayout {
id: blockChainPanel
property alias trDialog: transactionDialog
property alias blockChainRepeater: blockChainRepeater
property variant model
property int scenarioIndex
property var states: ({})
spacing: 0
property int previousWidth
property variant debugTrRequested: []
signal chainChanged(var blockIndex, var txIndex, var item)
signal chainReloaded
signal txSelected(var blockIndex, var txIndex)
signal rebuilding
signal accountAdded(string address, string amount)
Connections
{
target: projectModel.stateListModel
onAccountsValidated:
{
if (rebuild.accountsSha3 !== codeModel.sha3(JSON.stringify(_accounts)))
rebuild.needRebuild("AccountsChanged")
else
rebuild.notNeedRebuild("AccountsChanged")
}
onContractsValidated:
{
if (rebuild.contractsSha3 !== codeModel.sha3(JSON.stringify(_contracts)))
rebuild.needRebuild("ContractsChanged")
else
rebuild.notNeedRebuild("ContractsChanged")
}
}
Connections
{
target: codeModel
onContractRenamed: {
rebuild.needRebuild("ContractRenamed")
}
onNewContractCompiled: {
rebuild.needRebuild("NewContractCompiled")
}
onCompilationComplete: {
for (var c in rebuild.contractsHex)
{
if (codeModel.contracts[c] === undefined || codeModel.contracts[c].codeHex !== rebuild.contractsHex[c])
{
if (!rebuild.containsRebuildCause("CodeChanged"))
{
rebuild.needRebuild("CodeChanged")
}
return
}
}
rebuild.notNeedRebuild("CodeChanged")
}
}
onChainChanged: {
if (rebuild.txSha3[blockIndex][txIndex] !== codeModel.sha3(JSON.stringify(model.blocks[blockIndex].transactions[txIndex])))
{
rebuild.txChanged.push(rebuild.txSha3[blockIndex][txIndex])
rebuild.needRebuild("txChanged")
}
else {
for (var k in rebuild.txChanged)
{
if (rebuild.txChanged[k] === rebuild.txSha3[blockIndex][txIndex])
{
rebuild.txChanged.splice(k, 1)
break
}
}
if (rebuild.txChanged.length === 0)
rebuild.notNeedRebuild("txChanged")
}
}
onWidthChanged:
{
var minWidth = scenarioMinWidth - 20 // margin
if (width <= minWidth || previousWidth <= minWidth)
{
fromWidth = 250
toWidth = 240
}
else
{
var diff = (width - previousWidth) / 3;
fromWidth = fromWidth + diff < 250 ? 250 : fromWidth + diff
toWidth = toWidth + diff < 240 ? 240 : toWidth + diff
}
previousWidth = width
}
function getState(record)
{
return states[record]
}
function load(scenario, index)
{
if (!scenario)
return;
if (model)
rebuild.startBlinking()
model = scenario
scenarioIndex = index
genesis.scenarioIndex = index
states = []
blockModel.clear()
for (var b in model.blocks)
blockModel.append(model.blocks[b])
previousWidth = width
}
property int statusWidth: 30
property int fromWidth: 250
property int toWidth: 240
property int debugActionWidth: 40
property int horizontalMargin: 10
property int cellSpacing: 10
RowLayout
{
Layout.preferredHeight: 10
}
Rectangle
{
Layout.preferredHeight: 500
Layout.preferredWidth: parent.width
border.color: "#cccccc"
border.width: 2
color: "white"
ScrollView
{
id: blockChainScrollView
anchors.fill: parent
anchors.topMargin: 8
ColumnLayout
{
id: blockChainLayout
width: parent.width
spacing: 20
Block
{
id: genesis
scenario: blockChainPanel.model
scenarioIndex: scenarioIndex
Layout.preferredWidth: blockChainScrollView.width
Layout.preferredHeight: 60
blockIndex: -1
transactions: []
status: ""
number: -2
trHeight: 60
}
Repeater // List of blocks
{
id: blockChainRepeater
model: blockModel
function editTx(blockIndex, txIndex)
{
itemAt(blockIndex).editTx(txIndex)
}
function select(blockIndex, txIndex)
{
itemAt(blockIndex).select(txIndex)
}
Block
{
Connections
{
target: block
onTxSelected:
{
blockChainPanel.txSelected(index, txIndex)
}
}
id: block
scenario: blockChainPanel.model
Layout.preferredWidth: blockChainScrollView.width
Layout.preferredHeight:
{
return calculateHeight()
}
blockIndex: index
transactions:
{
if (index >= 0)
return blockModel.get(index).transactions
else
return []
}
transactionModel:
{
if (index >= 0)
return scenario.blocks[index].transactions
else
return []
}
status:
{
if (index >= 0)
return blockModel.get(index).status
else
return ""
}
number:
{
if (index >= 0)
return blockModel.get(index).number
else
return 0
}
}
}
}
}
}
ListModel
{
id: blockModel
function appendBlock(block)
{
blockModel.append(block);
}
function appendTransaction(tr)
{
blockModel.get(blockModel.count - 1).transactions.append(tr)
}
function removeTransaction(blockIndex, trIndex)
{
blockModel.get(blockIndex).transactions.remove(trIndex)
}
function removeLastBlock()
{
blockModel.remove(blockModel.count - 1)
}
function removeBlock(index)
{
blockModel.remove(index)
}
function getTransaction(block, tr)
{
return blockModel.get(block).transactions.get(tr)
}
function setTransaction(blockIndex, trIndex, tr)
{
blockModel.get(blockIndex).transactions.set(trIndex, tr)
}
function setTransactionProperty(blockIndex, trIndex, propertyName, value)
{
blockModel.get(blockIndex).transactions.set(trIndex, { propertyName: value })
}
}
Rectangle
{
Layout.preferredWidth: parent.width
Layout.preferredHeight: 70
color: "transparent"
RowLayout
{
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 10
spacing: 20
Rectangle {
Layout.preferredWidth: 100
Layout.preferredHeight: 30
ScenarioButton {
id: rebuild
text: qsTr("Rebuild")
width: 100
height: 30
roundLeft: true
roundRight: true
property variant contractsHex: ({})
property variant txSha3: ({})
property variant accountsSha3
property variant contractsSha3
property variant txChanged: []
property var blinkReasons: []
function needRebuild(reason)
{
rebuild.startBlinking()
blinkReasons.push(reason)
}
function containsRebuildCause(reason)
{
for (var c in blinkReasons)
{
if (blinkReasons[c] === reason)
return true
}
return false
}
function notNeedRebuild(reason)
{
for (var c in blinkReasons)
{
if (blinkReasons[c] === reason)
{
blinkReasons.splice(c, 1)
break
}
}
if (blinkReasons.length === 0)
rebuild.stopBlinking()
}
onClicked:
{
if (ensureNotFuturetime.running)
return;
rebuilding()
stopBlinking()
states = []
var retBlocks = [];
var bAdded = 0;
for (var j = 0; j < model.blocks.length; j++)
{
var b = model.blocks[j];
var block = {
hash: b.hash,
number: b.number,
transactions: [],
status: b.status
}
for (var k = 0; k < model.blocks[j].transactions.length; k++)
{
if (blockModel.get(j).transactions.get(k).saveStatus)
{
var tr = model.blocks[j].transactions[k]
tr.saveStatus = true
block.transactions.push(tr);
}
}
if (block.transactions.length > 0)
{
bAdded++
block.number = bAdded
block.status = "mined"
retBlocks.push(block)
}
}
if (retBlocks.length === 0)
retBlocks.push(projectModel.stateListModel.createEmptyBlock())
else
{
var last = retBlocks[retBlocks.length - 1]
last.number = -1
last.status = "pending"
}
model.blocks = retBlocks
blockModel.clear()
for (var j = 0; j < model.blocks.length; j++)
blockModel.append(model.blocks[j])
ensureNotFuturetime.start()
takeCodeSnapshot()
takeTxSnaphot()
takeAccountsSnapshot()
takeContractsSnapShot()
blinkReasons = []
clientModel.setupScenario(model);
}
function takeContractsSnapShot()
{
contractsSha3 = codeModel.sha3(JSON.stringify(model.contracts))
}
function takeAccountsSnapshot()
{
accountsSha3 = codeModel.sha3(JSON.stringify(model.accounts))
}
function takeCodeSnapshot()
{
contractsHex = {}
for (var c in codeModel.contracts)
contractsHex[c] = codeModel.contracts[c].codeHex
}
function takeTxSnaphot()
{
txSha3 = {}
txChanged = []
for (var j = 0; j < model.blocks.length; j++)
{
for (var k = 0; k < model.blocks[j].transactions.length; k++)
{
if (txSha3[j] === undefined)
txSha3[j] = {}
txSha3[j][k] = codeModel.sha3(JSON.stringify(model.blocks[j].transactions[k]))
}
}
}
buttonShortcut: ""
sourceImg: "qrc:/qml/img/recycleicon@2x.png"
}
}
Rectangle
{
Layout.preferredWidth: 200
Layout.preferredHeight: 30
color: "transparent"
ScenarioButton {
id: addTransaction
text: qsTr("Add Tx")
onClicked:
{
if (model && model.blocks)
{
var lastBlock = model.blocks[model.blocks.length - 1];
if (lastBlock.status === "mined")
{
var newblock = projectModel.stateListModel.createEmptyBlock()
blockModel.appendBlock(newblock)
model.blocks.push(newblock);
}
var item = TransactionHelper.defaultTransaction()
transactionDialog.stateAccounts = model.accounts
transactionDialog.execute = true
transactionDialog.editMode = false
transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item)
}
}
width: 100
height: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/sendtransactionicon@2x.png"
roundLeft: true
roundRight: false
}
Timer
{
id: ensureNotFuturetime
interval: 1000
repeat: false
running: false
}
Rectangle
{
width: 1
height: parent.height
anchors.right: addBlockBtn.left
color: "#ededed"
}
ScenarioButton {
id: addBlockBtn
text: qsTr("Add Block...")
anchors.left: addTransaction.right
roundLeft: false
roundRight: true
onClicked:
{
if (ensureNotFuturetime.running)
return
if (clientModel.mining || clientModel.running)
return
if (model.blocks.length > 0)
{
var lastBlock = model.blocks[model.blocks.length - 1]
if (lastBlock.status === "pending")
{
ensureNotFuturetime.start()
clientModel.mine()
}
else
addNewBlock()
}
else
addNewBlock()
}
function addNewBlock()
{
var block = projectModel.stateListModel.createEmptyBlock()
model.blocks.push(block)
blockModel.appendBlock(block)
}
width: 100
height: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/newblock@2x.png"
}
}
Connections
{
target: clientModel
onNewBlock:
{
if (!clientModel.running)
{
var lastBlock = model.blocks[model.blocks.length - 1]
lastBlock.status = "mined"
lastBlock.number = model.blocks.length
var lastB = blockModel.get(model.blocks.length - 1)
lastB.status = "mined"
lastB.number = model.blocks.length
addBlockBtn.addNewBlock()
}
}
onStateCleared:
{
}
onNewRecord:
{
var blockIndex = parseInt(_r.transactionIndex.split(":")[0]) - 1
var trIndex = parseInt(_r.transactionIndex.split(":")[1])
if (blockIndex <= model.blocks.length - 1)
{
var item = model.blocks[blockIndex]
if (trIndex <= item.transactions.length - 1)
{
var tr = item.transactions[trIndex]
tr.returned = _r.returned
tr.recordIndex = _r.recordIndex
tr.logs = _r.logs
tr.sender = _r.sender
tr.returnParameters = _r.returnParameters
var trModel = blockModel.getTransaction(blockIndex, trIndex)
trModel.returned = _r.returned
trModel.recordIndex = _r.recordIndex
trModel.logs = _r.logs
trModel.sender = _r.sender
trModel.returnParameters = _r.returnParameters
blockModel.setTransaction(blockIndex, trIndex, trModel)
blockChainRepeater.select(blockIndex, trIndex)
return;
}
}
// tr is not in the list.
var itemTr = TransactionHelper.defaultTransaction()
itemTr.saveStatus = false
itemTr.functionId = _r.function
itemTr.contractId = _r.contract
itemTr.gasAuto = true
itemTr.parameters = _r.parameters
itemTr.isContractCreation = itemTr.functionId === itemTr.contractId
itemTr.label = _r.label
itemTr.isFunctionCall = itemTr.functionId !== "" && itemTr.functionId !== "<none>"
itemTr.returned = _r.returned
itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei)
itemTr.sender = _r.sender
itemTr.recordIndex = _r.recordIndex
itemTr.logs = _r.logs
itemTr.returnParameters = _r.returnParameters
model.blocks[model.blocks.length - 1].transactions.push(itemTr)
blockModel.appendTransaction(itemTr)
blockChainRepeater.select(blockIndex, trIndex)
}
onNewState: {
states[_record] = _accounts
}
onMiningComplete:
{
}
}
ScenarioButton {
id: newAccount
text: qsTr("New Account...")
onClicked: {
var ac = projectModel.stateListModel.newAccount("O", QEther.Wei)
model.accounts.push(ac)
clientModel.addAccount(ac.secret);
for (var k in Object.keys(blockChainPanel.states))
blockChainPanel.states[k].accounts["0x" + ac.address] = "0 wei" // add the account in all the previous state (balance at O)
accountAdded("0x" + ac.address, "0")
}
Layout.preferredWidth: 100
Layout.preferredHeight: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/newaccounticon@2x.png"
roundLeft: true
roundRight: true
}
}
}
TransactionDialog {
id: transactionDialog
property bool execute
onAccepted: {
var item = transactionDialog.getItem()
if (execute)
{
var lastBlock = model.blocks[model.blocks.length - 1];
if (lastBlock.status === "mined")
{
var newBlock = projectModel.stateListModel.createEmptyBlock();
model.blocks.push(newBlock);
blockModel.appendBlock(newBlock)
}
if (!clientModel.running)
clientModel.executeTr(item)
}
else {
model.blocks[blockIndex].transactions[transactionIndex] = item
blockModel.setTransaction(blockIndex, transactionIndex, item)
chainChanged(blockIndex, transactionIndex, item)
}
}
}
}

73
mix/qml/CallStack.qml

@ -1,73 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
DebugInfoList
{
id: callStack
collapsible: true
title : qsTr("Call Stack")
enableSelection: true
itemDelegate:
Item {
anchors.fill: parent
Rectangle {
anchors.fill: parent
color: "#4A90E2"
visible: styleData.selected;
}
RowLayout
{
id: row
anchors.fill: parent
Rectangle
{
color: "#f7f7f7"
Layout.fillWidth: true
Layout.minimumWidth: 30
Layout.maximumWidth: 30
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
font.family: "monospace"
anchors.leftMargin: 5
color: "#4a4a4a"
text: styleData.row;
font.pointSize: dbgStyle.general.basicFontSize
width: parent.width - 5
elide: Text.ElideRight
}
}
Rectangle
{
color: "transparent"
Layout.fillWidth: true
Layout.minimumWidth: parent.width - 30
Layout.maximumWidth: parent.width - 30
Text {
anchors.leftMargin: 5
width: parent.width - 5
wrapMode: Text.NoWrap
anchors.left: parent.left
font.family: "monospace"
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
text: styleData.value;
elide: Text.ElideRight
font.pointSize: dbgStyle.general.basicFontSize
}
}
}
Rectangle {
anchors.top: row.bottom
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom
}
}
}

89
mix/qml/CodeEditor.qml

@ -1,89 +0,0 @@
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.1
import "."
Item {
signal editorTextChanged
function setText(text) {
codeEditor.text = text;
}
function getText() {
return codeEditor.text;
}
function setFocus() {
codeEditor.forceActiveFocus();
}
anchors.fill: parent
id: contentView
width: parent.width
height: parent.height * 0.7
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
textColor: "#EEE8D5"
style: TextAreaStyle {
backgroundColor: "#002B36"
}
anchors.left: lineColumn.right
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
wrapMode: TextEdit.NoWrap
frameVisible: false
height: parent.height
font.family: "Monospace"
font.pointSize: style.general.basicFontSize
width: parent.width
tabChangesFocus: false
Keys.onPressed: {
if (event.key === Qt.Key_Tab) {
codeEditor.insert(codeEditor.cursorPosition, "\t");
event.accepted = true;
}
}
onTextChanged: {
editorTextChanged();
}
}
}

13
mix/qml/CodeEditorStyle.qml

@ -1,13 +0,0 @@
import QtQuick 2.0
QtObject {
function absoluteSize(rel)
{
return systemPointSize + rel;
}
property QtObject general: QtObject {
property int basicFontSize: absoluteSize(1)
}
}

387
mix/qml/CodeEditorView.qml

@ -1,387 +0,0 @@
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
Item {
id: codeEditorView
property string currentDocumentId: ""
property string sourceInError
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 < openDocCount; i++) {
if (editorListModel.get(i).documentId === documentId) {
return editors.itemAt(i).item.getText();
}
}
return "";
}
function getContracts()
{
var ctr = []
for (var i = 0; i < openDocCount; i++)
{
if (editorListModel.get(i).isContract)
{
ctr.push(editors.itemAt(i).item)
}
}
return ctr;
}
function isDocumentOpen(documentId) {
for (var i = 0; i < openDocCount; i++)
if (editorListModel.get(i).documentId === documentId &&
editors.itemAt(i).item)
return true;
return false;
}
function openDocument(document) {
loadDocument(document);
currentDocumentId = document.documentId;
}
function loadDocument(document) {
for (var i = 0; i < openDocCount; i++)
if (editorListModel.get(i).documentId === document.documentId)
return; //already open
if (editorListModel.count <= openDocCount)
editorListModel.append(document);
else
{
editorListModel.set(openDocCount, document);
doLoadDocument(editors.itemAt(openDocCount).item, editorListModel.get(openDocCount), false)
loadComplete();
}
openDocCount++;
}
function doLoadDocument(editor, document, create) {
var data = fileIo.readFile(document.path);
if (create)
{
editor.onLoadComplete.connect(function() {
codeEditorView.loadComplete();
});
editor.onEditorTextChanged.connect(function() {
documentEdit(editor.document.documentId);
if (editor.document.isContract)
codeModel.registerCodeChange(editor.document.documentId, editor.getText());
});
editor.onBreakpointsChanged.connect(function() {
if (editor.document.isContract)
breakpointsChanged(editor.document.documentId);
});
editor.onIsCleanChanged.connect(function() {
isCleanChanged(editor.isClean, editor.document.documentId);
});
}
editor.document = document;
editor.setFontSize(editorSettings.fontSize);
editor.sourceName = document.documentId;
editor.setText(data, document.syntaxMode);
editor.changeGeneration();
}
function getEditor(documentId) {
for (var i = 0; i < openDocCount; i++)
{
if (editorListModel.get(i).documentId === documentId)
return editors.itemAt(i).item;
}
return null;
}
function highlightExecution(documentId, location)
{
var editor = getEditor(documentId);
if (editor)
{
if (documentId !== location.sourceName)
findAndHightlight(location.start, location.end, location.sourceName)
else
editor.highlightExecution(location);
}
}
// Execution is not in the current document. Try:
// Open targeted document and hightlight (TODO) or
// Warn user that file is not available
function findAndHightlight(start, end, sourceName)
{
var editor = getEditor(currentDocumentId);
if (editor)
editor.showWarning(qsTr("Currently debugging in " + sourceName + ". Source not available."));
}
function editingContract() {
for (var i = 0; i < openDocCount; i++)
if (editorListModel.get(i).documentId === currentDocumentId)
return editorListModel.get(i).isContract;
return false;
}
function getBreakpoints() {
var bpMap = {};
for (var i = 0; i < openDocCount; i++) {
var documentId = editorListModel.get(i).documentId;
var editor = editors.itemAt(i).item;
if (editor) {
bpMap[documentId] = editor.getBreakpoints();
}
}
return bpMap;
}
function toggleBreakpoint() {
var editor = getEditor(currentDocumentId);
if (editor)
editor.toggleBreakpoint();
}
function resetEditStatus(docId) {
var editor = getEditor(docId);
if (editor)
editor.changeGeneration();
}
function goToCompilationError() {
if (sourceInError === "")
return;
if (currentDocumentId !== sourceInError)
projectModel.openDocument(sourceInError);
for (var i = 0; i < openDocCount; i++)
{
var doc = editorListModel.get(i);
if (doc.isContract && doc.documentId === sourceInError)
{
var editor = editors.itemAt(i).item;
if (editor)
editor.goToCompilationError();
break;
}
}
}
function setFontSize(size) {
if (size <= 10 || size >= 48)
return;
editorSettings.fontSize = size;
for (var i = 0; i < editors.count; i++)
editors.itemAt(i).item.setFontSize(size);
}
function displayGasEstimation(checked)
{
var editor = getEditor(currentDocumentId);
if (editor)
editor.displayGasEstimation(checked);
}
Component.onCompleted: projectModel.codeEditor = codeEditorView;
Connections {
target: codeModel
onCompilationError: {
sourceInError = _firstErrorLoc.source;
}
onCompilationComplete: {
sourceInError = "";
var gasCosts = codeModel.gasCostByDocumentId(currentDocumentId);
var editor = getEditor(currentDocumentId);
if (editor)
editor.setGasCosts(gasCosts);
}
}
Connections {
target: projectModel
onDocumentOpened: {
openDocument(document);
}
onProjectSaving: {
for (var i = 0; i < openDocCount; i++)
{
var doc = editorListModel.get(i);
if (editors.itemAt(i))
{
var editor = editors.itemAt(i).item;
if (editor)
fileIo.writeFile(doc.path, editor.getText());
}
}
}
onProjectSaved: {
if (projectModel.appIsClosing || projectModel.projectIsClosing)
return;
for (var i = 0; i < openDocCount; i++)
{
var doc = editorListModel.get(i);
resetEditStatus(doc.documentId);
}
}
onProjectClosed: {
currentDocumentId = "";
openDocCount = 0;
}
onDocumentSaved: {
resetEditStatus(documentId);
}
onContractSaved: {
resetEditStatus(documentId);
}
onDocumentSaving: {
for (var i = 0; i < editorListModel.count; i++)
{
var doc = editorListModel.get(i);
if (doc.path === document.path)
{
fileIo.writeFile(document.path, editors.itemAt(i).item.getText());
break;
}
}
}
}
CodeEditorStyle
{
id: style;
}
MessageDialog
{
id: messageDialog
title: qsTr("File Changed")
text: qsTr("This file has been changed outside of the editor. Do you want to reload it?")
standardButtons: StandardButton.Yes | StandardButton.No
property variant item
property variant doc
onYes: {
doLoadDocument(item, doc, false);
resetEditStatus(doc.documentId);
}
}
Repeater {
id: editors
model: editorListModel
onItemRemoved: {
item.item.unloaded = true;
}
delegate: Loader {
id: loader
active: false
asynchronous: true
anchors.fill: parent
source: appService.haveWebEngine ? "WebCodeEditor.qml" : "CodeEditor.qml"
visible: (index >= 0 && index < openDocCount && currentDocumentId === editorListModel.get(index).documentId)
property bool changed: false
onVisibleChanged: {
loadIfNotLoaded()
if (visible && item)
{
loader.item.setFocus();
if (changed)
{
changed = false;
messageDialog.item = loader.item;
messageDialog.doc = editorListModel.get(index);
messageDialog.open();
}
loader.item.displayGasEstimation(gasEstimationAction.checked);
}
}
Component.onCompleted: {
loadIfNotLoaded()
}
onLoaded: {
doLoadDocument(loader.item, editorListModel.get(index), true)
}
Connections
{
target: projectModel
onDocumentChanged: {
if (!item)
return;
var current = editorListModel.get(index);
if (documentId === current.documentId)
{
if (currentDocumentId === current.documentId)
{
messageDialog.item = loader.item;
messageDialog.doc = editorListModel.get(index);
messageDialog.open();
}
else
changed = true
}
}
onDocumentUpdated: {
var document = projectModel.getDocument(documentId);
for (var i = 0; i < editorListModel.count; i++)
if (editorListModel.get(i).documentId === documentId)
{
editorListModel.set(i, document);
break;
}
}
onDocumentRemoved: {
for (var i = 0; i < editorListModel.count; i++)
if (editorListModel.get(i).documentId === documentId)
{
editorListModel.remove(i);
openDocCount--;
break;
}
}
}
function loadIfNotLoaded () {
if (visible && !active) {
active = true;
}
}
}
}
ListModel {
id: editorListModel
}
Action {
id: increaseFontSize
text: qsTr("Increase Font Size")
shortcut: "Ctrl+="
onTriggered: setFontSize(editorSettings.fontSize + 1)
}
Action {
id: decreaseFontSize
text: qsTr("Decrease Font Size")
shortcut: "Ctrl+-"
onTriggered: setFontSize(editorSettings.fontSize - 1)
}
Settings {
id: editorSettings
property int fontSize: 12;
}
}

9
mix/qml/CommonSeparator.qml

@ -1,9 +0,0 @@
import QtQuick 2.0
import "."
Rectangle
{
height: 1
color: appStyle.generic.layout.separatorColor
}

33
mix/qml/DebugBasicInfo.qml

@ -1,33 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
RowLayout {
property string titleStr
function update(_value)
{
currentStepValue.text = _value;
}
Rectangle {
width: 120
height: parent.height
color: "#e5e5e5"
Text
{
id: title
font.pixelSize: 12
anchors.centerIn: parent
color: "#a2a2a2"
font.family: "Sans Serif"
text: titleStr
}
}
Text
{
font.pixelSize: 13
id: currentStepValue
}
}

152
mix/qml/DebugInfoList.qml

@ -1,152 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
ColumnLayout {
id: root
property string title
property variant listModel;
property bool collapsible;
property bool collapsed;
property bool enableSelection: false;
property real storedHeight: 0;
property Component itemDelegate
property Component componentDelegate
property alias item: loader.item
signal rowActivated(int index)
spacing: 0
function collapse()
{
storedHeight = childrenRect.height;
storageContainer.collapse();
}
function show()
{
storageContainer.expand();
}
Component.onCompleted:
{
if (storageContainer.parent.parent.height === 25)
storageContainer.collapse();
else
{
if (storageContainer.parent.parent.height === 0)
storageContainer.parent.parent.height = 200;
storageContainer.expand();
}
}
RowLayout {
height: 25
id: header
Image {
source: "img/closedtriangleindicator.png"
width: 15
height: 15
id: storageImgArrow
}
Text {
anchors.left: storageImgArrow.right
color: "#8b8b8b"
text: title
id: storageListTitle
}
MouseArea
{
enabled: collapsible
anchors.fill: parent
onClicked: {
if (collapsible)
{
if (collapsed)
{
storageContainer.expand();
if (storedHeight <= 25)
storedHeight = 200;
storageContainer.parent.parent.height = storedHeight;
}
else
{
storedHeight = root.childrenRect.height;
storageContainer.collapse();
}
}
}
}
}
Rectangle
{
id: storageContainer
border.width: 3
border.color: "#deddd9"
Layout.fillWidth: true
Layout.fillHeight: true
function collapse() {
storageImgArrow.source = "qrc:/qml/img/closedtriangleindicator.png";
if (storageContainer.parent.parent.height > 25)
storageContainer.parent.parent.height = 25;
collapsed = true;
}
function expand() {
storageImgArrow.source = "qrc:/qml/img/opentriangleindicator.png";
collapsed = false;
}
Loader
{
id: loader
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 3
anchors.leftMargin: 3
width: parent.width - 3
height: parent.height - 6
onHeightChanged: {
if (height <= 0 && collapsible) {
storageContainer.collapse();
}
else if (height > 0 && collapsed) {
storageContainer.expand();
}
}
sourceComponent: componentDelegate ? componentDelegate : table
}
Component
{
id: table
TableView
{
clip: true;
alternatingRowColors: false
anchors.fill: parent
model: listModel
selectionMode: enableSelection ? SelectionMode.SingleSelection : SelectionMode.NoSelection
headerDelegate: null
itemDelegate: root.itemDelegate
onActivated: rowActivated(row);
Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < listModel.length) {
var str = "";
for (var i = 0; i < listModel.length; i++)
str += listModel[i] + "\n";
clipboard.text = str;
}
}
TableViewColumn {
role: "modelData"
width: parent.width
}
}
}
}
}

671
mix/qml/Debugger.qml

@ -1,671 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "."
Rectangle {
id: debugPanel
property alias debugSlider: statesSlider
property alias solLocals: solLocals
property alias solStorage: solStorage
property alias solCallStack: solCallStack
property alias vmCallStack: callStack
property alias vmStorage: storage
property alias vmMemory: memoryDump
property alias vmCallData: callDataDump
signal debugExecuteLocation(string documentId, var location)
property string compilationErrorMessage
property bool assemblyMode: false
signal panelClosed
objectName: "debugPanel"
color: "#ededed"
clip: true
onVisibleChanged:
{
if (visible)
forceActiveFocus();
}
onAssemblyModeChanged:
{
Debugger.updateMode();
machineStates.updateHeight();
}
function setTr(tr)
{
trName.text = tr.label
}
function displayCompilationErrorIfAny()
{
debugScrollArea.visible = false;
compilationErrorArea.visible = true;
machineStates.visible = false;
var errorInfo = ErrorLocationFormater.extractErrorInfo(compilationErrorMessage, false);
errorLocation.text = errorInfo.errorLocation;
errorDetail.text = errorInfo.errorDetail;
errorLine.text = errorInfo.line;
}
function update(data, giveFocus)
{
if (data === null)
Debugger.init(null);
else if (data.states.length === 0)
Debugger.init(null);
else if (codeModel.hasContract)
{
Debugger.init(data);
debugScrollArea.visible = true;
machineStates.visible = true;
}
if (giveFocus)
forceActiveFocus();
}
function setBreakpoints(bp)
{
Debugger.setBreakpoints(bp);
}
DebuggerPaneStyle {
id: dbgStyle
}
Connections {
target: clientModel
onDebugDataReady: {
update(_debugData, false);
}
}
Connections {
target: codeModel
onCompilationComplete: {
debugPanel.compilationErrorMessage = "";
}
onCompilationError: {
debugPanel.compilationErrorMessage = _error;
}
}
Settings {
id: splitSettings
property alias callStackHeight: callStackRect.height
property alias storageHeightSettings: storageRect.height
property alias memoryDumpHeightSettings: memoryRect.height
property alias callDataHeightSettings: callDataRect.height
property alias solCallStackHeightSettings: solStackRect.height
property alias solStorageHeightSettings: solStorageRect.height
property alias solLocalsHeightSettings: solLocalsRect.height
}
ColumnLayout {
id: debugScrollArea
anchors.fill: parent
spacing: 0
RowLayout
{
Layout.preferredWidth: parent.width
Layout.preferredHeight: 30
Rectangle
{
Layout.preferredWidth: parent.width
Layout.preferredHeight: parent.height
color: "transparent"
Text {
anchors.centerIn: parent
text: qsTr("Current Transaction")
}
Rectangle
{
anchors.left: parent.left
anchors.leftMargin: 10
width: 30
height: parent.height
color: "transparent"
anchors.verticalCenter: parent.verticalCenter
Image {
source: "qrc:/qml/img/leftarrow@2x.png"
width: parent.width
fillMode: Image.PreserveAspectFit
anchors.centerIn: parent
}
MouseArea
{
anchors.fill: parent
onClicked:
{
Debugger.init(null);
panelClosed()
}
}
}
}
}
RowLayout
{
Layout.preferredWidth: parent.width
Layout.preferredHeight: 30
Rectangle
{
Layout.preferredWidth: parent.width
Layout.preferredHeight: parent.height
color: "#2C79D3"
Text {
id: trName
color: "white"
anchors.centerIn: parent
}
}
}
ScrollView
{
property int sideMargin: 10
id: machineStates
Layout.fillWidth: true
Layout.fillHeight: true
function updateHeight() {
var h = buttonRow.childrenRect.height;
if (assemblyMode)
h += assemblyCodeRow.childrenRect.height + callStackRect.childrenRect.height + storageRect.childrenRect.height + memoryRect.childrenRect.height + callDataRect.childrenRect.height;
else
h += solStackRect.childrenRect.height + solLocalsRect.childrenRect.height + solStorageRect.childrenRect.height;
statesLayout.height = h + 120;
}
Component.onCompleted: updateHeight();
ColumnLayout {
id: statesLayout
anchors.top: parent.top
anchors.topMargin: 15
anchors.left: parent.left;
anchors.leftMargin: machineStates.sideMargin
width: debugScrollArea.width - machineStates.sideMargin * 2 - 20
spacing: machineStates.sideMargin
Rectangle {
// step button + slider
id: buttonRow
height: 30
Layout.fillWidth: true
color: "transparent"
Rectangle {
anchors.fill: parent
color: "transparent"
RowLayout {
anchors.fill: parent
id: jumpButtons
spacing: 3
layoutDirection: Qt.LeftToRight
StepActionImage
{
id: runBackAction;
enabledStateImg: "qrc:/qml/img/jumpoutback.png"
disableStateImg: "qrc:/qml/img/jumpoutbackdisabled.png"
onClicked: Debugger.runBack()
width: 23
buttonShortcut: "Ctrl+Shift+F5"
buttonTooltip: qsTr("Run Back")
visible: false
}
StepActionImage
{
id: jumpOutBackAction;
enabledStateImg: "qrc:/qml/img/jumpoutback.png"
disableStateImg: "qrc:/qml/img/jumpoutbackdisabled.png"
onClicked: Debugger.stepOutBack()
width: 23
buttonShortcut: "Ctrl+Shift+F11"
buttonTooltip: qsTr("Step Out Back")
}
StepActionImage
{
id: jumpIntoBackAction
enabledStateImg: "qrc:/qml/img/jumpintoback.png"
disableStateImg: "qrc:/qml/img/jumpintobackdisabled.png"
onClicked: Debugger.stepIntoBack()
width: 23
buttonShortcut: "Ctrl+F11"
buttonTooltip: qsTr("Step Into Back")
}
StepActionImage
{
id: jumpOverBackAction
enabledStateImg: "qrc:/qml/img/jumpoverback.png"
disableStateImg: "qrc:/qml/img/jumpoverbackdisabled.png"
onClicked: Debugger.stepOverBack()
width: 23
buttonShortcut: "Ctrl+F10"
buttonTooltip: qsTr("Step Over Back")
}
StepActionImage
{
id: jumpOverForwardAction
enabledStateImg: "qrc:/qml/img/jumpoverforward.png"
disableStateImg: "qrc:/qml/img/jumpoverforwarddisabled.png"
onClicked: Debugger.stepOverForward()
width: 23
buttonShortcut: "F10"
buttonTooltip: qsTr("Step Over Forward")
}
StepActionImage
{
id: jumpIntoForwardAction
enabledStateImg: "qrc:/qml/img/jumpintoforward.png"
disableStateImg: "qrc:/qml/img/jumpintoforwarddisabled.png"
onClicked: Debugger.stepIntoForward()
width: 23
buttonShortcut: "F11"
buttonTooltip: qsTr("Step Into Forward")
}
StepActionImage
{
id: jumpOutForwardAction
enabledStateImg: "qrc:/qml/img/jumpoutforward.png"
disableStateImg: "qrc:/qml/img/jumpoutforwarddisabled.png"
onClicked: Debugger.stepOutForward()
width: 45
buttonShortcut: "Shift+F11"
buttonTooltip: qsTr("Step Out Forward")
buttonRight: true
}
StepActionImage
{
id: runForwardAction
enabledStateImg: "qrc:/qml/img/jumpoutforward.png"
disableStateImg: "qrc:/qml/img/jumpoutforwarddisabled.png"
onClicked: Debugger.runForward()
width: 45
buttonShortcut: "Ctrl+F5"
buttonTooltip: qsTr("Run Forward")
visible: false
buttonRight: true
}
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
color: "transparent"
Layout.fillWidth: true
Layout.minimumWidth: parent.width * 0.2
Layout.alignment: Qt.AlignRight
Slider {
id: statesSlider
anchors.fill: parent
tickmarksEnabled: true
stepSize: 1.0
onValueChanged: Debugger.jumpTo(value);
style: SliderStyle {
groove: Rectangle {
implicitHeight: 3
color: "#7da4cd"
radius: 8
}
handle: Rectangle {
anchors.centerIn: parent
color: control.pressed ? "white" : "lightgray"
border.color: "gray"
border.width: 2
implicitWidth: 10
implicitHeight: 10
radius: 12
}
}
}
}
}
}
}
Rectangle {
// Assembly code
id: assemblyCodeRow
Layout.fillWidth: true
height: 405
implicitHeight: 405
color: "transparent"
visible: assemblyMode
Rectangle
{
id: stateListContainer
anchors.top : parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: parent.width * 0.4
height: parent.height
border.width: 3
border.color: "#deddd9"
color: "white"
TableView {
id: statesList
anchors.fill: parent
anchors.leftMargin: 3
anchors.rightMargin: 3
anchors.topMargin: 3
anchors.bottomMargin: 3
clip: true
headerDelegate: null
itemDelegate: renderDelegate
model: ListModel {}
TableViewColumn {
role: "line"
width: parent.width - 10
}
}
Component {
id: highlightBar
Rectangle {
radius: 4
anchors.fill: parent
y: statesList.currentItem.y
color: "#4A90E2"
}
}
Component {
id: renderDelegate
Item {
Rectangle {
radius: 4
anchors.fill: parent
color: "#4A90E2"
visible: styleData.selected;
}
RowLayout {
id: wrapperItem
anchors.fill: parent
spacing: 5
Text {
anchors.left: parent.left
anchors.leftMargin: 10
width: 15
color: "#b2b3ae"
text: styleData.value.split(' ')[0]
font.family: "monospace"
font.pointSize: dbgStyle.general.basicFontSize
wrapMode: Text.NoWrap
id: id
}
Text {
anchors.left: id.right;
wrapMode: Text.NoWrap
color: styleData.selected ? "white" : "black"
font.family: "monospace"
text: styleData.value.replace(styleData.value.split(' ')[0], '')
font.pointSize: dbgStyle.general.basicFontSize
}
}
}
}
}
Rectangle {
id: debugInfoContainer
width: parent.width * 0.6 - machineStates.sideMargin
anchors.top : parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
height: parent.height
color: "transparent"
ColumnLayout
{
width: parent.width
anchors.fill: parent
spacing: 0
DebugBasicInfo {
id: currentStep
titleStr: qsTr("Current Step")
Layout.fillWidth: true
height: 30
}
DebugBasicInfo {
id: mem
titleStr: qsTr("Adding Memory")
Layout.fillWidth: true
height: 30
}
DebugBasicInfo {
id: stepCost
titleStr: qsTr("Step Cost")
Layout.fillWidth: true
height: 30
}
DebugBasicInfo {
id: gasSpent
titleStr: qsTr("Total Gas Spent")
Layout.fillWidth: true
height: 30
}
DebugInfoList
{
Layout.fillHeight: true
Layout.fillWidth: true
id: stack
collapsible: false
title : qsTr("Stack")
itemDelegate: Item {
id: renderedItem
width: parent.width
RowLayout
{
anchors.fill: parent
Rectangle
{
id: indexColumn
color: "#f7f7f7"
Layout.fillWidth: true
Layout.minimumWidth: 30
Layout.preferredWidth: 30
Layout.maximumWidth: 30
Layout.minimumHeight: parent.height
Text {
anchors.centerIn: parent
anchors.leftMargin: 5
font.family: "monospace"
color: "#4a4a4a"
text: styleData.row;
font.pointSize: dbgStyle.general.basicFontSize
}
}
Rectangle
{
anchors.left: indexColumn.right
Layout.fillWidth: true
Layout.minimumWidth: 15
Layout.preferredWidth: 15
Layout.minimumHeight: parent.height
Text {
anchors.left: parent.left
anchors.leftMargin: 5
font.family: "monospace"
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
text: styleData.value
font.pointSize: dbgStyle.general.basicFontSize
}
}
}
Rectangle {
id: separator
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom
}
}
}
}
}
}
SplitView
{
id: splitInfoList
Layout.fillHeight: true
Layout.fillWidth: true
orientation: Qt.Vertical
Rectangle
{
id: solStackRect;
color: "transparent"
Layout.minimumHeight: 25
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
visible: !assemblyMode
CallStack {
anchors.fill: parent
id: solCallStack
}
}
Rectangle
{
id: solLocalsRect;
color: "transparent"
Layout.minimumHeight: 25
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
visible: !assemblyMode
VariablesView {
title : qsTr("Locals")
anchors.fill: parent
id: solLocals
}
}
Rectangle
{
id: solStorageRect;
color: "transparent"
Layout.minimumHeight: 25
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
visible: !assemblyMode
VariablesView {
title : qsTr("Members")
anchors.fill: parent
id: solStorage
}
}
Rectangle
{
id: callStackRect;
color: "transparent"
Layout.minimumHeight: 25
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
visible: assemblyMode
CallStack {
anchors.fill: parent
id: callStack
onRowActivated: Debugger.displayFrame(index);
}
}
Rectangle
{
id: storageRect
color: "transparent"
width: parent.width
Layout.minimumHeight: 25
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
visible: assemblyMode
StorageView {
anchors.fill: parent
id: storage
}
}
Rectangle
{
id: memoryRect;
color: "transparent"
width: parent.width
Layout.minimumHeight: 25
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
visible: assemblyMode
DebugInfoList {
id: memoryDump
anchors.fill: parent
collapsible: true
title: qsTr("Memory Dump")
itemDelegate:
Item {
height: 29
width: parent.width - 3;
ItemDelegateDataDump {}
}
}
}
Rectangle
{
id: callDataRect
color: "transparent"
width: parent.width
Layout.minimumHeight: 25
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
visible: assemblyMode
DebugInfoList {
id: callDataDump
anchors.fill: parent
collapsible: true
title: qsTr("Call Data")
itemDelegate:
Item {
height: 29
width: parent.width - 3;
ItemDelegateDataDump {}
}
}
}
Rectangle
{
id: bottomRect;
width: parent.width
Layout.minimumHeight: 20
color: "transparent"
}
}
}
}
}
}

16
mix/qml/DebuggerPaneStyle.qml

@ -1,16 +0,0 @@
import QtQuick 2.0
QtObject {
function absoluteSize(rel)
{
return systemPointSize + rel;
}
property QtObject general: QtObject {
property int basicFontSize: absoluteSize(-2)
property string basicColor: "#4a4a4a"
property string basicFont: "monospace"
property int dataDumpFontSize: absoluteSize(-3)
}
}

10
mix/qml/DefaultLabel.qml

@ -1,10 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 1.1
Label {
text: text
}

6
mix/qml/DefaultTextField.qml

@ -1,6 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 1.1
TextField {
id: titleField
}

540
mix/qml/DeployContractStep.qml

@ -1,540 +0,0 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
import Qt.labs.settings 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
import org.ethereum.qml.QEther 1.0
Rectangle {
property variant paramsModel: []
property variant worker
property variant gas: []
property alias gasPrice: gasPriceInput
color: "#E3E3E3E3"
signal deployed
anchors.fill: parent
id: root
property int labelWidth: 150
property bool verifyDeploy: true
function show()
{
visible = true
contractList.currentIndex = 0
contractList.change()
accountsModel.clear()
for (var k in worker.accounts)
accountsModel.append(worker.accounts[k])
if (worker.currentAccount === "" && worker.accounts.length > 0)
{
worker.currentAccount = worker.accounts[0].id
accountsList.currentIndex = 0
}
verifyDeployedContract()
deployedAddresses.refresh()
worker.renewCtx()
verifyDeploy = true
worker.pooler.onTriggered.connect(function() {
if (root.visible && verifyDeploy)
verifyDeployedContract();
})
}
function verifyDeployedContract()
{
if (projectModel.deployBlockNumber !== -1)
{
worker.verifyHashes(projectModel.deploymentTrHashes, function (bn, trLost)
{
root.updateVerification(bn, trLost)
});
}
}
function updateVerification(blockNumber, trLost)
{
var nb = parseInt(blockNumber - projectModel.deployBlockNumber)
verificationTextArea.visible = false
verificationLabel.visible = true
if (nb >= 10)
{
verificationLabel.text = qsTr("contracts deployment verified")
verificationLabel.color = "green"
}
else
{
verificationLabel.text = nb
if (trLost.length > 0)
{
verifyDeploy = false
verificationTextArea.visible = true
verificationLabel.visible = false
verificationTextArea.text = ""
deploymentStepChanged("following transactions are invalidated:")
verificationTextArea.text += "\n" + qsTr("Transactions lost") + "\n"
verificationTextArea.textColor = "red"
for (var k in trLost)
{
deploymentStepChanged(trLost[k])
verificationTextArea.text += trLost[k] + "\n"
}
}
}
}
RowLayout
{
anchors.fill: parent
anchors.margins: 10
ColumnLayout
{
anchors.top: parent.top
Layout.preferredWidth: parent.width * 0.40 - 20
Layout.fillHeight: true
id: scenarioList
Label
{
Layout.fillWidth: true
text: qsTr("Pick Scenario to deploy")
}
ComboBox
{
id: contractList
Layout.preferredWidth: parent.width - 20
model: projectModel.stateListModel
textRole: "title"
onCurrentIndexChanged:
{
if (root.visible)
change()
}
function change()
{
trListModel.clear()
if (currentIndex > -1)
{
for (var k = 0; k < projectModel.stateListModel.get(currentIndex).blocks.count; k++)
{
for (var j = 0; j < projectModel.stateListModel.get(currentIndex).blocks.get(k).transactions.count; j++)
trListModel.append(projectModel.stateListModel.get(currentIndex).blocks.get(k).transactions.get(j));
}
for (var k = 0; k < trListModel.count; k++)
trList.itemAt(k).init()
ctrDeployCtrLabel.calculateContractDeployGas();
}
}
}
Rectangle
{
Layout.fillHeight: true
Layout.preferredWidth: parent.width - 20
id: trContainer
color: "white"
border.color: "#cccccc"
border.width: 1
ScrollView
{
anchors.fill: parent
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
ColumnLayout
{
spacing: 0
ListModel
{
id: trListModel
}
Repeater
{
id: trList
model: trListModel
ColumnLayout
{
Layout.fillWidth: true
spacing: 5
Layout.preferredHeight:
{
if (index > -1)
return 20 + trListModel.get(index)["parameters"].count * 20
else
return 20
}
function init()
{
paramList.clear()
if (trListModel.get(index).parameters)
{
for (var k in trListModel.get(index).parameters)
paramList.append({ "name": k, "value": trListModel.get(index).parameters[k] })
}
}
Label
{
id: trLabel
Layout.preferredHeight: 20
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 5
anchors.leftMargin: 10
text:
{
if (index > -1)
return trListModel.get(index).label
else
return ""
}
}
ListModel
{
id: paramList
}
Repeater
{
Layout.preferredHeight:
{
if (index > -1)
return trListModel.get(index)["parameters"].count * 20
else
return 0
}
model: paramList
Label
{
Layout.preferredHeight: 20
anchors.left: parent.left
anchors.leftMargin: 20
text: name + "=" + value
font.italic: true
}
}
Rectangle
{
Layout.preferredWidth: scenarioList.width
Layout.preferredHeight: 1
color: "#cccccc"
}
}
}
}
}
}
}
ColumnLayout
{
anchors.top: parent.top
Layout.preferredHeight: parent.height - 25
ColumnLayout
{
anchors.top: parent.top
Layout.preferredWidth: parent.width * 0.60
Layout.fillHeight: true
id: deploymentOption
spacing: 8
Label
{
anchors.left: parent.left
anchors.leftMargin: 105
text: qsTr("Deployment options")
}
ListModel
{
id: accountsModel
}
RowLayout
{
Layout.fillWidth: true
Rectangle
{
width: labelWidth
Label
{
text: qsTr("Account")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
ComboBox
{
id: accountsList
textRole: "id"
model: accountsModel
Layout.preferredWidth: 235
onCurrentTextChanged:
{
worker.currentAccount = currentText
accountBalance.text = worker.balance(currentText).format()
}
}
Label
{
id: accountBalance
}
}
RowLayout
{
Layout.fillWidth: true
Rectangle
{
width: labelWidth
Label
{
text: qsTr("Gas Price")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
Ether
{
id: gasPriceInput
displayUnitSelection: true
displayFormattedValue: true
edit: true
function toHexWei()
{
return "0x" + gasPriceInput.value.toWei().hexValue()
}
}
Connections
{
target: gasPriceInput
onValueChanged:
{
ctrDeployCtrLabel.calculateContractDeployGas()
}
onAmountChanged:
{
ctrDeployCtrLabel.setCost()
}
onUnitChanged:
{
ctrDeployCtrLabel.setCost()
}
}
Connections
{
target: worker
id: gasPriceLoad
property bool loaded: false
onGasPriceLoaded:
{
gasPriceInput.value = QEtherHelper.createEther(worker.gasPriceInt.value(), QEther.Wei)
gasPriceLoad.loaded = true
ctrDeployCtrLabel.calculateContractDeployGas()
}
}
}
RowLayout
{
id: ctrDeployCtrLabel
Layout.fillWidth: true
property int cost
function calculateContractDeployGas()
{
if (!root.visible)
return;
var sce = projectModel.stateListModel.getState(contractList.currentIndex)
worker.estimateGas(sce, function(gas) {
if (gasPriceLoad.loaded)
{
root.gas = gas
cost = 0
for (var k in gas)
cost += gas[k]
setCost()
}
});
}
function setCost()
{
var ether = QEtherHelper.createBigInt(cost);
var gasTotal = ether.multiply(gasPriceInput.value);
gasToUseInput.value = QEtherHelper.createEther(gasTotal.value(), QEther.Wei, parent);
}
Rectangle
{
width: labelWidth
Label
{
text: qsTr("Deployment Cost")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
Ether
{
id: gasToUseInput
displayUnitSelection: false
displayFormattedValue: true
edit: false
Layout.preferredWidth: 350
}
}
Rectangle
{
border.color: "#cccccc"
border.width: 2
Layout.fillWidth: true
Layout.preferredHeight: parent.height + 25
color: "transparent"
id: rectDeploymentVariable
ScrollView
{
anchors.fill: parent
anchors.topMargin: 4
anchors.bottomMargin: 4
ColumnLayout
{
RowLayout
{
id: deployedRow
Layout.fillWidth: true
Rectangle
{
width: labelWidth
Label
{
id: labelAddresses
text: qsTr("Deployed Contracts")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
ColumnLayout
{
anchors.top: parent.top
anchors.topMargin: 1
width: parent.width
id: deployedAddresses
function refresh()
{
textAddresses.text = ""
deployedRow.visible = Object.keys(projectModel.deploymentAddresses).length > 0
textAddresses.text = JSON.stringify(projectModel.deploymentAddresses, null, ' ')
}
TextArea
{
anchors.fill: parent
id: textAddresses
}
}
}
RowLayout
{
id: verificationRow
Layout.fillWidth: true
visible: Object.keys(projectModel.deploymentAddresses).length > 0
Rectangle
{
width: labelWidth
Label
{
text: qsTr("Verifications")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
TextArea
{
id: verificationTextArea
visible: false
}
Label
{
id: verificationLabel
visible: true
}
}
}
}
}
}
Rectangle
{
Layout.preferredWidth: parent.width
Layout.alignment: Qt.BottomEdge
Button
{
Layout.preferredHeight: 22
anchors.right: deployBtn.left
text: qsTr("Reset")
action: clearDeployAction
}
Action {
id: clearDeployAction
onTriggered: {
worker.forceStopPooling()
if (projectModel.deploymentDir && projectModel.deploymentDir !== "")
fileIo.deleteDir(projectModel.deploymentDir)
projectModel.cleanDeploymentStatus()
deploymentDialog.steps.reset()
}
}
Button
{
id: deployBtn
anchors.right: parent.right
text: qsTr("Deploy Contracts")
onClicked:
{
projectModel.deployedScenarioIndex = contractList.currentIndex
NetworkDeploymentCode.deployContracts(root.gas, gasPriceInput.toHexWei(), function(addresses, trHashes)
{
projectModel.deploymentTrHashes = trHashes
worker.verifyHashes(trHashes, function (nb, trLost)
{
projectModel.deployBlockNumber = nb
projectModel.saveProject()
root.updateVerification(nb, trLost)
root.deployed()
})
projectModel.deploymentAddresses = addresses
projectModel.saveProject()
deployedAddresses.refresh()
});
}
}
}
}
}
}

187
mix/qml/DeploymentDialog.qml

@ -1,187 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
import "."
Dialog {
id: modalDeploymentDialog
modality: Qt.ApplicationModal
width: 1000
height: 450
visible: false
property alias deployStep: deployStep
property alias packageStep: packageStep
property alias registerStep: registerStep
property alias worker: worker
property alias steps: steps
function close()
{
visible = false;
worker.pooler.running = false
}
function open()
{
deployStep.visible = false
packageStep.visible = false
registerStep.visible = false
steps.init()
worker.renewCtx()
worker.pooler.running = true
visible = true;
}
DeploymentWorker
{
id: worker
}
contentItem: Rectangle {
color: appStyle.generic.layout.backgroundColor
anchors.fill: parent
ColumnLayout
{
spacing: 5
anchors.fill: parent
anchors.margins: 10
Rectangle
{
id: explanation
Layout.preferredWidth: parent.width - 50
Layout.preferredHeight: 50
color: "transparent"
Label
{
id: info
anchors.centerIn: parent
text: qsTr("Putting your dapp live is a multi step process. You can read more about it on the")
}
Text {
anchors.left: info.right
anchors.leftMargin: 7
id: linkText
text: '<html><style type="text/css"></style><a href="https://github.com/ethereum/wiki/wiki/Mix:-The-DApp-IDE#deployment-to-network">guide to uploading</a></html>'
onLinkActivated: Qt.openUrlExternally("https://github.com/ethereum/wiki/wiki/Mix:-The-DApp-IDE#deployment-to-network")
anchors.verticalCenter: parent.verticalCenter
MouseArea
{
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
}
}
RowLayout
{
ColumnLayout
{
Layout.preferredHeight: parent.height - 50
Layout.preferredWidth: 200
DeploymentDialogSteps
{
id: steps
worker: worker
}
}
Connections
{
target: steps
property variant selected
onSelected:
{
if (selected)
selected.visible = false
switch (step)
{
case "deploy":
{
selected = deployStep
break;
}
case "package":
{
selected = packageStep
break;
}
case "register":
{
selected = registerStep
break;
}
}
selected.show()
}
}
ColumnLayout
{
Layout.preferredHeight: parent.height - 50
Layout.preferredWidth: parent.width - 200
DeployContractStep
{
id: deployStep
visible: false
worker: worker
}
PackagingStep
{
id: packageStep
visible: false
worker: worker
}
RegisteringStep
{
id: registerStep
visible: false
worker: worker
}
}
}
Rectangle
{
Layout.fillWidth: true
Layout.preferredHeight: 30
color: "transparent"
Button
{
text: qsTr("Cancel")
anchors.right: parent.right
anchors.rightMargin: 10
onClicked:
{
modalDeploymentDialog.close()
}
}
}
}
}
MessageDialog {
id: deployDialog
standardButtons: StandardButton.Ok
icon: StandardIcon.Warning
}
MessageDialog {
id: errorDialog
standardButtons: StandardButton.Ok
icon: StandardIcon.Critical
}
}

292
mix/qml/DeploymentDialogSteps.qml

@ -1,292 +0,0 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
Rectangle {
anchors.fill: parent
color: "white"
property var worker
property variant sel
signal selected(string step)
id: root
function refreshCurrent()
{
menu.itemAt(sel).select()
}
function init()
{
menu.itemAt(0).select()
}
function itemClicked(step)
{
selected(step)
}
function reset()
{
for (var k in deployLogs.logs)
{
deployLogs.logs[k] = ""
}
deployLogs.switchLogs()
refreshCurrent()
}
border.color: "#cccccc"
border.width: 1
Connections
{
id: deployStatus
target: deploymentDialog.deployStep
onDeployed:
{
console.log("deployed")
}
}
Connections
{
id: packagedStatus
target: deploymentDialog.packageStep
onPackaged:
{
console.log("packaged")
}
}
Connections
{
id: registerStatus
target: deploymentDialog.registerStep
onRegistered:
{
console.log("registered")
}
}
ColumnLayout
{
anchors.fill: parent
anchors.margins: 1
spacing: 0
Repeater
{
id: menu
model: [
{
step: 1,
type:"deploy",
label: qsTr("Deploy contracts")
},
{
step: 2,
type:"package",
label: qsTr("Package Dapp")
},
{
step: 3,
type:"register",
label: qsTr("Register Dapp")
}
]
Rectangle
{
Layout.preferredHeight: 50
Layout.fillWidth: true
color: "white"
id: itemContainer
function select()
{
if (sel !== undefined)
{
menu.itemAt(sel).unselect()
}
labelContainer.state = "selected"
sel = index
itemClicked(menu.model[index].type)
deployLogs.switchLogs()
}
function unselect()
{
labelContainer.state = ""
}
Rectangle {
width: 40
height: 40
color: "transparent"
border.color: "#cccccc"
border.width: 2
radius: width*0.5
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
id: labelContainer
Label
{
color: "#cccccc"
id: label
anchors.centerIn: parent
text: menu.model[index].step
}
states: [
State {
name: "selected"
PropertyChanges { target: label; color: "white" }
PropertyChanges { target: labelContainer.border; color: "white" }
PropertyChanges { target: detail; color: "white" }
PropertyChanges { target: itemContainer; color: "#3395FE" }
}
]
}
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.left: label.parent.right
width: parent.width - 40
height: 40
color: "transparent"
Label
{
id: detail
color: "black"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
text: menu.model[index].label
}
}
MouseArea
{
anchors.fill: parent
onClicked:
{
itemContainer.select()
}
}
}
}
Connections {
property var logs: ({})
id: deployLogs
function switchLogs()
{
if (root.sel)
{
if (!logs[root.sel])
logs[root.sel] = ""
log.text = logs[root.sel]
}
}
target: projectModel
onDeploymentStarted:
{
if (!logs[root.sel])
logs[root.sel] = ""
logs[root.sel] = logs[root.sel] + qsTr("Running deployment...") + "\n"
log.text = logs[root.sel]
}
onDeploymentError:
{
if (!logs[root.sel])
logs[root.sel] = ""
logs[root.sel] = logs[root.sel] + error + "\n"
log.text = logs[root.sel]
}
onDeploymentComplete:
{
if (!logs[root.sel])
logs[root.sel] = ""
logs[root.sel] = logs[root.sel] + qsTr("Deployment complete") + "\n"
log.text = logs[root.sel]
}
onDeploymentStepChanged:
{
if (!logs[root.sel])
logs[root.sel] = ""
logs[root.sel] = logs[root.sel] + message + "\n"
log.text = logs[root.sel]
}
}
Rectangle
{
Layout.fillWidth: true
Layout.preferredHeight: 2
color: "#cccccc"
}
ScrollView
{
Layout.fillHeight: true
Layout.fillWidth: true
Text
{
anchors.left: parent.left
anchors.leftMargin: 2
font.pointSize: 9
font.italic: true
id: log
}
}
Rectangle
{
Layout.preferredHeight: 20
Layout.fillWidth: true
color: "#cccccc"
LogsPaneStyle
{
id: style
}
Label
{
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Logs")
font.italic: true
font.pointSize: style.absoluteSize(-1)
}
Button
{
height: 20
width: 20
anchors.right: parent.right
action: clearAction
iconSource: "qrc:/qml/img/cleariconactive.png"
tooltip: qsTr("Clear Messages")
}
Action {
id: clearAction
enabled: log.text !== ""
tooltip: qsTr("Clear")
onTriggered: {
deployLogs.logs[root.sel] = ""
log.text = deployLogs.logs[root.sel]
}
}
}
}
}

250
mix/qml/DeploymentWorker.qml

@ -1,250 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import org.ethereum.qml.CodeModel 1.0
import org.ethereum.qml.ClientModel 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
import "."
Item
{
property string currentAccount
property string gasPrice
property alias gasPriceInt: gasPriceInt
property variant balances: ({})
property variant accounts: []
property alias pooler: pooler
signal gasPriceLoaded()
function renewCtx()
{
accounts = []
balances = {}
var requests = [{
//accounts
jsonrpc: "2.0",
method: "eth_accounts",
params: null,
id: 0
}];
TransactionHelper.rpcCall(requests, function(arg1, arg2)
{
var ids = JSON.parse(arg2)[0].result;
requests = [];
accounts = []
for (var k in ids)
{
requests.push({
//accounts
jsonrpc: "2.0",
method: "eth_getBalance",
params: [ids[k], 'latest'],
id: k
});
accounts.push({ "id": ids[k] })
}
TransactionHelper.rpcCall(requests, function (request, response){
var balanceRet = JSON.parse(response);
balances = {}
for (var k in balanceRet)
{
var ether = QEtherHelper.createEther(balanceRet[k].result, QEther.Wei);
balances[accounts[k].id] = ether
}
}, function(){});
}, function(){});
NetworkDeploymentCode.gasPrice(function(price) {
gasPrice = price;
gasPriceInt.setValue(price);
gasPriceLoaded()
}, function(){});
}
function balance(account)
{
for (var k in accounts)
{
if (accounts[k].id === account)
return balances[account]
}
return null
}
function stopForInputError(inError)
{
errorDialog.text = "";
if (inError.length > 0)
{
errorDialog.text = qsTr("The length of a string cannot exceed 32 characters.\nPlease verify the following value(s):\n\n")
for (var k in inError)
errorDialog.text += inError[k] + "\n";
errorDialog.open();
return true;
}
return false;
}
function forceStopPooling()
{
poolLog.stop()
}
function waitForTrReceipt(hash, callback)
{
poolLog.callBack = callback;
poolLog.hash = hash
poolLog.elapsed = 0;
poolLog.start();
}
function verifyHash(tr, hash, callBack)
{
var h = {}
h[tr] = hash
verifyHashes(h, function (bn, trLost)
{
callBack(bn, trLost)
});
}
function verifyHashes(trHashes, callback)
{
//trHashes : { "trLabel": 'hash' }
var requests = [];
var req = 0
requests.push({
jsonrpc: "2.0",
method: "eth_blockNumber",
params: [],
id: req
});
var label = []
for (var k in trHashes)
{
req++
label[req] = k
requests.push({
jsonrpc: "2.0",
method: "eth_getTransactionReceipt",
params: [trHashes[k]],
id: req
});
}
TransactionHelper.rpcCall(requests, function (httpRequest, response){
var ret = JSON.parse(response)
var b = ret[0].result;
var trLost = []
for (var k in ret)
{
if (!ret[k].result)
trLost.push(label[ret[k].id])
}
callback(parseInt(b, 16), trLost)
});
}
Component.onCompleted:
{
renewCtx()
}
BigIntValue
{
id: gasPriceInt
}
function estimateGas(scenario, callback)
{
if (!clientModelGasEstimation.running)
{
for (var si = 0; si < projectModel.listModel.count; si++)
{
var document = projectModel.listModel.get(si);
if (document.isContract)
codeModelGasEstimation.registerCodeChange(document.documentId, fileIo.readFile(document.path));
}
gasEstimationConnect.callback = callback
clientModelGasEstimation.setupScenario(scenario)
}
}
Connections
{
id: gasEstimationConnect
target: clientModelGasEstimation
property var callback
onRunComplete: {
callback(clientModelGasEstimation.gasCosts)
}
}
CodeModel {
id: codeModelGasEstimation
}
ClientModel
{
id: clientModelGasEstimation
codeModel: codeModelGasEstimation
Component.onCompleted:
{
init("/tmp/bcgas/")
}
}
Timer
{
id: pooler
interval: 5000
repeat: true
running: false
}
Timer
{
id: poolLog
property var callBack
property int elapsed
property string hash
interval: 2000
running: false
repeat: true
onTriggered: {
elapsed += interval;
var requests = [];
var jsonRpcRequestId = 0;
requests.push({
jsonrpc: "2.0",
method: "eth_getTransactionReceipt",
params: [ hash ],
id: jsonRpcRequestId++
});
TransactionHelper.rpcCall(requests, function (httpRequest, response){
console.log(response)
var receipt = JSON.parse(response)[0].result
if (receipt)
{
stop();
callBack(1, receipt);
}
else if (elapsed > 2500000)
{
stop();
callBack(-1, null);
}
})
}
}
}

99
mix/qml/Ether.qml

@ -1,99 +0,0 @@
/*
* Display a row containing :
* - The amount of Ether.
* - The unit used.
* - User-friendly string representation of the amout of Ether (if displayFormattedValue == true).
* 'value' has to be a QEther obj.
*/
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
RowLayout {
id: etherEdition
property bool displayFormattedValue;
property bool edit;
property variant value;
property bool displayUnitSelection
onValueChanged: update()
Component.onCompleted: update()
signal amountChanged
signal unitChanged
function update()
{
if (value)
{
etherValueEdit.text = value.value;
selectUnit(value.unit);
}
}
function selectUnit(unit)
{
units.currentIndex = unit;
}
DefaultTextField
{
onTextChanged:
{
if (value !== undefined)
{
value.setValue(text)
formattedValue.text = value.format();
amountChanged()
}
}
readOnly: !edit
visible: edit
id: etherValueEdit;
}
ComboBox
{
id: units
visible: displayUnitSelection;
onCurrentTextChanged:
{
if (value)
{
value.setUnit(currentText);
formattedValue.text = value.format();
unitChanged()
}
}
model: ListModel {
id: unitsModel
ListElement { text: "Uether"; }
ListElement { text: "Vether"; }
ListElement { text: "Dether"; }
ListElement { text: "Nether"; }
ListElement { text: "Yether"; }
ListElement { text: "Zether"; }
ListElement { text: "Eether"; }
ListElement { text: "Pether"; }
ListElement { text: "Tether"; }
ListElement { text: "Gether"; }
ListElement { text: "Mether"; }
ListElement { text: "grand"; }
ListElement { text: "ether"; }
ListElement { text: "finney"; }
ListElement { text: "szabo"; }
ListElement { text: "Gwei"; }
ListElement { text: "Mwei"; }
ListElement { text: "Kwei"; }
ListElement { text: "wei"; }
}
}
Text
{
visible: displayFormattedValue
id: formattedValue
}
}

15
mix/qml/EtherValue.qml

@ -1,15 +0,0 @@
/*
* Used to instanciate a QEther obj using Qt.createComponent function.
*/
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
import org.ethereum.qml.QEther 1.0
QEther
{
id: basicEther
value: "100000000000"
unit: QEther.Wei
}

302
mix/qml/FilesSection.qml

@ -1,302 +0,0 @@
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.3
import QtQuick.Dialogs 1.2
import "."
Rectangle
{
Layout.fillWidth: true
Layout.minimumHeight: hiddenHeightTopLevel()
height: hiddenHeightTopLevel()
Layout.maximumHeight: hiddenHeightTopLevel()
id: wrapperItem
signal documentSelected(string doc, string groupName)
property alias model: filesList.model
property string sectionName;
property variant selManager;
property int index;
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;
}
function hiddenHeightRepeater()
{
return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count;
}
function hiddenHeightElement()
{
return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight;
}
function getDocumentIndex(documentId)
{
for (var i = 0; i < model.count; i++)
if (model.get(i).documentId === documentId)
return i;
return -1;
}
function removeDocument(documentId)
{
var i = getDocumentIndex(documentId);
if (i !== -1)
model.remove(i);
}
ColumnLayout {
anchors.fill: parent
spacing: 0
SourceSansProRegular
{
id: fileNameFont
}
SourceSansProBold
{
id: boldFont
}
RowLayout
{
anchors.top: parent.top
id: rowCol
height: projectFilesStyle.documentsList.height
Layout.fillWidth: true
Image {
source: "qrc:/qml/img/opentriangleindicator_filesproject.png"
width: 15
sourceSize.width: 12
id: imgArrow
anchors.right: section.left
anchors.rightMargin: 8
anchors.top: parent.top
anchors.topMargin: 10
}
Text
{
id: section
text: sectionName
anchors.left: parent.left
anchors.leftMargin: projectFilesStyle.general.leftMargin
color: projectFilesStyle.documentsList.sectionColor
font.family: boldFont.name
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: imgArrow; source: "qrc:/qml/img/closedtriangleindicator_filesproject.png" }
}
]
}
MouseArea {
id: titleMouseArea
anchors.fill: parent
hoverEnabled: true
z: 2
onClicked: {
if (section.state === "hidden")
section.state = "";
else
section.state = "hidden";
}
}
}
ColumnLayout {
height: wrapperItem.hiddenHeightRepeater()
Layout.minimumHeight: wrapperItem.hiddenHeightRepeater()
Layout.preferredHeight: wrapperItem.hiddenHeightRepeater()
Layout.maximumHeight: wrapperItem.hiddenHeightRepeater()
width: parent.width
visible: section.state !== "hidden"
spacing: 0
Repeater
{
id: filesList
visible: section.state !== "hidden"
Rectangle
{
visible: section.state !== "hidden"
id: rootItem
Layout.fillWidth: true
Layout.minimumHeight: wrapperItem.hiddenHeightElement()
Layout.preferredHeight: wrapperItem.hiddenHeightElement()
Layout.maximumHeight: wrapperItem.hiddenHeightElement()
height: wrapperItem.hiddenHeightElement()
color: isSelected ? projectFilesStyle.documentsList.highlightColor : "transparent"
property bool isSelected
property bool renameMode
Row {
spacing: 3
anchors.verticalCenter: parent.verticalCenter
anchors.fill: parent
anchors.left: parent.left
anchors.leftMargin: projectFilesStyle.general.leftMargin + 2
Text {
id: nameText
height: parent.height
visible: !renameMode
color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color
text: name;
font.family: fileNameFont.name
font.pointSize: projectFilesStyle.documentsList.fontSize
verticalAlignment: Text.AlignVCenter
Connections
{
target: selManager
onSelected: {
if (groupName != sectionName)
rootItem.isSelected = false;
else if (doc === documentId)
rootItem.isSelected = true;
else
rootItem.isSelected = false;
if (rootItem.isSelected && section.state === "hidden")
section.state = "";
}
onIsCleanChanged: {
if (groupName === sectionName && doc === documentId)
editStatusLabel.visible = !isClean;
}
}
}
DefaultLabel {
id: editStatusLabel
visible: false
color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color
verticalAlignment: Text.AlignVCenter
text: "*"
width: 10
height: parent.height
}
}
TextInput {
id: textInput
text: nameText.text
visible: renameMode
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: projectFilesStyle.general.leftMargin
MouseArea {
id: textMouseArea
anchors.fill: parent
hoverEnabled: true
z: 2
onClicked: {
textInput.forceActiveFocus();
}
}
onVisibleChanged: {
if (visible) {
selectAll();
forceActiveFocus();
}
}
onAccepted: close(true);
onCursorVisibleChanged: {
if (!cursorVisible)
close(false);
}
onFocusChanged: {
if (!focus)
close(false);
}
function close(accept) {
rootItem.renameMode = false;
if (accept)
{
var i = getDocumentIndex(documentId);
projectModel.renameDocument(documentId, textInput.text);
wrapperItem.model.set(i, projectModel.getDocument(documentId));
}
}
}
MouseArea {
id: mouseArea
z: 1
hoverEnabled: false
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked:{
if (mouse.button === Qt.RightButton)
{
if (isContract)
contextMenuContract.popup();
else
contextMenu.popup();
}
else if (mouse.button === Qt.LeftButton)
{
rootItem.isSelected = true;
projectModel.openDocument(documentId);
documentSelected(documentId, groupName);
}
}
}
Menu {
id: contextMenu
MenuItem {
text: qsTr("Rename")
onTriggered: {
rootItem.renameMode = true;
}
}
MenuItem {
text: qsTr("Delete")
onTriggered: {
deleteConfirmation.open();
}
}
}
Menu {
id: contextMenuContract
MenuItem {
text: qsTr("Delete")
onTriggered: {
deleteConfirmation.open();
}
}
}
MessageDialog
{
id: deleteConfirmation
text: qsTr("Are you sure to delete this file ?")
standardButtons: StandardIcon.Ok | StandardIcon.Cancel
onAccepted:
{
projectModel.removeDocument(documentId);
wrapperItem.removeDocument(documentId);
}
}
}
}
}
}
}

61
mix/qml/ItemDelegateDataDump.qml

@ -1,61 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
import "."
Rectangle {
anchors.fill: parent
RowLayout
{
id: row;
anchors.fill: parent
spacing: 2
Rectangle
{
id: firstCol;
color: "#f7f7f7"
Layout.fillWidth: true
Layout.minimumWidth: 35
Layout.preferredWidth: 35
Layout.maximumWidth: 35
Layout.minimumHeight: parent.height
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 3
font.family: "monospace"
font.bold: true
color: "#4a4a4a"
text: modelData[0]
font.pointSize: dbgStyle.general.dataDumpFontSize;
}
}
Rectangle
{
Layout.fillWidth: true
Layout.minimumWidth: 110
Layout.preferredWidth: 110
Layout.maximumWidth: 110
Layout.minimumHeight: parent.height
Text {
font.family: "monospace"
font.bold: true
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
color: "#4a4a4a"
text: modelData[1]
font.pointSize: dbgStyle.general.dataDumpFontSize
}
}
}
Rectangle {
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom
}
}

133
mix/qml/KeyValuePanel.qml

@ -1,133 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "js/TransactionHelper.js" as TransactionHelper
import "js/QEtherHelper.js" as QEtherHelper
import "."
ColumnLayout {
id: root
property alias title: titleLabel.text
property variant _data
property string role
property alias model: modelKeyValue
function add(key, value)
{
modelKeyValue.append({ "key": key, "value": value })
}
function clear()
{
modelKeyValue.clear()
}
function init()
{
modelKeyValue.clear()
if (typeof(computeData) !== "undefined" && computeData instanceof Function)
computeData()
else
{
if (_data !== undefined && _data[role] !== undefined)
{
var keys = Object.keys(_data[role])
for (var k in keys)
{
modelKeyValue.append({ "key": keys[k] === "" ? "undefined" : keys[k], "value": _data[role][keys[k]] })
}
}
}
}
RowLayout
{
Layout.preferredHeight: 20
Layout.fillWidth: true
Label
{
id: titleLabel
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
color: "white"
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 100
ListModel
{
id: modelKeyValue
}
Rectangle
{
Layout.fillWidth: true
Layout.fillHeight: true
color: "white"
radius: 2
ScrollView
{
id: columnValues
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
anchors.fill: parent
clip: true
ColumnLayout
{
spacing: 0
id: colValue
anchors.top: parent.top
anchors.topMargin: 5
Repeater
{
id: repeaterKeyValue
model: modelKeyValue
Row
{
Layout.preferredHeight: 30
spacing: 5
anchors.left: colValue.left
anchors.leftMargin: 5
Label
{
maximumLineCount: 1
text: {
if (index >= 0 && repeaterKeyValue.model.get(index).key !== undefined)
return repeaterKeyValue.model.get(index).key
else
return ""
}
}
Label
{
text: "="
}
Label
{
maximumLineCount: 1
text: {
if (index >= 0 && repeaterKeyValue.model.get(index).value !== undefined)
return repeaterKeyValue.model.get(index).value
else
return ""
}
}
}
}
}
}
}
}
}

648
mix/qml/LogsPane.qml

@ -1,648 +0,0 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.SortFilterProxyModel 1.0
Rectangle
{
property variant statusPane
property variant currentStatus
property int contentXPos: logStyle.generic.layout.dateWidth + logStyle.generic.layout.typeWidth - 70
function clear()
{
logsModel.clear();
statusPane.clear();
currentStatus = undefined;
}
function push(_level, _type, _content)
{
_content = _content.replace(/\n/g, " ")
logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": _content, "level": _level });
}
onVisibleChanged:
{
if (currentStatus && visible && (logsModel.count === 0 || logsModel.get(0).content !== currentStatus.content || logsModel.get(0).date !== currentStatus.date))
logsModel.insert(0, { "type": currentStatus.type, "date": currentStatus.date, "content": currentStatus.content, "level": currentStatus.level });
else if (!visible)
{
for (var k = 0; k < logsModel.count; k++)
{
if (logsModel.get(k).type === "Comp") //do not keep compilation logs.
logsModel.remove(k);
}
}
}
anchors.fill: parent
radius: 10
color: "transparent"
id: logsPane
Column {
z: 2
height: parent.height - rowAction.height
width: parent.width
spacing: 0
ListModel {
id: logsModel
}
ScrollView
{
id: scrollView
height: parent.height
width: parent.width
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
Column
{
id: logsRect
spacing: 0
Repeater {
id: logsRepeater
clip: true
property string frontColor: "transparent"
model: SortFilterProxyModel {
id: proxyModel
source: logsModel
property var roles: ["-", "javascript", "run", "state", "comp", "deployment"]
Component.onCompleted: {
filterType = regEx(proxyModel.roles);
}
function search(_value)
{
filterContent = _value;
}
function toogleFilter(_value)
{
var count = roles.length;
for (var i in roles)
{
if (roles[i] === _value)
{
roles.splice(i, 1);
break;
}
}
if (count === roles.length)
roles.push(_value);
filterType = regEx(proxyModel.roles);
}
function regEx(_value)
{
return "(?:" + roles.join('|') + ")";
}
filterType: "(?:javascript|run|state|comp|deployment)"
filterContent: ""
filterSyntax: SortFilterProxyModel.RegExp
filterCaseSensitivity: Qt.CaseInsensitive
}
Rectangle
{
width: logStyle.generic.layout.dateWidth + logStyle.generic.layout.contentWidth + logStyle.generic.layout.typeWidth
height: 30
color:
{
var cl;
if (level === "warning" || level === "error")
cl = logStyle.generic.layout.errorColor;
else
cl = index % 2 === 0 ? "transparent" : logStyle.generic.layout.logAlternateColor;
if (index === 0)
logsRepeater.frontColor = cl;
return cl;
}
Component.onCompleted:
{
logsPane.contentXPos = logContent.x
}
MouseArea
{
anchors.fill: parent
onClicked:
{
if (logContent.elide === Text.ElideNone)
{
logContent.elide = Text.ElideRight;
logContent.wrapMode = Text.NoWrap
parent.height = 30;
}
else
{
logContent.elide = Text.ElideNone;
logContent.wrapMode = Text.WordWrap;
parent.height = logContent.lineCount * 30;
}
}
}
DefaultLabel {
id: dateLabel
text: date;
font.family: logStyle.generic.layout.logLabelFont
width: logStyle.generic.layout.dateWidth
font.pointSize: appStyle.absoluteSize(-1)
clip: true
elide: Text.ElideRight
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter
color: {
parent.getColor(level);
}
}
DefaultLabel {
text: type;
id: typeLabel
font.family: logStyle.generic.layout.logLabelFont
width: logStyle.generic.layout.typeWidth
clip: true
elide: Text.ElideRight
font.pointSize: appStyle.absoluteSize(-1)
anchors.left: dateLabel.right
anchors.leftMargin: 2
anchors.verticalCenter: parent.verticalCenter
color: {
parent.getColor(level);
}
}
Text {
id: logContent
text: content;
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: typeLabel.right
anchors.leftMargin: 2
color: {
parent.getColor(level);
}
}
function getColor()
{
if (level === "error")
return "red";
else if (level === "warning")
return "orange";
else
return "#808080";
}
}
}
}
}
Component {
id: itemDelegate
DefaultLabel {
text: styleData.value;
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-1)
color: {
if (proxyModel.get(styleData.row).level === "error")
return "red";
else if (proxyModel.get(styleData.row).level === "warning")
return "orange";
else
return "#808080";
}
}
}
}
Rectangle
{
gradient: Gradient {
GradientStop { position: 0.0; color: "#f1f1f1" }
GradientStop { position: 1.0; color: "#d9d7da" }
}
Layout.preferredHeight: logStyle.generic.layout.headerHeight
height: logStyle.generic.layout.headerHeight
width: logsPane.width
anchors.bottom: parent.bottom
Row
{
id: rowAction
anchors.leftMargin: logStyle.generic.layout.leftMargin
anchors.left: parent.left
spacing: logStyle.generic.layout.headerButtonSpacing
height: parent.height
Rectangle
{
color: "transparent"
height: parent.height
width: 40
DefaultLabel
{
width: 40
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
color: logStyle.generic.layout.logLabelColor
font.pointSize: appStyle.absoluteSize(-3)
font.family: logStyle.generic.layout.logLabelFont
text: qsTr("Show:")
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
ToolButton {
id: javascriptButton
checkable: true
height: logStyle.generic.layout.headerButtonHeight
width: 30
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("javascript")
}
tooltip: qsTr("JavaScript")
style:
ButtonStyle {
label:
Item {
Rectangle
{
width: labelJs.width
height: labelJs.height
anchors.centerIn: parent
color: "transparent"
DefaultLabel {
id: labelJs
width: 15
elide: Text.ElideRight
anchors.horizontalCenter: parent.horizontalCenter
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-3)
color: logStyle.generic.layout.logLabelColor
text: qsTr("JS")
}
}
}
background:
Rectangle {
color: javascriptButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
}
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
ToolButton {
id: runButton
checkable: true
height: logStyle.generic.layout.headerButtonHeight
width: 40
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("run")
}
tooltip: qsTr("Run")
style:
ButtonStyle {
label:
Item {
Rectangle
{
width: labelRun.width
height: labelRun.height
anchors.centerIn: parent
color: "transparent"
DefaultLabel {
id: labelRun
width: 25
anchors.horizontalCenter: parent.horizontalCenter
elide: Text.ElideRight
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-3)
color: logStyle.generic.layout.logLabelColor
text: qsTr("Run")
}
}
}
background:
Rectangle {
color: runButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
}
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
ToolButton {
id: stateButton
checkable: true
height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
width: 40
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("state")
}
tooltip: qsTr("State")
style:
ButtonStyle {
label:
Item {
Rectangle
{
width: labelState.width
height: labelState.height
anchors.centerIn: parent
color: "transparent"
DefaultLabel {
id: labelState
width: 30
anchors.horizontalCenter: parent.horizontalCenter
elide: Text.ElideRight
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-3)
color: logStyle.generic.layout.logLabelColor
text: qsTr("State")
}
}
}
background:
Rectangle {
color: stateButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
}
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
ToolButton {
id: deloyButton
checkable: true
height: logStyle.generic.layout.headerButtonHeight
width: 50
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("deployment")
}
tooltip: qsTr("Deployment")
style:
ButtonStyle {
label:
Item {
Rectangle
{
width: labelDeploy.width
height: labelDeploy.height
anchors.centerIn: parent
color: "transparent"
DefaultLabel {
width: 40
id: labelDeploy
anchors.horizontalCenter: parent.horizontalCenter
elide: Text.ElideRight
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-3)
color: logStyle.generic.layout.logLabelColor
text: qsTr("Deploy.")
}
}
}
background:
Rectangle {
color: deloyButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
}
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
}
Row
{
height: parent.height
anchors.right: parent.right
anchors.rightMargin: 10
spacing: 10
Rectangle
{
height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
color: "transparent"
width: 20
Button
{
id: clearButton
action: clearAction
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
height: 25
style:
ButtonStyle {
background:
Rectangle {
height: logStyle.generic.layout.headerButtonHeight
implicitHeight: logStyle.generic.layout.headerButtonHeight
color: "transparent"
}
}
}
Image {
id: clearImage
source: clearAction.enabled ? "qrc:/qml/img/cleariconactive.png" : "qrc:/qml/img/clearicon.png"
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
width: 20
height: 25
}
Action {
id: clearAction
enabled: logsModel.count > 0
tooltip: qsTr("Clear")
onTriggered: {
logsPane.clear()
}
}
}
Rectangle
{
height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
color: "transparent"
width: 20
Button
{
id: copyButton
action: copyAction
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
height: 25
style:
ButtonStyle {
background:
Rectangle {
height: logStyle.generic.layout.headerButtonHeight
implicitHeight: logStyle.generic.layout.headerButtonHeight
color: "transparent"
}
}
}
Image {
id: copyImage
source: copyAction.enabled ? "qrc:/qml/img/copyiconactive.png" : "qrc:/qml/img/copyicon.png"
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
width: 20
height: 25
}
Action {
id: copyAction
enabled: logsModel.count > 0
tooltip: qsTr("Copy to Clipboard")
onTriggered: {
var content = "";
for (var k = 0; k < logsModel.count; k++)
{
var log = logsModel.get(k);
content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n";
}
clipboard.text = content;
}
}
}
Rectangle
{
width: 120
radius: 10
height: 25
color: "white"
anchors.verticalCenter: parent.verticalCenter
Image
{
id: searchImg
source: "qrc:/qml/img/searchicon.png"
fillMode: Image.PreserveAspectFit
width: 20
height: 25
z: 3
}
DefaultTextField
{
id: searchBox
z: 2
width: 100
anchors.left: searchImg.right
anchors.leftMargin: -7
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-3)
font.italic: true
text: qsTr(" - Search - ")
onFocusChanged:
{
if (!focus && text === "")
text = qsTr(" - Search - ");
else if (focus && text === qsTr(" - Search - "))
text = "";
}
onTextChanged: {
if (text === qsTr(" - Search - "))
proxyModel.search("");
else
proxyModel.search(text);
}
style:
TextFieldStyle {
background: Rectangle {
radius: 10
}
}
}
}
}
}
}

30
mix/qml/LogsPaneStyle.qml

@ -1,30 +0,0 @@
import QtQuick 2.0
QtObject {
function absoluteSize(rel)
{
return systemPointSize + rel;
}
property QtObject generic: QtObject {
property QtObject layout: QtObject {
property string backgroundColor: "#f7f7f7"
property int headerHeight: 30
property int headerButtonSpacing: 0
property int leftMargin: 10
property int headerButtonHeight: 30
property string logLabelColor: "#4a4a4a"
property string logLabelFont: "sans serif"
property int headerInputWidth: 200
property int dateWidth: 100
property int typeWidth: 100
property int contentWidth: 560
property string logAlternateColor: "#f6f5f6"
property string errorColor: "#fffcd5"
property string buttonSeparatorColor1: "#d3d0d0"
property string buttonSeparatorColor2: "#f2f1f2"
property string buttonSelected: "#dcdcdc"
}
}
}

298
mix/qml/MacFileDialog.qml

@ -1,298 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0 as ControlsPrivate
import QtQuick.Dialogs 1.2
import QtQuick.Dialogs.Private 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import Qt.labs.folderlistmodel 2.1
import Qt.labs.settings 1.0
AbstractDialog {
id: root
property string folder: view.model.folder
property var nameFilters: []
property bool selectFolder: false
property bool selectExisting: true
property int selectedNameFilterIndex: -1
property var selectedNameFilterExtensions: []
property string selection: ""
property alias fileUrl: root.selection
function selectNameFilter(text) {
}
function clearSelection(text) {
selection = "";
}
function addSelection(text) {
selection = text;
}
onVisibleChanged: {
if (visible) {
view.needsWidthAdjustment = true
view.selection.clear()
view.focus = true
}
}
Component.onCompleted: {
folder = fileIo.pathToUrl(fileIo.homePath);
view.model.nameFilters = root.selectedNameFilterExtensions
filterField.currentIndex = root.selectedNameFilterIndex
root.favoriteFolders = settings.favoriteFolders
}
Component.onDestruction: {
settings.favoriteFolders = root.favoriteFolders
}
property Settings settings: Settings {
category: "QQControlsFileDialog"
property alias width: root.width
property alias height: root.height
property variant favoriteFolders: []
}
property bool showFocusHighlight: false
property SystemPalette palette: SystemPalette { }
property var favoriteFolders: []
function dirDown(path) {
view.selection.clear()
root.folder = "file://" + path
}
function dirUp() {
view.selection.clear()
if (view.model.parentFolder != "")
root.folder = view.model.parentFolder
}
function acceptSelection() {
// transfer the view's selections to QQuickFileDialog
clearSelection()
if (selectFolder && view.selection.count === 0)
addSelection(folder)
else {
view.selection.forEach(function(idx) {
if (view.model.isFolder(idx)) {
if (selectFolder)
addSelection(view.model.get(idx, "fileURL"))
} else {
if (!selectFolder)
addSelection(view.model.get(idx, "fileURL"))
}
})
}
accept()
}
property Action dirUpAction: Action {
text: "\ue810"
shortcut: "Ctrl+U"
onTriggered: dirUp()
tooltip: qsTr("Go up to the folder containing this one")
}
Rectangle {
id: window
implicitWidth: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 100, view.implicitWidth))
implicitHeight: Math.min(root.__maximumDimension, Screen.pixelDensity * 80)
color: root.palette.window
Binding {
target: view.model
property: "folder"
value: root.folder
}
Binding {
target: currentPathField
property: "text"
value: fileIo.urlToPath(root.folder)
}
Keys.onPressed: {
event.accepted = true
switch (event.key) {
case Qt.Key_Back:
case Qt.Key_Escape:
reject()
break
default:
event.accepted = false
break
}
}
Keys.forwardTo: [view.flickableItem]
TableView {
id: view
sortIndicatorVisible: true
width: parent.width
anchors.top: titleBar.bottom
anchors.bottom: bottomBar.top
property bool needsWidthAdjustment: true
selectionMode: root.selectMultiple ?
(ControlsPrivate.Settings.hasTouchScreen ? SelectionMode.MultiSelection : SelectionMode.ExtendedSelection) :
SelectionMode.SingleSelection
onRowCountChanged: if (needsWidthAdjustment && rowCount > 0) {
resizeColumnsToContents()
needsWidthAdjustment = false
}
model: FolderListModel {
showFiles: !root.selectFolder
nameFilters: root.selectedNameFilterExtensions
sortField: (view.sortIndicatorColumn === 0 ? FolderListModel.Name :
(view.sortIndicatorColumn === 1 ? FolderListModel.Type :
(view.sortIndicatorColumn === 2 ? FolderListModel.Size : FolderListModel.LastModified)))
sortReversed: view.sortIndicatorOrder === Qt.DescendingOrder
}
onActivated: if (view.focus) {
if (view.selection.count > 0 && view.model.isFolder(row)) {
dirDown(view.model.get(row, "filePath"))
} else {
root.acceptSelection()
}
}
onClicked: currentPathField.text = view.model.get(row, "filePath")
TableViewColumn {
id: fileNameColumn
role: "fileName"
title: qsTr("Filename")
delegate: Item {
implicitWidth: pathText.implicitWidth + pathText.anchors.leftMargin + pathText.anchors.rightMargin
Text {
id: fileIcon
width: height
verticalAlignment: Text.AlignVCenter
font.family: iconFont.name
property alias unicode: fileIcon.text
FontLoader { id: iconFont; source: "qrc:/QtQuick/Dialogs/qml/icons.ttf"; onNameChanged: console.log("custom font" + name) }
x: 4
height: parent.height - 2
unicode: view.model.isFolder(styleData.row) ? "\ue804" : "\ue802"
}
Text {
id: pathText
text: styleData.value
anchors {
left: parent.left
right: parent.right
leftMargin: 36 + 6
rightMargin: 4
verticalCenter: parent.verticalCenter
}
color: styleData.textColor
elide: Text.ElideRight
renderType: ControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering
}
}
}
TableViewColumn {
role: "fileSuffix"
title: qsTr("Type", "file type (extension)")
// TODO should not need to create a whole new component just to customize the text value
// something like textFormat: function(text) { return view.model.get(styleData.row, "fileIsDir") ? "folder" : text }
delegate: Item {
implicitWidth: sizeText.implicitWidth + sizeText.anchors.leftMargin + sizeText.anchors.rightMargin
Text {
id: sizeText
text: view.model.get(styleData.row, "fileIsDir") ? "folder" : styleData.value
anchors {
left: parent.left
right: parent.right
leftMargin: 4
rightMargin: 4
verticalCenter: parent.verticalCenter
}
color: styleData.textColor
elide: Text.ElideRight
renderType: ControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering
}
}
}
TableViewColumn {
role: "fileSize"
title: qsTr("Size", "file size")
horizontalAlignment: Text.AlignRight
}
TableViewColumn { id: modifiedColumn; role: "fileModified" ; title: qsTr("Modified", "last-modified time") }
TableViewColumn { id: accessedColumn; role: "fileAccessed" ; title: qsTr("Accessed", "last-accessed time") }
}
ToolBar {
id: titleBar
RowLayout {
anchors.fill: parent
ToolButton {
action: dirUpAction
//style: IconButtonStyle { }
Layout.maximumWidth: height * 1.5
}
TextField {
id: currentPathField
Layout.fillWidth: true
function doAccept() {
root.clearSelection()
if (root.addSelection(fileIo.pathToUrl(text)))
root.accept()
else
root.folder = fileIo.pathFolder(text)
}
onAccepted: doAccept()
}
}
}
Item {
id: bottomBar
width: parent.width
height: buttonRow.height + buttonRow.spacing * 2
anchors.bottom: parent.bottom
Row {
id: buttonRow
anchors.right: parent.right
anchors.rightMargin: spacing
anchors.verticalCenter: parent.verticalCenter
spacing: 4
ComboBox {
id: filterField
model: root.nameFilters
visible: !selectFolder
width: bottomBar.width - cancelButton.width - okButton.width - parent.spacing * 6
anchors.verticalCenter: parent.verticalCenter
onCurrentTextChanged: {
root.selectNameFilter(currentText)
view.model.nameFilters = root.selectedNameFilterExtensions
}
}
Button {
id: cancelButton
text: qsTr("Cancel")
onClicked: root.reject()
}
Button {
id: okButton
text: root.selectFolder ? qsTr("Choose") : (selectExisting ? qsTr("Open") : qsTr("Save"))
onClicked: {
if (view.model.isFolder(view.currentIndex) && !selectFolder)
dirDown(view.model.get(view.currentIndex, "filePath"))
else if (!(root.selectExisting))
currentPathField.doAccept()
else
root.acceptSelection()
}
}
}
}
}
}

246
mix/qml/MainContent.qml

@ -1,246 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper
import "js/TransactionHelper.js" as TransactionHelper
import "."
Rectangle {
objectName: "mainContent"
signal keyPressed(variant event)
focus: true
Keys.enabled: true
Keys.onPressed:
{
root.keyPressed(event.key);
}
anchors.fill: parent
id: root
property alias rightViewVisible: scenarioExe.visible
property alias webViewVisible: webPreview.visible
property alias webView: webPreview
property alias projectViewVisible: projectList.visible
property alias projectNavigator: projectList
property alias runOnProjectLoad: mainSettings.runOnProjectLoad
property alias rightPane: scenarioExe
property alias debuggerPanel: debugPanel
property alias codeEditor: codeEditor
property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally
property bool firstCompile: true
property int scenarioMinWidth: 620
Connections {
target: codeModel
onCompilationComplete: {
if (firstCompile) {
firstCompile = false;
if (runOnProjectLoad)
startQuickDebugging();
}
}
}
Connections {
target: debugPanel
onDebugExecuteLocation: {
codeEditor.highlightExecution(documentId, location);
}
}
Connections {
target: codeEditor
onBreakpointsChanged: {
debugPanel.setBreakpoints(codeEditor.getBreakpoints());
}
}
function startQuickDebugging()
{
ensureRightView();
projectModel.stateListModel.debugDefaultState();
}
function toggleRightView() {
scenarioExe.visible = !scenarioExe.visible;
}
function ensureRightView() {
scenarioExe.visible = true;
}
function rightViewIsVisible()
{
return scenarioExe.visible;
}
function hideRightView() {
scenarioExe.visible = lfalse;
}
function toggleWebPreview() {
webPreview.visible = !webPreview.visible;
}
function toggleProjectView() {
projectList.visible = !projectList.visible;
}
function toggleWebPreviewOrientation() {
codeWebSplitter.orientation = (codeWebSplitter.orientation === Qt.Vertical ? Qt.Horizontal : Qt.Vertical);
}
//TODO: move this to debugger.js after refactoring, introduce events
function toggleBreakpoint() {
codeEditor.toggleBreakpoint();
}
function displayCompilationErrorIfAny()
{
scenarioExe.visible = true;
scenarioExe.displayCompilationErrorIfAny();
}
Settings {
id: mainSettings
property alias codeWebOrientation: codeWebSplitter.orientation
property alias webWidth: webPreview.width
property alias webHeight: webPreview.height
property alias showProjectView: projectList.visible
property bool runOnProjectLoad: true
property int scenarioMinWidth: scenarioMinWidth
}
ColumnLayout
{
id: mainColumn
anchors.fill: parent
spacing: 0
Rectangle {
width: parent.width
height: 50
Layout.row: 0
Layout.fillWidth: true
Layout.preferredHeight: 50
id: headerView
Rectangle
{
gradient: Gradient {
GradientStop { position: 0.0; color: "#f1f1f1" }
GradientStop { position: 1.0; color: "#d9d7da" }
}
id: headerPaneContainer
anchors.fill: parent
StatusPane
{
anchors.fill: parent
webPreview: webPreview
}
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: "#8c8c8c"
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: root.height - headerView.height;
Settings {
id: splitSettings
property alias projectWidth: projectList.width
property alias contentViewWidth: contentView.width
property alias rightViewWidth: scenarioExe.width
}
Splitter
{
anchors.fill: parent
orientation: Qt.Horizontal
ProjectList {
id: projectList
width: 350
Layout.minimumWidth: 250
Layout.fillHeight: true
Connections {
target: projectModel.codeEditor
}
}
Rectangle {
id: contentView
Layout.fillHeight: true
Layout.fillWidth: true
Splitter {
id: codeWebSplitter
anchors.fill: parent
orientation: Qt.Vertical
CodeEditorView {
id: codeEditor
height: parent.height * 0.6
anchors.top: parent.top
Layout.fillWidth: true
Layout.fillHeight: true
}
WebPreview {
id: webPreview
height: parent.height * 0.4
Layout.fillWidth: codeWebSplitter.orientation === Qt.Vertical
Layout.fillHeight: codeWebSplitter.orientation === Qt.Horizontal
Layout.minimumHeight: 200
Layout.minimumWidth: 200
}
}
}
ScenarioExecution
{
id: scenarioExe;
visible: false;
Layout.fillHeight: true
Keys.onEscapePressed: visible = false
Layout.minimumWidth: scenarioMinWidth
anchors.right: parent.right
}
Debugger
{
id: debugPanel
visible: false
Layout.fillHeight: true
Keys.onEscapePressed: visible = false
Layout.minimumWidth: scenarioMinWidth
anchors.right: parent.right
}
Connections {
target: clientModel
onDebugDataReady: {
scenarioExe.visible = false
debugPanel.visible = true
debugPanel.width = scenarioExe.width
if (scenarioExe.bc.debugTrRequested)
debugPanel.setTr(scenarioExe.bc.model.blocks[scenarioExe.bc.debugTrRequested[0]].transactions[scenarioExe.bc.debugTrRequested[1]])
}
}
Connections {
target: debugPanel
onPanelClosed: {
debugPanel.visible = false
scenarioExe.visible = true
scenarioExe.width = debugPanel.width
}
}
}
}
}
}

30
mix/qml/ModalDialog.qml

@ -1,30 +0,0 @@
import QtQuick 2.2
import QtQuick.Window 2.0
Window
{
id: modalDialog
title: ""
modality: Qt.WindowModal
height: 400
width: 700
visible: false
Loader
{
focus: true
id: modalDialogContent
objectName: "modalDialogContent"
anchors.fill: parent
}
function open()
{
visible = true;
}
function close()
{
visible = false;
modalDialogContent.source = "";
modalDialogContent.sourceComponent = undefined;
modalDialog.destroy();
}
}

123
mix/qml/NewProjectDialog.qml

@ -1,123 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
Item
{
property alias projectTitle: titleField.text
readonly property string projectPath: "file://" + pathField.text
property alias pathFieldText: pathField.text
signal accepted
function open() {
newProjectWin.visible = true;
titleField.focus = true;
}
function close() {
newProjectWin.visible = false;
}
function acceptAndClose() {
close();
accepted();
}
Dialog {
id: newProjectWin
modality: Qt.ApplicationModal
title: qsTr("New Project");
width: 640
height: 120
visible: false
contentItem: Rectangle {
anchors.fill: parent
GridLayout
{
id: dialogContent
columns: 2
anchors.fill: parent
anchors.margins: 10
rowSpacing: 10
columnSpacing: 10
Label {
text: qsTr("Title")
}
TextField {
id: titleField
focus: true
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
}
}
Label {
text: qsTr("Path")
}
RowLayout {
TextField {
id: pathField
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
}
}
Button {
text: qsTr("Browse")
onClicked:
{
newProjectWin.close()
createProjectFileDialog.open()
}
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
id: okButton;
enabled: titleField.text != "" && pathField.text != ""
text: qsTr("OK");
onClicked: {
acceptAndClose();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
}
}
Component.onCompleted: pathField.text = fileIo.homePath
}
QFileDialog {
id: createProjectFileDialog
visible: false
title: qsTr("Please choose a path for the project")
selectFolder: true
onAccepted: {
var u = createProjectFileDialog.fileUrl.toString();
if (u.indexOf("file://") == 0)
u = u.substring(7, u.length)
if (Qt.platform.os == "windows" && u.indexOf("/") == 0)
u = u.substring(1, u.length);
pathField.text = u;
newProjectWin.open()
}
}
}

212
mix/qml/PackagingStep.qml

@ -1,212 +0,0 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
Rectangle {
property variant paramsModel: []
property variant worker
color: "#E3E3E3E3"
anchors.fill: parent
id: root
property string packageHash
property string packageBase64
property alias localPackageUrl: localPackageUrl.text
property alias lastDeployDate: lastDeployLabel.text
property string deploymentId
property string packageDir
signal packaged
function show()
{
visible = true
}
QFileDialog {
id: ressourcesFolder
visible: false
title: qsTr("Please choose a path")
selectFolder: true
selectExisting: true
property variant target
onAccepted: {
var u = ressourcesFolder.fileUrl.toString();
if (u.indexOf("file://") == 0)
u = u.substring(7, u.length)
if (Qt.platform.os == "windows" && u.indexOf("/") == 0)
u = u.substring(1, u.length);
target.text = u;
}
}
ColumnLayout
{
anchors.top: parent.top
anchors.topMargin: 10
width: parent.width
id: col
spacing: 20
anchors.left: parent.left
anchors.leftMargin: 10
Label
{
anchors.top: parent.top
Layout.fillWidth: true
text: qsTr("Upload and update your Dapp assets")
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 5
Label
{
text: qsTr("Save Package to")
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
}
DefaultTextField
{
id: packageFolder
visible: true
Layout.preferredWidth: 360
text: projectPath + "package/"
}
Button
{
text: qsTr("select")
onClicked: {
ressourcesFolder.target = packageFolder
ressourcesFolder.open()
}
}
}
Rectangle
{
Layout.fillWidth: true
Layout.preferredHeight: 40
color: "transparent"
Button
{
id: generatePackageBtn
anchors.left: parent.left
anchors.leftMargin: 10
text: qsTr("Generate Package")
onClicked:
{
NetworkDeploymentCode.packageDapp(projectModel.deploymentAddresses);
projectModel.saveProject()
root.packaged()
}
}
RowLayout
{
anchors.top: generatePackageBtn.bottom
anchors.topMargin: 10
visible: root.lastDeployDate !== ""
Label
{
id: lastPackage
text: qsTr("Last Package")
}
Label
{
id: lastDeployLabel
}
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 5
Label
{
text: qsTr("Local package URL")
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
}
DefaultTextField
{
id: localPackageUrl
Layout.preferredWidth: 450
readOnly: true
}
}
Label
{
Layout.preferredWidth: 300
text: qsTr("You have to upload the package to a remote folder, or use a service like pastebin")
wrapMode: Text.WordWrap
clip: true
}
Rectangle
{
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
Button
{
Layout.preferredWidth: 200
text: qsTr("Copy Base64")
anchors.left: parent.left
anchors.leftMargin: 10
onClicked:
{
clipboard.text = deploymentDialog.packageStep.packageBase64;
}
}
}
Rectangle
{
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
Button
{
Layout.preferredWidth: 200
text: qsTr("Open pastebin")
anchors.left: parent.left
anchors.leftMargin: 10
onClicked:
{
Qt.openUrlExternally("http://pastebin.com/");
}
}
}
}
}

32
mix/qml/ProjectFilesStyle.qml

@ -1,32 +0,0 @@
import QtQuick 2.0
QtObject {
function absoluteSize(rel)
{
return systemPointSize + rel;
}
property QtObject general: QtObject {
property int leftMargin: 45
}
property QtObject title: QtObject {
property string color: "#808080"
property string background: "#f0f0f0"
property int height: 55
property int fontSize: absoluteSize(7);// 18
}
property QtObject documentsList: QtObject {
property string background: "#f7f7f7"
property string color: "#4d4d4d"
property string sectionColor: "#808080"
property string selectedColor: "white"
property string highlightColor: "#4a90e2"
property int height: 35
property int fileNameHeight: 30
property int fontSize: absoluteSize(2)// 13
property int sectionFontSize: absoluteSize(2)// 13
}
}

225
mix/qml/ProjectList.qml

@ -1,225 +0,0 @@
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.3
import Qt.labs.settings 1.0
import "."
Item {
property bool renameMode: false;
property alias sections: sectionRepeater
ProjectFilesStyle {
id: projectFilesStyle
}
ColumnLayout {
anchors.fill: parent
id: filesCol
spacing: 0
SourceSansProLight
{
id: srcSansProLight
}
Rectangle
{
color: projectFilesStyle.title.background
height: projectFilesStyle.title.height
Layout.fillWidth: true
Image {
id: projectIcon
source: "qrc:/qml/img/dappProjectIcon.png"
anchors.right: projectTitle.left
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 6
fillMode: Image.PreserveAspectFit
width: 32
height: 32
}
Text
{
id: projectTitle
color: projectFilesStyle.title.color
text: projectModel.projectTitle
anchors.verticalCenter: parent.verticalCenter
visible: !projectModel.isEmpty;
anchors.left: parent.left
anchors.leftMargin: projectFilesStyle.general.leftMargin
font.family: srcSansProLight.name
font.pointSize: projectFilesStyle.title.fontSize
font.weight: Font.Light
}
Text
{
text: "-"
anchors.right: parent.right
anchors.rightMargin: 15
font.family: srcSansProLight.name
font.pointSize: projectFilesStyle.title.fontSize
anchors.verticalCenter: parent.verticalCenter
font.weight: Font.Light
}
}
Rectangle
{
Layout.fillWidth: true
height: 3
color: projectFilesStyle.documentsList.background
}
Rectangle
{
Layout.fillWidth: true
Layout.fillHeight: true
color: projectFilesStyle.documentsList.background
ColumnLayout
{
anchors.top: parent.top
width: parent.width
spacing: 0
Repeater {
model: [qsTr("Contracts"), qsTr("Javascript"), qsTr("Web Pages"), qsTr("Styles"), qsTr("Images"), qsTr("Misc")];
signal selected(string doc, string groupName)
signal isCleanChanged(string doc, string groupName, var isClean)
property int incr: -1;
id: sectionRepeater
FilesSection
{
id: section;
sectionName: modelData
index:
{
for (var k in sectionRepeater.model)
{
if (sectionRepeater.model[k] === modelData)
return k;
}
}
model: sectionModel
selManager: sectionRepeater
onDocumentSelected: {
selManager.selected(doc, groupName);
}
ListModel
{
id: sectionModel
}
Connections {
target: codeModel
onContractRenamed: {
if (modelData === "Contracts")
{
var ci = 0;
for (var si = 0; si < projectModel.listModel.count; si++) {
var document = projectModel.listModel.get(si);
if (document.isContract) {
var compiledDoc = codeModel.contractByDocumentId(document.documentId);
if (_documentId === document.documentId && _newName !== document.name) {
document.name = _newName;
projectModel.listModel.set(si, document);
sectionModel.set(ci, document);
}
ci++;
}
}
}
}
onCompilationComplete: {
if (modelData === "Contracts") {
var ci = 0;
for (var si = 0; si < projectModel.listModel.count; si++) {
var document = projectModel.listModel.get(si);
if (document.isContract) {
var compiledDoc = codeModel.contractByDocumentId(document.documentId);
if (compiledDoc && compiledDoc.documentId === document.documentId && compiledDoc.contract.name !== document.name) {
document.name = compiledDoc.contract.name;
projectModel.listModel.set(si, document);
sectionModel.set(ci, document);
}
ci++;
}
}
}
}
}
Connections {
id: projectModelConnection
target: projectModel
function addDocToSubModel()
{
for (var k = 0; k < projectModel.listModel.count; k++)
{
var item = projectModel.listModel.get(k);
if (item.groupName === modelData)
sectionModel.append(item);
}
}
onIsCleanChanged: {
for (var si = 0; si < sectionModel.count; si++) {
var document = sectionModel.get(si);
if (documentId === document.documentId && document.groupName === modelData)
{
selManager.isCleanChanged(documentId, modelData, isClean);
break;
}
}
}
onDocumentOpened: {
if (document.groupName === modelData)
sectionRepeater.selected(document.documentId, modelData);
}
onNewProject: {
sectionModel.clear();
}
onProjectClosed: {
sectionModel.clear();
}
onProjectLoaded: {
sectionModel.clear();
addDocToSubModel();
if (modelData === "Contracts")
{
var selItem = projectModel.listModel.get(0);
projectModel.openDocument(selItem.documentId);
sectionRepeater.selected(selItem.documentId, modelData);
}
}
onDocumentAdded:
{
var newDoc = projectModel.getDocument(documentId);
if (newDoc.groupName === modelData)
{
sectionModel.append(newDoc);
projectModel.openDocument(newDoc.documentId);
sectionRepeater.selected(newDoc.documentId, modelData);
}
}
}
}
}
}
}
}
}

230
mix/qml/ProjectModel.qml

@ -1,230 +0,0 @@
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
import "js/ProjectModel.js" as ProjectModelCode
import "js/NetworkDeployment.js" as NetworkDeploymentCode
Item {
id: projectModel
signal projectClosed
signal projectLoading(var projectData)
signal projectLoaded()
signal documentSaving(var document)
signal documentChanged(var documentId)
signal documentOpened(var document)
signal documentRemoved(var documentId)
signal documentUpdated(var documentId) //renamed
signal documentAdded(var documentId)
signal projectSaving()
signal projectFileSaving(var projectData)
signal projectSaved()
signal projectFileSaved()
signal newProject(var projectData)
signal documentSaved(var documentId)
signal contractSaved(var documentId)
signal deploymentStarted()
signal deploymentStepChanged(string message)
signal deploymentComplete()
signal deploymentError(string error)
signal isCleanChanged(var isClean, string documentId)
property bool isEmpty: (projectPath === "")
readonly property string projectFileName: ".mix"
property bool appIsClosing: false
property bool projectIsClosing: false
property string projectPath: ""
property string projectTitle: ""
property string currentDocumentId: ""
property var deploymentAddresses: ({})
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
property int deployedScenarioIndex
property string applicationUrlEth
property string applicationUrlHttp
property string deployBlockNumber
property var deploymentTrHashes
property string registerContentHashTrHash
property string registerUrlTrHash
property int registerContentHashBlockNumber: -1
property int registerUrlBlockNumber: -1
//interface
function saveAll() { ProjectModelCode.saveAll(); }
function saveCurrentDocument() { ProjectModelCode.saveCurrentDocument(); }
function createProject() { ProjectModelCode.createProject(); }
function closeProject(callBack) { ProjectModelCode.closeProject(callBack); }
function saveProject() { ProjectModelCode.saveProject(); }
function saveProjectFile() { ProjectModelCode.saveProjectFile(); }
function loadProject(path) { ProjectModelCode.loadProject(path); }
function newHtmlFile() { ProjectModelCode.newHtmlFile(); }
function newJsFile() { ProjectModelCode.newJsFile(); }
function newCssFile() { ProjectModelCode.newCssFile(); }
function newContract() { ProjectModelCode.newContract(); }
function openDocument(documentId) { ProjectModelCode.openDocument(documentId); }
function openNextDocument() { ProjectModelCode.openNextDocument(); }
function openPrevDocument() { ProjectModelCode.openPrevDocument(); }
function renameDocument(documentId, newName) { ProjectModelCode.renameDocument(documentId, newName); }
function removeDocument(documentId) { ProjectModelCode.removeDocument(documentId); }
function getDocument(documentId) { return ProjectModelCode.getDocument(documentId); }
function getDocumentIdByName(documentName) { return ProjectModelCode.getDocumentIdByName(documentName); }
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); }
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); }
function deployProject() { NetworkDeploymentCode.deployProject(false); }
function registerToUrlHint(url, gasPrice, callback) { NetworkDeploymentCode.registerToUrlHint(url, gasPrice, callback); }
function formatAppUrl() { NetworkDeploymentCode.formatAppUrl(url); }
function cleanDeploymentStatus()
{
deployedScenarioIndex = 0
deployBlockNumber = ""
deploymentTrHashes = {}
deploymentAddresses = {}
deploymentDir = ""
deploymentDialog.packageStep.packageHash = ""
deploymentDialog.packageStep.packageBase64 = ""
deploymentDialog.packageStep.packageDir = ""
deploymentDialog.packageStep.lastDeployDate = ""
deploymentDialog.packageStep.localPackageUrl = ""
saveProject()
cleanRegisteringStatus()
}
function cleanRegisteringStatus()
{
applicationUrlEth = ""
applicationUrlHttp = ""
registerContentHashTrHash = ""
registerUrlTrHash = ""
registerContentHashBlockNumber = -1
registerUrlBlockNumber = -1
saveProject()
}
Connections {
target: mainApplication
onLoaded: {
if (mainApplication.trackLastProject && projectSettings.lastProjectPath && projectSettings.lastProjectPath !== "")
projectModel.loadProject(projectSettings.lastProjectPath)
}
}
Connections {
target: codeEditor
onIsCleanChanged: {
for (var i in unsavedFiles)
{
if (unsavedFiles[i] === documentId && isClean)
unsavedFiles.splice(i, 1);
}
if (!isClean)
unsavedFiles.push(documentId);
isCleanChanged(isClean, documentId);
}
}
NewProjectDialog {
id: newProjectDialog
visible: false
onAccepted: {
var title = newProjectDialog.projectTitle;
var path = newProjectDialog.projectPath;
ProjectModelCode.doCreateProject(title, path);
}
}
Connections
{
target: fileIo
property bool saving: false
onFileChanged:
{
fileIo.watchFileChanged(_filePath);
var documentId = ProjectModelCode.getDocumentByPath(_filePath);
documentChanged(documentId);
}
}
MessageDialog {
id: saveMessageDialog
title: qsTr("Project")
text: qsTr("Some files require to be saved. Do you want to save changes?");
standardButtons: StandardButton.Yes | StandardButton.No | StandardButton.Cancel
icon: StandardIcon.Question
property var callBack;
onYes: {
projectIsClosing = true;
projectModel.saveAll();
unsavedFiles = [];
ProjectModelCode.doCloseProject();
if (callBack)
callBack();
}
onRejected: {}
onNo: {
projectIsClosing = true;
unsavedFiles = [];
ProjectModelCode.doCloseProject();
if (callBack)
callBack();
}
}
MessageDialog {
id: deployWarningDialog
title: qsTr("Project")
text:
{
if (Object.keys(projectModel.deploymentAddresses).length > 0)
return qsTr("This project has been already deployed to the network. Do you want to redeploy it? (Contract state will be reset)")
else
return qsTr("This action will deploy to the network. Do you want to deploy it?")
}
icon: StandardIcon.Question
standardButtons: StandardButton.Ok | StandardButton.Abort
onAccepted: {
NetworkDeploymentCode.startDeployProject(true);
}
}
MessageDialog {
id: deployResourcesDialog
title: qsTr("Project")
standardButtons: StandardButton.Ok
}
DeploymentDialog
{
id: deploymentDialog
}
ListModel {
id: projectListModel
}
StateListModel {
id: projectStateListModel
}
Connections
{
target: projectModel
onProjectClosed: {
projectPath = "";
}
}
Settings {
id: projectSettings
property string lastProjectPath;
}
}

226
mix/qml/QAddressView.qml

@ -1,226 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
import QtQuick.Layouts 1.1
import "js/InputValidator.js" as InputValidator
ColumnLayout
{
property alias value: textinput.text
property alias accountRef: ctrModel
property string subType
property bool readOnly
property alias currentIndex: trCombobox.currentIndex
property alias displayInput: textInputRect.visible
property variant accounts
signal indexChanged()
spacing: 0
id: editRoot
height:
{
if (isArray() && !readOnly)
return 60
else
return 30
}
width: 320
SourceSansProBold
{
id: boldFont
}
function isArray()
{
InputValidator.init()
return InputValidator.isArray(subType)
}
function currentValue() {
return value;
}
function currentType()
{
return accountRef.get(trCombobox.currentIndex).type;
}
function current()
{
return accountRef.get(trCombobox.currentIndex);
}
function load()
{
accountRef.clear();
if (subType.indexOf("contract") !== -1 || subType.indexOf("address") !== -1)
{
var trCr = 0;
if (blockChainPanel)
for (var k = 0; k < blockChainPanel.model.blocks.length; k++)
{
if (k > blockIndex)
break;
for (var i = 0; i < blockChainPanel.model.blocks[k].transactions.length; i++)
{
if (i > transactionIndex)
break;
var tr = blockChainPanel.model.blocks[k].transactions[i]
if (tr.functionId === tr.contractId)
{
accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" });
trCr++;
}
}
}
}
if (subType.indexOf("address") !== -1)
{
for (k = 0; k < accounts.length; k++)
{
if (accounts[k].address === undefined)
accounts[k].address = clientModel.address(accounts[k].secret);
accountRef.append({ "itemid": accounts[k].name, "value": "0x" + accounts[k].address, "type": "address" });
}
}
}
function init()
{
btnAdd.visible = isArray()
textinput.readOnly = readOnly
if (isArray() || readOnly)
displayInput = true
else
displayInput = false
if (isArray() || !readOnly)
trCombobox.visible = true
else
trCombobox.visible = false
if (!trCombobox.visible)
{
rowCombobox.visible = false
rowCombobox.height = 0
trCombobox.height = 0
textinput.anchors.top = textinput.parent.top
}
if (!readOnly)
{
trCombobox.currentIndex = 0
for (var k = 0; k < ctrModel.count; k++)
{
if (ctrModel.get(k).value === value)
{
trCombobox.currentIndex = k
break
}
}
}
if (!isArray())
trCombobox.update()
else if (value === "")
textinput.text = "[]"
}
function select(address)
{
for (var k = 0; k < accountRef.count; k++)
{
if (accountRef.get(k).value === address)
{
trCombobox.currentIndex = k;
break;
}
}
}
ListModel
{
id: ctrModel
}
Row
{
anchors.top: parent.top
height: 30
id: rowCombobox
ComboBox
{
property bool selected: false
id: trCombobox
model: ctrModel
width: 265
textRole: "itemid"
function update()
{
trCombobox.selected = false;
if (currentText === "")
return;
else if (currentText !== " - ")
{
if (model.get(currentIndex).type === "contract")
textinput.text = "<" + currentText + ">";
else
textinput.text = model.get(currentIndex).value; //address
trCombobox.selected = true;
}
else if (textinput.text.indexOf("<") === 0)
{
textinput.text = "";
}
indexChanged();
}
onCurrentIndexChanged: {
if (!isArray())
update()
}
}
Button
{
id: btnAdd
text: qsTr("Add")
visible: false
onClicked:
{
var ar = JSON.parse(textinput.text)
ar.push(trCombobox.model.get(currentIndex).value)
textinput.text = JSON.stringify(ar)
}
}
}
Rectangle {
radius: 4
width: 350
height: 30
id: textInputRect
TextField {
id: textinput
text: value
anchors.fill: parent
clip: true
font.family: boldFont.name
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
onTextChanged:
{
if (trCombobox.selected)
{
trCombobox.currentIndex = 0;
trCombobox.selected = false;
}
}
}
}
}

109
mix/qml/QBoolTypeView.qml

@ -1,109 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 1.3
import "js/InputValidator.js" as InputValidator
Item
{
id: editRoot
property string value
property string defaultValue
property bool readOnly: !boolCombo.enabled
property string subType
height: 20
width: 150
onReadOnlyChanged: {
boolCombo.enabled = !readOnly;
}
function isArray()
{
InputValidator.init()
return InputValidator.isArray(subType)
}
function init()
{
if (!isArray())
{
boolArray.visible = false
boolCombo.visible = true
value = format(value)
var setValue = "1"
if (value === "")
setValue = parseInt(defaultValue);
else
setValue = parseInt(value);
boolCombo.checked = setValue === "1" ? true: false
boolCombo.enabled = !readOnly;
}
else
{
boolArray.visible = true
boolCombo.visible = false
if (value === "")
boolArray.text = "[]"
else
boolArray.text = value
var formattedparam = []
var param = JSON.parse(boolArray.text)
for (var k in JSON.parse(boolArray.text))
formattedparam.push(parseInt(format(param[k])))
boolArray.text = JSON.stringify(formattedparam)
}
}
function finalize()
{
if (isArray())
value = boolArray.text
}
function format(value)
{
value = value === true ? "1" : value
value = value === false ? "0" : value;
value = value === "true" ? "1" : value
value = value === "false" ? "0" : value;
return value
}
Rectangle {
color: "transparent"
anchors.fill: parent
CheckBox
{
property bool inited;
Component.onCompleted:
{
init();
inited = true;
}
id: boolCombo
anchors.fill: parent
onCheckedChanged:
{
if (inited)
value = checked ? "1" : "0"
}
text: qsTr("True")
}
TextField
{
id: boolArray
onTextChanged:
{
value = text
}
}
}
}

5
mix/qml/QFileDialog.qml

@ -1,5 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
FileDialog { }

27
mix/qml/QHashTypeView.qml

@ -1,27 +0,0 @@
import QtQuick 2.0
Item
{
property alias value: textinput.text
property alias readOnly: textinput.readOnly
id: editRoot
height: 20
width: 150
SourceSansProBold
{
id: boldFont
}
TextInput {
id: textinput
text: value
wrapMode: Text.WrapAnywhere
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
}
}

26
mix/qml/QIntTypeView.qml

@ -1,26 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 1.1
Item
{
property alias value: textinput.text
property alias readOnly: textinput.readOnly
id: editRoot
width: 350
DebuggerPaneStyle {
id: dbgStyle
}
TextField {
anchors.verticalCenter: parent.verticalCenter
id: textinput
selectByMouse: true
text: value
implicitWidth: 350
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
}
}

15
mix/qml/QRealTypeView.qml

@ -1,15 +0,0 @@
import QtQuick 2.0
Component
{
Rectangle {
anchors.fill: parent
Text{
anchors.fill: parent
text: qsTr("Real")
}
}
}

31
mix/qml/QStringTypeView.qml

@ -1,31 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 1.1
Item
{
property alias value: textinput.text
property alias readOnly: textinput.readOnly
id: editRoot
width: 350
DebuggerPaneStyle {
id: dbgStyle
}
TextField {
anchors.verticalCenter: parent.verticalCenter
id: textinput
selectByMouse: true
text: value
width: 350
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
}
}

7
mix/qml/QVariableDeclaration.qml

@ -1,7 +0,0 @@
import QtQuick 2.0
import org.ethereum.qml.QVariableDeclaration 1.0
QVariableDeclaration
{
}

13
mix/qml/QVariableDefinition.qml

@ -1,13 +0,0 @@
/*
* Used to instanciate a QVariableDefinition obj using Qt.createComponent function.
*/
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
import org.ethereum.qml.QVariableDefinition 1.0
QVariableDefinition
{
id: qVariableDefinition
}

352
mix/qml/RegisteringStep.qml

@ -1,352 +0,0 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import Qt.labs.settings 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
import "."
Rectangle {
property variant worker
property string eth: registrarAddr.text
property int ownedRegistrarDeployGas: 1179075 // TODO: Use sol library to calculate gas requirement for each tr.
property int ownedRegistrarSetSubRegistrarGas: 50000
property int ownedRegistrarSetContentHashGas: 50000
property int urlHintSuggestUrlGas: 70000
id: root
color: "#E3E3E3E3"
anchors.fill: parent
signal registered
function show()
{
ctrRegisterLabel.calculateRegisterGas()
if (applicationUrlHttpCtrl.text === "")
applicationUrlHttpCtrl.text = projectModel.applicationUrlHttp
if (applicationUrlEthCtrl.text === "")
applicationUrlEthCtrl.text = projectModel.applicationUrlEth
visible = true
worker.pooler.onTriggered.connect(function() {
if (root.visible)
verifyRegistering();
})
}
function verifyRegistering()
{
verificationEthUrl.text = ""
if (projectModel.registerContentHashTrHash !== "" && projectModel.registerContentHashBlockNumber !== -1)
{
worker.verifyHash("registerHash", projectModel.registerContentHashTrHash, function(bn, trLost)
{
updateVerification(projectModel.registerContentHashBlockNumber, bn, trLost, verificationEthUrl, "registerHash")
});
}
else if (projectModel.registerContentHashTrHash !== "" && projectModel.registerContentHashBlockNumber === -1)
verificationEthUrl.text = qsTr("waiting verifications")
verificationUrl.text = ""
if (projectModel.registerUrlTrHash !== "" && projectModel.registerUrlBlockNumber !== -1)
{
worker.verifyHash("registerUrl", projectModel.registerUrlTrHash, function(bn, trLost)
{
updateVerification(projectModel.registerUrlBlockNumber, bn, trLost, verificationUrl, "registerUrl")
});
}
else if (projectModel.registerUrlTrHash !== "" && projectModel.registerUrlBlockNumber === -1)
verificationUrl.text = qsTr("waiting verifications")
}
function updateVerification(originbn, bn, trLost, ctrl, trContext)
{
if (trLost.length === 0)
{
ctrl.text = bn - originbn
if (parseInt(bn - originbn) >= 10)
{
ctrl.color= "green"
ctrl.text= qsTr("verified")
}
else
ctrl.text += qsTr(" verifications")
}
else
{
deploymentStepChanged(trContext + qsTr(" has been invalidated.") + trLost[0] + " " + qsTr("no longer present") )
ctrl.text = qsTr("invalidated")
}
}
ColumnLayout
{
anchors.top: parent.top
width: parent.width
anchors.topMargin: 10
id: col
spacing: 20
anchors.left: parent.left
anchors.leftMargin: 10
Label
{
anchors.top: parent.top
Layout.fillWidth: true
text: qsTr("Register your Dapp on the Name registrar Contract")
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 5
Label
{
text: qsTr("Root Registrar address")
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
}
DefaultTextField
{
id: registrarAddr
text: "c6d9d2cd449a754c494264e1809c50e34d64562b"
visible: true
Layout.preferredWidth: 450
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 5
Label
{
text: qsTr("Http URL")
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
}
DefaultTextField
{
id: applicationUrlHttpCtrl
Layout.preferredWidth: 450
}
Label
{
id: verificationUrl
anchors.verticalCenter: parent.verticalCenter
font.italic: true
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 5
Label
{
text: qsTr("Registration Cost")
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
id: ctrRegisterLabel
function calculateRegisterGas()
{
if (!modalDeploymentDialog.visible)
return;
appUrlFormatted.text = NetworkDeploymentCode.formatAppUrl(applicationUrlEthCtrl.text).join('/');
NetworkDeploymentCode.checkPathCreationCost(applicationUrlEthCtrl.text, function(pathCreationCost)
{
var ether = QEtherHelper.createBigInt(pathCreationCost);
if (deploymentDialog.deployStep.gasPrice.value)
{
var gasTotal = ether.multiply(deploymentDialog.deployStep.gasPrice.value.toWei());
gasToUseDeployInput.value = QEtherHelper.createEther(gasTotal.value(), QEther.Wei, parent);
gasToUseDeployInput.update();
}
});
}
}
}
Ether
{
id: gasToUseDeployInput
displayUnitSelection: false
displayFormattedValue: true
edit: false
Layout.preferredWidth: 235
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 5
Label
{
text: qsTr("Ethereum URL")
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle
{
height: 25
color: "transparent"
Layout.preferredWidth: 450
DefaultTextField
{
width: 450
id: applicationUrlEthCtrl
onTextChanged: {
ctrRegisterLabel.calculateRegisterGas();
}
}
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 4
Label
{
text: qsTr("Formatted Ethereum URL")
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
}
DefaultLabel
{
id: appUrlFormatted
anchors.verticalCenter: parent.verticalCenter;
anchors.topMargin: 10
font.italic: true
font.pointSize: appStyle.absoluteSize(-1)
}
Label
{
id: verificationEthUrl
anchors.verticalCenter: parent.verticalCenter;
anchors.topMargin: 10
font.italic: true
font.pointSize: appStyle.absoluteSize(-1)
}
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
width: parent.width
function registerHash(gasPrice, callback)
{
var inError = [];
var ethUrl = NetworkDeploymentCode.formatAppUrl(applicationUrlEthCtrl.text);
for (var k in ethUrl)
{
if (ethUrl[k].length > 32)
inError.push(qsTr("Member too long: " + ethUrl[k]) + "\n");
}
if (!worker.stopForInputError(inError))
{
NetworkDeploymentCode.registerDapp(ethUrl, gasPrice, function(){
projectModel.applicationUrlEth = applicationUrlEthCtrl.text
projectModel.saveProject()
verificationEthUrl.text = qsTr("waiting verifications")
worker.waitForTrReceipt(projectModel.registerContentHashTrHash, function(status, receipt)
{
worker.verifyHash("registerHash", projectModel.registerContentHashTrHash, function(bn, trLost)
{
projectModel.registerContentHashBlockNumber = bn
projectModel.saveProject()
root.updateVerification(bn, bn, trLost, verificationEthUrl)
callback()
});
});
})
}
}
function registerUrl(gasPrice, callback)
{
if (applicationUrlHttp.text === "" || deploymentDialog.packageHash === "")
{
deployDialog.title = text;
deployDialog.text = qsTr("Please provide the link where the resources are stored and ensure the package is aleary built using the deployment step.")
deployDialog.open();
return;
}
var inError = [];
if (applicationUrlHttpCtrl.text.length > 32)
inError.push(qsTr(applicationUrlHttpCtrl.text));
if (!worker.stopForInputError(inError))
{
var url = applicationUrlHttpCtrl.text
url = url.replace("http://", "").replace("https://", "")
registerToUrlHint(url, gasPrice, function(){
projectModel.applicationUrlHttp = applicationUrlHttpCtrl.text
projectModel.saveProject()
verificationUrl.text = qsTr("waiting verifications")
worker.waitForTrReceipt(projectModel.registerUrlTrHash, function(status, receipt)
{
worker.verifyHash("registerUrl", projectModel.registerUrlTrHash, function(bn, trLost)
{
projectModel.registerUrlBlockNumber = bn
projectModel.saveProject()
root.updateVerification(bn, bn, trLost, verificationUrl)
root.registered()
callback()
});
})
})
}
}
Button
{
anchors.right: parent.right
anchors.rightMargin: 10
text: qsTr("Register Dapp")
width: 30
onClicked:
{
verificationEthUrl.text = ""
verificationUrl.text = ""
projectModel.cleanRegisteringStatus()
var gasPrice = deploymentDialog.deployStep.gasPrice.toHexWei()
parent.registerHash(gasPrice, function(){
parent.registerUrl(gasPrice, function(){})
})
}
}
}
}

130
mix/qml/ScenarioButton.qml

@ -1,130 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
import QtGraphicalEffects 1.0
Rectangle {
id: buttonActionContainer
property string text
property string buttonShortcut
property string sourceImg
property string fillColor
property alias roundLeft: left.visible
property alias roundRight: right.visible
signal clicked
function startBlinking()
{
if (!blinkTimer.running)
blinkTimer.start()
}
function stopBlinking()
{
blinkTimer.stop()
}
Rectangle
{
id: left
width: 10
height: parent.height
anchors.left: contentRectangle.left
anchors.leftMargin: -4
radius: 15
}
Rectangle {
id: contentRectangle
anchors.fill: parent
color: "white"
property variant colorGradient: ["#FFFFFF", "#FFFEFC", "#FFFDF9", "#FFFCF7", "#FFFBF4", "#FFFAF2", "#FFF9EF", "#FFF8EC", "#FFF7EA", "#FFF6E7", "#FFF5E5", "#FFF5E2", "#FFF4E0", "#FFF3DD", "#FFF2DA", "#FFF1D8", "#FFF0D5", "#FFEFD3", "#FFEED0", "#FFEDCE", "#FFECCB", "#FFEBC8", "#FFEBC6", "#FFEAC3", "#FFE9C1", "#FFE8BE", "#FFE7BC", "#FFE6B9", "#FFE5B6", "#FFE4B4", "#FFE3B1", "#FFE2AF", "#FFE1AC", "#FFE1AA", "#FFE0A7", "#FFDFA4", "#FFDEA2", "#FFDD9F", "#FFDC9D", "#FFDB9A", "#FFDA97", "#FFD995", "#FFD892", "#FFD790", "#FFD78D", "#FFD68B", "#FFD588", "#FFD485", "#FFD383", "#FFD280", "#FFD17E", "#FFD07B", "#FFCF79", "#FFCE76", "#FFCD73", "#FFCD71", "#FFCC6E", "#FFCB6C", "#FFCA69", "#FFC967", "#FFC864", "#FFC761", "#FFC65F", "#FFC55C", "#FFC45A", "#FFC357", "#FFC355", "#FFC252", "#FFC14F", "#FFC04D", "#FFBF4A", "#FFBE48", "#FFBD45", "#FFBC42", "#FFBB40", "#FFBA3D", "#FFB93B", "#FFB938", "#FFB836", "#FFB733", "#FFB630", "#FFB52E", "#FFB42B", "#FFB329", "#FFB226", "#FFB124", "#FFB021", "#FFAF1E", "#FFAF1C", "#FFAE19", "#FFAD17", "#FFAC14", "#FFAB12", "#FFAA0F", "#FFA90C", "#FFA80A", "#FFA707", "#FFA605", "#FFA502", "#FFA500"]
Timer
{
id: blinkTimer
repeat: true
interval: 40
running: false
property int index: 0
property int direction: 1
onTriggered: {
index = index + direction
var color = parent.colorGradient[index]
left.color = color
right.color = color
parent.color = parent.colorGradient[index]
if (index >= parent.colorGradient.length - 1)
direction = -1
else if (index <= 0)
direction = 1
}
onRunningChanged: {
if (!running)
{
left.color = "white"
right.color = "white"
parent.color = "white"
index = 0
direction = 1
}
}
}
Image {
id: debugImage
anchors {
left: parent.left
right: parent.right
top: parent.top
bottom: parent.bottom
bottomMargin: debugImg.pressed ? -2 : 0;
topMargin: debugImg.pressed ? 2 : 0;
}
source: sourceImg
fillMode: Image.PreserveAspectFit
}
Button {
anchors.fill: parent
id: debugImg
action: buttonAction
style: ButtonStyle {
background: Rectangle {
color: "transparent"
}
}
}
Action {
id: buttonAction
shortcut: buttonShortcut
onTriggered: {
buttonActionContainer.clicked();
}
}
}
Rectangle
{
id: right
width: 10
height: parent.height
anchors.right: contentRectangle.right
anchors.rightMargin: -4
radius: 15
}
Rectangle
{
anchors.top: contentRectangle.bottom
anchors.topMargin: 15
width: parent.width
Label
{
text: buttonActionContainer.text
anchors.centerIn: parent
}
}
}

128
mix/qml/ScenarioExecution.qml

@ -1,128 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "."
Rectangle {
color: "#ededed"
property alias bc: blockChain
Connections
{
target: projectModel
onProjectLoaded: {
loader.init()
}
}
ScrollView
{
anchors.fill: parent
onWidthChanged: {
columnExe.width = width - 40
}
ColumnLayout
{
id: columnExe
Layout.preferredWidth: parent.width
anchors.left: parent.left
anchors.leftMargin: 15
ColumnLayout
{
id: scenarioColumn
width: parent.width
spacing: 10
ScenarioLoader
{
anchors.horizontalCenter: parent.horizontalCenter
height: 100
Layout.preferredWidth: 400
width: 400
id: loader
}
Connections
{
target: blockChain
onChainChanged:
{
loader.needSaveOrReload()
}
}
Rectangle
{
Layout.preferredWidth: parent.width
height: 1
color: "#cccccc"
}
Connections
{
target: loader
onLoaded:
{
watchers.clear()
blockChain.load(scenario, loader.selectedScenarioIndex)
}
}
BlockChain
{
id: blockChain
width: parent.width
}
Connections
{
target: blockChain
property var currentSelectedBlock
property var currentSelectedTx
onTxSelected:
{
currentSelectedBlock = blockIndex
currentSelectedTx = txIndex
updateWatchers(blockIndex, txIndex)
}
function updateWatchers(blockIndex, txIndex)
{
var tx = blockChain.model.blocks[blockIndex].transactions[txIndex]
var state = blockChain.getState(tx.recordIndex)
watchers.updateWidthTx(tx, state, blockIndex, txIndex)
}
onRebuilding: {
watchers.clear()
}
onAccountAdded: {
watchers.addAccount(address, "0 wei")
}
}
}
Watchers
{
id: watchers
bc: blockChain
Layout.fillWidth: true
Layout.preferredHeight: 740
}
Rectangle
{
color: "transparent"
Layout.preferredHeight: 50
Layout.fillWidth: true
}
}
}
}

386
mix/qml/ScenarioLoader.qml

@ -1,386 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.InverseMouseArea 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "."
ColumnLayout
{
id: blockChainSelector
signal restored(variant scenario)
signal saved(variant scenario)
signal duplicated(variant scenario)
signal loaded(variant scenario)
signal renamed(variant scenario)
signal deleted()
property alias selectedScenarioIndex: scenarioList.currentIndex
spacing: 0
function init()
{
scenarioList.load()
}
function needSaveOrReload()
{
}
RowLayout
{
Layout.preferredWidth: 560
anchors.horizontalCenter: parent.horizontalCenter
Layout.preferredHeight: 75
spacing: 0
anchors.top: parent.top
anchors.topMargin: 10
Row
{
Layout.preferredWidth: 100 * 5 + 30
Layout.preferredHeight: 50
spacing: 25
Rectangle
{
color: "white"
width: 251 + 30
height: 30
Rectangle
{
id: left
width: 10
height: parent.height
anchors.left: parent.left
anchors.leftMargin: -5
radius: 15
}
Image {
source: "qrc:/qml/img/edittransaction.png"
height: parent.height - 10
fillMode: Image.PreserveAspectFit
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 4
id: editImg
MouseArea
{
anchors.fill: parent
onClicked:
{
scenarioNameEdit.toggleEdit()
}
}
}
Image {
source: "qrc:/qml/img/delete_sign.png"
height: parent.height - 16
fillMode: Image.PreserveAspectFit
id: deleteImg
anchors.left: editImg.right
anchors.top: parent.top
anchors.topMargin: 8
visible: projectModel.stateListModel.count > 1
MouseArea
{
anchors.fill: parent
onClicked:
{
if (projectModel.stateListModel.count > 1)
{
projectModel.stateListModel.deleteState(scenarioList.currentIndex)
scenarioList.init()
}
}
}
}
Label
{
MouseArea
{
anchors.fill: parent
onClicked:
{
if (projectModel.stateListModel.count > 1)
{
projectModel.stateListModel.deleteState(scenarioList.currentIndex)
scenarioList.init()
}
}
}
}
Connections
{
target: projectModel.stateListModel
onStateDeleted: {
scenarioList.init()
}
}
ComboBox
{
id: scenarioList
anchors.left: deleteImg.right
anchors.leftMargin: 2
model: projectModel.stateListModel
anchors.top: parent.top
textRole: "title"
height: parent.height
width: 150
signal updateView()
onCurrentIndexChanged:
{
restoreScenario.restore()
}
function init()
{
scenarioList.currentIndex = 0
deleted()
}
function load()
{
var state = projectModel.stateListModel.getState(currentIndex)
if (state)
loaded(state)
}
style: ComboBoxStyle {
id: style
background: Rectangle {
color: "white"
anchors.fill: parent
}
label: Rectangle {
property alias label: comboLabel
anchors.fill: parent
color: "white"
Label {
id: comboLabel
maximumLineCount: 1
elide: Text.ElideRight
width: parent.width
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
Component.onCompleted:
{
comboLabel.updateLabel()
}
function updateLabel()
{
comboLabel.text = ""
if (scenarioList.currentIndex > - 1 && scenarioList.currentIndex < projectModel.stateListModel.count)
comboLabel.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title
}
Connections {
target: blockChainSelector
onLoaded: {
if (projectModel.stateListModel.count > 0)
comboLabel.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title
else
return ""
}
onRenamed: {
comboLabel.text = scenario.title
scenarioNameEdit.text = scenario.title
}
onDeleted: {
comboLabel.updateLabel()
}
}
}
}
}
}
TextField
{
id: scenarioNameEdit
anchors.left: deleteImg.right
anchors.leftMargin: 2
height: parent.height
z: 5
visible: false
width: 150
Keys.onEnterPressed:
{
toggleEdit()
}
Keys.onReturnPressed:
{
toggleEdit()
}
function toggleEdit()
{
scenarioList.visible = !scenarioList.visible
scenarioNameEdit.visible = !scenarioNameEdit.visible
if (!scenarioNameEdit.visible)
scenarioNameEdit.save()
else
{
scenarioNameEdit.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title
scenarioNameEdit.forceActiveFocus()
outsideClick.active = true
}
}
function save()
{
outsideClick.active = false
projectModel.stateListModel.getState(scenarioList.currentIndex).title = scenarioNameEdit.text
projectModel.saveProjectFile()
saved(state)
scenarioList.model.get(scenarioList.currentIndex).title = scenarioNameEdit.text
scenarioList.currentIndex = scenarioList.currentIndex
renamed(projectModel.stateListModel.getState(scenarioList.currentIndex))
}
style: TextFieldStyle {
background: Rectangle {
radius: 2
implicitWidth: 100
implicitHeight: 30
color: "white"
border.color: "#cccccc"
border.width: 1
}
}
InverseMouseArea {
id: outsideClick
anchors.fill: parent
active: false
onClickedOutside: {
scenarioNameEdit.toggleEdit()
}
}
}
Rectangle
{
width: 1
height: parent.height
anchors.right: addScenario.left
color: "#ededed"
}
ScenarioButton {
id: addScenario
anchors.left: scenarioList.right
width: 100
height: parent.height
buttonShortcut: ""
sourceImg: "qrc:/qml/img/restoreicon@2x.png"
onClicked: {
var item = projectModel.stateListModel.createDefaultState();
item.title = qsTr("New Scenario")
projectModel.stateListModel.appendState(item)
projectModel.stateListModel.save()
scenarioList.currentIndex = projectModel.stateListModel.count - 1
clientModel.setupScenario(item);
}
text: qsTr("New...")
roundRight: true
roundLeft: false
}
}
Rectangle
{
width: 100 * 3
height: 30
color: "transparent"
ScenarioButton {
id: restoreScenario
width: 100
height: parent.height
buttonShortcut: ""
sourceImg: "qrc:/qml/img/restoreicon@2x.png"
onClicked: {
restore()
}
text: qsTr("Restore")
function restore()
{
var state = projectModel.stateListModel.reloadStateFromProject(scenarioList.currentIndex)
if (state)
{
restored(state)
loaded(state)
}
}
roundRight: false
roundLeft: true
}
Rectangle
{
width: 1
height: parent.height
anchors.right: saveScenario.left
color: "#ededed"
}
ScenarioButton {
id: saveScenario
anchors.left: restoreScenario.right
text: qsTr("Save")
onClicked: {
projectModel.saveProjectFile()
saved(state)
}
width: 100
height: parent.height
buttonShortcut: ""
sourceImg: "qrc:/qml/img/saveicon@2x.png"
roundRight: false
roundLeft: false
}
Rectangle
{
width: 1
height: parent.height
anchors.right: duplicateScenario.left
color: "#ededed"
}
ScenarioButton
{
id: duplicateScenario
anchors.left: saveScenario.right
text: qsTr("Duplicate")
onClicked: {
projectModel.stateListModel.duplicateState(scenarioList.currentIndex)
duplicated(state)
}
width: 100
height: parent.height
buttonShortcut: ""
sourceImg: "qrc:/qml/img/duplicateicon@2x.png"
roundRight: true
roundLeft: false
}
}
}
}
}

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

Loading…
Cancel
Save