Browse Source

Merge pull request #2466 from yann300/deploydialog

Mix - Deploydialog
cl-refactor
Arkadiy Paronyan 9 years ago
parent
commit
59720bb114
  1. 94
      mix/ClientModel.cpp
  2. 8
      mix/ClientModel.h
  3. 5
      mix/CodeModel.h
  4. 14
      mix/FileIo.cpp
  5. 2
      mix/FileIo.h
  6. 35
      mix/MixClient.cpp
  7. 2
      mix/MixClient.h
  8. 1
      mix/QBigInt.h
  9. 5
      mix/qml.qrc
  10. 6
      mix/qml/Application.qml
  11. 110
      mix/qml/BlockChain.qml
  12. 13
      mix/qml/CodeEditorView.qml
  13. 488
      mix/qml/DeployContractStep.qml
  14. 585
      mix/qml/DeploymentDialog.qml
  15. 210
      mix/qml/DeploymentDialogSteps.qml
  16. 239
      mix/qml/DeploymentWorker.qml
  17. 7
      mix/qml/Ether.qml
  18. 207
      mix/qml/PackagingStep.qml
  19. 35
      mix/qml/ProjectModel.qml
  20. 310
      mix/qml/RegisteringStep.qml
  21. 8
      mix/qml/ScenarioButton.qml
  22. 1
      mix/qml/ScenarioExecution.qml
  23. 276
      mix/qml/ScenarioLoader.qml
  24. 5
      mix/qml/StateListModel.qml
  25. 7
      mix/qml/StatusPane.qml
  26. 35
      mix/qml/TransactionDialog.qml
  27. 4
      mix/qml/Watchers.qml
  28. 200
      mix/qml/js/NetworkDeployment.js
  29. 45
      mix/qml/js/ProjectModel.js
  30. 7
      mix/qml/js/TransactionHelper.js

94
mix/ClientModel.cpp

@ -81,13 +81,7 @@ ClientModel::ClientModel():
qRegisterMetaType<QInstruction*>("QInstruction");
qRegisterMetaType<QCode*>("QCode");
qRegisterMetaType<QCallData*>("QCallData");
qRegisterMetaType<RecordLogEntry*>("RecordLogEntry*");
m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
m_ethAccounts = make_shared<FixedAccountHolder>([=](){return m_client.get();}, std::vector<KeyPair>());
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_ethAccounts, std::vector<KeyPair>(), m_client.get()));
connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection);
qRegisterMetaType<RecordLogEntry*>("RecordLogEntry*");
}
ClientModel::~ClientModel()
@ -95,6 +89,19 @@ ClientModel::~ClientModel()
m_runFuture.waitForFinished();
}
void ClientModel::init(QString _dbpath)
{
m_dbpath = _dbpath;
if (m_dbpath.isEmpty())
m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
else
m_client.reset(new MixClient(m_dbpath.toStdString()));
m_ethAccounts = make_shared<FixedAccountHolder>([=](){return m_client.get();}, std::vector<KeyPair>());
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_ethAccounts, std::vector<KeyPair>(), m_client.get()));
connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection);
}
QString ClientModel::apiCall(QString const& _message)
{
try
@ -295,36 +302,40 @@ void ClientModel::finalizeBlock()
}
}
TransactionSettings ClientModel::transaction(QVariant const& _tr) const
{
QVariantMap transaction = _tr.toMap();
QString contractId = transaction.value("contractId").toString();
QString functionId = transaction.value("functionId").toString();
bool gasAuto = transaction.value("gasAuto").toBool();
u256 gas = 0;
if (transaction.value("gas").data())
gas = boost::get<u256>(qvariant_cast<QBigInt*>(transaction.value("gas"))->internalValue());
else
gasAuto = true;
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
QString sender = transaction.value("sender").toString();
bool isContractCreation = transaction.value("isContractCreation").toBool();
bool isFunctionCall = transaction.value("isFunctionCall").toBool();
if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later
contractId = m_codeModel->contracts().keys()[0];
Secret f = Secret(sender.toStdString());
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, f, isContractCreation, isFunctionCall);
transactionSettings.parameterValues = transaction.value("parameters").toMap();
if (contractId == functionId || functionId == "Constructor")
transactionSettings.functionId.clear();
return transactionSettings;
}
void ClientModel::processNextTransactions()
{
WriteGuard(x_queueTransactions);
vector<TransactionSettings> transactionSequence;
for (auto const& t: m_queueTransactions.front())
{
QVariantMap transaction = t.toMap();
QString contractId = transaction.value("contractId").toString();
QString functionId = transaction.value("functionId").toString();
bool gasAuto = transaction.value("gasAuto").toBool();
u256 gas = 0;
if (transaction.value("gas").data())
gas = boost::get<u256>(qvariant_cast<QBigInt*>(transaction.value("gas"))->internalValue());
else
gasAuto = true;
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
QString sender = transaction.value("sender").toString();
bool isContractCreation = transaction.value("isContractCreation").toBool();
bool isFunctionCall = transaction.value("isFunctionCall").toBool();
if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later
contractId = m_codeModel->contracts().keys()[0];
Secret f = Secret(sender.toStdString());
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, f, isContractCreation, isFunctionCall);
transactionSettings.parameterValues = transaction.value("parameters").toMap();
if (contractId == functionId || functionId == "Constructor")
transactionSettings.functionId.clear();
TransactionSettings transactionSettings = transaction(t);
transactionSequence.push_back(transactionSettings);
}
executeSequence(transactionSequence);
@ -351,7 +362,6 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence)
if (!transaction.isFunctionCall)
{
callAddress(Address(address.toStdString()), bytes(), transaction);
onNewTransaction();
continue;
}
@ -437,7 +447,6 @@ void ClientModel::executeTr(QVariantMap _tr)
}
}
std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId)
{
std::pair<QString, int> ret = std::make_pair(_contractId, 0);
@ -674,12 +683,27 @@ void ClientModel::debugRecord(unsigned _index)
Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
{
return m_client->submitTransaction(_ctrTransaction.sender, _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice, _ctrTransaction.gasAuto);
eth::TransactionSkeleton ts;
ts.creation = true;
ts.value = _ctrTransaction.value;
ts.data = _code;
ts.gas = _ctrTransaction.gas;
ts.gasPrice = _ctrTransaction.gasPrice;
ts.from = toAddress(_ctrTransaction.sender);
return m_client->submitTransaction(ts, _ctrTransaction.sender, _ctrTransaction.gasAuto).second;
}
void ClientModel::callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
{
m_client->submitTransaction(_tr.sender, _tr.value, _contract, _data, _tr.gas, _tr.gasPrice, _tr.gasAuto);
eth::TransactionSkeleton ts;
ts.creation = false;
ts.value = _tr.value;
ts.to = _contract;
ts.data = _data;
ts.gas = _tr.gas;
ts.gasPrice = _tr.gasPrice;
ts.from = toAddress(_tr.sender);
m_client->submitTransaction(ts, _tr.sender, _tr.gasAuto);
}
RecordLogEntry* ClientModel::lastBlock() const

8
mix/ClientModel.h

@ -171,7 +171,7 @@ public:
Q_PROPERTY(QVariantMap contractAddresses READ contractAddresses NOTIFY contractAddressesChanged)
/// @returns deployed contracts gas costs
Q_PROPERTY(QVariantList gasCosts READ gasCosts NOTIFY gasCostsChanged)
// @returns the last block
/// @returns the last block
Q_PROPERTY(RecordLogEntry* lastBlock READ lastBlock CONSTANT)
/// ethereum.js RPC request entry point
/// @param _message RPC request in Json format
@ -191,6 +191,10 @@ public:
Q_INVOKABLE void addAccount(QString const& _secret);
/// Return the address associated with the current secret
Q_INVOKABLE QString resolveAddress(QString const& _secret);
/// Compute required gas for a list of transactions @arg _tr
QBigInt computeRequiredGas(QVariantList _tr);
/// init eth client
Q_INVOKABLE void init(QString _dbpath);
public slots:
/// Setup scenario, run transaction sequence, show debugger for the last transaction
@ -266,6 +270,7 @@ private:
void finalizeBlock();
void stopExecution();
void setupExecutionChain();
TransactionSettings transaction(QVariant const& _tr) const;
std::atomic<bool> m_running;
std::atomic<bool> m_mining;
@ -284,6 +289,7 @@ private:
CodeModel* m_codeModel = nullptr;
QList<QVariantList> m_queueTransactions;
mutable boost::shared_mutex x_queueTransactions;
QString m_dbpath;
};
}

5
mix/CodeModel.h

@ -33,6 +33,7 @@
#include <libdevcore/Guards.h>
#include <libevmcore/Params.h>
#include <libevmasm/Assembly.h>
#include <libdevcore/SHA3.h>
#include "SolidityType.h"
#include "QBigInt.h"
@ -246,8 +247,12 @@ public:
void gasEstimation(solidity::CompilerStack const& _cs);
/// Gas cost by doc id
Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const;
/// Gas cost by @arg contractName @arg functionName
Q_INVOKABLE QVariantList gasCostBy(QString const& _contractName, QString const& _functionName) const;
/// Set optimize code
Q_INVOKABLE void setOptimizeCode(bool _value);
/// sha3
Q_INVOKABLE QString sha3(QString _source) { return QString::fromStdString(dev::sha3(_source.toStdString()).hex()); }
int txGas() { return static_cast<int>(dev::eth::c_txGas); }
int callStipend() { return static_cast<int>(dev::eth::c_callStipend); }

14
mix/FileIo.cpp

@ -72,8 +72,16 @@ QString FileIo::pathFromUrl(QString const& _url)
void FileIo::makeDir(QString const& _url)
{
QDir dirPath(pathFromUrl(_url));
if (!dirPath.exists())
dirPath.mkpath(dirPath.path());
if (dirPath.exists())
dirPath.removeRecursively();
dirPath.mkpath(dirPath.path());
}
void FileIo::deleteDir(QString const& _url)
{
QDir dirPath(pathFromUrl(_url));
if (dirPath.exists())
dirPath.removeRecursively();
}
QString FileIo::readFile(QString const& _url)
@ -186,7 +194,7 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder)
QUrl url(_deploymentFolder + "package.dapp");
QFile compressed(url.path());
QByteArray qFileBytes((char*)dapp.data(), static_cast<int>(dapp.size()));
if (compressed.open(QIODevice::WriteOnly))
if (compressed.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
compressed.write(qFileBytes);
compressed.flush();

2
mix/FileIo.h

@ -68,6 +68,8 @@ public:
Q_INVOKABLE void stopWatching(QString const& _path);
/// Delete a file
Q_INVOKABLE void deleteFile(QString const& _path);
/// delete a directory
Q_INVOKABLE void deleteDir(QString const& _url);
private:
QString getHomePath() const;

35
mix/MixClient.cpp

@ -79,7 +79,6 @@ MixClient::~MixClient()
void MixClient::resetState(std::unordered_map<Address, Account> const& _accounts, Secret const& _miner)
{
WriteGuard l(x_state);
Guard fl(x_filtersWatches);
@ -124,22 +123,16 @@ Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secre
return ret;
}
void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret)
ExecutionResult MixClient::debugTransaction(Transaction const& _t, State const& _state, LastHashes const& _lastHashes, bool _call)
{
Transaction t = _gasAuto ? replaceGas(_t, m_state.gasLimitRemaining()) : _t;
// do debugging run first
LastHashes lastHashes(256);
lastHashes[0] = bc().numberHash(bc().number());
for (unsigned i = 1; i < 256; ++i)
lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256();
State execState = _state;
execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation
execState.addBalance(_t.sender(), _t.gas() * _t.gasPrice()); //give it enough balance for gas estimation
eth::ExecutionResult er;
Executive execution(execState, lastHashes, 0);
Executive execution(execState, _lastHashes, 0);
execution.setResultRecipient(er);
execution.initialize(t);
execution.initialize(_t);
execution.execute();
std::vector<MachineState> machineStates;
std::vector<unsigned> levels;
std::vector<MachineCode> codes;
@ -235,7 +228,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
}
ExecutionResult d;
d.inputParameters = t.data();
d.inputParameters = _t.data();
d.result = er;
d.machineStates = machineStates;
d.executionCode = std::move(codes);
@ -249,12 +242,26 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
if (!_call)
d.transactionIndex = m_state.pending().size();
d.executonIndex = m_executions.size();
return d;
}
void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret)
{
Transaction t = _gasAuto ? replaceGas(_t, m_state.gasLimitRemaining()) : _t;
// do debugging run first
LastHashes lastHashes(256);
lastHashes[0] = bc().numberHash(bc().number());
for (unsigned i = 1; i < 256; ++i)
lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256();
ExecutionResult d = debugTransaction(t, _state, lastHashes, _call);
// execute on a state
if (!_call)
{
t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t;
er = _state.execute(lastHashes, t);
eth::ExecutionResult er = _state.execute(lastHashes, t);
if (t.isCreation() && _state.code(d.contractAddress).empty())
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment"));
d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend;

2
mix/MixClient.h

@ -61,7 +61,7 @@ public:
virtual std::pair<h256, Address> submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret) override { return submitTransaction(_ts, _secret, false); }
std::pair<h256, Address> submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret, bool _gasAuto);
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict);
ExecutionResult debugTransaction(dev::eth::Transaction const& _t, eth:: State const& _state, eth::LastHashes const& _lastHashes, bool _call);
void setAddress(Address _us) override;
void startMining() override;
void stopMining() override;

1
mix/QBigInt.h

@ -85,6 +85,7 @@ public:
/// Set the value of the BigInteger used. Will use u256 type. Invokable from QML.
Q_INVOKABLE void setValue(QString const& _value) { m_internalValue = dev::jsToU256(_value.toStdString()); }
Q_INVOKABLE void setBigInt(QString const& _value) { m_internalValue = bigint(_value.toStdString()); }
void setBigInt(u256 const& _value) { m_internalValue = _value; }
/// Subtract by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* subtract(QBigInt* const& _value) const;
/// Add @a _value to the current big integer. Invokable from QML.

5
mix/qml.qrc

@ -71,5 +71,10 @@
<file>qml/ScenarioButton.qml</file>
<file>qml/Watchers.qml</file>
<file>qml/KeyValuePanel.qml</file>
<file>qml/DeployContractStep.qml</file>
<file>qml/RegisteringStep.qml</file>
<file>qml/DeploymentDialogSteps.qml</file>
<file>qml/PackagingStep.qml</file>
<file>qml/DeploymentWorker.qml</file>
</qresource>
</RCC>

6
mix/qml/Application.qml

@ -42,6 +42,10 @@ ApplicationWindow {
ClientModel {
id: clientModel
codeModel: codeModel
Component.onCompleted:
{
init("/tmp")
}
}
ProjectModel {
@ -202,7 +206,7 @@ ApplicationWindow {
text: qsTr("Deploy")
shortcut: "F5"
onTriggered: mainContent.startQuickDebugging()
enabled: codeModel.hasContract && !clientModel.running
enabled: codeModel.hasContract && !clientModel.running && projectModel.stateListModel.defaultStateName() !== ""
}
Action {

110
mix/qml/BlockChain.qml

@ -20,7 +20,7 @@ ColumnLayout {
spacing: 0
property int previousWidth
property variant debugTrRequested: []
signal chainChanged
signal chainChanged(var blockIndex, var txIndex, var item)
signal chainReloaded
signal txSelected(var blockIndex, var txIndex)
signal rebuilding
@ -30,15 +30,46 @@ ColumnLayout {
{
target: codeModel
onContractRenamed: {
rebuild.startBlinking()
rebuild.needRebuild("ContractRenamed")
}
onNewContractCompiled: {
rebuild.startBlinking()
rebuild.needRebuild("NewContractCompiled")
}
onCompilationComplete: {
for (var c in rebuild.contractsHex)
{
if (codeModel.contracts[c] === undefined || codeModel.contracts[c].codeHex !== rebuild.contractsHex[c])
{
if (!rebuild.containsRebuildCause("CodeChanged"))
{
rebuild.needRebuild("CodeChanged")
}
return
}
}
rebuild.notNeedRebuild("CodeChanged")
}
}
onChainChanged: {
rebuild.startBlinking()
if (rebuild.txSha3[blockIndex][txIndex] !== codeModel.sha3(JSON.stringify(model.blocks[blockIndex].transactions[txIndex])))
{
rebuild.txChanged.push(rebuild.txSha3[blockIndex][txIndex])
rebuild.needRebuild("txChanged")
}
else {
for (var k in rebuild.txChanged)
{
if (rebuild.txChanged[k] === rebuild.txSha3[blockIndex][txIndex])
{
rebuild.txChanged.splice(k, 1)
break
}
}
if (rebuild.txChanged.length === 0)
rebuild.notNeedRebuild("txChanged")
}
}
onWidthChanged:
@ -174,7 +205,8 @@ ColumnLayout {
Connections
{
target: block
onTxSelected: {
onTxSelected:
{
blockChainPanel.txSelected(index, txIndex)
}
}
@ -274,8 +306,7 @@ ColumnLayout {
Rectangle {
Layout.preferredWidth: 100
Layout.preferredHeight: 30
Layout.preferredHeight: 30
ScenarioButton {
id: rebuild
text: qsTr("Rebuild")
@ -283,6 +314,42 @@ ColumnLayout {
height: 30
roundLeft: true
roundRight: true
property variant contractsHex: ({})
property variant txSha3: ({})
property variant txChanged: []
property var blinkReasons: []
function needRebuild(reason)
{
rebuild.startBlinking()
blinkReasons.push(reason)
}
function containsRebuildCause(reason)
{
for (var c in blinkReasons)
{
if (blinkReasons[c] === reason)
return true
}
return false
}
function notNeedRebuild(reason)
{
for (var c in blinkReasons)
{
if (blinkReasons[c] === reason)
{
blinkReasons.splice(c, 1)
break
}
}
if (blinkReasons.length === 0)
rebuild.stopBlinking()
}
onClicked:
{
if (ensureNotFuturetime.running)
@ -335,8 +402,34 @@ ColumnLayout {
blockModel.append(model.blocks[j])
ensureNotFuturetime.start()
takeCodeSnapshot()
takeTxSnaphot()
blinkReasons = []
clientModel.setupScenario(model);
}
function takeCodeSnapshot()
{
contractsHex = {}
for (var c in codeModel.contracts)
contractsHex[c] = codeModel.contracts[c].codeHex
}
function takeTxSnaphot()
{
txSha3 = {}
txChanged = []
for (var j = 0; j < model.blocks.length; j++)
{
for (var k = 0; k < model.blocks[j].transactions.length; k++)
{
if (txSha3[j] === undefined)
txSha3[j] = {}
txSha3[j][k] = codeModel.sha3(JSON.stringify(model.blocks[j].transactions[k]))
}
}
}
buttonShortcut: ""
sourceImg: "qrc:/qml/img/recycleicon@2x.png"
}
@ -548,9 +641,8 @@ ColumnLayout {
else {
model.blocks[blockIndex].transactions[transactionIndex] = item
blockModel.setTransaction(blockIndex, transactionIndex, item)
chainChanged()
chainChanged(blockIndex, transactionIndex, item)
}
}
}
}

13
mix/qml/CodeEditorView.qml

@ -24,6 +24,19 @@ Item {
return "";
}
function getContracts()
{
var ctr = []
for (var i = 0; i < openDocCount; i++)
{
if (editorListModel.get(i).isContract)
{
ctr.push(editors.itemAt(i).item)
}
}
return ctr;
}
function isDocumentOpen(documentId) {
for (var i = 0; i < openDocCount; i++)
if (editorListModel.get(i).documentId === documentId &&

488
mix/qml/DeployContractStep.qml

@ -0,0 +1,488 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
import Qt.labs.settings 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
import org.ethereum.qml.QEther 1.0
Rectangle {
property variant paramsModel: []
property variant worker
property variant gas: []
color: "#E3E3E3E3"
anchors.fill: parent
id: root
property int labelWidth: 150
function show()
{
visible = true
contractList.currentIndex = 0
contractList.change()
accountsModel.clear()
for (var k in worker.accounts)
{
accountsModel.append(worker.accounts[k])
}
if (worker.accounts.length > 0)
worker.currentAccount = worker.accounts[0].id
if (projectModel.deployBlockNumber !== -1)
{
worker.verifyHashes(projectModel.deploymentTrHashes, function (bn, trLost)
{
root.updateVerification(bn, trLost)
});
}
deployedAddresses.refresh()
worker.renewCtx()
}
function updateVerification(blockNumber, trLost)
{
verificationLabel.text = blockNumber - projectModel.deployBlockNumber
if (trLost.length > 0)
{
verificationLabel.text += "\n" + qsTr("Transactions lost") + "\n"
for (var k in trLost)
{
verificationLabel.text += trLost[k] + "\n"
}
}
}
RowLayout
{
anchors.fill: parent
anchors.margins: 10
ColumnLayout
{
anchors.top: parent.top
Layout.preferredWidth: parent.width * 0.40 - 20
Layout.fillHeight: true
id: scenarioList
Label
{
Layout.fillWidth: true
text: qsTr("Pick Scenario to deploy")
}
ComboBox
{
id: contractList
Layout.preferredWidth: parent.width - 20
model: projectModel.stateListModel
textRole: "title"
onCurrentIndexChanged:
{
if (root.visible)
change()
}
function change()
{
trListModel.clear()
if (currentIndex > -1)
{
for (var k = 0; k < projectModel.stateListModel.get(currentIndex).blocks.count; k++)
{
for (var j = 0; j < projectModel.stateListModel.get(currentIndex).blocks.get(k).transactions.count; j++)
{
trListModel.append(projectModel.stateListModel.get(currentIndex).blocks.get(k).transactions.get(j));
}
}
for (var k = 0; k < trListModel.count; k++)
{
trList.itemAt(k).init()
}
ctrDeployCtrLabel.calculateContractDeployGas();
}
}
}
Rectangle
{
Layout.fillHeight: true
Layout.preferredWidth: parent.width - 20
id: trContainer
color: "white"
border.color: "#cccccc"
border.width: 1
ScrollView
{
anchors.fill: parent
ColumnLayout
{
spacing: 0
ListModel
{
id: trListModel
}
Repeater
{
id: trList
model: trListModel
ColumnLayout
{
Layout.fillWidth: true
spacing: 5
Layout.preferredHeight:
{
if (index > -1)
return 20 + trListModel.get(index)["parameters"].count * 20
else
return 20
}
function init()
{
paramList.clear()
if (trListModel.get(index).parameters)
{
for (var k in trListModel.get(index).parameters)
{
paramList.append({ "name": k, "value": trListModel.get(index).parameters[k] })
}
}
}
Label
{
id: trLabel
Layout.preferredHeight: 20
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 5
anchors.leftMargin: 10
text:
{
if (index > -1)
return trListModel.get(index).label
else
return ""
}
}
ListModel
{
id: paramList
}
Repeater
{
Layout.preferredHeight:
{
if (index > -1)
return trListModel.get(index)["parameters"].count * 20
else
return 0
}
model: paramList
Label
{
Layout.preferredHeight: 20
anchors.left: parent.left
anchors.leftMargin: 20
text: name + "=" + value
font.italic: true
}
}
}
}
}
}
}
}
ColumnLayout
{
anchors.top: parent.top
Layout.preferredHeight: parent.height - 25
ColumnLayout
{
anchors.top: parent.top
Layout.preferredWidth: parent.width * 0.60
Layout.fillHeight: true
id: deploymentOption
spacing: 8
Label
{
anchors.left: parent.left
anchors.leftMargin: 105
text: qsTr("Deployment options")
}
ListModel
{
id: accountsModel
}
RowLayout
{
Layout.fillWidth: true
Rectangle
{
width: labelWidth
Label
{
text: qsTr("Account")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
ComboBox
{
id: accountsList
textRole: "id"
model: accountsModel
Layout.preferredWidth: 235
onCurrentTextChanged:
{
worker.currentAccount = currentText
accountBalance.text = worker.balance(currentText).format()
console.log(worker.balance(currentText).format())
}
}
Label
{
id: accountBalance
}
}
RowLayout
{
Layout.fillWidth: true
Rectangle
{
width: labelWidth
Label
{
text: qsTr("Gas Price")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
Ether
{
id: gasPriceInput
displayUnitSelection: true
displayFormattedValue: true
edit: true
}
Connections
{
target: gasPriceInput
onValueChanged:
{
ctrDeployCtrLabel.calculateContractDeployGas()
}
onAmountChanged:
{
ctrDeployCtrLabel.setCost()
}
onUnitChanged:
{
ctrDeployCtrLabel.setCost()
}
}
Connections
{
target: worker
id: gasPriceLoad
property bool loaded: false
onGasPriceLoaded:
{
gasPriceInput.value = QEtherHelper.createEther(worker.gasPriceInt.value(), QEther.Wei)
gasPriceLoad.loaded = true
ctrDeployCtrLabel.calculateContractDeployGas()
}
}
}
RowLayout
{
id: ctrDeployCtrLabel
Layout.fillWidth: true
property int cost
function calculateContractDeployGas()
{
if (!root.visible)
return;
var sce = projectModel.stateListModel.getState(contractList.currentIndex)
worker.estimateGas(sce, function(gas) {
if (gasPriceLoad.loaded)
{
root.gas = gas
cost = 0
for (var k in gas)
{
cost += gas[k]
}
setCost()
}
});
}
function setCost()
{
var ether = QEtherHelper.createBigInt(cost);
var gasTotal = ether.multiply(gasPriceInput.value);
gasToUseInput.value = QEtherHelper.createEther(gasTotal.value(), QEther.Wei, parent);
}
Rectangle
{
width: labelWidth
Label
{
text: qsTr("Cost Estimate")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
Ether
{
id: gasToUseInput
displayUnitSelection: false
displayFormattedValue: true
edit: false
Layout.preferredWidth: 350
}
}
RowLayout
{
id: deployedRow
Layout.fillWidth: true
Rectangle
{
width: labelWidth
Label
{
id: labelAddresses
text: qsTr("Deployed Contracts")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
ColumnLayout
{
anchors.top: parent.top
anchors.topMargin: 1
ListModel
{
id: deployedAddrModel
}
Repeater
{
id: deployedAddresses
model: deployedAddrModel
function refresh()
{
deployedAddrModel.clear()
deployedRow.visible = Object.keys(projectModel.deploymentAddresses).length > 0
for (var k in projectModel.deploymentAddresses)
{
if (k.indexOf("-") !== -1) // this is an contract instance. ctr without - are the last deployed (to support old project)
deployedAddrModel.append({ id: k, value: projectModel.deploymentAddresses[k]})
}
}
Rectangle
{
Layout.preferredHeight: 20
Layout.preferredWidth: 235
color: "transparent"
Label
{
id: labelContract
width: 112
elide: Text.ElideRight
text: index > -1 ? deployedAddrModel.get(index).id : ""
}
TextField
{
width: 123
anchors.verticalCenter: parent.verticalCenter
anchors.left: labelContract.right
text: index > - 1 ? deployedAddrModel.get(index).value : ""
}
}
}
}
}
RowLayout
{
id: verificationRow
Layout.fillWidth: true
visible: Object.keys(projectModel.deploymentAddresses).length > 0
Rectangle
{
width: labelWidth
Label
{
text: qsTr("Verifications")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
Label
{
id: verificationLabel
maximumLineCount: 20
}
}
}
Rectangle
{
Layout.preferredWidth: parent.width
Layout.alignment: Qt.BottomEdge
Button
{
anchors.right: parent.right
text: qsTr("Deploy Contracts")
onClicked:
{
projectModel.deployedScenarioIndex = contractList.currentIndex
NetworkDeploymentCode.deployContracts(root.gas, function(addresses, trHashes)
{
projectModel.deploymentTrHashes = trHashes
worker.verifyHashes(trHashes, function (nb, trLost)
{
projectModel.deployBlockNumber = nb
projectModel.saveProject()
root.updateVerification(nb, trLost)
})
projectModel.deploymentAddresses = addresses
projectModel.saveProject()
deployedAddresses.refresh()
});
}
}
}
}
}
}

585
mix/qml/DeploymentDialog.qml

@ -14,22 +14,14 @@ import "."
Dialog {
id: modalDeploymentDialog
modality: Qt.ApplicationModal
width: 735
width: 1000
height: 450
visible: false
property int ownedRegistrarDeployGas: 1179075 // TODO: Use sol library to calculate gas requirement for each tr.
property int ownedRegistrarSetSubRegistrarGas: 50000
property int ownedRegistrarSetContentHashGas: 50000
property int urlHintSuggestUrlGas: 70000
property alias applicationUrlEth: applicationUrlEth.text
property alias applicationUrlHttp: applicationUrlHttp.text
property alias localPackageUrl: localPackageUrl.text
property string packageHash
property string packageBase64
property string eth: registrarAddr.text
property string currentAccount
property string gasPrice
property variant paramsModel: []
property alias deployStep: deployStep
property alias packageStep: packageStep
property alias registerStep: registerStep
property alias worker: worker
function close()
{
@ -38,552 +30,127 @@ Dialog {
function open()
{
deployStep.visible = false
packageStep.visible = false
registerStep.visible = false
steps.init()
worker.renewCtx()
visible = true;
var requests = [{
//accounts
jsonrpc: "2.0",
method: "eth_accounts",
params: null,
id: 0
}];
TransactionHelper.rpcCall(requests, function(arg1, arg2)
{
modelAccounts.clear();
var ids = JSON.parse(arg2)[0].result;
requests = [];
for (var k in ids)
{
modelAccounts.append({ "id": ids[k] })
requests.push({
//accounts
jsonrpc: "2.0",
method: "eth_getBalance",
params: [ids[k], 'latest'],
id: k
});
}
if (ids.length > 0)
currentAccount = modelAccounts.get(0).id;
TransactionHelper.rpcCall(requests, function (request, response){
var balanceRet = JSON.parse(response);
for (var k in balanceRet)
{
var ether = QEtherHelper.createEther(balanceRet[k].result, QEther.Wei);
comboAccounts.balances.push(ether.format());
comboAccounts.weiBalances.push(balanceRet[k].result);
}
balance.text = comboAccounts.balances[0];
});
});
if (clientModel.gasCosts.length === 0)
{
errorDialog.text = qsTr("Please run the state one time before deploying in order to calculate gas requirement.");
errorDialog.open();
}
else
{
NetworkDeploymentCode.gasPrice(function(price) {
gasPrice = price;
gasPriceInt.setValue(gasPrice);
ctrDeployCtrLabel.calculateContractDeployGas();
ctrRegisterLabel.calculateRegisterGas();
});
}
}
function stopForInputError(inError)
DeploymentWorker
{
errorDialog.text = "";
if (inError.length > 0)
{
errorDialog.text = qsTr("The length of a string cannot exceed 32 characters.\nPlease verify the following value(s):\n\n")
for (var k in inError)
errorDialog.text += inError[k] + "\n";
errorDialog.open();
return true;
}
return false;
}
function pad(h)
{
// TODO move this to QHashType class
while (h.length < 64)
{
h = '0' + h;
}
return h;
}
function waitForTrCountToIncrement(callBack)
{
poolLog.callBack = callBack;
poolLog.k = -1;
poolLog.elapsed = 0;
poolLog.start();
}
BigIntValue
{
id: gasPriceInt
}
Timer
{
id: poolLog
property var callBack
property int k: -1
property int elapsed
interval: 500
running: false
repeat: true
onTriggered: {
elapsed += interval;
var requests = [];
var jsonRpcRequestId = 0;
requests.push({
jsonrpc: "2.0",
method: "eth_getTransactionCount",
params: [ currentAccount, "pending" ],
id: jsonRpcRequestId++
});
TransactionHelper.rpcCall(requests, function (httpRequest, response){
response = response.replace(/,0+/, ''); // ==> result:27,00000000
var count = JSON.parse(response)[0].result
console.log("count " + count);
if (k < parseInt(count) && k > 0)
{
stop();
callBack(1);
}
else if (elapsed > 25000)
{
stop();
callBack(-1);
}
else
k = parseInt(JSON.parse(response)[0].result);
})
}
}
SourceSansProRegular
{
id: lightFont
id: worker
}
contentItem: Rectangle {
color: appStyle.generic.layout.backgroundColor
anchors.fill: parent
Column
ColumnLayout
{
spacing: 5
anchors.fill: parent
anchors.margins: 10
ColumnLayout
RowLayout
{
id: containerDeploy
Layout.fillWidth: true
Layout.preferredHeight: 500
RowLayout
id: explanation
Layout.preferredWidth: parent.width - 50
Layout.preferredHeight: 50
Label
{
Rectangle
{
Layout.preferredWidth: 357
DefaultLabel
{
text: qsTr("Deployment")
font.family: lightFont.name
font.underline: true
anchors.centerIn: parent
}
}
Button
{
action: displayHelpAction
iconSource: "qrc:/qml/img/help.png"
}
Action {
id: displayHelpAction
tooltip: qsTr("Help")
onTriggered: {
Qt.openUrlExternally("https://github.com/ethereum/wiki/wiki/Mix:-The-DApp-IDE#deployment-to-network")
}
}
Button
{
action: openFolderAction
iconSource: "qrc:/qml/img/openedfolder.png"
}
Action {
id: openFolderAction
enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Open Package Folder")
onTriggered: {
fileIo.openFileBrowser(projectModel.deploymentDir);
}
}
Button
{
action: b64Action
iconSource: "qrc:/qml/img/b64.png"
}
Action {
id: b64Action
enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Copy Base64 conversion to ClipBoard")
onTriggered: {
clipboard.text = deploymentDialog.packageBase64;
}
}
Button
{
action: exitAction
iconSource: "qrc:/qml/img/exit.png"
}
Action {
id: exitAction
tooltip: qsTr("Exit")
onTriggered: {
close()
}
}
anchors.centerIn: parent
text: qsTr("Putting your dapp live is a multi step process. You can read more about it on the 'guide to uploading'.")
}
}
GridLayout
RowLayout
{
ColumnLayout
{
columns: 2
width: parent.width
DefaultLabel
Layout.preferredHeight: parent.height - 50
Layout.preferredWidth: 200
DeploymentDialogSteps
{
text: qsTr("State:")
id: steps
worker: worker
}
}
Rectangle
Connections
{
target: steps
property variant selected
onSelected:
{
width: 300
color: "transparent"
height: 25
id: paramsRect
ComboBox
if (selected)
selected.visible = false
switch (step)
{
id: statesList
textRole: "title"
model: projectModel.stateListModel
onCurrentIndexChanged : {
ctrDeployCtrLabel.calculateContractDeployGas();
ctrRegisterLabel.calculateRegisterGas();
}
}
}
DefaultLabel
{
text: qsTr("Root Registrar address:")
visible: true //still use it for now in dev env.
}
DefaultTextField
{
Layout.preferredWidth: 350
id: registrarAddr
text: "c6d9d2cd449a754c494264e1809c50e34d64562b"
visible: true
}
DefaultLabel
{
text: qsTr("Account used to deploy:")
}
Rectangle
{
width: 300
height: 25
color: "transparent"
ComboBox {
id: comboAccounts
property var balances: []
property var weiBalances: []
onCurrentIndexChanged : {
if (modelAccounts.count > 0)
{
currentAccount = modelAccounts.get(currentIndex).id;
balance.text = balances[currentIndex];
balanceInt.setValue(weiBalances[currentIndex]);
ctrDeployCtrLabel.calculateContractDeployGas();
ctrRegisterLabel.calculateRegisterGas();
}
}
model: ListModel {
id: modelAccounts
}
case "deploy":
{
selected = deployStep
break;
}
DefaultLabel
case "package":
{
anchors.verticalCenter: parent.verticalCenter
anchors.left: comboAccounts.right
anchors.leftMargin: 20
id: balance;
selected = packageStep
break;
}
BigIntValue
case "register":
{
id: balanceInt
selected = registerStep
break;
}
}
selected.show()
}
}
DefaultLabel
ColumnLayout
{
text: qsTr("Amount of gas to use for contract deployment: ")
id: ctrDeployCtrLabel
function calculateContractDeployGas()
Layout.preferredHeight: parent.height - 50
Layout.preferredWidth: parent.width - 200
DeployContractStep
{
var ether = QEtherHelper.createBigInt(NetworkDeploymentCode.gasUsed());
var gasTotal = ether.multiply(gasPriceInt);
gasToUseInput.value = QEtherHelper.createEther(gasTotal.value(), QEther.Wei, parent);
gasToUseDeployInput.update();
id: deployStep
visible: false
worker: worker
}
}
Ether {
id: gasToUseInput
displayUnitSelection: false
displayFormattedValue: true
Layout.preferredWidth: 350
}
DefaultLabel
{
text: qsTr("Amount of gas to use for dapp registration: ")
id: ctrRegisterLabel
function calculateRegisterGas()
PackagingStep
{
if (!modalDeploymentDialog.visible)
return;
appUrlFormatted.text = NetworkDeploymentCode.formatAppUrl(applicationUrlEth.text).join('/');
NetworkDeploymentCode.checkPathCreationCost(function(pathCreationCost)
{
var ether = QEtherHelper.createBigInt(pathCreationCost);
var gasTotal = ether.multiply(gasPriceInt);
gasToUseDeployInput.value = QEtherHelper.createEther(gasTotal.value(), QEther.Wei, parent);
gasToUseDeployInput.update();
});
id: packageStep
visible: false
worker: worker
}
}
Ether {
id: gasToUseDeployInput
displayUnitSelection: false
displayFormattedValue: true
Layout.preferredWidth: 350
}
DefaultLabel
{
text: qsTr("Ethereum Application URL: ")
}
Rectangle
{
Layout.fillWidth: true
height: 25
color: "transparent"
DefaultTextField
RegisteringStep
{
width: 200
id: applicationUrlEth
onTextChanged: {
ctrRegisterLabel.calculateRegisterGas();
}
}
DefaultLabel
{
id: appUrlFormatted
anchors.verticalCenter: parent.verticalCenter;
anchors.left: applicationUrlEth.right
font.italic: true
font.pointSize: appStyle.absoluteSize(-1)
id: registerStep
visible: false
worker: worker
}
}
}
RowLayout
{
Layout.fillWidth: true
Rectangle
{
Layout.preferredWidth: 357
color: "transparent"
}
Button
{
id: deployButton
action: runAction
iconSource: "qrc:/qml/img/run.png"
}
Action {
id: runAction
tooltip: qsTr("Deploy contract(s) and Package resources files.")
onTriggered: {
var inError = [];
var ethUrl = NetworkDeploymentCode.formatAppUrl(applicationUrlEth.text);
for (var k in ethUrl)
{
if (ethUrl[k].length > 32)
inError.push(qsTr("Member too long: " + ethUrl[k]) + "\n");
}
if (!stopForInputError(inError))
{
projectModel.deployedState = statesList.currentText;
if (contractRedeploy.checked)
deployWarningDialog.open();
else
NetworkDeploymentCode.startDeployProject(false);
}
}
}
CheckBox
{
anchors.left: deployButton.right
id: contractRedeploy
enabled: Object.keys(projectModel.deploymentAddresses).length > 0
checked: Object.keys(projectModel.deploymentAddresses).length == 0
text: qsTr("Deploy Contract(s)")
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle
{
width: parent.width
height: 1
color: "#5891d3"
}
ColumnLayout
{
id: containerRegister
Layout.fillWidth: true
Layout.preferredHeight: 500
RowLayout
{
Layout.preferredHeight: 25
Rectangle
{
Layout.preferredWidth: 356
DefaultLabel
{
text: qsTr("Registration")
font.family: lightFont.name
font.underline: true
anchors.centerIn: parent
}
}
}
GridLayout
{
columns: 2
Layout.fillWidth: true
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("Local package URL")
}
DefaultTextField
{
Layout.preferredWidth: 350
id: localPackageUrl
readOnly: true
}
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("Web Application Resources URL: ")
}
DefaultTextField
{
Layout.preferredWidth: 350
id: applicationUrlHttp
enabled: rowRegister.isOkToRegister()
}
}
RowLayout
Layout.preferredHeight: 30
color: "transparent"
Button
{
id: rowRegister
Layout.fillWidth: true
Rectangle
text: qsTr("Cancel")
anchors.right: parent.right
anchors.rightMargin: 10
onClicked:
{
Layout.preferredWidth: 357
color: "transparent"
}
function isOkToRegister()
{
return Object.keys(projectModel.deploymentAddresses).length > 0 && deploymentDialog.packageHash !== "";
}
Button {
action: registerAction
iconSource: "qrc:/qml/img/note.png"
}
BigIntValue
{
id: registerUrlHintGas
Component.onCompleted:
{
setValue(modalDeploymentDialog.urlHintSuggestUrlGas);
}
}
Action {
id: registerAction
enabled: rowRegister.isOkToRegister()
tooltip: qsTr("Register hosted Web Application.")
onTriggered: {
if (applicationUrlHttp.text === "" || deploymentDialog.packageHash === "")
{
deployDialog.title = text;
deployDialog.text = qsTr("Please provide the link where the resources are stored and ensure the package is aleary built using the deployment step.")
deployDialog.open();
return;
}
var inError = [];
if (applicationUrlHttp.text.length > 32)
inError.push(qsTr(applicationUrlHttp.text));
if (!stopForInputError(inError))
NetworkDeploymentCode.registerToUrlHint();
}
modalDeploymentDialog.close()
}
}
}
}
}

210
mix/qml/DeploymentDialogSteps.qml

@ -0,0 +1,210 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
Rectangle {
anchors.fill: parent
color: "white"
property var worker
property variant sel
signal selected(string step)
id: root
function refreshCurrent()
{
menu.itemAt(sel).select()
}
function init()
{
menu.itemAt(0).select()
}
function itemClicked(step)
{
selected(step)
}
border.color: "#cccccc"
border.width: 1
ColumnLayout
{
anchors.fill: parent
anchors.margins: 1
Repeater
{
id: menu
model: [
{
step: 1,
type:"deploy",
label: qsTr("Deploy contracts")
},
{
step: 2,
type:"package",
label: qsTr("Package files")
},
{
step: 3,
type:"register",
label: qsTr("Register Dapp")
}
]
Rectangle
{
Layout.preferredHeight: 50
Layout.fillWidth: true
color: "white"
id: itemContainer
function select()
{
if (sel !== undefined)
{
menu.itemAt(sel).unselect()
}
labelContainer.state = "selected"
sel = index
itemClicked(menu.model[index].type)
}
function unselect()
{
labelContainer.state = ""
}
Rectangle {
width: 40
height: 40
color: "transparent"
border.color: "#cccccc"
border.width: 2
radius: width*0.5
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
id: labelContainer
Label
{
color: "#cccccc"
id: label
anchors.centerIn: parent
text: menu.model[index].step
}
states: [
State {
name: "selected"
PropertyChanges { target: label; color: "white" }
PropertyChanges { target: labelContainer.border; color: "white" }
PropertyChanges { target: detail; color: "white" }
PropertyChanges { target: itemContainer; color: "#3395FE" }
}
]
}
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.left: label.parent.right
width: parent.width - 40
height: 40
color: "transparent"
Label
{
id: detail
color: "black"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
text: menu.model[index].label
}
}
MouseArea
{
anchors.fill: parent
onClicked:
{
itemContainer.select()
}
}
}
}
Connections {
target: projectModel
onDeploymentStarted: log.text = log.text + qsTr("Running deployment...") + "\n"
onDeploymentError: log.text = log.text + error + "\n"
onDeploymentComplete: log.text = log.text + qsTr("Deployment complete") + "\n"
onDeploymentStepChanged: log.text = log.text + message + "\n"
}
Rectangle
{
Layout.fillWidth: true
Layout.preferredHeight: 1
color: "#cccccc"
}
RowLayout
{
anchors.horizontalCenter: parent.horizontalCenter
Layout.preferredHeight: 20
anchors.left: parent.left
anchors.leftMargin: 2
Button
{
Layout.preferredHeight: 22
Layout.preferredWidth: 22
action: clearAction
iconSource: "qrc:/qml/img/cleariconactive.png"
}
Action {
id: clearAction
enabled: log.text !== ""
tooltip: qsTr("Clear")
onTriggered: {
log.text = ""
}
}
Button
{
Layout.preferredHeight: 22
text: qsTr("Clear Deployment")
action: clearDeployAction
}
Action {
id: clearDeployAction
onTriggered: {
worker.forceStopPooling()
fileIo.deleteDir(projectModel.deploymentDir)
projectModel.cleanDeploymentStatus()
root.refreshCurrent()
log.text = ""
}
}
}
ScrollView
{
Layout.fillHeight: true
Layout.fillWidth: true
Text
{
anchors.left: parent.left
anchors.leftMargin: 2
font.pointSize: 9
font.italic: true
id: log
}
}
}
}

239
mix/qml/DeploymentWorker.qml

@ -0,0 +1,239 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import org.ethereum.qml.CodeModel 1.0
import org.ethereum.qml.ClientModel 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
import "."
Item
{
property string currentAccount
property string gasPrice
property alias gasPriceInt: gasPriceInt
property variant balances: ({})
property variant accounts: []
signal gasPriceLoaded()
function renewCtx()
{
accounts = []
balances = {}
var requests = [{
//accounts
jsonrpc: "2.0",
method: "eth_accounts",
params: null,
id: 0
}];
TransactionHelper.rpcCall(requests, function(arg1, arg2)
{
var ids = JSON.parse(arg2)[0].result;
requests = [];
for (var k in ids)
{
requests.push({
//accounts
jsonrpc: "2.0",
method: "eth_getBalance",
params: [ids[k], 'latest'],
id: k
});
accounts.push({ "id": ids[k] })
}
TransactionHelper.rpcCall(requests, function (request, response){
var balanceRet = JSON.parse(response);
for (var k in balanceRet)
{
var ether = QEtherHelper.createEther(balanceRet[k].result, QEther.Wei);
balances[accounts[k].id] = ether
}
}, function(){});
}, function(){});
NetworkDeploymentCode.gasPrice(function(price) {
gasPrice = price;
gasPriceInt.setValue(price);
gasPriceLoaded()
}, function(){});
}
function balance(account)
{
for (var k in accounts)
{
if (accounts[k].id === account)
return balances[account]
}
return null
}
function stopForInputError(inError)
{
errorDialog.text = "";
if (inError.length > 0)
{
errorDialog.text = qsTr("The length of a string cannot exceed 32 characters.\nPlease verify the following value(s):\n\n")
for (var k in inError)
errorDialog.text += inError[k] + "\n";
errorDialog.open();
return true;
}
return false;
}
function forceStopPooling()
{
poolLog.stop()
}
function waitForTrReceipt(hash, callback)
{
poolLog.callBack = callback;
poolLog.hash = hash
poolLog.elapsed = 0;
poolLog.start();
}
function verifyHash(tr, hash, callBack)
{
var h = {}
h[tr] = hash
verifyHashes(h, function (bn, trLost)
{
callBack(bn, trLost)
});
}
function verifyHashes(trHashes, callback)
{
//trHashes : { "trLabel": 'hash' }
var requests = [];
var req = 0
requests.push({
jsonrpc: "2.0",
method: "eth_blockNumber",
params: [],
id: req
});
var label = {}
for (var k in trHashes)
{
req++
label[req] = k
requests.push({
jsonrpc: "2.0",
method: "eth_getTransactionReceipt",
params: [trHashes[k]],
id: req
});
}
TransactionHelper.rpcCall(requests, function (httpRequest, response){
console.log(response)
var ret = JSON.parse(response)
var b = ret[0].result;
var trLost = []
for (var k in ret)
{
if (ret[k].result === null)
trLost.push(label[ret[k]])
}
callback(parseInt(b, 16), trLost)
});
}
Component.onCompleted:
{
renewCtx()
}
BigIntValue
{
id: gasPriceInt
}
function estimateGas(scenario, callback)
{
if (!clientModelGasEstimation.running)
{
var ctr = projectModel.codeEditor.getContracts()
for (var k in ctr)
{
codeModelGasEstimation.registerCodeChange(ctr[k].document.documentId, ctr[k].getText());
}
gasEstimationConnect.callback = callback
clientModelGasEstimation.setupScenario(scenario)
}
}
Connections
{
id: gasEstimationConnect
target: clientModelGasEstimation
property var callback
onRunComplete: {
callback(clientModelGasEstimation.gasCosts)
}
}
CodeModel {
id: codeModelGasEstimation
}
ClientModel {
id: clientModelGasEstimation
codeModel: codeModelGasEstimation
Component.onCompleted:
{
init("/tmp/bcgas/")
}
}
Timer
{
id: poolLog
property var callBack
property int elapsed
property string hash
interval: 500
running: false
repeat: true
onTriggered: {
elapsed += interval;
var requests = [];
var jsonRpcRequestId = 0;
requests.push({
jsonrpc: "2.0",
method: "eth_getTransactionReceipt",
params: [ hash ],
id: jsonRpcRequestId++
});
TransactionHelper.rpcCall(requests, function (httpRequest, response){
console.log(response)
var receipt = JSON.parse(response)[0].result
if (receipt)
{
stop();
callBack(1, receipt);
}
else if (elapsed > 250000)
{
stop();
callBack(-1, null);
}
})
}
}
}

7
mix/qml/Ether.qml

@ -18,6 +18,8 @@ RowLayout {
property bool displayUnitSelection
onValueChanged: update()
Component.onCompleted: update()
signal amountChanged
signal unitChanged
function update()
@ -37,16 +39,17 @@ RowLayout {
DefaultTextField
{
implicitWidth: 200
onTextChanged:
{
if (value !== undefined)
{
value.setValue(text)
formattedValue.text = value.format();
amountChanged()
}
}
readOnly: !edit
visible: edit
id: etherValueEdit;
}
@ -54,13 +57,13 @@ RowLayout {
{
id: units
visible: displayUnitSelection;
implicitWidth: 145
onCurrentTextChanged:
{
if (value)
{
value.setUnit(currentText);
formattedValue.text = value.format();
unitChanged()
}
}

207
mix/qml/PackagingStep.qml

@ -0,0 +1,207 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
Rectangle {
property variant paramsModel: []
property variant worker
color: "#E3E3E3E3"
anchors.fill: parent
id: root
property string packageHash
property string packageBase64
property alias localPackageUrl: localPackageUrl.text
property alias lastDeployDate: lastDeployLabel.text
property string deploymentId
property string packageDir
function show()
{
visible = true
}
FileDialog {
id: ressourcesFolder
visible: false
title: qsTr("Please choose a path")
selectFolder: true
property variant target
onAccepted: {
var u = ressourcesFolder.fileUrl.toString();
if (u.indexOf("file://") == 0)
u = u.substring(7, u.length)
if (Qt.platform.os == "windows" && u.indexOf("/") == 0)
u = u.substring(1, u.length);
target.text = u;
}
}
ColumnLayout
{
anchors.top: parent.top
anchors.topMargin: 10
width: parent.width
id: col
spacing: 20
Label
{
anchors.top: parent.top
Layout.fillWidth: true
anchors.left: parent.left
anchors.leftMargin: 10
text: qsTr("Upload and update your Dapp assets")
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 2
Label
{
text: qsTr("Save Package to")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
DefaultTextField
{
id: packageFolder
visible: true
Layout.preferredWidth: 150
text: projectPath + "package/"
}
Button
{
text: qsTr("select")
onClicked: {
ressourcesFolder.target = packageFolder
ressourcesFolder.open()
}
}
}
Rectangle
{
Layout.fillWidth: true
Layout.preferredHeight: 40
color: "transparent"
Button
{
id: generatePackageBtn
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Generate Package")
onClicked:
{
NetworkDeploymentCode.packageDapp(projectModel.deploymentAddresses);
projectModel.saveProject()
}
}
RowLayout
{
anchors.top: generatePackageBtn.bottom
anchors.topMargin: 10
visible: root.lastDeployDate !== ""
anchors.horizontalCenter: parent.horizontalCenter
Label
{
id: lastPackage
text: qsTr("Last Package")
}
Label
{
id: lastDeployLabel
}
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 2
Label
{
text: qsTr("Local package URL")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
DefaultTextField
{
id: localPackageUrl
Layout.preferredWidth: 235
readOnly: true
}
}
Label
{
Layout.preferredWidth: 300
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("You have to upload the package to a remote folder, or use a service like pastebin")
wrapMode: Text.WordWrap
clip: true
}
Rectangle
{
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
Button
{
Layout.preferredWidth: 200
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Copy Base64")
onClicked:
{
clipboard.text = deploymentDialog.packageStep.packageBase64;
}
}
}
Rectangle
{
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
Button
{
Layout.preferredWidth: 200
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Open pastebin")
onClicked:
{
Qt.openUrlExternally("http://pastebin.com/");
}
}
}
}
}

35
mix/qml/ProjectModel.qml

@ -40,7 +40,7 @@ Item {
property string projectPath: ""
property string projectTitle: ""
property string currentDocumentId: ""
property var deploymentAddresses: []
property var deploymentAddresses: ({})
property string deploymentDir
property var listModel: projectListModel
property var stateListModel: projectStateListModel.model
@ -48,7 +48,15 @@ Item {
property CodeEditorView codeEditor: null
property var unsavedFiles: []
property alias newProjectDialog: newProjectDialog
property string deployedState
property int deployedScenarioIndex
property string applicationUrlEth
property string applicationUrlHttp
property string deployBlockNumber
property var deploymentTrHashes
property string registerContentHashTrHash
property string registerUrlTrHash
property int registerContentHashBlockNumber: -1
property int registerUrlBlockNumber: -1
//interface
function saveAll() { ProjectModelCode.saveAll(); }
@ -72,9 +80,30 @@ Item {
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); }
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); }
function deployProject() { NetworkDeploymentCode.deployProject(false); }
function registerToUrlHint() { NetworkDeploymentCode.registerToUrlHint(); }
function registerToUrlHint(url, callback) { NetworkDeploymentCode.registerToUrlHint(url, callback); }
function formatAppUrl() { NetworkDeploymentCode.formatAppUrl(url); }
function cleanDeploymentStatus()
{
deployedScenarioIndex = 0
applicationUrlEth = ""
applicationUrlHttp = ""
deployBlockNumber = ""
deploymentTrHashes = {}
registerContentHashTrHash = ""
registerUrlTrHash = ""
registerContentHashBlockNumber = -1
registerUrlBlockNumber = -1
deploymentAddresses = {}
deploymentDir = ""
deploymentDialog.packageStep.packageHash = ""
deploymentDialog.packageStep.packageBase64 = ""
deploymentDialog.packageStep.packageDir = ""
deploymentDialog.packageStep.lastDeployDate = ""
deploymentDialog.packageStep.localPackageUrl = ""
saveProject()
}
Connections {
target: mainApplication
onLoaded: {

310
mix/qml/RegisteringStep.qml

@ -0,0 +1,310 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import Qt.labs.settings 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
import "."
Rectangle {
property variant worker
property string eth: registrarAddr.text
property int ownedRegistrarDeployGas: 1179075 // TODO: Use sol library to calculate gas requirement for each tr.
property int ownedRegistrarSetSubRegistrarGas: 50000
property int ownedRegistrarSetContentHashGas: 50000
property int urlHintSuggestUrlGas: 70000
id: root
color: "#E3E3E3E3"
anchors.fill: parent
function show()
{
ctrRegisterLabel.calculateRegisterGas()
applicationUrlEthCtrl.text = projectModel.applicationUrlEth
applicationUrlHttpCtrl.text = projectModel.applicationUrlHttp
visible = true
verificationEthUrl.text = ""
if (projectModel.registerContentHashTrHash !== "")
{
worker.verifyHash("registerHash", projectModel.registerContentHashTrHash, function(bn, trLost)
{
updateVerification(projectModel.registerContentHashBlockNumber, bn, trLost, verificationEthUrl)
});
}
verificationUrl.text = ""
if (projectModel.registerUrlTrHash !== "")
{
worker.verifyHash("registerUrl", projectModel.registerUrlTrHash, function(bn, trLost)
{
updateVerification(projectModel.registerUrlBlockNumber, bn, trLost, verificationUrl)
});
}
}
function updateVerification(originbn, bn, trLost, ctrl)
{
if (trLost.length === 0)
{
ctrl.text = bn - originbn
ctrl.text += qsTr(" verifications")
}
else
{
ctrl.text = qsTr("invalidated")
}
}
ColumnLayout
{
anchors.top: parent.top
width: parent.width
anchors.topMargin: 10
id: col
spacing: 20
Label
{
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: 10
Layout.fillWidth: true
text: qsTr("Register your Dapp on the Name registrar Contract")
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 2
Label
{
text: qsTr("Root Registrar address")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
DefaultTextField
{
id: registrarAddr
text: "c6d9d2cd449a754c494264e1809c50e34d64562b"
visible: true
Layout.preferredWidth: 235
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 2
Label
{
text: qsTr("Http URL")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
DefaultTextField
{
id: applicationUrlHttpCtrl
Layout.preferredWidth: 235
}
Label
{
id: verificationUrl
anchors.verticalCenter: parent.verticalCenter
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 2
Label
{
text: qsTr("Gas to use for dapp registration")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
id: ctrRegisterLabel
function calculateRegisterGas()
{
if (!modalDeploymentDialog.visible)
return;
appUrlFormatted.text = NetworkDeploymentCode.formatAppUrl(applicationUrlEthCtrl.text).join('/');
NetworkDeploymentCode.checkPathCreationCost(applicationUrlEthCtrl.text, function(pathCreationCost)
{
var ether = QEtherHelper.createBigInt(pathCreationCost);
var gasTotal = ether.multiply(worker.gasPriceInt);
gasToUseDeployInput.value = QEtherHelper.createEther(gasTotal.value(), QEther.Wei, parent);
gasToUseDeployInput.update();
});
}
}
}
Ether
{
id: gasToUseDeployInput
displayUnitSelection: true
displayFormattedValue: true
edit: true
Layout.preferredWidth: 235
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 2
Label
{
text: qsTr("Ethereum URL")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle
{
height: 25
color: "transparent"
Layout.preferredWidth: 235
DefaultTextField
{
width: 235
id: applicationUrlEthCtrl
onTextChanged: {
ctrRegisterLabel.calculateRegisterGas();
}
}
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 20
Rectangle
{
Layout.preferredWidth: col.width / 2
Label
{
text: qsTr("Formatted Ethereum URL")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
DefaultLabel
{
id: appUrlFormatted
anchors.verticalCenter: parent.verticalCenter;
anchors.topMargin: 10
font.italic: true
font.pointSize: appStyle.absoluteSize(-1)
}
Label
{
id: verificationEthUrl
}
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
width: parent.width
function registerHash(callback)
{
var inError = [];
var ethUrl = NetworkDeploymentCode.formatAppUrl(applicationUrlEthCtrl.text);
for (var k in ethUrl)
{
if (ethUrl[k].length > 32)
inError.push(qsTr("Member too long: " + ethUrl[k]) + "\n");
}
if (!worker.stopForInputError(inError))
{
NetworkDeploymentCode.registerDapp(ethUrl, function(){
projectModel.applicationUrlEth = applicationUrlEthCtrl.text
projectModel.saveProject()
worker.waitForTrReceipt(projectModel.registerContentHashTrHash, function(status, receipt)
{
worker.verifyHash("registerHash", projectModel.registerContentHashTrHash, function(bn, trLost)
{
projectModel.registerContentHashBlockNumber = bn
projectModel.saveProject()
root.updateVerification(bn, bn, trLost, verificationEthUrl)
callback()
});
});
})
}
}
function registerUrl()
{
if (applicationUrlHttp.text === "" || deploymentDialog.packageHash === "")
{
deployDialog.title = text;
deployDialog.text = qsTr("Please provide the link where the resources are stored and ensure the package is aleary built using the deployment step.")
deployDialog.open();
return;
}
var inError = [];
if (applicationUrlHttpCtrl.text.length > 32)
inError.push(qsTr(applicationUrlHttpCtrl.text));
if (!worker.stopForInputError(inError))
{
registerToUrlHint(applicationUrlHttpCtrl.text, function(){
projectModel.applicationUrlHttp = applicationUrlHttpCtrl.text
projectModel.saveProject()
worker.waitForTrReceipt(projectModel.registerUrlTrHash, function(status, receipt)
{
worker.verifyHash("registerUrl", projectModel.registerUrlTrHash, function(bn, trLost)
{
projectModel.registerUrlBlockNumber = bn
projectModel.saveProject()
root.updateVerification(bn, bn, trLost, verificationUrl)
});
})
})
}
}
Button
{
anchors.right: parent.right
anchors.rightMargin: 10
text: qsTr("Register Dapp")
width: 30
onClicked:
{
parent.registerHash(function(){
parent.registerUrl()
})
}
}
}
}

8
mix/qml/ScenarioButton.qml

@ -30,8 +30,8 @@ Rectangle {
id: left
width: 10
height: parent.height
anchors.left: parent.left
anchors.leftMargin: -8
anchors.left: contentRectangle.left
anchors.leftMargin: -4
radius: 15
}
@ -111,8 +111,8 @@ Rectangle {
id: right
width: 10
height: parent.height
anchors.left: contentRectangle.right
anchors.leftMargin: -8
anchors.right: contentRectangle.right
anchors.rightMargin: -4
radius: 15
}

1
mix/qml/ScenarioExecution.qml

@ -32,7 +32,6 @@ Rectangle {
{
id: columnExe
Layout.preferredWidth: parent.width
width: parent.width - 40
anchors.left: parent.left
anchors.leftMargin: 15
ColumnLayout

276
mix/qml/ScenarioLoader.qml

@ -6,6 +6,7 @@ import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.InverseMouseArea 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "."
@ -18,6 +19,7 @@ ColumnLayout
signal duplicated(variant scenario)
signal loaded(variant scenario)
signal renamed(variant scenario)
signal deleted()
spacing: 0
function init()
{
@ -29,146 +31,92 @@ ColumnLayout
editStatus.visible = true
}
Rectangle
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 30
color: "transparent"
Rectangle
Layout.preferredWidth: 560
anchors.horizontalCenter: parent.horizontalCenter
Layout.preferredHeight: 60
spacing: 0
anchors.top: parent.top
anchors.topMargin: 10
Row
{
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
color: "transparent"
Label
{
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
id: scenarioName
font.bold: true
}
Layout.preferredWidth: 100 * 5 + 30
Layout.preferredHeight: 50
spacing: 25
TextInput
Rectangle
{
id: scenarioNameEdit
visible: false
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Keys.onEnterPressed:
{
save()
}
function edit()
{
editIconRect.anchors.left = scenarioNameEdit.right
editStatus.parent.anchors.left = scenarioNameEdit.right
scenarioNameEdit.forceActiveFocus()
}
function save()
{
editIconRect.anchors.left = scenarioName.right
editStatus.parent.anchors.left = scenarioName.right
scenarioName.text = scenarioNameEdit.text
scenarioName.visible = true
scenarioNameEdit.visible = false
projectModel.stateListModel.getState(scenarioList.currentIndex).title = scenarioName.text
projectModel.saveProjectFile()
saved(state)
scenarioList.model.get(scenarioList.currentIndex).title = scenarioName.text
scenarioList.currentIndex = scenarioList.currentIndex
renamed(projectModel.stateListModel.getState(scenarioList.currentIndex))
}
}
color: "white"
width: 251 + 30
height: 30
Connections
{
target: blockChainSelector
onLoaded:
Rectangle
{
scenarioName.text = scenario.title
scenarioNameEdit.text = scenario.title
}
}
Rectangle
{
anchors.left: scenarioName.right
anchors.top: scenarioName.top
anchors.leftMargin: 2
Layout.preferredWidth: 20
Text {
id: editStatus
text: "*"
visible: false
id: left
width: 10
height: parent.height
anchors.left: parent.left
anchors.leftMargin: -5
radius: 15
}
}
Rectangle
{
id: editIconRect
anchors.top: scenarioName.top
anchors.topMargin: 6
anchors.left: scenarioName.right
anchors.leftMargin: 20
Image {
source: "qrc:/qml/img/edittransaction.png"
width: 30
height: parent.height - 10
fillMode: Image.PreserveAspectFit
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 4
id: editImg
MouseArea
{
anchors.fill: parent
onClicked:
{
scenarioName.visible = !scenarioName.visible
scenarioNameEdit.visible = !scenarioNameEdit.visible
if (!scenarioNameEdit.visible)
scenarioNameEdit.save()
else
scenarioNameEdit.edit()
scenarioNameEdit.toggleEdit()
}
}
}
}
}
}
RowLayout
{
Layout.preferredWidth: 560
anchors.horizontalCenter: parent.horizontalCenter
Layout.preferredHeight: 50
spacing: 0
Row
{
Layout.preferredWidth: 100 * 5
Layout.preferredHeight: 50
spacing: 15
Rectangle
{
color: "transparent"
width: 251
height: 30
Rectangle
Label
{
width: 10
anchors.left: editImg.right
text: "X"
height: parent.height
anchors.right: scenarioList.left
anchors.rightMargin: -8
radius: 15
color: "#cccccc"
id: deleteImg
anchors.top: parent.top
anchors.topMargin: 6
visible: projectModel.stateListModel.count > 1
MouseArea
{
anchors.fill: parent
onClicked:
{
if (projectModel.stateListModel.count > 1)
{
projectModel.stateListModel.deleteState(scenarioList.currentIndex)
scenarioList.currentIndex = 0
deleted()
}
}
}
}
ComboBox
{
id: scenarioList
anchors.left: deleteImg.right
anchors.leftMargin: 2
model: projectModel.stateListModel
anchors.top: parent.top
textRole: "title"
height: parent.height
width: 150
signal updateView()
onCurrentIndexChanged:
{
restoreScenario.restore()
@ -177,15 +125,18 @@ ColumnLayout
function load()
{
var state = projectModel.stateListModel.getState(currentIndex)
loaded(state)
if (state)
loaded(state)
}
style: ComboBoxStyle {
id: style
background: Rectangle {
color: "white"
anchors.fill: parent
}
label: Rectangle {
property alias label: comboLabel
anchors.fill: parent
color: "white"
Label {
@ -194,20 +145,33 @@ ColumnLayout
elide: Text.ElideRight
width: parent.width
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
text: {
if (projectModel.stateListModel.getState(scenarioList.currentIndex))
return projectModel.stateListModel.getState(scenarioList.currentIndex).title
else
return ""
anchors.left: parent.left
Component.onCompleted:
{
comboLabel.updateLabel()
}
function updateLabel()
{
comboLabel.text = ""
if (scenarioList.currentIndex > - 1 && scenarioList.currentIndex < projectModel.stateListModel.count)
comboLabel.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title
}
Connections {
target: blockChainSelector
onLoaded: {
comboLabel.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title
if (projectModel.stateListModel.count > 0)
comboLabel.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title
else
return ""
}
onRenamed: {
comboLabel.text = scenario.title
scenarioNameEdit.text = scenario.title
}
onDeleted: {
comboLabel.updateLabel()
}
}
}
@ -215,9 +179,74 @@ ColumnLayout
}
}
TextField
{
id: scenarioNameEdit
anchors.left: deleteImg.right
anchors.leftMargin: 2
height: parent.height
z: 5
visible: false
width: 150
Keys.onEnterPressed:
{
toggleEdit()
}
Keys.onReturnPressed:
{
toggleEdit()
}
function toggleEdit()
{
scenarioList.visible = !scenarioList.visible
scenarioNameEdit.visible = !scenarioNameEdit.visible
if (!scenarioNameEdit.visible)
scenarioNameEdit.save()
else
{
scenarioNameEdit.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title
scenarioNameEdit.forceActiveFocus()
outsideClick.active = true
}
}
function save()
{
outsideClick.active = false
projectModel.stateListModel.getState(scenarioList.currentIndex).title = scenarioNameEdit.text
projectModel.saveProjectFile()
saved(state)
scenarioList.model.get(scenarioList.currentIndex).title = scenarioNameEdit.text
scenarioList.currentIndex = scenarioList.currentIndex
renamed(projectModel.stateListModel.getState(scenarioList.currentIndex))
}
style: TextFieldStyle {
background: Rectangle {
radius: 2
implicitWidth: 100
implicitHeight: 30
color: "white"
border.color: "#cccccc"
border.width: 1
}
}
InverseMouseArea {
id: outsideClick
anchors.fill: parent
active: false
onClickedOutside: {
scenarioNameEdit.toggleEdit()
}
}
}
Rectangle
{
width: 1
width: 1
height: parent.height
anchors.right: addScenario.left
color: "#ededed"
@ -236,7 +265,6 @@ ColumnLayout
projectModel.stateListModel.appendState(item)
projectModel.stateListModel.save()
scenarioList.currentIndex = projectModel.stateListModel.count - 1
scenarioNameEdit.edit()
}
text: qsTr("New..")
roundRight: true
@ -251,15 +279,6 @@ ColumnLayout
height: 30
color: "transparent"
Rectangle
{
width: 10
height: parent.height
anchors.right: restoreScenario.left
anchors.rightMargin: -4
radius: 15
}
ScenarioButton {
id: restoreScenario
width: 100
@ -275,7 +294,6 @@ ColumnLayout
var state = projectModel.stateListModel.reloadStateFromFromProject(scenarioList.currentIndex)
if (state)
{
editStatus.visible = false
restored(state)
loaded(state)
}

5
mix/qml/StateListModel.qml

@ -403,7 +403,10 @@ Item {
function defaultStateName()
{
return stateList[defaultStateIndex].title;
if (stateList.length > 0)
return stateList[defaultStateIndex].title;
else
return ""
}
function reloadStateFromFromProject(index)

7
mix/qml/StatusPane.qml

@ -110,13 +110,6 @@ Rectangle {
}
}
Connections {
target:projectModel
onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "Deployment");
onDeploymentError: errorMessage(error, "Deployment");
onDeploymentComplete: infoMessage(qsTr("Deployment complete"), "Deployment");
onDeploymentStepChanged: infoMessage(message, "Deployment");
}
Connections {
target: codeModel
onCompilationComplete:

35
mix/qml/TransactionDialog.qml

@ -125,9 +125,9 @@ Dialog {
function loadParameters() {
paramsModel = []
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
var contract = codeModel.contracts[contractFromToken(contractCreationComboBox.currentValue())];
var contract = codeModel.contracts[TransactionHelper.contractFromToken(contractCreationComboBox.currentValue())];
if (contract) {
var func = contract.contract.functions[functionComboBox.currentIndex + 1];
var func = getFunction(functionComboBox.currentText, contract);
if (func) {
var parameters = func.parameters;
for (var p = 0; p < parameters.length; p++)
@ -138,6 +138,18 @@ Dialog {
initTypeLoader();
}
function getFunction(name, contract)
{
for (var k in contract.contract.functions)
{
if (contract.contract.functions[k].name === name)
{
return contract.contract.functions[k]
}
}
return null
}
function initTypeLoader()
{
paramScroll.value = {}
@ -188,7 +200,7 @@ Dialog {
if (!item.isContractCreation)
{
item.contractId = recipientsAccount.currentValue();
item.label = contractFromToken(item.contractId) + "." + item.functionId + "()";
item.label = TransactionHelper.contractFromToken(item.contractId) + "." + item.functionId + "()";
if (recipientsAccount.current().type === "address")
{
item.functionId = "";
@ -205,14 +217,7 @@ Dialog {
item.sender = senderComboBox.model[senderComboBox.currentIndex].secret;
item.parameters = paramValues;
return item;
}
function contractFromToken(token)
{
if (token.indexOf('<') === 0)
return token.replace("<", "").replace(">", "").split(" - ")[0];
return token;
}
}
function load(isContractCreation, isFunctionCall, functionId, contractId)
{
@ -236,7 +241,7 @@ Dialog {
{
labelRecipient.text = qsTr("Recipient Contract")
functionRect.show()
loadFunctions(contractFromToken(recipientsAccount.currentValue()))
loadFunctions(TransactionHelper.contractFromToken(recipientsAccount.currentValue()))
loadParameters();
paramScroll.updateView()
}
@ -416,7 +421,7 @@ Dialog {
onIndexChanged:
{
if (rbbuttonList.current.objectName === "trTypeExecute")
loadFunctions(contractFromToken(currentValue()))
loadFunctions(TransactionHelper.contractFromToken(currentValue()))
}
}
@ -587,7 +592,7 @@ Dialog {
target: functionComboBox
onCurrentIndexChanged:
{
estimatedGas.displayGas(contractFromToken(recipientsAccount.currentValue()), functionComboBox.currentText)
estimatedGas.displayGas(TransactionHelper.contractFromToken(recipientsAccount.currentValue()), functionComboBox.currentText)
}
}
@ -604,7 +609,7 @@ Dialog {
function updateView()
{
if (rbbuttonList.current.objectName === "trTypeExecute")
estimatedGas.displayGas(contractFromToken(recipientsAccount.currentValue()), functionComboBox.currentText)
estimatedGas.displayGas(TransactionHelper.contractFromToken(recipientsAccount.currentValue()), functionComboBox.currentText)
else if (rbbuttonList.current.objectName === "trTypeCreate")
{
var contractName = contractCreationComboBox.currentValue()

4
mix/qml/Watchers.qml

@ -83,7 +83,7 @@ Rectangle {
Label {
id: fromLabel
text: qsTr("from")
visible: from.text !== ""
visible: false
color: "#EAB920"
}
Label {
@ -97,7 +97,7 @@ Rectangle {
Label {
id: toLabel
text: qsTr("to")
visible: from.text !== ""
visible: false
color: "#EAB920"
}
Label {

200
mix/qml/js/NetworkDeployment.js

@ -32,38 +32,34 @@ function deployProject(force) {
deploymentDialog.open();
}
function startDeployProject(erasePrevious)
{
var date = new Date();
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
if (!erasePrevious)
{
finalizeDeployment(deploymentId, projectModel.deploymentAddresses);
return;
}
function deployContracts(gas, callback)
{
deploymentGas = gas;
var jsonRpcUrl = "http://127.0.0.1:8080";
console.log("Deploying " + deploymentId + " to " + jsonRpcUrl);
console.log("Deploying to " + jsonRpcUrl);
deploymentStarted();
var ctrAddresses = {};
var state = retrieveState(projectModel.deployedState);
console.log(JSON.stringify(state));
var state = retrieveState(projectModel.deployedScenarioIndex);
if (!state)
{
var txt = qsTr("Unable to find state " + projectModel.deployedState);
var txt = qsTr("Unable to find this scenario");
deploymentError(txt);
console.log(txt);
return;
}
executeTr(0, state, ctrAddresses, function (){
finalizeDeployment(deploymentId, ctrAddresses);
var trHashes = {}
executeTr(0, 0, state, ctrAddresses, trHashes, function(){
projectModel.deploymentAddresses = ctrAddresses;
deploymentStepChanged(qsTr("Scenario deployed. Please wait for verifications"))
if (callback)
callback(ctrAddresses, trHashes)
});
}
function checkPathCreationCost(callBack)
function checkPathCreationCost(ethUrl, callBack)
{
var dappUrl = formatAppUrl(deploymentDialog.applicationUrlEth);
var dappUrl = formatAppUrl(ethUrl);
checkEthPath(dappUrl, true, function(success, cause) {
if (!success)
{
@ -85,7 +81,7 @@ function checkPathCreationCost(callBack)
else
{
deploymentStepChanged(qsTr("Your Dapp can be registered here."));
callBack((dappUrl.length - 1) * (deploymentDialog.ownedRegistrarDeployGas + deploymentDialog.ownedRegistrarSetSubRegistrarGas) + deploymentDialog.ownedRegistrarSetContentHashGas);
callBack((dappUrl.length - 1) * (deploymentDialog.registerStep.ownedRegistrarDeployGas + deploymentDialog.registerStep.ownedRegistrarSetSubRegistrarGas) + deploymentDialog.registerStep.ownedRegistrarSetContentHashGas);
}
});
}
@ -97,19 +93,13 @@ function gasUsed()
for (var g in gasCosts)
{
gas += gasCosts[g];
console.log(" gasCost " + gasCosts[g]);
}
return gas;
}
function retrieveState(state)
function retrieveState(stateIndex)
{
for (var k = 0; k < projectModel.stateListModel.count; k++)
{
if (projectModel.stateListModel.get(k).title === state)
return projectModel.stateListModel.get(k);
}
return null;
return projectModel.stateListModel.get(stateIndex);
}
function replaceParamToken(paramsDef, params, ctrAddresses)
@ -134,6 +124,7 @@ function replaceParamToken(paramsDef, params, ctrAddresses)
function getFunction(ctrName, functionId)
{
ctrName = contractFromToken(ctrName)
if (codeModel.contracts[ctrName] === undefined)
return null;
if (ctrName === functionId)
@ -148,20 +139,25 @@ function getFunction(ctrName, functionId)
}
}
function executeTr(trIndex, state, ctrAddresses, callBack)
var deploymentGas
var trRealIndex = -1
function executeTr(blockIndex, trIndex, state, ctrAddresses, trHashes, callBack)
{
var tr = state.transactions.get(trIndex);
trRealIndex++;
var tr = state.blocks.get(blockIndex).transactions.get(trIndex);
if (!tr)
callBack()
var func = getFunction(tr.contractId, tr.functionId);
if (!func)
executeTrNextStep(trIndex, state, ctrAddresses, callBack);
executeTrNextStep(blockIndex, trIndex, state, ctrAddresses, trHashes, callBack);
else
{
var gasCost = clientModel.toHex(clientModel.gasCosts[trIndex]);
var rpcParams = { "from": deploymentDialog.currentAccount, "gas": "0x" + gasCost };
var gasCost = clientModel.toHex(deploymentGas[trRealIndex]);
var rpcParams = { "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost };
var params = replaceParamToken(func.parameters, tr.parameters, ctrAddresses);
var encodedParams = clientModel.encodeParams(params, tr.contractId, tr.functionId);
var encodedParams = clientModel.encodeParams(params, contractFromToken(tr.contractId), tr.functionId);
if (state.contractId === state.functionId)
if (tr.contractId === tr.functionId)
rpcParams.code = codeModel.contracts[tr.contractId].codeHex + encodedParams.join("");
else
rpcParams.data = func.hash + encodedParams.join("");
@ -177,28 +173,55 @@ function executeTr(trIndex, state, ctrAddresses, callBack)
var txt = qsTr(tr.contractId + "." + tr.functionId + "() ...")
deploymentStepChanged(txt);
console.log(txt);
if (tr.contractId === tr.functionId)
{
ctrAddresses[tr.contractId] = JSON.parse(response)[0].result
ctrAddresses[tr.contractId + " - " + trIndex] = JSON.parse(response)[0].result //get right ctr address if deploy more than one contract of same type.
}
deploymentDialog.waitForTrCountToIncrement(function(status) {
var hash = JSON.parse(response)[0].result
trHashes[tr.contractId + "." + tr.functionId + "()"] = hash
deploymentDialog.worker.waitForTrReceipt(hash, function(status, receipt){
if (status === -1)
trCountIncrementTimeOut();
else
executeTrNextStep(trIndex, state, ctrAddresses, callBack)
});
{
if (tr.contractId === tr.functionId)
{
ctrAddresses[tr.contractId] = receipt.contractAddress
ctrAddresses[tr.contractId + " - " + trIndex] = receipt.contractAddress //get right ctr address if deploy more than one contract of same type.
}
executeTrNextStep(blockIndex, trIndex, state, ctrAddresses, trHashes, callBack)
}
})
});
}
}
function executeTrNextStep(trIndex, state, ctrAddresses, callBack)
function retrieveContractAddress(trHash, callback)
{
var requests = [{
jsonrpc: "2.0",
method: "eth_getTransactionReceipt",
params: [ trHash ],
id: jsonRpcRequestId
}];
rpcCall(requests, function (httpCall, response){
callback(JSON.parse(response)[0].contractAddress)
})
}
function executeTrNextStep(blockIndex, trIndex, state, ctrAddresses, trHashes, callBack)
{
trIndex++;
if (trIndex < state.transactions.count)
executeTr(trIndex, state, ctrAddresses, callBack);
if (trIndex < state.blocks.get(blockIndex).transactions.count)
executeTr(blockIndex, trIndex, state, ctrAddresses, trHashes, callBack);
else
callBack();
{
blockIndex++
if (blockIndex < state.blocks.count)
{
executeTr(blockIndex, 0, state, ctrAddresses, trHashes, callBack);
}
else
{
callBack();
}
}
}
function gasPrice(callBack, error)
@ -216,9 +239,20 @@ function gasPrice(callBack, error)
});
}
function finalizeDeployment(deploymentId, addresses) {
function packageDapp(addresses)
{
var date = new Date();
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
deploymentDialog.packageStep.deploymentId = deploymentId
deploymentStepChanged(qsTr("Packaging application ..."));
var deploymentDir = projectPath + deploymentId + "/";
if (deploymentDialog.packageStep.packageDir !== "")
deploymentDir = deploymentDialog.packageStep.packageDir
else
deploymentDir = projectPath + "package/"
projectModel.deploymentDir = deploymentDir;
fileIo.makeDir(deploymentDir);
for (var i = 0; i < projectListModel.count; i++) {
@ -253,27 +287,29 @@ function finalizeDeployment(deploymentId, addresses) {
"\taddress: \"" + addresses[c] + "\"\n" +
"};\n" +
contractAccessor + ".contractClass = web3.eth.contract(" + contractAccessor + ".interface);\n" +
contractAccessor + ".contract = new " + contractAccessor + ".contractClass(" + contractAccessor + ".address);\n";
contractAccessor + ".contract = " + contractAccessor + ".contractClass.at(" + contractAccessor + ".address);\n";
}
fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs);
deploymentAddresses = addresses;
saveProject();
var packageRet = fileIo.makePackage(deploymentDir);
deploymentDialog.packageHash = packageRet[0];
deploymentDialog.packageBase64 = packageRet[1];
deploymentDialog.localPackageUrl = packageRet[2] + "?hash=" + packageRet[0];
var applicationUrlEth = deploymentDialog.applicationUrlEth;
deploymentDialog.packageStep.packageHash = packageRet[0];
deploymentDialog.packageStep.packageBase64 = packageRet[1];
deploymentDialog.packageStep.localPackageUrl = packageRet[2] + "?hash=" + packageRet[0];
deploymentDialog.packageStep.lastDeployDate = date
deploymentComplete()
}
applicationUrlEth = formatAppUrl(applicationUrlEth);
function registerDapp(url, callback)
{
deploymentStepChanged(qsTr("Registering application on the Ethereum network ..."));
checkEthPath(applicationUrlEth, false, function (success) {
checkEthPath(url, false, function (success) {
if (!success)
return;
deploymentComplete();
deployResourcesDialog.text = qsTr("Register Web Application to finalize deployment.");
deployResourcesDialog.open();
if (callback)
callback()
});
}
@ -283,8 +319,8 @@ function checkEthPath(dappUrl, checkOnly, callBack)
{
// convenient for dev purpose, should not be possible in normal env.
if (!checkOnly)
reserve(deploymentDialog.eth, function() {
registerContentHash(deploymentDialog.eth, callBack); // we directly create a dapp under the root registrar.
reserve(deploymentDialog.registerStep.eth, function() {
registerContentHash(deploymentDialog.registerStep.eth, callBack); // we directly create a dapp under the root registrar.
});
else
callBack(true);
@ -298,7 +334,7 @@ function checkEthPath(dappUrl, checkOnly, callBack)
//subRegistrar()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "gas": "0xffff", "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0xe1fa8e84" + str }, "pending" ],
params: [ { "gas": "0xffff", "from": deploymentDialog.worker.currentAccount, "to": '0x' + deploymentDialog.registerStep.eth, "data": "0xe1fa8e84" + str }, "pending" ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
@ -327,12 +363,12 @@ function isOwner(addr, callBack)
//getOwner()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0xb387ef92" }, "pending" ],
params: [ { "from": deploymentDialog.worker.currentAccount, "to": '0x' + addr, "data": "0xb387ef92" }, "pending" ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
var res = JSON.parse(response);
callBack(normalizeAddress(deploymentDialog.currentAccount) === normalizeAddress(res[0].result));
callBack(normalizeAddress(deploymentDialog.worker.currentAccount) === normalizeAddress(res[0].result));
});
}
@ -374,7 +410,7 @@ function continueRegistration(dappUrl, addr, callBack, checkOnly)
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x5a3a05bd" + str }, "pending" ],
params: [ { "from": deploymentDialog.worker.currentAccount, "to": '0x' + addr, "data": "0x5a3a05bd" + str }, "pending" ],
id: jsonRpcRequestId++
});
@ -406,11 +442,11 @@ function continueRegistration(dappUrl, addr, callBack, checkOnly)
deploymentStepChanged(txt);
//current registrar is owned => ownedregistrar creation and continue.
requests = [];
var gasCost = clientModel.toHex(deploymentDialog.ownedRegistrarDeployGas);
var gasCost = clientModel.toHex(deploymentDialog.registerStep.ownedRegistrarDeployGas);
requests.push({
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": "0x" + gasCost, "code": "0x600080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331781556105cd90819061003990396000f3007c010000000000000000000000000000000000000000000000000000000060003504630198489281146100b257806321f8a721146100e45780632dff6941146100ee5780633b3b57de1461010e5780635a3a05bd1461013e5780635fd4b08a146101715780637dd564111461017d57806389a69c0e14610187578063b387ef92146101bb578063b5c645bd146101f4578063be99a98014610270578063c3d014d6146102a8578063d93e7573146102dc57005b73ffffffffffffffffffffffffffffffffffffffff600435166000908152600160205260409020548060005260206000f35b6000808052602081f35b600435600090815260026020819052604090912001548060005260206000f35b600435600090815260026020908152604082205473ffffffffffffffffffffffffffffffffffffffff1680835291f35b600435600090815260026020908152604082206001015473ffffffffffffffffffffffffffffffffffffffff1680835291f35b60008060005260206000f35b6000808052602081f35b60005461030c9060043590602435903373ffffffffffffffffffffffffffffffffffffffff908116911614610569576105c9565b60005473ffffffffffffffffffffffffffffffffffffffff168073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b600435600090815260026020819052604090912080546001820154919092015473ffffffffffffffffffffffffffffffffffffffff9283169291909116908273ffffffffffffffffffffffffffffffffffffffff166000528173ffffffffffffffffffffffffffffffffffffffff166020528060405260606000f35b600054610312906004359060243590604435903373ffffffffffffffffffffffffffffffffffffffff90811691161461045457610523565b6000546103189060043590602435903373ffffffffffffffffffffffffffffffffffffffff90811691161461052857610565565b60005461031e90600435903373ffffffffffffffffffffffffffffffffffffffff90811691161461032457610451565b60006000f35b60006000f35b60006000f35b60006000f35b60008181526002602090815260408083205473ffffffffffffffffffffffffffffffffffffffff16835260019091529020548114610361576103e1565b6000818152600260205260408082205473ffffffffffffffffffffffffffffffffffffffff169183917ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85459190a360008181526002602090815260408083205473ffffffffffffffffffffffffffffffffffffffff16835260019091528120555b600081815260026020819052604080832080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054909116905590910182905582917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b50565b600083815260026020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001683179055806104bb57827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc60006040a2610522565b73ffffffffffffffffffffffffffffffffffffffff8216837ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a854560006040a373ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604090208390555b5b505050565b600082815260026020819052604080832090910183905583917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b5050565b60008281526002602052604080822060010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905583917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b505056" } ],
params: [ { "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost, "code": "0x600080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331781556105cd90819061003990396000f3007c010000000000000000000000000000000000000000000000000000000060003504630198489281146100b257806321f8a721146100e45780632dff6941146100ee5780633b3b57de1461010e5780635a3a05bd1461013e5780635fd4b08a146101715780637dd564111461017d57806389a69c0e14610187578063b387ef92146101bb578063b5c645bd146101f4578063be99a98014610270578063c3d014d6146102a8578063d93e7573146102dc57005b73ffffffffffffffffffffffffffffffffffffffff600435166000908152600160205260409020548060005260206000f35b6000808052602081f35b600435600090815260026020819052604090912001548060005260206000f35b600435600090815260026020908152604082205473ffffffffffffffffffffffffffffffffffffffff1680835291f35b600435600090815260026020908152604082206001015473ffffffffffffffffffffffffffffffffffffffff1680835291f35b60008060005260206000f35b6000808052602081f35b60005461030c9060043590602435903373ffffffffffffffffffffffffffffffffffffffff908116911614610569576105c9565b60005473ffffffffffffffffffffffffffffffffffffffff168073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b600435600090815260026020819052604090912080546001820154919092015473ffffffffffffffffffffffffffffffffffffffff9283169291909116908273ffffffffffffffffffffffffffffffffffffffff166000528173ffffffffffffffffffffffffffffffffffffffff166020528060405260606000f35b600054610312906004359060243590604435903373ffffffffffffffffffffffffffffffffffffffff90811691161461045457610523565b6000546103189060043590602435903373ffffffffffffffffffffffffffffffffffffffff90811691161461052857610565565b60005461031e90600435903373ffffffffffffffffffffffffffffffffffffffff90811691161461032457610451565b60006000f35b60006000f35b60006000f35b60006000f35b60008181526002602090815260408083205473ffffffffffffffffffffffffffffffffffffffff16835260019091529020548114610361576103e1565b6000818152600260205260408082205473ffffffffffffffffffffffffffffffffffffffff169183917ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85459190a360008181526002602090815260408083205473ffffffffffffffffffffffffffffffffffffffff16835260019091528120555b600081815260026020819052604080832080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054909116905590910182905582917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b50565b600083815260026020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001683179055806104bb57827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc60006040a2610522565b73ffffffffffffffffffffffffffffffffffffffff8216837ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a854560006040a373ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604090208390555b5b505050565b600082815260026020819052604080832090910183905583917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b5050565b60008281526002602052604080822060010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905583917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b505056" } ],
id: jsonRpcRequestId++
});
@ -420,19 +456,19 @@ function continueRegistration(dappUrl, addr, callBack, checkOnly)
var txt = qsTr("Please wait " + dappUrl[0] + " is registering ...");
deploymentStepChanged(txt);
console.log(txt);
deploymentDialog.waitForTrCountToIncrement(function(status) {
deploymentDialog.worker.waitForTrCountToIncrement(function(status) {
if (status === -1)
{
trCountIncrementTimeOut();
return;
}
var crLevel = clientModel.encodeStringParam(dappUrl[0]);
var gasCost = clientModel.toHex(deploymentDialog.ownedRegistrarSetSubRegistrarGas);
var gasCost = clientModel.toHex(deploymentDialog.registerStep.ownedRegistrarSetSubRegistrarGas);
requests.push({
//setRegister()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": "0x" + gasCost, "to": '0x' + addr, "data": "0x89a69c0e" + crLevel + deploymentDialog.pad(newCtrAddress) } ],
params: [ { "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost, "to": '0x' + addr, "data": "0x89a69c0e" + crLevel + newCtrAddress } ],
id: jsonRpcRequestId++
});
@ -465,7 +501,7 @@ function reserve(registrar, callBack)
//reserve()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": "0xfffff", "to": '0x' + registrar, "data": "0x432ced04" + paramTitle } ],
params: [ { "from": deploymentDialog.worker.currentAccount, "gas": "0xfffff", "to": '0x' + registrar, "data": "0x432ced04" + paramTitle } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
@ -479,39 +515,45 @@ function registerContentHash(registrar, callBack)
var txt = qsTr("Finalizing Dapp registration ...");
deploymentStepChanged(txt);
console.log(txt);
console.log("register url " + deploymentDialog.packageStep.packageHash + " " + projectModel.projectTitle)
projectModel.registerContentHashTrHash = ""
var requests = [];
var paramTitle = clientModel.encodeStringParam(projectModel.projectTitle);
var gasCost = clientModel.toHex(deploymentDialog.ownedRegistrarSetContentHashGas);
var gasCost = clientModel.toHex(deploymentDialog.registerStep.ownedRegistrarSetContentHashGas);
requests.push({
//setContent()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": "0x" + gasCost, "to": '0x' + registrar, "data": "0xc3d014d6" + paramTitle + deploymentDialog.packageHash } ],
params: [ { "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost, "to": '0x' + registrar, "data": "0xc3d014d6" + paramTitle + deploymentDialog.packageStep.packageHash } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
projectModel.registerContentHashTrHash = JSON.parse(response)[0].result
callBack(true);
});
}
function registerToUrlHint()
function registerToUrlHint(url, callback)
{
deploymentStepChanged(qsTr("Registering application Resources (" + deploymentDialog.applicationUrlHttp) + ") ...");
console.log("register url " + deploymentDialog.packageStep.packageHash + " " + url)
deploymentStepChanged(qsTr("Registering application Resources..."))
urlHintAddress(function(urlHint){
var requests = [];
var paramUrlHttp = clientModel.encodeStringParam(deploymentDialog.applicationUrlHttp);
var gasCost = clientModel.toHex(deploymentDialog.urlHintSuggestUrlGas);
var paramUrlHttp = clientModel.encodeStringParam(url)
var gasCost = clientModel.toHex(deploymentDialog.registerStep.urlHintSuggestUrlGas);
requests.push({
//urlHint => suggestUrl
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "to": '0x' + urlHint, "from": deploymentDialog.currentAccount, "gas": "0x" + gasCost, "data": "0x584e86ad" + deploymentDialog.packageHash + paramUrlHttp } ],
params: [ { "to": '0x' + urlHint, "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost, "data": "0x584e86ad" + deploymentDialog.packageStep.packageHash + paramUrlHttp } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
projectModel.registerUrlTrHash = JSON.parse(response)[0].result
deploymentComplete();
if (callback)
callback()
});
});
}
@ -524,7 +566,7 @@ function urlHintAddress(callBack)
//registrar: get UrlHint addr
jsonrpc: "2.0",
method: "eth_call",
params: [ { "to": '0x' + deploymentDialog.eth, "from": deploymentDialog.currentAccount, "data": "0x3b3b57de" + urlHint }, "pending" ],
params: [ { "to": '0x' + deploymentDialog.registerStep.eth, "from": deploymentDialog.worker.currentAccount, "data": "0x3b3b57de" + urlHint }, "pending" ],
id: jsonRpcRequestId++
});
@ -545,6 +587,8 @@ function normalizeAddress(addr)
function formatAppUrl(url)
{
if (!url)
return [projectModel.projectTitle];
if (url.toLowerCase().lastIndexOf("/") === url.length - 1)
url = url.substring(0, url.length - 1);
if (url.toLowerCase().indexOf("eth://") === 0)

45
mix/qml/js/ProjectModel.js

@ -74,11 +74,19 @@ function saveProjectFile()
files: [],
title: projectTitle,
deploymentAddresses: deploymentAddresses,
applicationUrlEth: deploymentDialog.applicationUrlEth,
applicationUrlHttp: deploymentDialog.applicationUrlHttp,
packageHash: deploymentDialog.packageHash,
packageBase64: deploymentDialog.packageBase64,
deploymentDir: projectModel.deploymentDir
applicationUrlEth: projectModel.applicationUrlEth,
applicationUrlHttp: projectModel.applicationUrlHttp,
packageHash: deploymentDialog.packageStep.packageHash,
packageBase64: deploymentDialog.packageStep.packageBase64,
deploymentDir: deploymentDialog.packageStep.packageDir,
lastPackageDate: deploymentDialog.packageStep.lastDeployDate,
deployBlockNumber: projectModel.deployBlockNumber,
localPackageUrl: deploymentDialog.packageStep.localPackageUrl,
deploymentTrHashes: JSON.stringify(projectModel.deploymentTrHashes),
registerContentHashTrHash: projectModel.registerContentHashTrHash,
registerUrlTrHash: projectModel.registerUrlTrHash,
registerContentHashBlockNumber: projectModel.registerContentHashBlockNumber,
registerUrlBlockNumber: projectModel.registerUrlBlockNumber
};
for (var i = 0; i < projectListModel.count; i++)
projectData.files.push({
@ -107,18 +115,34 @@ function loadProject(path) {
if (projectData.deploymentDir)
projectModel.deploymentDir = projectData.deploymentDir
if (projectData.packageHash)
deploymentDialog.packageHash = projectData.packageHash
deploymentDialog.packageStep.packageHash = projectData.packageHash
if (projectData.packageBase64)
deploymentDialog.packageBase64 = projectData.packageBase64
deploymentDialog.packageStep.packageBase64 = projectData.packageBase64
if (projectData.applicationUrlEth)
deploymentDialog.applicationUrlEth = projectData.applicationUrlEth
projectModel.applicationUrlEth = projectData.applicationUrlEth
if (projectData.applicationUrlHttp)
deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp
projectModel.applicationUrlHttp = projectData.applicationUrlHttp
if (projectData.lastPackageDate)
deploymentDialog.packageStep.lastDeployDate = projectData.lastPackageDate
if (projectData.deployBlockNumber)
projectModel.deployBlockNumber = projectData.deployBlockNumber
if (projectData.localPackageUrl)
deploymentDialog.packageStep.localPackageUrl = projectData.localPackageUrl
if (projectData.deploymentTrHashes)
projectModel.deploymentTrHashes = JSON.parse(projectData.deploymentTrHashes)
if (projectData.registerUrlTrHash)
projectModel.registerUrlTrHash = projectData.registerUrlTrHash
if (projectData.registerContentHashTrHash)
projectModel.registerContentHashTrHash = projectData.registerContentHashTrHash
if (projectData.registerContentHashBlockNumber)
projectModel.registerContentHashBlockNumber = projectData.registerContentHashBlockNumber
if (projectData.registerUrlBlockNumber)
projectModel.registerUrlBlockNumber = projectData.registerUrlBlockNumber
if (!projectData.title) {
var parts = path.split("/");
projectData.title = parts[parts.length - 2];
}
deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : [];
deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : {};
projectTitle = projectData.title;
projectPath = path;
if (!projectData.files)
@ -360,3 +384,4 @@ function generateFileName(name, extension) {
} while (fileIo.fileExists(filePath));
return fileName
}

7
mix/qml/js/TransactionHelper.js

@ -45,3 +45,10 @@ function rpcCall(requests, callBack, error)
httpRequest.send(rpcRequest);
}
function contractFromToken(token)
{
if (token.indexOf('<') === 0)
return token.replace("<", "").replace(">", "").split(" - ")[0];
return token;
}

Loading…
Cancel
Save