Browse Source

Merge pull request #2359 from yann300/event_watcher

Mix - events and watchers panel
cl-refactor
Arkadiy Paronyan 10 years ago
parent
commit
d9d073be5a
  1. 42
      mix/ClientModel.cpp
  2. 14
      mix/ClientModel.h
  3. 2
      mix/CodeModel.cpp
  4. 2
      mix/qml.qrc
  5. 244
      mix/qml/Block.qml
  6. 84
      mix/qml/BlockChain.qml
  7. 134
      mix/qml/KeyValuePanel.qml
  8. 2
      mix/qml/MainContent.qml
  9. 124
      mix/qml/ScenarioExecution.qml
  10. 9
      mix/qml/ScenarioLoader.qml
  11. 3
      mix/qml/StateListModel.qml
  12. 7
      mix/qml/TransactionDialog.qml
  13. 203
      mix/qml/Watchers.qml
  14. 5
      mix/res.qrc

42
mix/ClientModel.cpp

@ -206,6 +206,21 @@ QVariantList ClientModel::gasCosts() const
return res; return res;
} }
void ClientModel::addAccount(QString const& _secret)
{
KeyPair key(Secret(_secret.toStdString()));
m_accountsSecret.push_back(key);
Address address = key.address();
m_accounts[address] = Account(u256(0), Account::NormalCreation);
m_ethAccounts->setAccounts(m_accountsSecret);
}
QString ClientModel::resolveAddress(QString const& _secret)
{
KeyPair key(Secret(_secret.toStdString()));
return "0x" + QString::fromStdString(key.address().hex());
}
void ClientModel::setupScenario(QVariantMap _scenario) void ClientModel::setupScenario(QVariantMap _scenario)
{ {
onStateReset(); onStateReset();
@ -336,6 +351,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence)
if (!transaction.isFunctionCall) if (!transaction.isFunctionCall)
{ {
callAddress(Address(address.toStdString()), bytes(), transaction); callAddress(Address(address.toStdString()), bytes(), transaction);
onNewTransaction(); onNewTransaction();
continue; continue;
} }
@ -674,7 +690,7 @@ RecordLogEntry* ClientModel::lastBlock() const
strGas << blockInfo.gasUsed; strGas << blockInfo.gasUsed;
stringstream strNumber; stringstream strNumber;
strNumber << blockInfo.number; strNumber << blockInfo.number;
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantList()); RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantMap(), QVariantList());
QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership);
return record; return record;
} }
@ -735,6 +751,7 @@ void ClientModel::onNewTransaction()
Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress; Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress;
auto contractAddressIter = m_contractNames.find(contractAddress); auto contractAddressIter = m_contractNames.find(contractAddress);
QVariantMap inputParameters; QVariantMap inputParameters;
QVariantMap returnParameters;
QVariantList logs; QVariantList logs;
if (contractAddressIter != m_contractNames.end()) if (contractAddressIter != m_contractNames.end())
{ {
@ -754,6 +771,11 @@ void ClientModel::onNewTransaction()
returned += "("; returned += "(";
returned += returnValues.join(", "); returned += returnValues.join(", ");
returned += ")"; returned += ")";
QStringList returnParams = encoder.decode(funcDef->returnParameters(), tr.result.output);
for (int k = 0; k < returnParams.length(); ++k)
returnParameters.insert(funcDef->returnParameters().at(k)->name(), returnParams.at(k));
bytes data = tr.inputParameters; bytes data = tr.inputParameters;
data.erase(data.begin(), data.begin() + 4); data.erase(data.begin(), data.begin() + 4);
QStringList parameters = encoder.decode(funcDef->parametersList(), data); QStringList parameters = encoder.decode(funcDef->parametersList(), data);
@ -837,9 +859,25 @@ void ClientModel::onNewTransaction()
} }
RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction,
gasUsed, sender, label, inputParameters, logs); gasUsed, sender, label, inputParameters, returnParameters, logs);
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newRecord(log); emit newRecord(log);
// retrieving all accounts balance
QVariantMap state;
QVariantMap accountBalances;
for (auto const& ctr : m_contractAddresses)
{
u256 wei = m_client->balanceAt(ctr.second, PendingBlock);
accountBalances.insert("0x" + QString::fromStdString(ctr.second.hex()), QEther(wei, QEther::Wei).format());
}
for (auto const& account : m_accounts)
{
u256 wei = m_client->balanceAt(account.first, PendingBlock);
accountBalances.insert("0x" + QString::fromStdString(account.first.hex()), QEther(wei, QEther::Wei).format());
}
state.insert("accounts", accountBalances);
emit newState(recordIndex, state);
} }
} }

14
mix/ClientModel.h

@ -32,6 +32,7 @@
#include <QVariableDeclaration.h> #include <QVariableDeclaration.h>
#include <libethereum/Account.h> #include <libethereum/Account.h>
#include "MachineStates.h" #include "MachineStates.h"
#include "QEther.h"
namespace dev namespace dev
{ {
@ -115,6 +116,8 @@ class RecordLogEntry: public QObject
Q_PROPERTY(QString label MEMBER m_label CONSTANT) Q_PROPERTY(QString label MEMBER m_label CONSTANT)
/// input parameters /// input parameters
Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT) Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT)
/// return parameters
Q_PROPERTY(QVariantMap returnParameters MEMBER m_returnParameters CONSTANT)
/// logs /// logs
Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT) Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT)
@ -128,9 +131,9 @@ public:
RecordLogEntry(): RecordLogEntry():
m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {} m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {}
RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed, RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed,
QString _sender, QString _label, QVariantMap _inputParameters, QVariantList _logs): QString _sender, QString _label, QVariantMap _inputParameters, QVariantMap _returnParameters, QVariantList _logs):
m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed), m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed),
m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_logs(_logs) {} m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_returnParameters(_returnParameters), m_logs(_logs) {}
private: private:
unsigned m_recordIndex; unsigned m_recordIndex;
@ -146,6 +149,7 @@ private:
QString m_sender; QString m_sender;
QString m_label; QString m_label;
QVariantMap m_inputParameters; QVariantMap m_inputParameters;
QVariantMap m_returnParameters;
QVariantList m_logs; QVariantList m_logs;
}; };
@ -183,6 +187,10 @@ public:
Q_INVOKABLE QString encodeStringParam(QString const& _param); Q_INVOKABLE QString encodeStringParam(QString const& _param);
/// To Hex number /// To Hex number
Q_INVOKABLE QString toHex(QString const& _int); Q_INVOKABLE QString toHex(QString const& _int);
/// Add new account to the model
Q_INVOKABLE void addAccount(QString const& _secret);
/// Return the address associated with the current secret
Q_INVOKABLE QString resolveAddress(QString const& _secret);
public slots: public slots:
/// Setup scenario, run transaction sequence, show debugger for the last transaction /// Setup scenario, run transaction sequence, show debugger for the last transaction
@ -236,6 +244,8 @@ signals:
void newRecord(RecordLogEntry* _r); void newRecord(RecordLogEntry* _r);
/// State (transaction log) cleared /// State (transaction log) cleared
void stateCleared(); void stateCleared();
/// new state has been processed
void newState(unsigned _record, QVariantMap _accounts);
private: private:
RecordLogEntry* lastBlock() const; RecordLogEntry* lastBlock() const;

2
mix/CodeModel.cpp

@ -543,7 +543,7 @@ void CodeModel::retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type
SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
{ {
SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString()), std::vector<SolidityDeclaration>(), std::vector<QString>(), nullptr }; SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString(true)), std::vector<SolidityDeclaration>(), std::vector<QString>(), nullptr };
if (!_type) if (!_type)
return r; return r;
switch (_type->getCategory()) switch (_type->getCategory())

2
mix/qml.qrc

@ -69,5 +69,7 @@
<file>qml/ScenarioExecution.qml</file> <file>qml/ScenarioExecution.qml</file>
<file>qml/ScenarioLoader.qml</file> <file>qml/ScenarioLoader.qml</file>
<file>qml/ScenarioButton.qml</file> <file>qml/ScenarioButton.qml</file>
<file>qml/Watchers.qml</file>
<file>qml/KeyValuePanel.qml</file>
</qresource> </qresource>
</RCC> </RCC>

244
mix/qml/Block.qml

@ -22,6 +22,7 @@ ColumnLayout
property int blockIndex property int blockIndex
property variant scenario property variant scenario
property string labelColor: "#414141" property string labelColor: "#414141"
signal txSelected(var txIndex)
function calculateHeight() function calculateHeight()
{ {
@ -36,6 +37,14 @@ ColumnLayout
return trHeight return trHeight
} }
function editTx(txIndex)
{
transactionDialog.stateAccounts = scenario.accounts
transactionDialog.execute = false
transactionDialog.editMode = true
transactionDialog.open(txIndex, blockIndex, transactions.get(txIndex))
}
onOpenedTrChanged: onOpenedTrChanged:
{ {
Layout.preferredHeight = calculateHeight() Layout.preferredHeight = calculateHeight()
@ -156,11 +165,11 @@ ColumnLayout
Image { Image {
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: -9 anchors.leftMargin: -4
anchors.topMargin: -9 anchors.topMargin: 0
id: saveStatusImage id: saveStatusImage
source: "qrc:/qml/img/recyclediscard@2x.png" source: "qrc:/qml/img/recyclediscard@2x.png"
width: statusWidth + 20 width: statusWidth + 10
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
} }
@ -194,185 +203,125 @@ ColumnLayout
Rectangle Rectangle
{ {
Layout.preferredWidth: blockWidth Layout.preferredWidth: blockWidth
Layout.preferredHeight: parent.height Layout.preferredHeight: trHeight
height: trHeight
color: "#DEDCDC" color: "#DEDCDC"
id: rowContentTr id: rowContentTr
anchors.top: parent.top anchors.top: parent.top
property bool selected: false
Connections
{
target: blockChainPanel
onTxSelected: {
if (root.blockIndex !== blockIndex || index !== txIndex)
rowContentTr.deselect()
}
}
function deselect()
{
rowContentTr.selected = false
rowContentTr.color = "#DEDCDC"
hash.color = labelColor
func.color = labelColor
}
MouseArea MouseArea
{ {
anchors.fill: parent anchors.fill: parent
onClicked: {
if (!rowContentTr.selected)
{
rowContentTr.selected = true
rowContentTr.color = "#4F4F4F"
hash.color = "#EAB920"
func.color = "#EAB920"
txSelected(index)
}
else
rowContentTr.deselect()
}
onDoubleClicked: onDoubleClicked:
{ {
transactionDialog.stateAccounts = scenario.accounts root.editTx(index)
transactionDialog.execute = false
transactionDialog.open(index, blockIndex, transactions.get(index))
} }
} }
ColumnLayout RowLayout
{ {
anchors.top: parent.top Layout.fillWidth: true
width: parent.width Layout.preferredHeight: trHeight - 10
spacing: 20 anchors.verticalCenter: parent.verticalCenter
RowLayout Rectangle
{ {
anchors.top: parent.top Layout.preferredWidth: fromWidth
Layout.fillWidth: true anchors.left: parent.left
Rectangle anchors.leftMargin: horizontalMargin
Text
{ {
Layout.preferredWidth: fromWidth id: hash
anchors.left: parent.left width: parent.width - 30
anchors.leftMargin: horizontalMargin elide: Text.ElideRight
Text anchors.verticalCenter: parent.verticalCenter
{ maximumLineCount: 1
id: hash color: labelColor
width: parent.width - 30 font.pointSize: dbgStyle.absoluteSize(1)
elide: Text.ElideRight font.bold: true
anchors.verticalCenter: parent.verticalCenter text: {
maximumLineCount: 1 if (index >= 0)
color: labelColor return clientModel.resolveAddress(transactions.get(index).sender)
font.pointSize: dbgStyle.absoluteSize(1)
font.bold: true
text: {
if (index >= 0)
return transactions.get(index).sender
else
return ""
}
}
}
Rectangle
{
Layout.preferredWidth: toWidth
Text
{
id: func
text: {
if (index >= 0)
parent.parent.userFrienldyToken(transactions.get(index).label)
else
return ""
}
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
color: labelColor
font.pointSize: dbgStyle.absoluteSize(1)
font.bold: true
maximumLineCount: 1
width: parent.width
}
}
function userFrienldyToken(value)
{
if (value && value.indexOf("<") === 0)
{
if (value.split("> ")[1] === " - ")
return value.split(" - ")[0].replace("<", "")
else else
return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; return ""
}
else
return value
}
Rectangle
{
Layout.preferredWidth: valueWidth
Text
{
id: returnValue
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
maximumLineCount: 1
color: labelColor
font.bold: true
font.pointSize: dbgStyle.absoluteSize(1)
width: parent.width - 30
text: {
if (index >= 0 && transactions.get(index).returned)
return transactions.get(index).returned
else
return ""
}
} }
} }
}
Rectangle Rectangle
{
Layout.preferredWidth: toWidth
Text
{ {
Layout.preferredWidth: logsWidth id: func
Layout.preferredHeight: trHeight - 10 text: {
width: logsWidth if (index >= 0)
color: "transparent" parent.parent.userFrienldyToken(transactions.get(index).label)
Text else
{ return ""
id: logs
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 10
color: labelColor
font.bold: true
font.pointSize: dbgStyle.absoluteSize(1)
text: {
if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count)
return transactions.get(index).logs.count
else
return ""
}
}
MouseArea {
anchors.fill: parent
onClicked: {
rowTransaction.displayContent();
}
} }
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
color: labelColor
font.pointSize: dbgStyle.absoluteSize(1)
font.bold: true
maximumLineCount: 1
width: parent.width
} }
} }
RowLayout function userFrienldyToken(value)
{ {
id: rowDetailedContent if (value && value.indexOf("<") === 0)
visible: false
Layout.preferredHeight:{
if (index >= 0 && transactions.get(index).logs)
return 100 * transactions.get(index).logs.count
else
return 100
}
onVisibleChanged:
{ {
var lognb = transactions.get(index).logs.count if (value.split("> ")[1] === " - ")
if (visible) return value.split(" - ")[0].replace("<", "")
{
rowContentTr.Layout.preferredHeight = trHeight + 100 * lognb
openedTr += 100 * lognb
}
else else
{ return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()";
rowContentTr.Layout.preferredHeight = trHeight
openedTr -= 100 * lognb
}
} }
else
Text { return value
anchors.left: parent.left }
anchors.leftMargin: horizontalMargin
id: logsText
}
}
} }
} }
Rectangle Rectangle
{ {
width: debugActionWidth width: debugActionWidth
height: trHeight height: trHeight - 10
anchors.left: rowContentTr.right anchors.right: rowContentTr.right
anchors.topMargin: -6
anchors.top: rowContentTr.top anchors.top: rowContentTr.top
anchors.leftMargin: -50 anchors.rightMargin: 10
color: "transparent" color: "transparent"
Image { Image {
@ -380,7 +329,6 @@ ColumnLayout
source: "qrc:/qml/img/rightarrow@2x.png" source: "qrc:/qml/img/rightarrow@2x.png"
width: debugActionWidth width: debugActionWidth
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: transactions.get(index).recordIndex !== undefined visible: transactions.get(index).recordIndex !== undefined
} }

84
mix/qml/BlockChain.qml

@ -13,12 +13,18 @@ import "."
ColumnLayout { ColumnLayout {
id: blockChainPanel id: blockChainPanel
property alias trDialog: transactionDialog
property alias blockChainRepeater: blockChainRepeater
property variant model property variant model
property var states: ({})
spacing: 0 spacing: 0
property int previousWidth property int previousWidth
property variant debugTrRequested: [] property variant debugTrRequested: []
signal chainChanged signal chainChanged
signal chainReloaded signal chainReloaded
signal txSelected(var blockIndex, var txIndex)
signal rebuilding
signal accountAdded(string address, string amount)
Connections Connections
{ {
@ -40,20 +46,23 @@ ColumnLayout {
var minWidth = scenarioMinWidth - 20 // margin var minWidth = scenarioMinWidth - 20 // margin
if (width <= minWidth || previousWidth <= minWidth) if (width <= minWidth || previousWidth <= minWidth)
{ {
fromWidth = 100 fromWidth = 250
toWidth = 100 toWidth = 240
valueWidth = 200
} }
else else
{ {
var diff = (width - previousWidth) / 3; var diff = (width - previousWidth) / 3;
fromWidth = fromWidth + diff < 100 ? 100 : fromWidth + diff fromWidth = fromWidth + diff < 250 ? 250 : fromWidth + diff
toWidth = toWidth + diff < 100 ? 100 : toWidth + diff toWidth = toWidth + diff < 240 ? 240 : toWidth + diff
valueWidth = valueWidth + diff < 200 ? 200 : valueWidth + diff
} }
previousWidth = width previousWidth = width
} }
function getState(record)
{
return states[record]
}
function load(scenario) function load(scenario)
{ {
if (!scenario) if (!scenario)
@ -61,6 +70,7 @@ ColumnLayout {
if (model) if (model)
rebuild.startBlinking() rebuild.startBlinking()
model = scenario model = scenario
states = []
blockModel.clear() blockModel.clear()
for (var b in model.blocks) for (var b in model.blocks)
blockModel.append(model.blocks[b]) blockModel.append(model.blocks[b])
@ -68,10 +78,8 @@ ColumnLayout {
} }
property int statusWidth: 30 property int statusWidth: 30
property int fromWidth: 150 property int fromWidth: 250
property int toWidth: 100 property int toWidth: 240
property int valueWidth: 200
property int logsWidth: 40
property int debugActionWidth: 40 property int debugActionWidth: 40
property int horizontalMargin: 10 property int horizontalMargin: 10
property int cellSpacing: 10 property int cellSpacing: 10
@ -80,7 +88,7 @@ ColumnLayout {
{ {
id: header id: header
spacing: 0 spacing: 0
Layout.preferredHeight: 30 Layout.preferredHeight: 24
Rectangle Rectangle
{ {
Layout.preferredWidth: statusWidth Layout.preferredWidth: statusWidth
@ -91,12 +99,13 @@ ColumnLayout {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
source: "qrc:/qml/img/recycleicon@2x.png" source: "qrc:/qml/img/recycleicon@2x.png"
width: statusWidth + 20 width: statusWidth + 10
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
} }
} }
Rectangle Rectangle
{ {
anchors.verticalCenter: parent.verticalCenter
Layout.preferredWidth: fromWidth Layout.preferredWidth: fromWidth
Label Label
{ {
@ -109,21 +118,13 @@ ColumnLayout {
Label Label
{ {
text: "To" text: "To"
anchors.verticalCenter: parent.verticalCenter
Layout.preferredWidth: toWidth + cellSpacing Layout.preferredWidth: toWidth + cellSpacing
} }
Label Label
{
text: "Value"
Layout.preferredWidth: valueWidth + cellSpacing
}
Label
{
text: "Logs"
Layout.preferredWidth: logsWidth + cellSpacing
}
Label
{ {
text: "" text: ""
anchors.verticalCenter: parent.verticalCenter
Layout.preferredWidth: debugActionWidth Layout.preferredWidth: debugActionWidth
} }
} }
@ -162,8 +163,22 @@ ColumnLayout {
{ {
id: blockChainRepeater id: blockChainRepeater
model: blockModel model: blockModel
function editTx(blockIndex, txIndex)
{
itemAt(blockIndex).editTx(txIndex)
}
Block Block
{ {
Connections
{
target: block
onTxSelected: {
blockChainPanel.txSelected(index, txIndex)
}
}
id: block
scenario: blockChainPanel.model scenario: blockChainPanel.model
Layout.preferredWidth: blockChainScrollView.width Layout.preferredWidth: blockChainScrollView.width
Layout.preferredHeight: Layout.preferredHeight:
@ -248,6 +263,8 @@ ColumnLayout {
Rectangle Rectangle
{ {
Layout.preferredWidth: parent.width Layout.preferredWidth: parent.width
Layout.preferredHeight: 70
color: "transparent"
RowLayout RowLayout
{ {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
@ -270,7 +287,9 @@ ColumnLayout {
{ {
if (ensureNotFuturetime.running) if (ensureNotFuturetime.running)
return; return;
rebuilding()
stopBlinking() stopBlinking()
states = []
var retBlocks = []; var retBlocks = [];
var bAdded = 0; var bAdded = 0;
for (var j = 0; j < model.blocks.length; j++) for (var j = 0; j < model.blocks.length; j++)
@ -316,7 +335,7 @@ ColumnLayout {
blockModel.append(model.blocks[j]) blockModel.append(model.blocks[j])
ensureNotFuturetime.start() ensureNotFuturetime.start()
clientModel.setupScenario(model); clientModel.setupScenario(model);
} }
buttonShortcut: "" buttonShortcut: ""
sourceImg: "qrc:/qml/img/recycleicon@2x.png" sourceImg: "qrc:/qml/img/recycleicon@2x.png"
@ -346,6 +365,7 @@ ColumnLayout {
var item = TransactionHelper.defaultTransaction() var item = TransactionHelper.defaultTransaction()
transactionDialog.stateAccounts = model.accounts transactionDialog.stateAccounts = model.accounts
transactionDialog.execute = true transactionDialog.execute = true
transactionDialog.editMode = false
transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item) transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item)
} }
width: 100 width: 100
@ -397,7 +417,6 @@ ColumnLayout {
} }
else else
addNewBlock() addNewBlock()
} }
function addNewBlock() function addNewBlock()
@ -410,7 +429,7 @@ ColumnLayout {
height: 30 height: 30
buttonShortcut: "" buttonShortcut: ""
sourceImg: "qrc:/qml/img/addblock@2x.png" sourceImg: "qrc:/qml/img/newblock@2x.png"
} }
} }
@ -448,11 +467,13 @@ ColumnLayout {
tr.recordIndex = _r.recordIndex tr.recordIndex = _r.recordIndex
tr.logs = _r.logs tr.logs = _r.logs
tr.sender = _r.sender tr.sender = _r.sender
tr.returnParameters = _r.returnParameters
var trModel = blockModel.getTransaction(blockIndex, trIndex) var trModel = blockModel.getTransaction(blockIndex, trIndex)
trModel.returned = _r.returned trModel.returned = _r.returned
trModel.recordIndex = _r.recordIndex trModel.recordIndex = _r.recordIndex
trModel.logs = _r.logs trModel.logs = _r.logs
trModel.sender = _r.sender trModel.sender = _r.sender
trModel.returnParameters = _r.returnParameters
blockModel.setTransaction(blockIndex, trIndex, trModel) blockModel.setTransaction(blockIndex, trIndex, trModel)
return; return;
} }
@ -472,9 +493,15 @@ ColumnLayout {
itemTr.sender = _r.sender itemTr.sender = _r.sender
itemTr.recordIndex = _r.recordIndex itemTr.recordIndex = _r.recordIndex
itemTr.logs = _r.logs itemTr.logs = _r.logs
itemTr.returnParameters = _r.returnParameters
model.blocks[model.blocks.length - 1].transactions.push(itemTr) model.blocks[model.blocks.length - 1].transactions.push(itemTr)
blockModel.appendTransaction(itemTr) blockModel.appendTransaction(itemTr)
} }
onNewState: {
states[_record] = _accounts
}
onMiningComplete: onMiningComplete:
{ {
} }
@ -484,7 +511,12 @@ ColumnLayout {
id: newAccount id: newAccount
text: qsTr("New Account..") text: qsTr("New Account..")
onClicked: { onClicked: {
model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether)) var ac = projectModel.stateListModel.newAccount("O", QEther.Wei)
model.accounts.push(ac)
clientModel.addAccount(ac.secret);
for (var k in Object.keys(blockChainPanel.states))
blockChainPanel.states[k].accounts["0x" + ac.address] = "0 wei" // add the account in all the previous state (balance at O)
accountAdded(ac.address, "0")
} }
Layout.preferredWidth: 100 Layout.preferredWidth: 100
Layout.preferredHeight: 30 Layout.preferredHeight: 30

134
mix/qml/KeyValuePanel.qml

@ -0,0 +1,134 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "js/TransactionHelper.js" as TransactionHelper
import "js/QEtherHelper.js" as QEtherHelper
import "."
ColumnLayout {
id: root
property alias title: titleLabel.text
property variant _data
property string role
property alias model: modelKeyValue
function add(key, value)
{
modelKeyValue.append({ "key": key, "value": value })
}
function clear()
{
modelKeyValue.clear()
}
function init()
{
modelKeyValue.clear()
if (typeof(computeData) !== "undefined" && computeData instanceof Function)
computeData()
else
{
if (_data !== undefined && _data[role] !== undefined)
{
var keys = Object.keys(_data[role])
for (var k in keys)
{
modelKeyValue.append({ "key": keys[k] === "" ? "undefined" : keys[k], "value": _data[role][keys[k]] })
}
}
}
}
RowLayout
{
Layout.preferredHeight: 20
Layout.fillWidth: true
Label
{
id: titleLabel
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
color: "white"
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 100
ListModel
{
id: modelKeyValue
}
Rectangle
{
Layout.fillWidth: true
Layout.fillHeight: true
color: "white"
radius: 2
ScrollView
{
id: columnValues
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
anchors.fill: parent
clip: true
ColumnLayout
{
anchors.margins: 10
Repeater
{
id: repeaterKeyValue
model: modelKeyValue
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 30
spacing: 0
Rectangle
{
Layout.preferredWidth: columnValues.width / 2
Label
{
anchors.left: parent.left
anchors.leftMargin: 10
text: {
if (index >= 0 && repeaterKeyValue.model.get(index).key !== undefined)
return repeaterKeyValue.model.get(index).key
else
return ""
}
}
}
Rectangle
{
Layout.preferredWidth: columnValues.width / 2 - 10
Label
{
anchors.right: parent.right
anchors.rightMargin: 10
text: {
if (index >= 0 && repeaterKeyValue.model.get(index).value !== undefined)
return repeaterKeyValue.model.get(index).value
else
return ""
}
}
}
}
}
}
}
}
}
}

2
mix/qml/MainContent.qml

@ -31,7 +31,7 @@ Rectangle {
property alias codeEditor: codeEditor property alias codeEditor: codeEditor
property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally
property bool firstCompile: true property bool firstCompile: true
property int scenarioMinWidth: 590 property int scenarioMinWidth: 620
Connections { Connections {
target: codeModel target: codeModel

124
mix/qml/ScenarioExecution.qml

@ -8,6 +8,7 @@ import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "." import "."
Rectangle { Rectangle {
color: "#ededed" color: "#ededed"
property alias bc: blockChain property alias bc: blockChain
@ -18,50 +19,109 @@ Rectangle {
onProjectLoaded: { onProjectLoaded: {
loader.init() loader.init()
} }
} }
Column ScrollView
{ {
anchors.margins: 10
anchors.fill: parent anchors.fill: parent
spacing: 10 onWidthChanged: {
ScenarioLoader columnExe.width = width - 40
{
height: 100
width: parent.width
id: loader
} }
Connections ColumnLayout
{ {
target: blockChain id: columnExe
onChainChanged: Layout.preferredWidth: parent.width
{ width: parent.width - 40
loader.needSaveOrReload() anchors.left: parent.left
} anchors.leftMargin: 15
} ColumnLayout
{
id: scenarioColumn
width: parent.width
spacing: 10
ScenarioLoader
{
anchors.horizontalCenter: parent.horizontalCenter
height: 100
Layout.preferredWidth: 400
width: 400
id: loader
}
Rectangle Connections
{ {
width: parent.parent.width target: blockChain
height: 1 onChainChanged:
color: "#cccccc" {
} loader.needSaveOrReload()
}
}
Connections Rectangle
{ {
target: loader Layout.preferredWidth: parent.width
onLoaded: height: 1
color: "#cccccc"
}
Connections
{
target: loader
onLoaded:
{
watchers.clear()
blockChain.load(scenario)
}
}
BlockChain
{
id: blockChain
width: parent.width
}
Connections
{
target: blockChain
property var currentSelectedBlock
property var currentSelectedTx
onTxSelected: {
currentSelectedBlock = blockIndex
currentSelectedTx = txIndex
updateWatchers(blockIndex, txIndex)
}
function updateWatchers(blockIndex, txIndex){
var tx = blockChain.model.blocks[blockIndex].transactions[txIndex]
var state = blockChain.getState(tx.recordIndex)
watchers.updateWidthTx(tx, state, blockIndex, txIndex)
}
onRebuilding: {
watchers.clear()
}
onAccountAdded: {
watchers.addAccount(address, "0 wei")
}
}
}
Watchers
{ {
blockChain.load(scenario) id: watchers
bc: blockChain
Layout.fillWidth: true
Layout.preferredHeight: 740
} }
}
BlockChain Rectangle
{ {
id: blockChain color: "transparent"
width: parent.width Layout.preferredHeight: 50
Layout.fillWidth: true
}
} }
} }
} }

9
mix/qml/ScenarioLoader.qml

@ -107,11 +107,13 @@ ColumnLayout
Rectangle Rectangle
{ {
id: editIconRect id: editIconRect
anchors.top: scenarioName.top
anchors.topMargin: 6
anchors.left: scenarioName.right anchors.left: scenarioName.right
anchors.leftMargin: 15 anchors.leftMargin: 20
Image { Image {
source: "qrc:/qml/img/edit.png" source: "qrc:/qml/img/edittransaction.png"
width: 10 width: 30
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
@ -126,7 +128,6 @@ ColumnLayout
scenarioNameEdit.save() scenarioNameEdit.save()
else else
scenarioNameEdit.edit() scenarioNameEdit.edit()
} }
} }
} }

3
mix/qml/StateListModel.qml

@ -257,7 +257,8 @@ Item {
_secret = clientModel.newSecret(); _secret = clientModel.newSecret();
var address = clientModel.address(_secret); var address = clientModel.address(_secret);
var name = qsTr("Account") + "-" + address.substring(0, 4); var name = qsTr("Account") + "-" + address.substring(0, 4);
return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit), address: address }; var amount = QEtherHelper.createEther(_balance, _unit)
return { name: name, secret: _secret, balance: amount, address: address };
} }
function duplicateState(index) function duplicateState(index)

7
mix/qml/TransactionDialog.qml

@ -16,7 +16,8 @@ Dialog {
width: 580 width: 580
height: 500 height: 500
visible: false visible: false
title: qsTr("Edit Transaction") title: editMode ? qsTr("Edit Transaction") : qsTr("Add Transaction")
property bool editMode
property int transactionIndex property int transactionIndex
property int blockIndex property int blockIndex
property alias gas: gasValueEdit.gasValue; property alias gas: gasValueEdit.gasValue;
@ -390,7 +391,7 @@ Dialog {
objectName: "trTypeExecute" objectName: "trTypeExecute"
exclusiveGroup: rbbuttonList exclusiveGroup: rbbuttonList
height: 30 height: 30
text: qsTr("Execute Contract") text: qsTr("Transact with Contract")
} }
} }
} }
@ -687,7 +688,7 @@ Dialog {
} }
Button { Button {
text: qsTr("Update"); text: editMode ? qsTr("Update") : qsTr("Ok")
onClicked: { onClicked: {
var invalid = InputValidator.validate(paramsModel, paramValues); var invalid = InputValidator.validate(paramsModel, paramValues);
if (invalid.length === 0) if (invalid.length === 0)

203
mix/qml/Watchers.qml

@ -0,0 +1,203 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "js/TransactionHelper.js" as TransactionHelper
import "js/QEtherHelper.js" as QEtherHelper
import "."
Rectangle {
color: "#4F4F4F"
radius: 4
property variant tx
property variant currentState
property variant bc
property var blockIndex
property var txIndex
function clear()
{
from.text = ""
to.text = ""
value.text = ""
inputParams.clear()
returnParams.clear()
accounts.clear()
events.clear()
}
function addAccount(address, amount)
{
accounts.add(address, amount)
}
function updateWidthTx(_tx, _state, _blockIndex, _txIndex)
{
from.text = clientModel.resolveAddress(_tx.sender)
to.text = _tx.label
value.text = _tx.value.format()
tx = _tx
blockIndex = _blockIndex
txIndex = _txIndex
currentState = _state
inputParams.init()
if (_tx.isContractCreation)
{
returnParams.role = "creationAddr"
returnParams._data = {
creationAddr : {
}
}
returnParams._data.creationAddr[qsTr("contract address")] = _tx.returned
}
else
{
returnParams.role = "returnParameters"
returnParams._data = tx
}
returnParams.init()
accounts.init()
events.init()
}
Column {
anchors.fill: parent
spacing: 15
Rectangle
{
height: 15
width: parent.width - 30
color: "transparent"
Row
{
id: rowHeader
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: rowHeader.parent.top
anchors.topMargin: 6
spacing: 5
Label {
id: fromLabel
text: qsTr("from")
visible: from.text !== ""
color: "#EAB920"
}
Label {
id: from
color: "#EAB920"
elide: Text.ElideRight
maximumLineCount: 1
clip: true
width: 200
}
Label {
id: toLabel
text: qsTr("to")
visible: from.text !== ""
color: "#EAB920"
}
Label {
id: to
color: "#EAB920"
elide: Text.ElideRight
maximumLineCount: 1
clip: true
width: 100
}
Label {
id: value
color: "#EAB920"
font.italic: true
clip: true
}
}
Image {
anchors.right: rowHeader.parent.right
anchors.top: rowHeader.parent.top
anchors.topMargin: -3
source: "qrc:/qml/img/edittransaction2.png"
height: 30
fillMode: Image.PreserveAspectFit
visible: from.text !== ""
MouseArea
{
anchors.fill: parent
onClicked:
{
bc.blockChainRepeater.editTx(blockIndex, txIndex)
}
}
}
}
Rectangle {
height: 1
width: parent.width - 30
anchors.horizontalCenter: parent.horizontalCenter
border.color: "#cccccc"
border.width: 1
}
KeyValuePanel
{
height: 150
width: parent.width - 30
anchors.horizontalCenter: parent.horizontalCenter
id: inputParams
title: qsTr("INPUT PARAMETERS")
role: "parameters"
_data: tx
}
KeyValuePanel
{
height: 150
width: parent.width - 30
anchors.horizontalCenter: parent.horizontalCenter
id: returnParams
title: qsTr("RETURN PARAMETERS")
role: "returnParameters"
_data: tx
}
KeyValuePanel
{
height: 150
width: parent.width - 30
anchors.horizontalCenter: parent.horizontalCenter
id: accounts
title: qsTr("ACCOUNTS")
role: "accounts"
_data: currentState
}
KeyValuePanel
{
height: 150
width: parent.width - 30
anchors.horizontalCenter: parent.horizontalCenter
id: events
title: qsTr("EVENTS")
function computeData()
{
model.clear()
var ret = []
for (var k in tx.logs)
{
var param = ""
for (var p in tx.logs[k].param)
{
param += " " + tx.logs[k].param[p].value + " "
}
param = "(" + param + ")"
model.append({ "key": tx.logs[k].name, "value": param })
}
}
}
}
}

5
mix/res.qrc

@ -90,5 +90,10 @@
<file>qml/img/saveicon@2x.png</file> <file>qml/img/saveicon@2x.png</file>
<file>qml/img/sendtransactionicon.png</file> <file>qml/img/sendtransactionicon.png</file>
<file>qml/img/sendtransactionicon@2x.png</file> <file>qml/img/sendtransactionicon@2x.png</file>
<file>qml/img/edittransaction@2x.png</file>
<file>qml/img/newblock@2x.png</file>
<file>qml/img/edittransaction2@2x.png</file>
<file>qml/img/edittransaction.png</file>
<file>qml/img/edittransaction2.png</file>
</qresource> </qresource>
</RCC> </RCC>

Loading…
Cancel
Save