Browse Source

Mix: call filter for transaction log

cl-refactor
arkpar 10 years ago
parent
commit
0a831eb79b
  1. 25
      mix/ClientModel.cpp
  2. 32
      mix/ClientModel.h
  3. 8
      mix/MachineStates.h
  4. 73
      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<QCode*>("QCode");
qRegisterMetaType<QCallData*>("QCallData");
qRegisterMetaType<TransactionLogEntry*>("TransactionLogEntry");
qRegisterMetaType<RecordLogEntry*>("RecordLogEntry");
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
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);
showDebuggerForTransaction(t);
ExecutionResult const& e = m_client->executions().at(_index);
showDebuggerForTransaction(e);
}
void ClientModel::showDebugError(QString const& _error)
@ -346,9 +346,10 @@ void ClientModel::onStateReset()
void ClientModel::onNewTransaction()
{
unsigned block = m_client->number() + 1;
unsigned index = m_client->pendingExecutions().size() - 1;
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 value = QString::fromStdString(dev::toString(tr.value));
QString contract = address;
@ -359,7 +360,7 @@ void ClientModel::onNewTransaction()
//TODO: handle value transfer
FixedHash<4> functionHash;
bool call = false;
bool abi = false;
if (creation)
{
//contract creation
@ -374,12 +375,12 @@ void ClientModel::onNewTransaction()
}
else
{
//call
//transaction/call
if (tr.transactionData.size() > 0 && tr.transactionData.front().size() >= 4)
{
functionHash = FixedHash<4>(tr.transactionData.front().data(), FixedHash<4>::ConstructFromPointer);
function = QString::fromStdString(toJS(functionHash));
call = true;
abi = true;
}
else
function = QObject::tr("<none>");
@ -393,7 +394,7 @@ void ClientModel::onNewTransaction()
auto compilerRes = m_context->codeModel()->code();
QContractDefinition* def = compilerRes->contract();
contract = def->name();
if (call)
if (abi)
{
QFunctionDefinition* funcDef = def->getFunction(functionHash);
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);
emit newTransaction(log);
emit newRecord(log);
}
}

32
mix/ClientModel.h

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

8
mix/MachineStates.h

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

73
mix/MixClient.cpp

@ -15,7 +15,6 @@
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file MixClient.cpp
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* 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.sync(bc());
m_startState = m_state;
m_pendingExecutions.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();
@ -171,30 +169,33 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
d.value = _t.value();
if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
d.receipt = TransactionReceipt(execState.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage
m_pendingExecutions.emplace_back(std::move(d));
if (!_call)
d.transactionIndex = m_state.pending().size();
m_executions.emplace_back(std::move(d));
// execute on a state
_state.execute(lastHashes, rlp, nullptr, true);
// collect watches
h256Set changed;
Guard l(m_filterLock);
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters)
if ((unsigned)i.second.filter.latest() > bc().number())
{
// acceptable number.
auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1));
if (m.size())
if (!_call)
{
_state.execute(lastHashes, rlp, nullptr, true);
// collect watches
h256Set changed;
Guard l(m_filterLock);
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters)
if ((unsigned)i.second.filter.latest() > bc().number())
{
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1));
changed.insert(i.first);
// acceptable number.
auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1));
if (m.size())
{
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1));
changed.insert(i.first);
}
}
}
changed.insert(dev::eth::PendingChangedFilter);
noteChanged(changed);
changed.insert(dev::eth::PendingChangedFilter);
noteChanged(changed);
}
}
void MixClient::mine()
@ -206,28 +207,18 @@ void MixClient::mine()
bc().import(m_state.blockData(), m_stateDB);
m_state.sync(bc());
m_startState = m_state;
m_executions.emplace_back(std::move(m_pendingExecutions));
h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter };
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
{
if (m_pendingExecutions.size() > 0)
return m_pendingExecutions.back();
return m_executions.back().back();
return m_executions.back();
}
ExecutionResults const& MixClient::pendingExecutions() const
ExecutionResults const& MixClient::executions() const
{
return m_pendingExecutions;
return m_executions;
}
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);
u256 n = m_state.transactionsFrom(toAddress(_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)
@ -254,7 +245,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);
executeTransaction(t, m_state);
executeTransaction(t, m_state, false);
Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
return address;
}
@ -263,7 +254,7 @@ void MixClient::inject(bytesConstRef _rlp)
{
WriteGuard l(x_state);
eth::Transaction t(_rlp, CheckSignature::None);
executeTransaction(t, m_state);
executeTransaction(t, m_state, false);
}
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);
bytes rlp = t.rlp();
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp);
return m_pendingExecutions.back().returnValue;
executeTransaction(t, temp, true);
return lastExecution().returnValue;
}
u256 MixClient::balanceAt(Address _a, int _block) const

8
mix/MixClient.h

@ -45,9 +45,8 @@ public:
void resetState(u256 _balance);
KeyPair const& userAccount() const { return m_userAccount; }
void mine();
ExecutionResult const& execution(unsigned _block, unsigned _transaction) const;
ExecutionResult const& lastExecution() const;
ExecutionResults const& pendingExecutions() const;
ExecutionResults const& executions() const;
//dev::eth::Interface
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; }
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);
dev::eth::State asOf(int _block) const;
MixBlockChain& bc() { return *m_bc; }
@ -105,8 +104,7 @@ private:
mutable std::mutex m_filterLock;
std::map<h256, dev::eth::InstalledFilter> m_filters;
std::map<unsigned, dev::eth::ClientWatch> m_watches;
std::vector<ExecutionResults> m_executions;
ExecutionResults m_pendingExecutions;
ExecutionResults m_executions;
std::string m_dbPath;
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 = "";
}
}
onActivated: rowActivated(row);
TableViewColumn {
role: "modelData"
width: parent.width

41
mix/qml/TransactionLog.qml

@ -5,6 +5,14 @@ import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
Item {
property bool showLogs: true
property ListModel fullModel: ListModel{}
property ListModel transactionModel: ListModel{}
onShowLogsChanged: {
logTable.model = showLogs ? fullModel : transactionModel
}
Action {
id: addStateAction
text: "Add State"
@ -70,17 +78,13 @@ Item {
}
}
TableView {
id: logTable
Layout.fillWidth: true
Layout.fillHeight: true
model: logModel
model: fullModel
TableViewColumn {
role: "block"
title: qsTr("Block")
width: 40
}
TableViewColumn {
role: "tindex"
role: "transactionIndex"
title: qsTr("Index")
width: 40
}
@ -110,30 +114,31 @@ Item {
width: 120
}
onActivated: {
var item = logModel.get(row);
clientModel.debugTransaction(item.block, item.tindex);
var item = logTable.model.get(row);
clientModel.debugRecord(item.recordIndex);
}
Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logModel.count) {
var item = logModel.get(currentRow);
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) {
var item = logTable.model.get(currentRow);
appContext.toClipboard(item.returned);
}
}
}
}
ListModel {
id: logModel
}
Connections {
target: clientModel
onStateCleared: {
logModel.clear();
fullModel.clear();
transactionModel.clear();
}
onNewTransaction: {
onNewRecord: {
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: toggleWebPreviewAction }
MenuItem { action: toggleWebPreviewOrientationAction }
MenuItem { action: toggleCallsInLog }
}
}
@ -162,6 +163,15 @@ ApplicationWindow {
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 {
id: toggleRunOnLoadAction
text: qsTr("Load State on Startup")

1
mix/res.qrc

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

Loading…
Cancel
Save