Browse Source

Merge pull request #989 from arkpar/mix_ux

Mix: call filter for transaction log
cl-refactor
Gav Wood 10 years ago
parent
commit
8648ec93ee
  1. 25
      mix/ClientModel.cpp
  2. 32
      mix/ClientModel.h
  3. 6
      mix/MachineStates.h
  4. 39
      mix/MixClient.cpp
  5. 8
      mix/MixClient.h
  6. 31
      mix/qml/CallStack.qml
  7. 1
      mix/qml/DebugInfoList.qml
  8. 41
      mix/qml/TransactionLog.qml
  9. 10
      mix/qml/main.qml
  10. 1
      mix/res.qrc

25
mix/ClientModel.cpp

@ -82,7 +82,7 @@ ClientModel::ClientModel(AppContext* _context):
qRegisterMetaType<QInstruction*>("QInstruction"); qRegisterMetaType<QInstruction*>("QInstruction");
qRegisterMetaType<QCode*>("QCode"); qRegisterMetaType<QCode*>("QCode");
qRegisterMetaType<QCallData*>("QCallData"); qRegisterMetaType<QCallData*>("QCallData");
qRegisterMetaType<TransactionLogEntry*>("TransactionLogEntry"); qRegisterMetaType<RecordLogEntry*>("RecordLogEntry");
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
@ -313,10 +313,10 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
} }
void ClientModel::debugTransaction(unsigned _block, unsigned _index) void ClientModel::debugRecord(unsigned _index)
{ {
auto const& t = m_client->execution(_block, _index); ExecutionResult const& e = m_client->executions().at(_index);
showDebuggerForTransaction(t); showDebuggerForTransaction(e);
} }
void ClientModel::showDebugError(QString const& _error) void ClientModel::showDebugError(QString const& _error)
@ -346,9 +346,10 @@ void ClientModel::onStateReset()
void ClientModel::onNewTransaction() void ClientModel::onNewTransaction()
{ {
unsigned block = m_client->number() + 1;
unsigned index = m_client->pendingExecutions().size() - 1;
ExecutionResult const& tr = m_client->lastExecution(); ExecutionResult const& tr = m_client->lastExecution();
unsigned block = m_client->number() + 1;
unsigned recordIndex = m_client->executions().size() - 1;
QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex);
QString address = QString::fromStdString(toJS(tr.address)); QString address = QString::fromStdString(toJS(tr.address));
QString value = QString::fromStdString(dev::toString(tr.value)); QString value = QString::fromStdString(dev::toString(tr.value));
QString contract = address; QString contract = address;
@ -359,7 +360,7 @@ void ClientModel::onNewTransaction()
//TODO: handle value transfer //TODO: handle value transfer
FixedHash<4> functionHash; FixedHash<4> functionHash;
bool call = false; bool abi = false;
if (creation) if (creation)
{ {
//contract creation //contract creation
@ -374,12 +375,12 @@ void ClientModel::onNewTransaction()
} }
else else
{ {
//call //transaction/call
if (tr.transactionData.size() > 0 && tr.transactionData.front().size() >= 4) if (tr.transactionData.size() > 0 && tr.transactionData.front().size() >= 4)
{ {
functionHash = FixedHash<4>(tr.transactionData.front().data(), FixedHash<4>::ConstructFromPointer); functionHash = FixedHash<4>(tr.transactionData.front().data(), FixedHash<4>::ConstructFromPointer);
function = QString::fromStdString(toJS(functionHash)); function = QString::fromStdString(toJS(functionHash));
call = true; abi = true;
} }
else else
function = QObject::tr("<none>"); function = QObject::tr("<none>");
@ -393,7 +394,7 @@ void ClientModel::onNewTransaction()
auto compilerRes = m_context->codeModel()->code(); auto compilerRes = m_context->codeModel()->code();
QContractDefinition* def = compilerRes->contract(); QContractDefinition* def = compilerRes->contract();
contract = def->name(); contract = def->name();
if (call) if (abi)
{ {
QFunctionDefinition* funcDef = def->getFunction(functionHash); QFunctionDefinition* funcDef = def->getFunction(functionHash);
if (funcDef) if (funcDef)
@ -407,9 +408,9 @@ void ClientModel::onNewTransaction()
} }
} }
TransactionLogEntry* log = new TransactionLogEntry(block, index, contract, function, value, address, returned); RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall());
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newTransaction(log); emit newRecord(log);
} }
} }

32
mix/ClientModel.h

@ -69,13 +69,13 @@ struct TransactionSettings
/// UI Transaction log record /// UI Transaction log record
class TransactionLogEntry: public QObject class RecordLogEntry: public QObject
{ {
Q_OBJECT Q_OBJECT
/// Transaction block number /// Recording index
Q_PROPERTY(unsigned block MEMBER m_block CONSTANT) Q_PROPERTY(unsigned recordIndex MEMBER m_recordIndex CONSTANT)
/// Transaction index within the block /// Human readable transaction bloack and transaction index
Q_PROPERTY(unsigned tindex MEMBER m_index CONSTANT) Q_PROPERTY(QString transactionIndex MEMBER m_transactionIndex CONSTANT)
/// Contract name if any /// Contract name if any
Q_PROPERTY(QString contract MEMBER m_contract CONSTANT) Q_PROPERTY(QString contract MEMBER m_contract CONSTANT)
/// Function name if any /// Function name if any
@ -86,21 +86,25 @@ class TransactionLogEntry: public QObject
Q_PROPERTY(QString address MEMBER m_address CONSTANT) Q_PROPERTY(QString address MEMBER m_address CONSTANT)
/// Returned value or transaction address in case of creation /// Returned value or transaction address in case of creation
Q_PROPERTY(QString returned MEMBER m_returned CONSTANT) Q_PROPERTY(QString returned MEMBER m_returned CONSTANT)
/// true if call, false if transaction
Q_PROPERTY(bool call MEMBER m_call CONSTANT)
public: public:
TransactionLogEntry(): RecordLogEntry():
m_block(0), m_index(0) {} m_recordIndex(0), m_call(false) {}
TransactionLogEntry(int _block, int _index, QString _contract, QString _function, QString _value, QString _address, QString _returned): RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call):
m_block(_block), m_index(_index), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned) {} m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call) {}
private: private:
unsigned m_block; unsigned m_recordIndex;
unsigned m_index; QString m_transactionIndex;
QString m_contract; QString m_contract;
QString m_function; QString m_function;
QString m_value; QString m_value;
QString m_address; QString m_address;
QString m_returned; QString m_returned;
bool m_call;
}; };
/** /**
@ -133,8 +137,8 @@ public slots:
/// Setup state, run transaction sequence, show debugger for the last transaction /// Setup state, run transaction sequence, show debugger for the last transaction
/// @param _state JS object with state configuration /// @param _state JS object with state configuration
void setupState(QVariantMap _state); void setupState(QVariantMap _state);
/// Show the debugger for a specified transaction /// Show the debugger for a specified record
Q_INVOKABLE void debugTransaction(unsigned _block, unsigned _index); Q_INVOKABLE void debugRecord(unsigned _index);
private slots: private slots:
/// Update UI with machine states result. Display a modal dialog. /// Update UI with machine states result. Display a modal dialog.
@ -168,7 +172,7 @@ signals:
/// @param _message RPC response in Json format /// @param _message RPC response in Json format
void apiResponse(QString const& _message); void apiResponse(QString const& _message);
/// New transaction log entry /// New transaction log entry
void newTransaction(TransactionLogEntry* _tr); void newRecord(RecordLogEntry* _r);
/// State (transaction log) cleared /// State (transaction log) cleared
void stateCleared(); void stateCleared();

6
mix/MachineStates.h

@ -61,7 +61,7 @@ namespace mix
*/ */
struct ExecutionResult struct ExecutionResult
{ {
ExecutionResult() : receipt(dev::h256(), dev::h256(), dev::eth::LogEntries()) {} ExecutionResult(): transactionIndex(std::numeric_limits<unsigned>::max()) {}
std::vector<MachineState> machineStates; std::vector<MachineState> machineStates;
std::vector<bytes> transactionData; std::vector<bytes> transactionData;
@ -71,7 +71,9 @@ namespace mix
dev::Address sender; dev::Address sender;
dev::Address contractAddress; dev::Address contractAddress;
dev::u256 value; dev::u256 value;
dev::eth::TransactionReceipt receipt; unsigned transactionIndex;
bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); }
}; };
using ExecutionResults = std::vector<ExecutionResult>; using ExecutionResults = std::vector<ExecutionResult>;

39
mix/MixClient.cpp

@ -15,7 +15,6 @@
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file MixClient.cpp /** @file MixClient.cpp
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015 * @date 2015
* Ethereum IDE client. * Ethereum IDE client.
@ -90,11 +89,10 @@ void MixClient::resetState(u256 _balance)
m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Empty); m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Empty);
m_state.sync(bc()); m_state.sync(bc());
m_startState = m_state; m_startState = m_state;
m_pendingExecutions.clear();
m_executions.clear(); m_executions.clear();
} }
void MixClient::executeTransaction(Transaction const& _t, State& _state) void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call)
{ {
bytes rlp = _t.rlp(); bytes rlp = _t.rlp();
@ -171,12 +169,14 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
d.value = _t.value(); d.value = _t.value();
if (_t.isCreation()) if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
d.receipt = TransactionReceipt(execState.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage if (!_call)
m_pendingExecutions.emplace_back(std::move(d)); d.transactionIndex = m_state.pending().size();
m_executions.emplace_back(std::move(d));
// execute on a state // execute on a state
if (!_call)
{
_state.execute(lastHashes, rlp, nullptr, true); _state.execute(lastHashes, rlp, nullptr, true);
// collect watches // collect watches
h256Set changed; h256Set changed;
Guard l(m_filterLock); Guard l(m_filterLock);
@ -195,6 +195,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
} }
changed.insert(dev::eth::PendingChangedFilter); changed.insert(dev::eth::PendingChangedFilter);
noteChanged(changed); noteChanged(changed);
}
} }
void MixClient::mine() void MixClient::mine()
@ -206,28 +207,18 @@ void MixClient::mine()
bc().import(m_state.blockData(), m_stateDB); bc().import(m_state.blockData(), m_stateDB);
m_state.sync(bc()); m_state.sync(bc());
m_startState = m_state; m_startState = m_state;
m_executions.emplace_back(std::move(m_pendingExecutions));
h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter };
noteChanged(changed); noteChanged(changed);
} }
ExecutionResult const& MixClient::execution(unsigned _block, unsigned _transaction) const
{
if (_block == bc().number() + 1)
return m_pendingExecutions.at(_transaction);
return m_executions.at(_block - 1).at(_transaction);
}
ExecutionResult const& MixClient::lastExecution() const ExecutionResult const& MixClient::lastExecution() const
{ {
if (m_pendingExecutions.size() > 0) return m_executions.back();
return m_pendingExecutions.back();
return m_executions.back().back();
} }
ExecutionResults const& MixClient::pendingExecutions() const ExecutionResults const& MixClient::executions() const
{ {
return m_pendingExecutions; return m_executions;
} }
State MixClient::asOf(int _block) const State MixClient::asOf(int _block) const
@ -246,7 +237,7 @@ void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const
WriteGuard l(x_state); WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret)); u256 n = m_state.transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
executeTransaction(t, m_state); executeTransaction(t, m_state, false);
} }
Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
@ -254,7 +245,7 @@ Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init,
WriteGuard l(x_state); WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret)); u256 n = m_state.transactionsFrom(toAddress(_secret));
eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
executeTransaction(t, m_state); executeTransaction(t, m_state, false);
Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
return address; return address;
} }
@ -263,7 +254,7 @@ void MixClient::inject(bytesConstRef _rlp)
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
eth::Transaction t(_rlp, CheckSignature::None); eth::Transaction t(_rlp, CheckSignature::None);
executeTransaction(t, m_state); executeTransaction(t, m_state, false);
} }
void MixClient::flushTransactions() void MixClient::flushTransactions()
@ -282,8 +273,8 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp(); bytes rlp = t.rlp();
WriteGuard lw(x_state); //TODO: lock is required only for last execution state WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp); executeTransaction(t, temp, true);
return m_pendingExecutions.back().returnValue; return lastExecution().returnValue;
} }
u256 MixClient::balanceAt(Address _a, int _block) const u256 MixClient::balanceAt(Address _a, int _block) const

8
mix/MixClient.h

@ -45,9 +45,8 @@ public:
void resetState(u256 _balance); void resetState(u256 _balance);
KeyPair const& userAccount() const { return m_userAccount; } KeyPair const& userAccount() const { return m_userAccount; }
void mine(); void mine();
ExecutionResult const& execution(unsigned _block, unsigned _transaction) const;
ExecutionResult const& lastExecution() const; ExecutionResult const& lastExecution() const;
ExecutionResults const& pendingExecutions() const; ExecutionResults const& executions() const;
//dev::eth::Interface //dev::eth::Interface
void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
@ -90,7 +89,7 @@ public:
bool submitNonce(h256 const&) override { return false; } bool submitNonce(h256 const&) override { return false; }
private: private:
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state); void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call);
void noteChanged(h256Set const& _filters); void noteChanged(h256Set const& _filters);
dev::eth::State asOf(int _block) const; dev::eth::State asOf(int _block) const;
MixBlockChain& bc() { return *m_bc; } MixBlockChain& bc() { return *m_bc; }
@ -105,8 +104,7 @@ private:
mutable std::mutex m_filterLock; mutable std::mutex m_filterLock;
std::map<h256, dev::eth::InstalledFilter> m_filters; std::map<h256, dev::eth::InstalledFilter> m_filters;
std::map<unsigned, dev::eth::ClientWatch> m_watches; std::map<unsigned, dev::eth::ClientWatch> m_watches;
std::vector<ExecutionResults> m_executions; ExecutionResults m_executions;
ExecutionResults m_pendingExecutions;
std::string m_dbPath; std::string m_dbPath;
unsigned m_minigThreads; unsigned m_minigThreads;
}; };

31
mix/qml/CallStack.qml

@ -1,31 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
Item {
property alias model: callTable.model
signal frameActivated(int index)
ColumnLayout {
anchors.fill: parent
Text {
text: qsTr("Call Stack")
Layout.fillWidth: true
}
TableView {
id: callTable
Layout.fillWidth: true
Layout.fillHeight: true
headerDelegate: null
TableViewColumn {
role: "modelData"
title: qsTr("Address")
width: parent.width
}
onActivated: {
frameActivated(row);
}
}
}
}

1
mix/qml/DebugInfoList.qml

@ -115,6 +115,7 @@ ColumnLayout {
//storageContainer.state = ""; //storageContainer.state = "";
} }
} }
onActivated: rowActivated(row);
TableViewColumn { TableViewColumn {
role: "modelData" role: "modelData"
width: parent.width width: parent.width

41
mix/qml/TransactionLog.qml

@ -5,6 +5,14 @@ import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
Item { Item {
property bool showLogs: true
property ListModel fullModel: ListModel{}
property ListModel transactionModel: ListModel{}
onShowLogsChanged: {
logTable.model = showLogs ? fullModel : transactionModel
}
Action { Action {
id: addStateAction id: addStateAction
text: "Add State" text: "Add State"
@ -70,17 +78,13 @@ Item {
} }
} }
TableView { TableView {
id: logTable
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: logModel model: fullModel
TableViewColumn { TableViewColumn {
role: "block" role: "transactionIndex"
title: qsTr("Block")
width: 40
}
TableViewColumn {
role: "tindex"
title: qsTr("Index") title: qsTr("Index")
width: 40 width: 40
} }
@ -110,30 +114,31 @@ Item {
width: 120 width: 120
} }
onActivated: { onActivated: {
var item = logModel.get(row); var item = logTable.model.get(row);
clientModel.debugTransaction(item.block, item.tindex); clientModel.debugRecord(item.recordIndex);
} }
Keys.onPressed: { Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logModel.count) { if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) {
var item = logModel.get(currentRow); var item = logTable.model.get(currentRow);
appContext.toClipboard(item.returned); appContext.toClipboard(item.returned);
} }
} }
} }
} }
ListModel {
id: logModel
}
Connections { Connections {
target: clientModel target: clientModel
onStateCleared: { onStateCleared: {
logModel.clear(); fullModel.clear();
transactionModel.clear();
} }
onNewTransaction: { onNewRecord: {
if (recording.checked) if (recording.checked)
logModel.append(_tr); {
fullModel.append(_r);
if (!_r.call)
transactionModel.append(_r);
}
} }
} }

10
mix/qml/main.qml

@ -54,6 +54,7 @@ ApplicationWindow {
MenuItem { action: toggleTransactionLogAction } MenuItem { action: toggleTransactionLogAction }
MenuItem { action: toggleWebPreviewAction } MenuItem { action: toggleWebPreviewAction }
MenuItem { action: toggleWebPreviewOrientationAction } MenuItem { action: toggleWebPreviewOrientationAction }
MenuItem { action: toggleCallsInLog }
} }
} }
@ -162,6 +163,15 @@ ApplicationWindow {
onTriggered: mainContent.toggleWebPreviewOrientation(); onTriggered: mainContent.toggleWebPreviewOrientation();
} }
Action {
id: toggleCallsInLog
text: qsTr("Show Calls in Transaction Log")
shortcut: ""
checkable: true
checked: mainContent.rightPane.transactionLog.showLogs
onTriggered: mainContent.rightPane.transactionLog.showLogs = !mainContent.rightPane.transactionLog.showLogs
}
Action { Action {
id: toggleRunOnLoadAction id: toggleRunOnLoadAction
text: qsTr("Load State on Startup") text: qsTr("Load State on Startup")

1
mix/res.qrc

@ -61,7 +61,6 @@
<file>stdc/std.sol</file> <file>stdc/std.sol</file>
<file>qml/TransactionLog.qml</file> <file>qml/TransactionLog.qml</file>
<file>res/mix_256x256x32.png</file> <file>res/mix_256x256x32.png</file>
<file>qml/CallStack.qml</file>
<file>qml/QVariableDeclaration.qml</file> <file>qml/QVariableDeclaration.qml</file>
<file>qml/Style.qml</file> <file>qml/Style.qml</file>
<file>qml/qmldir</file> <file>qml/qmldir</file>

Loading…
Cancel
Save