Browse Source

transaction log

cl-refactor
arkpar 10 years ago
parent
commit
2d82cb7b6a
  1. 107
      mix/ClientModel.cpp
  2. 15
      mix/ClientModel.h
  3. 7
      mix/MixClient.cpp
  4. 9
      mix/MixClient.h
  5. 15
      mix/Web3Server.cpp
  6. 12
      mix/Web3Server.h
  7. 35
      mix/qml/MainContent.qml
  8. 7
      mix/qml/StateListModel.qml
  9. 4
      mix/qml/TransactionLog.qml
  10. 2
      mix/qml/WebPreview.qml

107
mix/ClientModel.cpp

@ -60,6 +60,7 @@ private:
QString m_response;
};
ClientModel::ClientModel(AppContext* _context):
m_context(_context), m_running(false), m_rpcConnector(new RpcConnector()), m_contractAddress(Address())
{
@ -75,10 +76,9 @@ ClientModel::ClientModel(AppContext* _context):
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient());
connect(m_client.get(), &MixClient::stateReset, this, &ClientModel::onStateReset, Qt::QueuedConnection);
connect(m_client.get(), &MixClient::newTransaction, this, &ClientModel::onNewTransaction, Qt::QueuedConnection);
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), std::vector<dev::KeyPair> { m_client->userAccount() }, m_client.get()));
connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection);
_context->appEngine()->rootContext()->setContextProperty("clientModel", this);
}
@ -109,7 +109,7 @@ void ClientModel::debugDeployment()
executeSequence(std::vector<TransactionSettings>(), 10000000 * ether);
}
void ClientModel::debugState(QVariantMap _state)
void ClientModel::setupState(QVariantMap _state)
{
u256 balance = (qvariant_cast<QEther*>(_state.value("balance")))->toU256Wei();
QVariantList transactions = _state.value("transactions").toList();
@ -126,7 +126,10 @@ void ClientModel::debugState(QVariantMap _state)
bool isStdContract = (transaction.value("stdContract").toBool());
if (isStdContract)
{
TransactionSettings transactionSettings(transaction.value("url").toString());
TransactionSettings transactionSettings(functionId, transaction.value("url").toString());
transactionSettings.gasPrice = 10000000000000;
transactionSettings.gas = 125000;
transactionSettings.value = 100;
transactionSequence.push_back(transactionSettings);
}
else
@ -166,18 +169,19 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
try
{
bytes contractCode = compilerRes->bytes();
QFunctionDefinition* f = nullptr;
ContractCallDataEncoder c;
m_client->resetState(_balance);
onStateReset();
for (auto const& t: _sequence)
{
Address address = m_contractAddress;
ContractCallDataEncoder encoder;
QFunctionDefinition* f = nullptr;
if (!t.stdContractUrl.isEmpty())
{
//std contract
dev::bytes const& stdContractCode = m_context->codeModel()->getStdContractCode(t.stdContractUrl);
Address address = deployContract(stdContractCode, t);
m_stdContractAddresses[t.functionId] = address;
m_stdContractNames[address] = t.functionId;
}
else
{
@ -199,7 +203,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
if (!f)
throw std::runtime_error("function " + t.functionId.toStdString() + " not found");
c.encode(f);
encoder.encode(f);
for (int p = 0; p < f->parametersList().size(); p++)
{
QVariableDeclaration* var = f->parametersList().at(p);
@ -207,7 +211,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
auto v = t.parameterValues.find(var->name());
if (v != t.parameterValues.cend())
value = v->second;
c.encode(var, value);
encoder.encode(var, value);
}
if (t.functionId.isEmpty())
@ -220,8 +224,9 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
}
}
else
callContract(address, c.encodedData(), t);
callContract(m_contractAddress, encoder.encodedData(), t);
}
onNewTransaction();
}
m_running = false;
emit runComplete();
@ -241,22 +246,27 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
}
void ClientModel::showDebugger()
{
auto const& last = m_client->record().back().transactions.back();
showDebuggerForTransaction(last);
}
void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
{
//we need to wrap states in a QObject before sending to QML.
QList<QObject*> wStates;
auto const& lastResult = m_client->record().back().transactions.back();
for (unsigned i = 0; i < lastResult.machineStates.size(); i++)
for (unsigned i = 0; i < _t.machineStates.size(); i++)
{
QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(lastResult.executionCode, lastResult.executionData.toBytes()));
s->setState(lastResult.machineStates[i]);
QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(_t.executionCode, _t.executionData.toBytes()));
s->setState(_t.machineStates[i]);
wStates.append(s);
}
QList<QVariableDefinition*> returnParameters;
//returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue);
//returnParameters = encoder.decode(f->returnParameters(), debuggingContent.returnValue);
//collect states for last transaction
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(lastResult.executionCode);
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(_t.executionCode);
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)));
@ -264,6 +274,13 @@ void ClientModel::showDebugger()
showDebuggerWindow();
}
void ClientModel::debugTransaction(unsigned _block, unsigned _index)
{
auto const& t = m_client->record().at(_block).transactions.at(_index);
showDebuggerForTransaction(t);
}
void ClientModel::showDebugError(QString const& _error)
{
//TODO: change that to a signal
@ -272,16 +289,7 @@ void ClientModel::showDebugError(QString const& _error)
Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
{
Address newAddress;
if (!_ctrTransaction.isEmpty())
newAddress = m_client->transact(m_client->userAccount().secret(), _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice);
else
{
u256 gasPrice = 10000000000000;
u256 gas = 125000;
u256 amount = 100;
newAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
}
Address newAddress = m_client->transact(m_client->userAccount().secret(), _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice);
return newAddress;
}
@ -292,6 +300,9 @@ void ClientModel::callContract(Address const& _contract, bytes const& _data, Tra
void ClientModel::onStateReset()
{
m_contractAddress = dev::Address();
m_stdContractAddresses.clear();
m_stdContractNames.clear();
emit stateCleared();
}
@ -305,26 +316,52 @@ void ClientModel::onNewTransaction()
QString contract = address;
QString function;
QString returned;
if (tr.contractAddress)
bool creation = tr.contractAddress != 0;
if (creation)
returned = QString::fromStdString(toJS(tr.contractAddress));
else
returned = QString::fromStdString(toJS(tr.returnValue));
//TODO: handle value transfer
FixedHash<4> functionHash;
if (tr.transactionData.size() >= 4)
functionHash = FixedHash<4>(tr.transactionData);
if (creation)
{
//contract creation
auto const stdContractName = m_stdContractNames.find(tr.contractAddress);
if (stdContractName != m_stdContractNames.end())
{
function = stdContractName->second;
contract = function;
}
else
{
function = QObject::tr("Constructor");
}
}
else
{
//call
if (tr.transactionData.size() >= 4)
{
functionHash = FixedHash<4>(tr.transactionData.data(), FixedHash<4>::ConstructFromPointer);
}
function = QString::fromStdString(toJS(functionHash));
}
if (tr.address == m_contractAddress || tr.contractAddress == m_contractAddress)
if (m_contractAddress != 0 && (tr.address == m_contractAddress || tr.contractAddress == m_contractAddress))
{
auto compilerRes = m_context->codeModel()->code();
QContractDefinition* def = compilerRes->contract();
contract = def->name();
QFunctionDefinition* funcDef = def->getFunction(functionHash);
if (funcDef)
function = funcDef->name();
if (!creation)
{
QFunctionDefinition* funcDef = def->getFunction(functionHash);
if (funcDef)
function = funcDef->name();
}
}
else
function = QString::fromStdString(toJS(functionHash));
TransactionLogEntry* log = new TransactionLogEntry(block, index, contract, function, value, address, returned);
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);

15
mix/ClientModel.h

@ -51,8 +51,8 @@ struct TransactionSettings
functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {}
TransactionSettings(u256 _value, u256 _gas, u256 _gasPrice):
value(_value), gas(_gas), gasPrice(_gasPrice) {}
TransactionSettings(QString const& _stdContract):
stdContractUrl(_stdContract) {}
TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl):
functionId(_stdContractName), stdContractUrl(_stdContractUrl) {}
/// Contract function name
QString functionId;
@ -66,10 +66,6 @@ struct TransactionSettings
std::map<QString, u256> parameterValues;
/// Standard contract url
QString stdContractUrl;
public:
/// @returns true if the functionId has not be set
bool isEmpty() const { return functionId.isNull() || functionId.isEmpty(); }
};
@ -100,7 +96,6 @@ private:
QString m_returned;
};
/**
* @brief Ethereum state control
*/
@ -128,7 +123,9 @@ public slots:
void debugDeployment();
/// Setup state, run transaction sequence, show debugger for the last transaction
/// @param _state JS object with state configuration
void debugState(QVariantMap _state);
void setupState(QVariantMap _state);
/// Show the debugger for a specified transaction
Q_INVOKABLE void debugTransaction(unsigned _block, unsigned _index);
private slots:
/// Update UI with machine states result. Display a modal dialog.
@ -167,6 +164,7 @@ private:
void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
void onNewTransaction();
void onStateReset();
void showDebuggerForTransaction(ExecutionResult const& _t);
AppContext* m_context;
std::atomic<bool> m_running;
@ -175,6 +173,7 @@ private:
std::unique_ptr<Web3Server> m_web3Server;
Address m_contractAddress;
std::map<QString, Address> m_stdContractAddresses;
std::map<Address, QString> m_stdContractNames;
};
}

7
mix/MixClient.cpp

@ -36,8 +36,11 @@ using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
const Secret c_stdSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074");
MixClient::MixClient():
m_userAccount(KeyPair::create())
m_userAccount(c_stdSecret)
{
resetState(10000000 * ether);
}
@ -51,7 +54,6 @@ void MixClient::resetState(u256 _balance)
genesis.state = m_state;
Block open;
m_blocks = Blocks { genesis, open }; //last block contains a list of pending transactions to be finalized
emit stateReset();
}
void MixClient::executeTransaction(Transaction const& _t, State& _state)
@ -120,7 +122,6 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
}
}
noteChanged(changed);
emit newTransaction();
}
void MixClient::validateBlock(int _block) const

9
mix/MixClient.h

@ -24,7 +24,6 @@
#pragma once
#include <vector>
#include <QObject>
#include <libethereum/Interface.h>
#include <libethereum/Client.h>
@ -83,10 +82,8 @@ struct Block
using Blocks = std::vector<Block>;
class MixClient: public QObject, public dev::eth::Interface
class MixClient: public dev::eth::Interface
{
Q_OBJECT
public:
MixClient();
/// Reset state to the empty state with given balance.
@ -133,10 +130,6 @@ public:
bool isMining() override;
eth::MineProgress miningProgress() const override;
signals:
void stateReset();
void newTransaction();
private:
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state);
void validateBlock(int _block) const;

15
mix/Web3Server.cpp

@ -53,3 +53,18 @@ void Web3Server::put(std::string const& _name, std::string const& _key, std::str
std::string k(_name + "/" + _key);
m_db[k] = _value;
}
std::string Web3Server::eth_transact(Json::Value const& _json)
{
std::string ret = WebThreeStubServerBase::eth_transact(_json);
emit newTransaction();
return ret;
}
std::string Web3Server::eth_call(Json::Value const& _json)
{
std::string ret = WebThreeStubServerBase::eth_call(_json);
emit newTransaction();
return ret;
}

12
mix/Web3Server.h

@ -24,6 +24,7 @@
#include <map>
#include <string>
#include <QObject>
#include <libweb3jsonrpc/WebThreeStubServerBase.h>
namespace dev
@ -32,11 +33,20 @@ namespace dev
namespace mix
{
class Web3Server: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
class Web3Server: public QObject, public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{
Q_OBJECT
public:
Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client);
signals:
void newTransaction();
protected:
virtual std::string eth_transact(Json::Value const& _json) override;
virtual std::string eth_call(Json::Value const& _json) override;
private:
dev::eth::Interface* client() override { return m_client; }
std::shared_ptr<dev::shh::Interface> face() override;

35
mix/qml/MainContent.qml

@ -35,40 +35,10 @@ Rectangle {
function startQuickDebugging()
{
var item = TransactionHelper.defaultTransaction();
item.executeConstructor = true;
if (codeModel.code.contract.constructor.parameters.length === 0)
{
ensureRightView();
startF5Debugging(item);
}
else
transactionDialog.open(0, item);
}
function startF5Debugging(transaction)
{
var ether = QEtherHelper.createEther("100000000000000000000000000", QEther.Wei);
var state = {
title: "",
balance: ether,
transactions: [transaction]
};
clientModel.debugState(state);
ensureRightView();
projectModel.stateListModel.debugDefaultState();
}
TransactionDialog {
id: transactionDialog
onAccepted: {
ensureRightView();
var item = transactionDialog.getItem();
item.executeConstructor = true;
startF5Debugging(item);
}
useTransactionDefaultValue: true
}
function toggleRightView() {
if (!rightView.visible)
rightView.show();
@ -99,7 +69,6 @@ Rectangle {
codeWebSplitter.orientation = (codeWebSplitter.orientation === Qt.Vertical ? Qt.Horizontal : Qt.Vertical);
}
CodeEditorExtensionManager {
headerView: headerPaneTabs;
rightView: rightPaneTabs;

7
mix/qml/StateListModel.qml

@ -159,9 +159,14 @@ Item {
stateDialog.open(index, stateList[index], defaultStateIndex === index);
}
function debugDefaultState() {
if (defaultStateIndex >= 0)
runState(defaultStateIndex);
}
function runState(index) {
var item = stateList[index];
clientModel.debugState(item);
clientModel.setupState(item);
}
function deleteState(index) {

4
mix/qml/TransactionLog.qml

@ -44,6 +44,10 @@ Item {
title: qsTr("Returned")
width: 120
}
onActivated: {
var item = logModel.get(row);
clientModel.debugTransaction(item.block, item.index);
}
}
ListModel {

2
mix/qml/WebPreview.qml

@ -114,7 +114,7 @@ Item {
onClientConnected: {
//filter polling spam
//TODO: do it properly
var log = _request.content.indexOf("eth_changed") < 0;
var log = true;//_request.content.indexOf("eth_changed") < 0;
if (log)
console.log(_request.content);
var response = clientModel.apiCall(_request.content);

Loading…
Cancel
Save