Browse Source

transaction log

cl-refactor
arkpar 10 years ago
parent
commit
b98456cce5
  1. 213
      mix/ClientModel.cpp
  2. 58
      mix/ClientModel.h
  3. 18
      mix/CodeModel.cpp
  4. 4
      mix/CodeModel.h
  5. 30
      mix/MixClient.cpp
  6. 17
      mix/MixClient.h
  7. 11
      mix/QContractDefinition.cpp
  8. 2
      mix/QContractDefinition.h
  9. 6
      mix/qml/Debugger.qml
  10. 13
      mix/qml/StateListModel.qml
  11. 6
      mix/qml/TransactionDialog.qml
  12. 63
      mix/qml/TransactionLog.qml
  13. 9
      mix/qml/WebPreview.qml
  14. 7
      mix/res.qrc
  15. 4
      mix/stdc/config.sol

213
mix/ClientModel.cpp

@ -71,9 +71,12 @@ ClientModel::ClientModel(AppContext* _context):
qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
qRegisterMetaType<AssemblyDebuggerData>("AssemblyDebuggerData");
qRegisterMetaType<TransactionLogEntry*>("TransactionLogEntry");
connect(this, &ClientModel::dataAvailable, this, &ClientModel::showDebugger, Qt::QueuedConnection);
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()));
@ -112,7 +115,6 @@ void ClientModel::debugState(QVariantMap _state)
QVariantList transactions = _state.value("transactions").toList();
std::vector<TransactionSettings> transactionSequence;
TransactionSettings constructorTr;
for (auto const& t: transactions)
{
QVariantMap transaction = t.toMap();
@ -120,24 +122,34 @@ void ClientModel::debugState(QVariantMap _state)
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)
bool isStdContract = (transaction.value("stdContract").toBool());
if (isStdContract)
{
QBigInt* param = qvariant_cast<QBigInt*>(p.value());
transactionSettings.parameterValues.insert(std::make_pair(p.key(), boost::get<dev::u256>(param->internalValue())));
TransactionSettings transactionSettings(transaction.value("url").toString());
transactionSequence.push_back(transactionSettings);
}
if (transaction.value("executeConstructor").toBool())
constructorTr = transactionSettings;
else
{
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())));
}
if (transaction.value("executeConstructor").toBool())
transactionSettings.functionId.clear();
transactionSequence.push_back(transactionSettings);
}
}
executeSequence(transactionSequence, balance, constructorTr);
executeSequence(transactionSequence, balance);
}
void ClientModel::executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance, TransactionSettings const& ctrTransaction)
void ClientModel::executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance)
{
if (m_running)
throw (std::logic_error("debugging already running"));
@ -146,7 +158,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
m_running = true;
emit runStarted();
emit stateChanged();
emit runStateChanged();
//run sequence
QtConcurrent::run([=]()
@ -154,60 +166,64 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
try
{
bytes contractCode = compilerRes->bytes();
std::vector<dev::bytes> transactonData;
QFunctionDefinition* f = nullptr;
ContractCallDataEncoder c;
//encode data for all transactions
m_client->resetState(_balance);
for (auto const& t: _sequence)
{
f = nullptr;
for (int tf = 0; tf < contractDef->functionsList().size(); tf++)
Address address = m_contractAddress;
if (!t.stdContractUrl.isEmpty())
{
if (contractDef->functionsList().at(tf)->name() == t.functionId)
{
f = contractDef->functionsList().at(tf);
break;
}
//std contract
dev::bytes const& stdContractCode = m_context->codeModel()->getStdContractCode(t.stdContractUrl);
Address address = deployContract(stdContractCode, t);
m_stdContractAddresses[t.functionId] = address;
}
if (!f)
throw std::runtime_error("function " + t.functionId.toStdString() + " not found");
c.encode(f);
for (int p = 0; p < f->parametersList().size(); p++)
else
{
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, ctrTransaction);
Address address = m_contractAddress;
for (unsigned i = 0; i < _sequence.size(); ++i)
debuggingContent = callContract(address, transactonData.at(i), _sequence.at(i));
QList<QVariableDefinition*> returnParameters;
//encode data
f = nullptr;
if (t.functionId.isEmpty())
f = contractDef->constructor();
else
{
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");
if (f)
returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue);
c.encode(f);
for (int p = 0; p < f->parametersList().size(); p++)
{
QVariableDeclaration* var = 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);
}
//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);
if (t.functionId.isEmpty())
{
Address newAddress = deployContract(contractCode, t);
if (newAddress != m_contractAddress)
{
m_contractAddress = newAddress;
contractAddressChanged();
}
}
else
callContract(address, c.encodedData(), t);
}
}
//collect states for last transaction
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode);
emit dataAvailable(returnParameters, wStates, code);
m_running = false;
emit runComplete();
}
catch(boost::exception const&)
@ -220,16 +236,31 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
emit runFailed(e.what());
}
m_running = false;
emit stateChanged();
emit runStateChanged();
});
}
void ClientModel::showDebugger(QList<QVariableDefinition*> const& _returnParam, QList<QObject*> const& _wStates, AssemblyDebuggerData const& _code)
void ClientModel::showDebugger()
{
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)));
//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++)
{
QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(lastResult.executionCode, lastResult.executionData.toBytes()));
s->setState(lastResult.machineStates[i]);
wStates.append(s);
}
QList<QVariableDefinition*> returnParameters;
//returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue);
//collect states for last transaction
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(lastResult.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)));
m_context->appEngine()->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(returnParameters)));
showDebuggerWindow();
}
@ -239,7 +270,7 @@ void ClientModel::showDebugError(QString const& _error)
m_context->displayMessageDialog(tr("Debugger"), _error);
}
ExecutionResult ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
{
Address newAddress;
if (!_ctrTransaction.isEmpty())
@ -251,21 +282,53 @@ ExecutionResult ClientModel::deployContract(bytes const& _code, TransactionSetti
u256 amount = 100;
newAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
}
if (newAddress != m_contractAddress)
{
m_contractAddress = newAddress;
contractAddressChanged();
}
ExecutionResult r = m_client->record().back().transactions.back();
return r;
return newAddress;
}
ExecutionResult ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
void 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->record().back().transactions.back();
return r;
}
void ClientModel::onStateReset()
{
emit stateCleared();
}
void ClientModel::onNewTransaction()
{
unsigned block = m_client->number();
unsigned index = m_client->record().back().transactions.size() - 1;
ExecutionResult const& tr = m_client->record().back().transactions.back();
QString address = QString::fromStdString(toJS(tr.address));
QString value = QString::fromStdString(dev::toString(tr.value));
QString contract = address;
QString function;
QString returned;
if (tr.contractAddress)
returned = QString::fromStdString(toJS(tr.contractAddress));
else
returned = QString::fromStdString(toJS(tr.returnValue));
FixedHash<4> functionHash;
if (tr.transactionData.size() >= 4)
functionHash = FixedHash<4>(tr.transactionData);
if (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();
}
else
function = QString::fromStdString(toJS(functionHash));
TransactionLogEntry* log = new TransactionLogEntry(block, index, contract, function, value, address, returned);
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newTransaction(log);
}
}

58
mix/ClientModel.h

@ -24,6 +24,7 @@
#pragma once
#include <atomic>
#include <map>
#include "DebuggingStateWrapper.h"
#include "MixClient.h"
@ -40,6 +41,7 @@ namespace mix
class AppContext;
class Web3Server;
class RpcConnector;
class QEther;
/// Backend transaction config class
struct TransactionSettings
@ -47,6 +49,10 @@ struct TransactionSettings
TransactionSettings() {}
TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice):
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) {}
/// Contract function name
QString functionId;
@ -58,6 +64,8 @@ struct TransactionSettings
u256 gasPrice;
/// Mapping from contract function parameter name to value
std::map<QString, u256> parameterValues;
/// Standard contract url
QString stdContractUrl;
public:
/// @returns true if the functionId has not be set
@ -65,6 +73,34 @@ public:
};
class TransactionLogEntry: public QObject
{
Q_OBJECT
Q_PROPERTY(unsigned block MEMBER m_block CONSTANT)
Q_PROPERTY(unsigned index MEMBER m_index CONSTANT)
Q_PROPERTY(QString contract MEMBER m_contract CONSTANT)
Q_PROPERTY(QString function MEMBER m_function CONSTANT)
Q_PROPERTY(QString value MEMBER m_value CONSTANT)
Q_PROPERTY(QString address MEMBER m_address CONSTANT)
Q_PROPERTY(QString returned MEMBER m_returned CONSTANT)
public:
TransactionLogEntry():
m_block(0), m_index(0) {}
TransactionLogEntry(int _block, int _index, QString _contract, QString _function, QString _value, QString _address, QString _returned):
m_block(_block), m_index(_index), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned) {}
private:
unsigned m_block;
unsigned m_index;
QString m_contract;
QString m_function;
QString m_value;
QString m_address;
QString m_returned;
};
/**
* @brief Ethereum state control
*/
@ -76,7 +112,7 @@ public:
ClientModel(AppContext* _context);
~ClientModel();
/// @returns true if currently executing contract code
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged)
Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged)
/// @returns address of the last executed contract
Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged)
/// ethereum.js RPC request entry point
@ -96,7 +132,7 @@ public slots:
private slots:
/// Update UI with machine states result. Display a modal dialog.
void showDebugger(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
void showDebugger();
/// Update UI with transaction run error.
void showDebugError(QString const& _error);
@ -113,21 +149,24 @@ signals:
/// Execution state changed
void newBlock();
/// Execution state changed
void stateChanged();
void runStateChanged();
/// Show debugger window request
void showDebuggerWindow();
/// ethereum.js RPC response ready
/// @param _message RPC response in Json format
void apiResponse(QString const& _message);
/// Emited when machine states are available.
void dataAvailable(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
/// New transaction log entry
void newTransaction(TransactionLogEntry* _tr);
/// State (transaction log) cleared
void stateCleared();
private:
QString contractAddress() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance, TransactionSettings const& _ctrTransaction = TransactionSettings());
ExecutionResult deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
ExecutionResult callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance);
dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
void onNewTransaction();
void onStateReset();
AppContext* m_context;
std::atomic<bool> m_running;
@ -135,6 +174,7 @@ private:
std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server;
Address m_contractAddress;
std::map<QString, Address> m_stdContractAddresses;
};
}

18
mix/CodeModel.cpp

@ -32,6 +32,7 @@
#include "QFunctionDefinition.h"
#include "QVariableDeclaration.h"
#include "CodeHighlighter.h"
#include "FileIo.h"
#include "CodeModel.h"
using namespace dev::mix;
@ -188,3 +189,20 @@ void CodeModel::updateFormatting(QTextDocument* _document)
{
m_result->codeHighlighter()->updateFormatting(_document, *m_codeHighlighterSettings);
}
dev::bytes const& CodeModel::getStdContractCode(const QString &_url)
{
auto cached = m_compiledContracts.find(_url);
if (cached != m_compiledContracts.end())
return cached->second;
FileIo fileIo;
std::string source = fileIo.readFile(_url).toStdString();
solidity::CompilerStack cs;
cs.setSource(source);
cs.compile(false);
dev::bytes code = cs.getBytecode();
m_compiledContracts.insert(std::make_pair(_url, std::move(code)));
return m_compiledContracts.at(_url);
}

4
mix/CodeModel.h

@ -24,6 +24,7 @@
#include <memory>
#include <atomic>
#include <map>
#include <QObject>
#include <QThread>
#include <libdevcore/Common.h>
@ -131,6 +132,8 @@ public:
bool hasContract() const;
/// Apply text document formatting. @todo Move this to editor module
void updateFormatting(QTextDocument* _document);
/// Get contract code by url. Contract is compiled on first access and cached
dev::bytes const& getStdContractCode(QString const& _url);
signals:
/// Emited on compilation state change
@ -163,6 +166,7 @@ private:
QThread m_backgroundThread;
BackgroundWorker m_backgroundWorker;
int m_backgroundJobId = 0; //protects from starting obsolete compilation job
std::map<QString, dev::bytes> m_compiledContracts; //by url
friend class BackgroundWorker;
};

30
mix/MixClient.cpp

@ -51,12 +51,14 @@ 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(bytesConstRef _rlp, State& _state)
void MixClient::executeTransaction(Transaction const& _t, State& _state)
{
bytes rlp = _t.rlp();
Executive execution(_state, LastHashes(), 0);
execution.setup(_rlp);
execution.setup(&rlp);
bytes code;
bytesConstRef data;
bool firstIteration = true;
@ -91,6 +93,12 @@ void MixClient::executeTransaction(bytesConstRef _rlp, State& _state)
d.machineStates = machineStates;
d.executionCode = code;
d.executionData = data;
d.transactionData = _t.data();
d.address = _t.receiveAddress();
d.sender = _t.sender();
d.value = _t.value();
if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
d.receipt = TransactionReceipt(m_state.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage
m_blocks.back().transactions.emplace_back(d);
@ -112,6 +120,7 @@ void MixClient::executeTransaction(bytesConstRef _rlp, State& _state)
}
}
noteChanged(changed);
emit newTransaction();
}
void MixClient::validateBlock(int _block) const
@ -152,8 +161,7 @@ void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp();
executeTransaction(&rlp, m_state);
executeTransaction(t, m_state);
}
Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
@ -161,8 +169,7 @@ Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init,
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
bytes rlp = t.rlp();
executeTransaction(&rlp, m_state);
executeTransaction(t, m_state);
Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
return address;
}
@ -170,7 +177,8 @@ Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init,
void MixClient::inject(bytesConstRef _rlp)
{
WriteGuard l(x_state);
executeTransaction(_rlp, m_state);
eth::Transaction t(_rlp, CheckSignature::None);
executeTransaction(t, m_state);
}
void MixClient::flushTransactions()
@ -189,7 +197,7 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp();
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(&rlp, temp);
executeTransaction(t, temp);
return m_blocks.back().transactions.back().returnValue;
}
@ -226,7 +234,11 @@ std::map<u256, u256> MixClient::storageAt(Address _a, int _block) const
eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const
{
Guard l(m_filterLock);
return logs(m_filters.at(m_watches.at(_watchId).id).filter);
h256 h = m_watches.at(_watchId).id;
auto filterIter = m_filters.find(h);
if (filterIter != m_filters.end())
return logs(filterIter->second.filter);
return eth::LocalisedLogEntries();
}
eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const

17
mix/MixClient.h

@ -24,6 +24,7 @@
#pragma once
#include <vector>
#include <QObject>
#include <libethereum/Interface.h>
#include <libethereum/Client.h>
@ -58,9 +59,14 @@ struct ExecutionResult
ExecutionResult(): receipt(dev::h256(), dev::h256(), dev::eth::LogEntries()) {}
std::vector<MachineState> machineStates;
bytes transactionData;
bytes executionCode;
bytesConstRef executionData;
bytes returnValue;
dev::Address address;
dev::Address sender;
dev::Address contractAddress;
dev::u256 value;
dev::eth::TransactionReceipt receipt;
};
@ -76,8 +82,11 @@ struct Block
using Blocks = std::vector<Block>;
class MixClient: public dev::eth::Interface
class MixClient: public QObject, public dev::eth::Interface
{
Q_OBJECT
public:
MixClient();
/// Reset state to the empty state with given balance.
@ -124,8 +133,12 @@ public:
bool isMining() override;
eth::MineProgress miningProgress() const override;
signals:
void stateReset();
void newTransaction();
private:
void executeTransaction(bytesConstRef _rlp, eth::State& _state);
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state);
void validateBlock(int _block) const;
void noteChanged(h256Set const& _filters);
dev::eth::State const& asOf(int _block) const;

11
mix/QContractDefinition.cpp

@ -44,3 +44,14 @@ QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const
m_functions.append(new QFunctionDefinition(it->second, i));
}
QFunctionDefinition* QContractDefinition::getFunction(FixedHash<4> _hash)
{
if (m_constructor->hash() == _hash)
return m_constructor;
for (auto const& f: m_functions)
if (f->hash() == _hash)
return f;
return nullptr;
}

2
mix/QContractDefinition.h

@ -46,6 +46,8 @@ public:
/// Get the constructor of the contract.
QFunctionDefinition* constructor() const { return m_constructor; }
QList<QFunctionDefinition*> const& functionsList() const { return m_functions; }
/// Find function by hash, returns nullptr if not found
QFunctionDefinition* getFunction(FixedHash<4> _hash);
private:
QList<QFunctionDefinition*> m_functions;
QFunctionDefinition* m_constructor;

6
mix/qml/Debugger.qml

@ -129,6 +129,12 @@ Rectangle {
anchors.fill: parent
Layout.fillWidth: true
Layout.fillHeight: true
TransactionLog {
Layout.fillWidth: true
height: 400
}
RowLayout {
// step button + slider
id: buttonRow

13
mix/qml/StateListModel.qml

@ -23,6 +23,7 @@ Item {
function fromPlainTransactionItem(t) {
return {
functionId: t.functionId,
url: t.url,
value: QEtherHelper.createEther(t.value.value, t.value.unit),
gas: QEtherHelper.createEther(t.gas.value, t.gas.unit),
gasPrice: QEtherHelper.createEther(t.gasPrice.value, t.gasPrice.unit),
@ -34,7 +35,7 @@ Item {
function toPlainStateItem(s) {
return {
title: s.title,
balance: { balance: s.balance.value, unit: s.balance.unit },
balance: { value: s.balance.value, unit: s.balance.unit },
transactions: s.transactions.map(toPlainTransactionItem)
};
}
@ -42,6 +43,7 @@ Item {
function toPlainTransactionItem(t) {
return {
functionId: t.functionId,
url: t.url,
value: { value: t.value.value, unit: t.value.unit },
gas: { value: t.gas.value, unit: t.gas.unit },
gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit },
@ -59,8 +61,10 @@ Item {
onProjectLoaded: {
if (!projectData.states)
projectData.states = [];
if (projectData.defaultStateIndex)
if (projectData.defaultStateIndex !== undefined)
defaultStateIndex = projectData.defaultStateIndex;
else
defaultStateIndex = -1;
var items = projectData.states;
for(var i = 0; i < items.length; i++) {
var item = fromPlainStateItem(items[i]);
@ -88,10 +92,13 @@ Item {
onAccepted: {
var item = stateDialog.getItem();
if (stateDialog.stateIndex < stateListModel.count) {
defaultStateIndex = stateDialog.isDefault;
if (stateDialog.isDefault)
defaultStateIndex = stateIndex;
stateList[stateDialog.stateIndex] = item;
stateListModel.set(stateDialog.stateIndex, item);
} else {
if (stateDialog.isDefault)
defaultStateIndex = 0;
stateList.push(item);
stateListModel.append(item);
}

6
mix/qml/TransactionDialog.qml

@ -223,17 +223,17 @@ Window {
TableViewColumn {
role: "name"
title: "Name"
title: qsTr("Name")
width: 120
}
TableViewColumn {
role: "type"
title: "Type"
title: qsTr("Type")
width: 120
}
TableViewColumn {
role: "value"
title: "Value"
title: qsTr("Value")
width: 120
}

63
mix/qml/TransactionLog.qml

@ -0,0 +1,63 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
Item {
TableView {
anchors.fill: parent
model: logModel
TableViewColumn {
role: "block"
title: qsTr("Block")
width: 40
}
TableViewColumn {
role: "index"
title: qsTr("Index")
width: 40
}
TableViewColumn {
role: "contract"
title: qsTr("Contract")
width: 120
}
TableViewColumn {
role: "function"
title: qsTr("Function")
width: 120
}
TableViewColumn {
role: "value"
title: qsTr("Value")
width: 120
}
TableViewColumn {
role: "address"
title: qsTr("Address")
width: 120
}
TableViewColumn {
role: "returned"
title: qsTr("Returned")
width: 120
}
}
ListModel {
id: logModel
}
Connections {
target: clientModel
onStateCleared: {
logModel.clear();
}
onNewTransaction: {
logModel.append(_tr);
}
}
}

9
mix/qml/WebPreview.qml

@ -112,9 +112,14 @@ Item {
accept: true
port: 8893
onClientConnected: {
console.log(_request.content);
//filter polling spam
//TODO: do it properly
var log = _request.content.indexOf("eth_changed") < 0;
if (log)
console.log(_request.content);
var response = clientModel.apiCall(_request.content);
console.log(response);
if (log)
console.log(response);
_request.setResponse(response);
}
}

7
mix/res.qrc

@ -46,7 +46,8 @@
<file>qml/js/TransactionHelper.js</file>
<file>qml/Splitter.qml</file>
<file>qml/ContractLibrary.qml</file>
<file>stdc/config.sol</file>
<file>stdc/namereg.sol</file>
</qresource>
<file>stdc/config.sol</file>
<file>stdc/namereg.sol</file>
<file>qml/TransactionLog.qml</file>
</qresource>
</RCC>

4
mix/stdc/config.sol

@ -2,8 +2,8 @@
// Simple global configuration registrar.
// @authors:
// Gav Wood <g@ethdev.com>
#require owned, mortal
contract Config is mortal, owned {
#require mortal
contract Config is mortal {
function register(uint id, address service) {
if (tx.origin != owner)
return;

Loading…
Cancel
Save