You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
8.3 KiB
240 lines
8.3 KiB
/*
|
|
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.
|
|
*/
|
|
|
|
#include <QtConcurrent/QtConcurrent>
|
|
#include <QDebug>
|
|
#include <QQmlContext>
|
|
#include <QQmlApplicationEngine>
|
|
#include <libdevcore/CommonJS.h>
|
|
#include <libethereum/Transaction.h>
|
|
#include <libqwebthree/QWebThree.h>
|
|
#include "AppContext.h"
|
|
#include "DebuggingStateWrapper.h"
|
|
#include "QContractDefinition.h"
|
|
#include "QVariableDeclaration.h"
|
|
#include "ContractCallDataEncoder.h"
|
|
#include "CodeModel.h"
|
|
#include "ClientModel.h"
|
|
#include "QEther.h"
|
|
#include "Web3Server.h"
|
|
#include "ClientModel.h"
|
|
|
|
using namespace dev;
|
|
using namespace dev::eth;
|
|
using namespace dev::mix;
|
|
|
|
ClientModel::ClientModel(AppContext* _context):
|
|
m_context(_context), m_running(false), m_qWebThree(nullptr)
|
|
{
|
|
qRegisterMetaType<QBigInt*>("QBigInt*");
|
|
qRegisterMetaType<QEther*>("QEther*");
|
|
qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*");
|
|
qRegisterMetaType<QVariableDefinitionList*>("QVariableDefinitionList*");
|
|
qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>");
|
|
qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>");
|
|
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
|
|
qRegisterMetaType<AssemblyDebuggerData>("AssemblyDebuggerData");
|
|
|
|
connect(this, &ClientModel::dataAvailable, this, &ClientModel::showDebugger, Qt::QueuedConnection);
|
|
m_client.reset(new MixClient());
|
|
|
|
m_qWebThree = new QWebThree(this);
|
|
m_qWebThreeConnector.reset(new QWebThreeConnector());
|
|
m_qWebThreeConnector->setQWeb(m_qWebThree);
|
|
m_web3Server.reset(new Web3Server(*m_qWebThreeConnector.get(), std::vector<dev::KeyPair> { m_client->userAccount() }, m_client.get()));
|
|
connect(m_qWebThree, &QWebThree::response, this, &ClientModel::apiResponse);
|
|
|
|
_context->appEngine()->rootContext()->setContextProperty("clientModel", this);
|
|
}
|
|
|
|
ClientModel::~ClientModel()
|
|
{
|
|
}
|
|
|
|
void ClientModel::apiRequest(QString const& _message)
|
|
{
|
|
(void)_message;
|
|
// m_qWebThree->postMessage(_message);
|
|
}
|
|
|
|
QString ClientModel::contractAddress() const
|
|
{
|
|
QString address = QString::fromStdString(dev::toJS(m_client->lastContractAddress()));
|
|
return address;
|
|
}
|
|
|
|
void ClientModel::debugDeployment()
|
|
{
|
|
executeSequence(std::vector<TransactionSettings>(), 10000000 * ether);
|
|
}
|
|
|
|
void ClientModel::debugState(QVariantMap _state)
|
|
{
|
|
u256 balance = (qvariant_cast<QEther*>(_state.value("balance")))->toU256Wei();
|
|
QVariantList transactions = _state.value("transactions").toList();
|
|
|
|
std::vector<TransactionSettings> transactionSequence;
|
|
|
|
for (auto const& t: transactions)
|
|
{
|
|
QVariantMap transaction = t.toMap();
|
|
|
|
QString functionId = transaction.value("functionId").toString();
|
|
u256 gas = (qvariant_cast<QEther*>(transaction.value("gas")))->toU256Wei();
|
|
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
|
|
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
|
|
QVariantMap params = transaction.value("parameters").toMap();
|
|
TransactionSettings transactionSettings(functionId, value, gas, gasPrice);
|
|
|
|
for (auto p = params.cbegin(); p != params.cend(); ++p)
|
|
{
|
|
QBigInt* param = qvariant_cast<QBigInt*>(p.value());
|
|
transactionSettings.parameterValues.insert(std::make_pair(p.key(), boost::get<dev::u256>(param->internalValue())));
|
|
}
|
|
|
|
transactionSequence.push_back(transactionSettings);
|
|
}
|
|
executeSequence(transactionSequence, balance);
|
|
}
|
|
|
|
void ClientModel::executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance)
|
|
{
|
|
if (m_running)
|
|
throw (std::logic_error("debugging already running"));
|
|
auto compilerRes = m_context->codeModel()->code();
|
|
std::shared_ptr<QContractDefinition> contractDef = compilerRes->sharedContract();
|
|
m_running = true;
|
|
|
|
emit runStarted();
|
|
emit stateChanged();
|
|
|
|
//run sequence
|
|
QtConcurrent::run([=]()
|
|
{
|
|
try
|
|
{
|
|
bytes contractCode = compilerRes->bytes();
|
|
std::vector<dev::bytes> transactonData;
|
|
QFunctionDefinition* f = nullptr;
|
|
ContractCallDataEncoder c;
|
|
//encode data for all transactions
|
|
for (auto const& t: _sequence)
|
|
{
|
|
f = nullptr;
|
|
for (int tf = 0; tf < contractDef->functionsList().size(); tf++)
|
|
{
|
|
if (contractDef->functionsList().at(tf)->name() == t.functionId)
|
|
{
|
|
f = contractDef->functionsList().at(tf);
|
|
break;
|
|
}
|
|
}
|
|
if (!f)
|
|
throw std::runtime_error("function " + t.functionId.toStdString() + " not found");
|
|
|
|
c.encode(f);
|
|
for (int p = 0; p < f->parametersList().size(); p++)
|
|
{
|
|
QVariableDeclaration* var = (QVariableDeclaration*)f->parametersList().at(p);
|
|
u256 value = 0;
|
|
auto v = t.parameterValues.find(var->name());
|
|
if (v != t.parameterValues.cend())
|
|
value = v->second;
|
|
c.encode(var, value);
|
|
}
|
|
transactonData.emplace_back(c.encodedData());
|
|
}
|
|
|
|
//run contract creation first
|
|
m_client->resetState(_balance);
|
|
ExecutionResult debuggingContent = deployContract(contractCode);
|
|
Address address = debuggingContent.contractAddress;
|
|
for (unsigned i = 0; i < _sequence.size(); ++i)
|
|
debuggingContent = callContract(address, transactonData.at(i), _sequence.at(i));
|
|
|
|
QList<QVariableDefinition*> returnParameters;
|
|
|
|
if (f)
|
|
returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue);
|
|
|
|
//we need to wrap states in a QObject before sending to QML.
|
|
QList<QObject*> wStates;
|
|
for (unsigned i = 0; i < debuggingContent.machineStates.size(); i++)
|
|
{
|
|
QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes()));
|
|
s->setState(debuggingContent.machineStates[i]);
|
|
wStates.append(s);
|
|
}
|
|
//collect states for last transaction
|
|
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode);
|
|
emit dataAvailable(returnParameters, wStates, code);
|
|
emit runComplete();
|
|
}
|
|
catch(boost::exception const&)
|
|
{
|
|
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
|
|
}
|
|
|
|
catch(std::exception const& e)
|
|
{
|
|
emit runFailed(e.what());
|
|
}
|
|
m_running = false;
|
|
emit stateChanged();
|
|
});
|
|
}
|
|
|
|
void ClientModel::showDebugger(QList<QVariableDefinition*> const& _returnParam, QList<QObject*> const& _wStates, AssemblyDebuggerData const& _code)
|
|
{
|
|
m_context->appEngine()->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates));
|
|
m_context->appEngine()->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code)));
|
|
m_context->appEngine()->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code)));
|
|
m_context->appEngine()->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam)));
|
|
showDebuggerWindow();
|
|
}
|
|
|
|
void ClientModel::showDebugError(QString const& _error)
|
|
{
|
|
//TODO: change that to a signal
|
|
m_context->displayMessageDialog(tr("Debugger"), _error);
|
|
}
|
|
|
|
ExecutionResult ClientModel::deployContract(bytes const& _code)
|
|
{
|
|
u256 gasPrice = 10000000000000;
|
|
u256 gas = 125000;
|
|
u256 amount = 100;
|
|
|
|
Address lastAddress = m_client->lastContractAddress();
|
|
Address newAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
|
|
ExecutionResult r = m_client->lastExecutionResult();
|
|
if (newAddress != lastAddress)
|
|
contractAddressChanged();
|
|
return r;
|
|
}
|
|
|
|
ExecutionResult ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
|
|
{
|
|
m_client->transact(m_client->userAccount().secret(), _tr.value, _contract, _data, _tr.gas, _tr.gasPrice);
|
|
ExecutionResult r = m_client->lastExecutionResult();
|
|
r.contractAddress = _contract;
|
|
return r;
|
|
}
|
|
|
|
|