Browse Source

Merge pull request #837 from yann300/constructorParamaterForDebug

Manage parameters in constructor #820
cl-refactor
Gav Wood 10 years ago
parent
commit
e04ebc4ecc
  1. 31
      mix/ClientModel.cpp
  2. 9
      mix/ClientModel.h
  3. 5
      mix/QContractDefinition.cpp
  4. 4
      mix/QContractDefinition.h
  5. 2
      mix/qml.qrc
  6. 6
      mix/qml/Debugger.qml
  7. 47
      mix/qml/MainContent.qml
  8. 1
      mix/qml/ProjectModel.qml
  9. 19
      mix/qml/StateDialog.qml
  10. 16
      mix/qml/StateList.qml
  11. 7
      mix/qml/StatusPane.qml
  12. 211
      mix/qml/TransactionDialog.qml
  13. 8
      mix/qml/js/QEtherHelper.js
  14. 13
      mix/qml/js/TransactionHelper.js
  15. 8
      mix/qml/main.qml

31
mix/ClientModel.cpp

@ -107,11 +107,10 @@ 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();
QString functionId = transaction.value("functionId").toString();
u256 gas = (qvariant_cast<QEther*>(transaction.value("gas")))->toU256Wei();
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
@ -125,12 +124,15 @@ void ClientModel::debugState(QVariantMap _state)
transactionSettings.parameterValues.insert(std::make_pair(p.key(), boost::get<dev::u256>(param->internalValue())));
}
transactionSequence.push_back(transactionSettings);
if (transaction.value("executeConstructor").toBool())
constructorTr = transactionSettings;
else
transactionSequence.push_back(transactionSettings);
}
executeSequence(transactionSequence, balance);
executeSequence(transactionSequence, balance, constructorTr);
}
void ClientModel::executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance)
void ClientModel::executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance, TransactionSettings const& ctrTransaction)
{
if (m_running)
throw (std::logic_error("debugging already running"));
@ -180,7 +182,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
//run contract creation first
m_client->resetState(_balance);
ExecutionResult debuggingContent = deployContract(contractCode);
ExecutionResult debuggingContent = deployContract(contractCode, ctrTransaction);
Address address = debuggingContent.contractAddress;
for (unsigned i = 0; i < _sequence.size(); ++i)
debuggingContent = callContract(address, transactonData.at(i), _sequence.at(i));
@ -232,14 +234,20 @@ void ClientModel::showDebugError(QString const& _error)
m_context->displayMessageDialog(tr("Debugger"), _error);
}
ExecutionResult ClientModel::deployContract(bytes const& _code)
ExecutionResult ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
{
u256 gasPrice = 10000000000000;
u256 gas = 125000;
u256 amount = 100;
Address newAddress;
if (!_ctrTransaction.isEmpty())
newAddress = m_client->transact(m_client->userAccount().secret(), _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice);
else
{
u256 gasPrice = 10000000000000;
u256 gas = 125000;
u256 amount = 100;
newAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
}
Address lastAddress = m_client->lastContractAddress();
Address newAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
ExecutionResult r = m_client->lastExecutionResult();
if (newAddress != lastAddress)
contractAddressChanged();
@ -256,4 +264,3 @@ ExecutionResult ClientModel::callContract(Address const& _contract, bytes const&
}
}

9
mix/ClientModel.h

@ -44,6 +44,7 @@ class RpcConnector;
/// Backend transaction config class
struct TransactionSettings
{
TransactionSettings() {}
TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice):
functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {}
@ -57,6 +58,10 @@ struct TransactionSettings
u256 gasPrice;
/// Mapping from contract function parameter name to value
std::map<QString, u256> parameterValues;
public:
/// @returns true if the functionId has not be set
bool isEmpty() const { return functionId.isNull() || functionId.isEmpty(); }
};
@ -115,8 +120,8 @@ signals:
private:
QString contractAddress() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance);
ExecutionResult deployContract(bytes const& _code);
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);
AppContext* m_context;

5
mix/QContractDefinition.cpp

@ -33,6 +33,11 @@ using namespace dev::mix;
QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract)
{
if (_contract->getConstructor() != nullptr)
m_constructor = new QFunctionDefinition(_contract->getConstructor(), -1);
else
m_constructor = new QFunctionDefinition();
auto interfaceFunctions = _contract->getInterfaceFunctions();
unsigned i = 0;
for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it, ++i)

4
mix/QContractDefinition.h

@ -36,15 +36,19 @@ class QContractDefinition: public QBasicNodeDefinition
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> functions READ functions CONSTANT)
Q_PROPERTY(dev::mix::QFunctionDefinition* constructor READ constructor CONSTANT)
public:
QContractDefinition() {}
QContractDefinition(solidity::ContractDefinition const* _contract);
/// Get all the functions of the contract.
QQmlListProperty<QFunctionDefinition> functions() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_functions); }
/// Get the constructor of the contract.
QFunctionDefinition* constructor() const { return m_constructor; }
QList<QFunctionDefinition*> const& functionsList() const { return m_functions; }
private:
QList<QFunctionDefinition*> m_functions;
QFunctionDefinition* m_constructor;
};
}

2
mix/qml.qrc

@ -41,6 +41,8 @@
<file>qml/Ether.qml</file>
<file>qml/EtherValue.qml</file>
<file>qml/BigIntValue.qml</file>
<file>qml/js/QEtherHelper.js</file>
<file>qml/js/TransactionHelper.js</file>
<file>qml/Splitter.qml</file>
</qresource>
</RCC>

6
mix/qml/Debugger.qml

@ -114,6 +114,7 @@ Rectangle {
contentWidth: parent.width
Rectangle
{
color: "transparent"
anchors.fill: parent
ColumnLayout
{
@ -317,7 +318,7 @@ Rectangle {
Rectangle {
Layout.fillWidth: true
height: parent.height //- 2 * stateListContainer.border.width
color: "transparent"
ColumnLayout
{
width: parent.width
@ -431,6 +432,7 @@ Rectangle {
Rectangle
{
id: storageRect
color: "transparent"
width: parent.width
Layout.minimumHeight: 25
Layout.maximumHeight: 223
@ -506,6 +508,7 @@ Rectangle {
Rectangle
{
id: memoryRect;
color: "transparent"
height: 25
width: parent.width
Layout.minimumHeight: 25
@ -527,6 +530,7 @@ Rectangle {
Rectangle
{
id: callDataRect
color: "transparent"
height: 25
width: parent.width
Layout.minimumHeight: 25

47
mix/qml/MainContent.qml

@ -4,6 +4,9 @@ import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper
import "js/TransactionHelper.js" as TransactionHelper
Rectangle {
@ -30,6 +33,42 @@ Rectangle {
contentView.width = parent.width - projectList.width;
}
function startQuickDebugging()
{
var item = TransactionHelper.defaultTransaction();
item.executeConstructor = true;
if (codeModel.code.contract.constructor.parameters.length === 0)
{
ensureRightView();
startF5Debugging(item);
}
else
transactionDialog.open(0, item);
}
function startF5Debugging(transaction)
{
var ether = QEtherHelper.createEther("100000000000000000000000000", QEther.Wei);
var state = {
title: "",
balance: ether,
transactions: [transaction]
};
clientModel.debugState(state);
}
TransactionDialog {
id: transactionDialog
onAccepted: {
ensureRightView();
var item = transactionDialog.getItem();
item.executeConstructor = true;
startF5Debugging(item);
}
useTransactionDefaultValue: true
}
function toggleRightView() {
if (!rightView.visible)
rightView.show();
@ -42,6 +81,11 @@ Rectangle {
rightView.show();
}
function rightViewIsVisible()
{
return rightView.visible;
}
function hideRightView() {
if (rightView.visible)
rightView.hide();
@ -55,9 +99,6 @@ Rectangle {
codeWebSplitter.orientation = (codeWebSplitter.orientation === Qt.Vertical ? Qt.Horizontal : Qt.Vertical);
}
function rightViewVisible() {
return rightView.visible;
}
CodeEditorExtensionManager {
headerView: headerPaneTabs;

1
mix/qml/ProjectModel.qml

@ -4,7 +4,6 @@ import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
import "js/ProjectModel.js" as ProjectModelCode
Item {

19
mix/qml/StateDialog.qml

@ -3,6 +3,8 @@ import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import org.ethereum.qml.QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper
import "js/TransactionHelper.js" as TransactionHelper
Window {
id: modalStateDialog
@ -118,27 +120,12 @@ Window {
transactionDialog.open(index, transactionsModel.get(index));
}
function ether(_value, _unit)
{
var etherComponent = Qt.createComponent("qrc:/qml/EtherValue.qml");
var ether = etherComponent.createObject(modalStateDialog);
ether.setValue(_value);
ether.setUnit(_unit);
return ether;
}
function addTransaction() {
// Set next id here to work around Qt bug
// https://bugreports.qt-project.org/browse/QTBUG-41327
// Second call to signal handler would just edit the item that was just created, no harm done
var item = {
value: ether("0", QEther.Wei),
functionId: "",
gas: ether("125000", QEther.Wei),
gasPrice: ether("100000", QEther.Wei)
};
var item = TransactionHelper.defaultTransaction();
transactionDialog.open(transactionsModel.count, item);
}

16
mix/qml/StateList.qml

@ -4,6 +4,7 @@ import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import org.ethereum.qml.QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper
Rectangle {
color: "#ededed"
@ -67,15 +68,22 @@ Rectangle {
id: stateListModel
function addState() {
var etherComponent = Qt.createComponent("qrc:/qml/EtherValue.qml");
var ether = etherComponent.createObject(stateListContainer);
ether.setValue("100000000000000000000000000");
ether.setUnit(QEther.Wei);
var ether = QEtherHelper.createEther("100000000000000000000000000", QEther.Wei);
var item = {
title: "",
balance: ether,
transactions: []
};
var ctorTr = {
value: QEtherHelper.createEther("100", QEther.Wei),
functionId: qsTr("Constructor"),
gas: QEtherHelper.createEther("125000", QEther.Wei),
gasPrice: QEtherHelper.createEther("10000000000000", QEther.Wei),
executeConstructor: true
};
item.transactions.push(ctorTr);
stateDialog.open(stateListModel.count, item);
}

7
mix/qml/StatusPane.qml

@ -113,9 +113,10 @@ Rectangle {
Action {
id: debugRunActionIcon
onTriggered: {
mainContent.toggleRightView();
if (mainContent.rightViewVisible)
clientModel.debugDeployment();
if (mainContent.rightViewIsVisible())
mainContent.hideRightView()
else
mainContent.startQuickDebugging();
}
enabled: false
}

211
mix/qml/TransactionDialog.qml

@ -3,6 +3,7 @@ import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
Window {
id: modalTransactionDialog
@ -18,15 +19,25 @@ Window {
property alias transactionValue: valueField.value;
property alias functionId: functionComboBox.currentText;
property var itemParams;
property bool isConstructorTransaction;
property bool useTransactionDefaultValue: false
signal accepted;
function open(index, item) {
rowFunction.visible = !useTransactionDefaultValue;
rowValue.visible = !useTransactionDefaultValue;
rowGas.visible = !useTransactionDefaultValue;
rowGasPrice.visible = !useTransactionDefaultValue;
transactionIndex = index;
gasField.value = item.gas;
gasPriceField.value = item.gasPrice;
valueField.value = item.value;
var functionId = item.functionId;
isConstructorTransaction = item.executeConstructor;
rowFunction.visible = !item.executeConstructor;
itemParams = item.parameters !== undefined ? item.parameters : {};
functionsModel.clear();
var functionIndex = -1;
@ -41,7 +52,17 @@ Window {
functionIndex = 0; //@todo suggest unused funtion
functionComboBox.currentIndex = functionIndex;
loadParameters();
paramsModel.clear();
if (!item.executeConstructor)
loadParameters();
else
{
var parameters = codeModel.code.contract.constructor.parameters;
for (var p = 0; p < parameters.length; p++) {
var pname = parameters[p].name;
paramsModel.append({ name: pname, type: parameters[p].type, value: itemParams[pname] !== undefined ? itemParams[pname].value() : "" });
}
}
visible = true;
valueField.focus = true;
}
@ -49,7 +70,6 @@ Window {
function loadParameters() {
if (!paramsModel)
return;
paramsModel.clear();
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
var func = codeModel.code.contract.functions[functionComboBox.currentIndex];
var parameters = func.parameters;
@ -67,112 +87,159 @@ Window {
function getItem()
{
var item = {
functionId: transactionDialog.functionId,
gas: transactionDialog.gas,
gasPrice: transactionDialog.gasPrice,
value: transactionDialog.transactionValue,
parameters: {}
var item;
if (!useTransactionDefaultValue)
{
item = {
functionId: transactionDialog.functionId,
gas: transactionDialog.gas,
gasPrice: transactionDialog.gasPrice,
value: transactionDialog.transactionValue,
parameters: {},
executeConstructor: isConstructorTransaction
};
}
else
{
item = TransactionHelper.defaultTransaction();
item.functionId = transactionDialog.functionId;
item.executeConstructor = isConstructorTransaction;
}
if (isConstructorTransaction)
item.functionId = qsTr("Constructor");
for (var p = 0; p < transactionDialog.transactionParams.count; p++) {
var parameter = transactionDialog.transactionParams.get(p);
var intComponent = Qt.createComponent("qrc:/qml/BigIntValue.qml");
var param = intComponent.createObject(modalTransactionDialog);
param.setValue(parameter.value);
item.parameters[parameter.name] = param;
}
return item;
}
GridLayout {
ColumnLayout {
id: dialogContent
columns: 2
anchors.fill: parent
width: parent.width
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 10
rowSpacing: 10
columnSpacing: 10
Label {
text: qsTr("Function")
}
ComboBox {
id: functionComboBox
spacing: 30
RowLayout
{
id: rowFunction
Layout.fillWidth: true
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: functionsModel
height: 150
Label {
Layout.preferredWidth: 75
text: qsTr("Function")
}
onCurrentIndexChanged: {
loadParameters();
ComboBox {
id: functionComboBox
Layout.fillWidth: true
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: functionsModel
}
onCurrentIndexChanged: {
loadParameters();
}
}
}
Label {
text: qsTr("Value")
}
Rectangle
RowLayout
{
id: rowValue
Layout.fillWidth: true
Ether {
id: valueField
edit: true
displayFormattedValue: true
Label {
Layout.preferredWidth: 75
text: qsTr("Value")
}
Rectangle
{
Layout.fillWidth: true
Ether {
id: valueField
edit: true
displayFormattedValue: true
}
}
}
Label {
text: qsTr("Gas")
}
Rectangle
RowLayout
{
id: rowGas
Layout.fillWidth: true
Ether {
id: gasField
edit: true
displayFormattedValue: true
Label {
Layout.preferredWidth: 75
text: qsTr("Gas")
}
Rectangle
{
Layout.fillWidth: true
Ether {
id: gasField
edit: true
displayFormattedValue: true
}
}
}
Label {
text: qsTr("Gas Price")
}
Rectangle
RowLayout
{
id: rowGasPrice
Layout.fillWidth: true
Ether {
id: gasPriceField
edit: true
displayFormattedValue: true
Label {
Layout.preferredWidth: 75
text: qsTr("Gas Price")
}
Rectangle
{
Layout.fillWidth: true
Ether {
id: gasPriceField
edit: true
displayFormattedValue: true
}
}
}
Label {
text: qsTr("Parameters")
}
TableView {
model: paramsModel
RowLayout
{
Layout.fillWidth: true
TableViewColumn {
role: "name"
title: "Name"
width: 120
}
TableViewColumn {
role: "type"
title: "Type"
width: 120
}
TableViewColumn {
role: "value"
title: "Value"
width: 120
Label {
text: qsTr("Parameters")
Layout.preferredWidth: 75
}
TableView {
model: paramsModel
Layout.fillWidth: true
itemDelegate: {
return editableDelegate;
TableViewColumn {
role: "name"
title: "Name"
width: 120
}
TableViewColumn {
role: "type"
title: "Type"
width: 120
}
TableViewColumn {
role: "value"
title: "Value"
width: 120
}
itemDelegate: {
return editableDelegate;
}
}
}
}

8
mix/qml/js/QEtherHelper.js

@ -0,0 +1,8 @@
function createEther(_value, _unit, _parent)
{
var etherComponent = Qt.createComponent("qrc:/qml/EtherValue.qml");
var ether = etherComponent.createObject();
ether.setValue(_value);
ether.setUnit(_unit);
return ether;
}

13
mix/qml/js/TransactionHelper.js

@ -0,0 +1,13 @@
Qt.include("QEtherHelper.js")
function defaultTransaction()
{
return {
value: createEther("0", QEther.Wei),
functionId: "",
gas: createEther("125000", QEther.Wei),
gasPrice: createEther("100000", QEther.Wei),
executeConstructor: false,
parameters: {}
};
}

8
mix/qml/main.qml

@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import CodeEditorExtensionManager 1.0
import org.ethereum.qml.QEther 1.0
ApplicationWindow {
id: mainApplication
@ -79,11 +80,8 @@ ApplicationWindow {
id: debugRunAction
text: "&Run"
shortcut: "F5"
onTriggered: {
mainContent.ensureRightView();
clientModel.debugDeployment();
}
enabled: codeModel.hasContract && !clientModel.running;
onTriggered: mainContent.startQuickDebugging()
enabled: codeModel.hasContract && !clientModel.running
}
Action {

Loading…
Cancel
Save