Browse Source

Merge pull request #1605 from arkpar/mix_ux

Mix: bugfixes
cl-refactor
Arkadiy Paronyan 10 years ago
parent
commit
e609c60bf9
  1. 5
      mix/ClientModel.cpp
  2. 5
      mix/CodeModel.cpp
  3. 5
      mix/qml.qrc
  4. 6
      mix/qml/DeploymentDialog.qml
  5. 1
      mix/qml/MainContent.qml
  6. 1
      mix/qml/ProjectList.qml
  7. 9
      mix/qml/ProjectModel.qml
  8. 30
      mix/qml/StepActionImage.qml
  9. 351
      mix/qml/js/NetworkDeployment.js
  10. 345
      mix/qml/js/ProjectModel.js
  11. 4
      mix/test/qml/TestMain.qml
  12. 18
      mix/test/qml/js/TestProject.js

5
mix/ClientModel.cpp

@ -216,7 +216,10 @@ void ClientModel::setupState(QVariantMap _state)
void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence, map<Secret, u256> const& _balances)
{
if (m_running)
BOOST_THROW_EXCEPTION(ExecutionStateException());
{
qWarning() << "Waiting for current execution to complete";
m_runFuture.waitForFinished();
}
m_running = true;
emit runStarted();

5
mix/CodeModel.cpp

@ -269,7 +269,10 @@ void CodeModel::runCompilationJob(int _jobId)
if (c_predefinedContracts.count(n) != 0)
continue;
QString name = QString::fromStdString(n);
QString sourceName = QString::fromStdString(*cs.getContractDefinition(n).getLocation().sourceName);
ContractDefinition const& contractDefinition = cs.getContractDefinition(n);
if (!contractDefinition.isFullyImplemented())
continue;
QString sourceName = QString::fromStdString(*contractDefinition.getLocation().sourceName);
auto sourceIter = m_pendingContracts.find(sourceName);
QString source = sourceIter != m_pendingContracts.end() ? sourceIter->second : QString();
CompiledContract* contract = new CompiledContract(cs, name, source);

5
mix/qml.qrc

@ -48,8 +48,8 @@
<file>qml/StatusPaneStyle.qml</file>
<file>qml/StepActionImage.qml</file>
<file>qml/StorageView.qml</file>
<file>qml/StatesComboBox.qml</file>
<file>qml/StructView.qml</file>
<file>qml/StatesComboBox.qml</file>
<file>qml/StructView.qml</file>
<file>qml/Style.qml</file>
<file>qml/TabStyle.qml</file>
<file>qml/TransactionDialog.qml</file>
@ -63,5 +63,6 @@
<file>qml/js/TransactionHelper.js</file>
<file>qml/js/Printer.js</file>
<file>qml/js/ansi2html.js</file>
<file>qml/js/NetworkDeployment.js</file>
</qresource>
</RCC>

6
mix/qml/DeploymentDialog.qml

@ -6,7 +6,7 @@ import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/ProjectModel.js" as ProjectModelCode
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
import "."
@ -356,7 +356,7 @@ Dialog {
tooltip: qsTr("Deploy contract(s) and Package resources files.")
onTriggered: {
var inError = [];
var ethUrl = ProjectModelCode.formatAppUrl(applicationUrlEth.text);
var ethUrl = NetworkDeploymentCode.formatAppUrl(applicationUrlEth.text);
for (var k in ethUrl)
{
if (ethUrl[k].length > 32)
@ -367,7 +367,7 @@ Dialog {
if (contractRedeploy.checked)
deployWarningDialog.open();
else
ProjectModelCode.startDeployProject(false);
NetworkDeploymentCode.startDeployProject(false);
}
}
}

1
mix/qml/MainContent.qml

@ -24,6 +24,7 @@ Rectangle {
property alias webViewVisible: webPreview.visible
property alias webView: webPreview
property alias projectViewVisible: projectList.visible
property alias projectNavigator: projectList
property alias runOnProjectLoad: mainSettings.runOnProjectLoad
property alias rightPane: rightView
property alias codeEditor: codeEditor

1
mix/qml/ProjectList.qml

@ -8,6 +8,7 @@ import "."
Item {
property bool renameMode: false;
property alias sections: sectionRepeater
ProjectFilesStyle {
id: projectFilesStyle

9
mix/qml/ProjectModel.qml

@ -5,6 +5,7 @@ import QtQuick.Controls 1.0
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
import "js/ProjectModel.js" as ProjectModelCode
import "js/NetworkDeployment.js" as NetworkDeploymentCode
Item {
id: projectModel
@ -69,9 +70,9 @@ Item {
function getDocumentIdByName(documentName) { return ProjectModelCode.getDocumentIdByName(documentName); }
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); }
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); }
function deployProject() { ProjectModelCode.deployProject(false); }
function registerToUrlHint() { ProjectModelCode.registerToUrlHint(); }
function formatAppUrl() { ProjectModelCode.formatAppUrl(url); }
function deployProject() { NetworkDeploymentCode.deployProject(false); }
function registerToUrlHint() { NetworkDeploymentCode.registerToUrlHint(); }
function formatAppUrl() { NetworkDeploymentCode.formatAppUrl(url); }
Connections {
target: mainApplication
@ -155,7 +156,7 @@ Item {
icon: StandardIcon.Question
standardButtons: StandardButton.Ok | StandardButton.Abort
onAccepted: {
ProjectModelCode.startDeployProject(true);
NetworkDeploymentCode.startDeployProject(true);
}
}

30
mix/qml/StepActionImage.qml

@ -3,8 +3,6 @@ import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
Rectangle {
id: buttonActionContainer
property string disableStateImg
@ -15,7 +13,6 @@ Rectangle {
property bool buttonRight
signal clicked
color: "transparent"
width: 35
height: 24
@ -43,8 +40,8 @@ Rectangle {
right: parent.right
top: parent.top
bottom: parent.bottom
bottomMargin:debugImg.pressed? 0 : 1;
topMargin:debugImg.pressed? 1 : 0;
bottomMargin: debugImg.pressed ? 0 : 1;
topMargin: debugImg.pressed ? 1 : 0;
}
color: "#FCFBFC"
radius: 3
@ -65,15 +62,14 @@ Rectangle {
right: parent.right
top: parent.top
bottom: parent.bottom
bottomMargin:debugImg.pressed? 0 : 1;
topMargin:debugImg.pressed? 1 : 0;
bottomMargin: debugImg.pressed ? 0 : 1;
topMargin: debugImg.pressed? 1 : 0;
}
color: "#FCFBFC"
radius: 3
}
}
Rectangle {
id: contentRectangle
width: 25
@ -87,8 +83,8 @@ Rectangle {
right: parent.right
top: parent.top
bottom: parent.bottom
bottomMargin:debugImg.pressed? 0 : 1;
topMargin:debugImg.pressed? 1 : 0;
bottomMargin: debugImg.pressed ? 0 : 1;
topMargin: debugImg.pressed ? 1 : 0;
}
color: "#FCFBFC"
@ -96,7 +92,7 @@ Rectangle {
id: debugImage
source: enabledStateImg
anchors.centerIn: parent
anchors.topMargin: debugImg.pressed? 1 : 0;
anchors.topMargin: debugImg.pressed ? 1 : 0;
fillMode: Image.PreserveAspectFit
width: 15
@ -105,28 +101,24 @@ Rectangle {
}
Button {
anchors.fill: parent
id: debugImg
action: buttonAction
style: Rectangle {
color: "transparent"
style: ButtonStyle {
background: Rectangle {
color: "transparent"
}
}
}
Action {
tooltip: buttonTooltip
id: buttonAction
shortcut: buttonShortcut
onTriggered: {
// contentRectangle.anchors.bottomMargin = 0
// contentRectangle.anchors.topMargin = 1
buttonActionContainer.clicked();
}
}
}
}

351
mix/qml/js/NetworkDeployment.js

@ -0,0 +1,351 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file NetworkDeployment.js
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @author Yann yann@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
Qt.include("TransactionHelper.js")
var jsonRpcRequestId = 1;
function deployProject(force) {
saveAll(); //TODO: ask user
deploymentDialog.open();
}
function startDeployProject(erasePrevious)
{
var date = new Date();
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
if (!erasePrevious)
{
finalizeDeployment(deploymentId, projectModel.deploymentAddresses);
return;
}
var jsonRpcUrl = "http://127.0.0.1:8080";
console.log("Deploying " + deploymentId + " to " + jsonRpcUrl);
deploymentStarted();
var ctrNames = Object.keys(codeModel.contracts);
var ctrAddresses = {};
deployContracts(0, ctrAddresses, ctrNames, function (){
finalizeDeployment(deploymentId, ctrAddresses);
});
}
function deployContracts(ctrIndex, ctrAddresses, ctrNames, callBack)
{
var code = codeModel.contracts[ctrNames[ctrIndex]].codeHex;
var requests = [{
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": deploymentDialog.gasToUse, "code": code } ],
id: 0
}];
rpcCall(requests, function (httpCall, response){
var txt = qsTr("Please wait while " + ctrNames[ctrIndex] + " is published ...")
deploymentStepChanged(txt);
console.log(txt);
ctrAddresses[ctrNames[ctrIndex]] = JSON.parse(response)[0].result
deploymentDialog.waitForTrCountToIncrement(function(status) {
if (status === -1)
{
trCountIncrementTimeOut();
return;
}
ctrIndex++;
if (ctrIndex < ctrNames.length)
deployContracts(ctrIndex, ctrAddresses, ctrNames, callBack);
else
callBack();
});
});
}
function finalizeDeployment(deploymentId, addresses) {
deploymentStepChanged(qsTr("Packaging application ..."));
var deploymentDir = projectPath + deploymentId + "/";
projectModel.deploymentDir = deploymentDir;
fileIo.makeDir(deploymentDir);
for (var i = 0; i < projectListModel.count; i++) {
var doc = projectListModel.get(i);
if (doc.isContract)
continue;
if (doc.isHtml) {
//inject the script to access contract API
//TODO: use a template
var html = fileIo.readFile(doc.path);
var insertAt = html.indexOf("<head>")
if (insertAt < 0)
insertAt = 0;
else
insertAt += 6;
html = html.substr(0, insertAt) +
"<script src=\"deployment.js\"></script>" +
html.substr(insertAt);
fileIo.writeFile(deploymentDir + doc.fileName, html);
}
else
fileIo.copyFile(doc.path, deploymentDir + doc.fileName);
}
//write deployment js
var deploymentJs =
"// Autogenerated by Mix\n" +
"contracts = {};\n";
for (var c in codeModel.contracts) {
var contractAccessor = "contracts[\"" + codeModel.contracts[c].contract.name + "\"]";
deploymentJs += contractAccessor + " = {\n" +
"\tinterface: " + codeModel.contracts[c].contractInterface + ",\n" +
"\taddress: \"" + addresses[c] + "\"\n" +
"};\n" +
contractAccessor + ".contractClass = web3.eth.contract(" + contractAccessor + ".interface);\n" +
contractAccessor + ".contract = new " + contractAccessor + ".contractClass(" + 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;
applicationUrlEth = formatAppUrl(applicationUrlEth);
deploymentStepChanged(qsTr("Registering application on the Ethereum network ..."));
checkEthPath(applicationUrlEth, function () {
deploymentComplete();
deployResourcesDialog.text = qsTr("Register Web Application to finalize deployment.");
deployResourcesDialog.open();
});
}
function checkEthPath(dappUrl, callBack)
{
if (dappUrl.length === 1)
registerContentHash(deploymentDialog.eth, callBack); // we directly create a dapp under the root registrar.
else
{
// the first owned reigstrar must have been created to follow the path.
var str = createString(dappUrl[0]);
var requests = [];
requests.push({
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "gas": 150000, "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
var res = JSON.parse(response);
var addr = normalizeAddress(res[0].result);
if (addr.replace(/0+/g, "") === "")
{
var errorTxt = qsTr("Path does not exists " + JSON.stringify(dappUrl) + ". Please register using Registration Dapp. Aborting.");
deploymentError(errorTxt);
console.log(errorTxt);
}
else
{
dappUrl.splice(0, 1);
checkRegistration(dappUrl, addr, callBack);
}
});
}
}
function checkRegistration(dappUrl, addr, callBack)
{
if (dappUrl.length === 1)
registerContentHash(addr, callBack); // We do not create the register for the last part, just registering the content hash.
else
{
var txt = qsTr("Checking " + JSON.stringify(dappUrl) + " ... in registrar " + addr);
deploymentStepChanged(txt);
console.log(txt);
var requests = [];
var registrar = {}
var str = createString(dappUrl[0]);
requests.push({
//getOwner()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "gas" : 2000, "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ],
id: jsonRpcRequestId++
});
requests.push({
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x6be16bed" + str.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
var res = JSON.parse(response);
var nextAddr = normalizeAddress(res[1].result);
var errorTxt;
if (res[1].result === "0x")
{
errorTxt = qsTr("Error when creating new owned regsitrar. Please use the regsitration Dapp. Aborting");
deploymentError(errorTxt);
console.log(errorTxt);
}
else if (normalizeAddress(deploymentDialog.currentAccount) !== normalizeAddress(res[0].result))
{
errorTxt = qsTr("You are not the owner of " + dappUrl[0] + ". Aborting");
deploymentError(errorTxt);
console.log(errorTxt);
}
else if (nextAddr.replace(/0+/g, "") !== "")
{
dappUrl.splice(0, 1);
checkRegistration(dappUrl, nextAddr, callBack);
}
else
{
var txt = qsTr("Registering sub domain " + dappUrl[0] + " ...");
console.log(txt);
deploymentStepChanged(txt);
//current registrar is owned => ownedregistrar creation and continue.
requests = [];
requests.push({
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 20000, "code": "0x60056013565b61059e8061001d6000396000f35b33600081905550560060003560e060020a90048063019848921461009a578063449c2090146100af5780635d574e32146100cd5780635fd4b08a146100e1578063618242da146100f65780636be16bed1461010b5780636c4489b414610129578063893d20e8146101585780639607730714610173578063c284bc2a14610187578063e50f599a14610198578063e5811b35146101af578063ec7b9200146101cd57005b6100a560043561031b565b8060005260206000f35b6100ba6004356103a0565b80600160a060020a031660005260206000f35b6100db600435602435610537565b60006000f35b6100ec600435610529565b8060005260206000f35b6101016004356103dd565b8060005260206000f35b6101166004356103bd565b80600160a060020a031660005260206000f35b61013460043561034b565b82600160a060020a031660005281600160a060020a03166020528060405260606000f35b610160610341565b80600160a060020a031660005260206000f35b6101816004356024356102b4565b60006000f35b6101926004356103fd565b60006000f35b6101a96004356024356044356101f2565b60006000f35b6101ba6004356101eb565b80600160a060020a031660005260206000f35b6101d8600435610530565b80600160a060020a031660005260206000f35b6000919050565b600054600160a060020a031633600160a060020a031614610212576102af565b8160026000858152602001908152602001600020819055508061023457610287565b81600160a060020a0316837f680ad70765443c2967675ab0fb91a46350c01c6df59bf9a41ff8a8dd097464ec60006000a3826001600084600160a060020a03168152602001908152602001600020819055505b827f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b505050565b600054600160a060020a031633600160a060020a0316146102d457610317565b806002600084815260200190815260200160002060010181905550817f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b5050565b60006001600083600160a060020a03168152602001908152602001600020549050919050565b6000600054905090565b6000600060006002600085815260200190815260200160002054925060026000858152602001908152602001600020600101549150600260008581526020019081526020016000206002015490509193909250565b600060026000838152602001908152602001600020549050919050565b600060026000838152602001908152602001600020600101549050919050565b600060026000838152602001908152602001600020600201549050919050565b600054600160a060020a031633600160a060020a03161461041d57610526565b80600160006002600085815260200190815260200160002054600160a060020a031681526020019081526020016000205414610458576104d2565b6002600082815260200190815260200160002054600160a060020a0316817f680ad70765443c2967675ab0fb91a46350c01c6df59bf9a41ff8a8dd097464ec60006000a36000600160006002600085815260200190815260200160002054600160a060020a03168152602001908152602001600020819055505b6002600082815260200190815260200160002060008101600090556001810160009055600281016000905550807f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b50565b6000919050565b6000919050565b600054600160a060020a031633600160a060020a0316146105575761059a565b806002600084815260200190815260200160002060020181905550817f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b505056" } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function(httpRequest, response) {
var newCtrAddress = normalizeAddress(JSON.parse(response)[0].result);
requests = [];
var txt = qsTr("Please wait " + dappUrl[0] + " is registering ...");
deploymentStepChanged(txt);
console.log(txt);
deploymentDialog.waitForTrCountToIncrement(function(status) {
if (status === -1)
{
trCountIncrementTimeOut();
return;
}
var crLevel = createString(dappUrl[0]).encodeValueAsString();
requests.push({
//setRegister()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function(request, response){
dappUrl.splice(0, 1);
checkRegistration(dappUrl, newCtrAddress, callBack);
});
});
});
}
});
}
}
function trCountIncrementTimeOut()
{
var error = qsTr("Something went wrong during the deployment. Please verify the amount of gas for this transaction and check your balance.")
console.log(error);
deploymentError(error);
}
function registerContentHash(registrar, callBack)
{
var txt = qsTr("Finalizing Dapp registration ...");
deploymentStepChanged(txt);
console.log(txt);
var requests = [];
var paramTitle = clientModel.encodeAbiString(projectModel.projectTitle);
requests.push({
//setContent()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle + deploymentDialog.packageHash } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
callBack();
});
}
function registerToUrlHint()
{
deploymentStepChanged(qsTr("Registering application Resources (" + deploymentDialog.applicationUrlHttp) + ") ...");
var requests = [];
var paramUrlHttp = createString(deploymentDialog.applicationUrlHttp);
requests.push({
//urlHint => suggestUrl
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 30000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
deploymentComplete();
});
}
function normalizeAddress(addr)
{
addr = addr.replace('0x', '');
if (addr.length <= 40)
return addr;
var left = addr.length - 40;
return addr.substring(left);
}
function formatAppUrl(url)
{
if (url.toLowerCase().indexOf("eth://") === 0)
url = url.substring(6);
if (url.toLowerCase().indexOf(projectModel.projectTitle + ".") === 0)
url = url.substring(projectModel.projectTitle.length + 1);
if (url === "")
return [projectModel.projectTitle];
var ret;
if (url.indexOf("/") === -1)
ret = url.split('.').reverse();
else
{
var slash = url.indexOf("/");
var left = url.substring(0, slash);
var leftA = left.split(".");
leftA.reverse();
var right = url.substring(slash + 1);
var rightA = right.split('/');
ret = leftA.concat(rightA);
}
if (ret[0].toLowerCase() === "eth")
ret.splice(0, 1);
ret.push(projectModel.projectTitle);
return ret;
}

345
mix/qml/js/ProjectModel.js

@ -19,8 +19,6 @@
* @date 2015
* Ethereum IDE client.
*/
Qt.include("QEtherHelper.js")
Qt.include("TransactionHelper.js")
var htmlTemplate = "<html>\n<head>\n<script>\n</script>\n</head>\n<body>\n<script>\n</script>\n</body>\n</html>";
var contractTemplate = "contract Contract {\n}\n";
@ -83,7 +81,10 @@ function saveProjectFile()
deploymentDir: projectModel.deploymentDir
};
for (var i = 0; i < projectListModel.count; i++)
projectData.files.push(projectListModel.get(i).fileName);
projectData.files.push({
title: projectListModel.get(i).name,
fileName: projectListModel.get(i).fileName,
});
projectFileSaving(projectData);
var json = JSON.stringify(projectData, null, "\t");
@ -122,7 +123,11 @@ function loadProject(path) {
projectData.files = [];
for(var i = 0; i < projectData.files.length; i++) {
addFile(projectData.files[i]);
var entry = projectData.files[i];
if (typeof(entry) === "string")
addFile(entry); //TODO: remove old project file support
else
addFile(entry.fileName, entry.title);
}
if (mainApplication.trackLastProject)
projectSettings.lastProjectPath = path;
@ -140,7 +145,7 @@ function loadProject(path) {
});
}
function addFile(fileName) {
function addFile(fileName, title) {
var p = projectPath + fileName;
var extension = fileName.substring(fileName.lastIndexOf("."), fileName.length);
var isContract = extension === ".sol";
@ -154,7 +159,7 @@ function addFile(fileName) {
contract: false,
path: p,
fileName: fileName,
name: fileName,
name: title !== undefined ? title : fileName,
documentId: fileName,
syntaxMode: syntaxMode,
isText: isContract || isHtml || isCss || isJs,
@ -344,331 +349,3 @@ function generateFileName(name, extension) {
} while (fileIo.fileExists(filePath));
return fileName
}
var jsonRpcRequestId = 1;
function deployProject(force) {
saveAll(); //TODO: ask user
deploymentDialog.open();
}
function startDeployProject(erasePrevious)
{
var date = new Date();
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
if (!erasePrevious)
{
finalizeDeployment(deploymentId, projectModel.deploymentAddresses);
return;
}
var jsonRpcUrl = "http://127.0.0.1:8080";
console.log("Deploying " + deploymentId + " to " + jsonRpcUrl);
deploymentStarted();
var ctrNames = Object.keys(codeModel.contracts);
var ctrAddresses = {};
deployContracts(0, ctrAddresses, ctrNames, function (){
finalizeDeployment(deploymentId, ctrAddresses);
});
}
function deployContracts(ctrIndex, ctrAddresses, ctrNames, callBack)
{
var code = codeModel.contracts[ctrNames[ctrIndex]].codeHex;
var requests = [{
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": deploymentDialog.gasToUse, "code": code } ],
id: 0
}];
rpcCall(requests, function (httpCall, response){
var txt = qsTr("Please wait while " + ctrNames[ctrIndex] + " is published ...")
deploymentStepChanged(txt);
console.log(txt);
ctrAddresses[ctrNames[ctrIndex]] = JSON.parse(response)[0].result
deploymentDialog.waitForTrCountToIncrement(function(status) {
if (status === -1)
{
trCountIncrementTimeOut();
return;
}
ctrIndex++;
if (ctrIndex < ctrNames.length)
deployContracts(ctrIndex, ctrAddresses, ctrNames, callBack);
else
callBack();
});
});
}
function finalizeDeployment(deploymentId, addresses) {
deploymentStepChanged(qsTr("Packaging application ..."));
var deploymentDir = projectPath + deploymentId + "/";
projectModel.deploymentDir = deploymentDir;
fileIo.makeDir(deploymentDir);
for (var i = 0; i < projectListModel.count; i++) {
var doc = projectListModel.get(i);
if (doc.isContract)
continue;
if (doc.isHtml) {
//inject the script to access contract API
//TODO: use a template
var html = fileIo.readFile(doc.path);
var insertAt = html.indexOf("<head>")
if (insertAt < 0)
insertAt = 0;
else
insertAt += 6;
html = html.substr(0, insertAt) +
"<script src=\"deployment.js\"></script>" +
html.substr(insertAt);
fileIo.writeFile(deploymentDir + doc.fileName, html);
}
else
fileIo.copyFile(doc.path, deploymentDir + doc.fileName);
}
//write deployment js
var deploymentJs =
"// Autogenerated by Mix\n" +
"contracts = {};\n";
for (var c in codeModel.contracts) {
var contractAccessor = "contracts[\"" + codeModel.contracts[c].contract.name + "\"]";
deploymentJs += contractAccessor + " = {\n" +
"\tinterface: " + codeModel.contracts[c].contractInterface + ",\n" +
"\taddress: \"" + addresses[c] + "\"\n" +
"};\n" +
contractAccessor + ".contractClass = web3.eth.contract(" + contractAccessor + ".interface);\n" +
contractAccessor + ".contract = new " + contractAccessor + ".contractClass(" + 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;
applicationUrlEth = formatAppUrl(applicationUrlEth);
deploymentStepChanged(qsTr("Registering application on the Ethereum network ..."));
checkEthPath(applicationUrlEth, function () {
deploymentComplete();
deployResourcesDialog.text = qsTr("Register Web Application to finalize deployment.");
deployResourcesDialog.open();
});
}
function checkEthPath(dappUrl, callBack)
{
if (dappUrl.length === 1)
registerContentHash(deploymentDialog.eth, callBack); // we directly create a dapp under the root registrar.
else
{
// the first owned reigstrar must have been created to follow the path.
var str = createString(dappUrl[0]);
var requests = [];
requests.push({
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "gas": 150000, "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
var res = JSON.parse(response);
var addr = normalizeAddress(res[0].result);
if (addr.replace(/0+/g, "") === "")
{
var errorTxt = qsTr("Path does not exists " + JSON.stringify(dappUrl) + ". Please register using Registration Dapp. Aborting.");
deploymentError(errorTxt);
console.log(errorTxt);
}
else
{
dappUrl.splice(0, 1);
checkRegistration(dappUrl, addr, callBack);
}
});
}
}
function checkRegistration(dappUrl, addr, callBack)
{
if (dappUrl.length === 1)
registerContentHash(addr, callBack); // We do not create the register for the last part, just registering the content hash.
else
{
var txt = qsTr("Checking " + JSON.stringify(dappUrl) + " ... in registrar " + addr);
deploymentStepChanged(txt);
console.log(txt);
var requests = [];
var registrar = {}
var str = createString(dappUrl[0]);
requests.push({
//getOwner()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "gas" : 2000, "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ],
id: jsonRpcRequestId++
});
requests.push({
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x6be16bed" + str.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
var res = JSON.parse(response);
var nextAddr = normalizeAddress(res[1].result);
var errorTxt;
if (res[1].result === "0x")
{
errorTxt = qsTr("Error when creating new owned regsitrar. Please use the regsitration Dapp. Aborting");
deploymentError(errorTxt);
console.log(errorTxt);
}
else if (normalizeAddress(deploymentDialog.currentAccount) !== normalizeAddress(res[0].result))
{
errorTxt = qsTr("You are not the owner of " + dappUrl[0] + ". Aborting");
deploymentError(errorTxt);
console.log(errorTxt);
}
else if (nextAddr.replace(/0+/g, "") !== "")
{
dappUrl.splice(0, 1);
checkRegistration(dappUrl, nextAddr, callBack);
}
else
{
var txt = qsTr("Registering sub domain " + dappUrl[0] + " ...");
console.log(txt);
deploymentStepChanged(txt);
//current registrar is owned => ownedregistrar creation and continue.
requests = [];
requests.push({
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 20000, "code": "0x60056013565b61059e8061001d6000396000f35b33600081905550560060003560e060020a90048063019848921461009a578063449c2090146100af5780635d574e32146100cd5780635fd4b08a146100e1578063618242da146100f65780636be16bed1461010b5780636c4489b414610129578063893d20e8146101585780639607730714610173578063c284bc2a14610187578063e50f599a14610198578063e5811b35146101af578063ec7b9200146101cd57005b6100a560043561031b565b8060005260206000f35b6100ba6004356103a0565b80600160a060020a031660005260206000f35b6100db600435602435610537565b60006000f35b6100ec600435610529565b8060005260206000f35b6101016004356103dd565b8060005260206000f35b6101166004356103bd565b80600160a060020a031660005260206000f35b61013460043561034b565b82600160a060020a031660005281600160a060020a03166020528060405260606000f35b610160610341565b80600160a060020a031660005260206000f35b6101816004356024356102b4565b60006000f35b6101926004356103fd565b60006000f35b6101a96004356024356044356101f2565b60006000f35b6101ba6004356101eb565b80600160a060020a031660005260206000f35b6101d8600435610530565b80600160a060020a031660005260206000f35b6000919050565b600054600160a060020a031633600160a060020a031614610212576102af565b8160026000858152602001908152602001600020819055508061023457610287565b81600160a060020a0316837f680ad70765443c2967675ab0fb91a46350c01c6df59bf9a41ff8a8dd097464ec60006000a3826001600084600160a060020a03168152602001908152602001600020819055505b827f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b505050565b600054600160a060020a031633600160a060020a0316146102d457610317565b806002600084815260200190815260200160002060010181905550817f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b5050565b60006001600083600160a060020a03168152602001908152602001600020549050919050565b6000600054905090565b6000600060006002600085815260200190815260200160002054925060026000858152602001908152602001600020600101549150600260008581526020019081526020016000206002015490509193909250565b600060026000838152602001908152602001600020549050919050565b600060026000838152602001908152602001600020600101549050919050565b600060026000838152602001908152602001600020600201549050919050565b600054600160a060020a031633600160a060020a03161461041d57610526565b80600160006002600085815260200190815260200160002054600160a060020a031681526020019081526020016000205414610458576104d2565b6002600082815260200190815260200160002054600160a060020a0316817f680ad70765443c2967675ab0fb91a46350c01c6df59bf9a41ff8a8dd097464ec60006000a36000600160006002600085815260200190815260200160002054600160a060020a03168152602001908152602001600020819055505b6002600082815260200190815260200160002060008101600090556001810160009055600281016000905550807f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b50565b6000919050565b6000919050565b600054600160a060020a031633600160a060020a0316146105575761059a565b806002600084815260200190815260200160002060020181905550817f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b505056" } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function(httpRequest, response) {
var newCtrAddress = normalizeAddress(JSON.parse(response)[0].result);
requests = [];
var txt = qsTr("Please wait " + dappUrl[0] + " is registering ...");
deploymentStepChanged(txt);
console.log(txt);
deploymentDialog.waitForTrCountToIncrement(function(status) {
if (status === -1)
{
trCountIncrementTimeOut();
return;
}
var crLevel = createString(dappUrl[0]).encodeValueAsString();
requests.push({
//setRegister()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function(request, response){
dappUrl.splice(0, 1);
checkRegistration(dappUrl, newCtrAddress, callBack);
});
});
});
}
});
}
}
function trCountIncrementTimeOut()
{
var error = qsTr("Something went wrong during the deployment. Please verify the amount of gas for this transaction and check your balance.")
console.log(error);
deploymentError(error);
}
function registerContentHash(registrar, callBack)
{
var txt = qsTr("Finalizing Dapp registration ...");
deploymentStepChanged(txt);
console.log(txt);
var requests = [];
var paramTitle = clientModel.encodeAbiString(projectModel.projectTitle);
requests.push({
//setContent()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle + deploymentDialog.packageHash } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
callBack();
});
}
function registerToUrlHint()
{
deploymentStepChanged(qsTr("Registering application Resources (" + deploymentDialog.applicationUrlHttp) + ") ...");
var requests = [];
var paramUrlHttp = createString(deploymentDialog.applicationUrlHttp);
requests.push({
//urlHint => suggestUrl
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 30000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
deploymentComplete();
});
}
function normalizeAddress(addr)
{
addr = addr.replace('0x', '');
if (addr.length <= 40)
return addr;
var left = addr.length - 40;
return addr.substring(left);
}
function formatAppUrl(url)
{
if (url.toLowerCase().indexOf("eth://") === 0)
url = url.substring(6);
if (url.toLowerCase().indexOf(projectModel.projectTitle + ".") === 0)
url = url.substring(projectModel.projectTitle.length + 1);
if (url === "")
return [projectModel.projectTitle];
var ret;
if (url.indexOf("/") === -1)
ret = url.split('.').reverse();
else
{
var slash = url.indexOf("/");
var left = url.substring(0, slash);
var leftA = left.split(".");
leftA.reverse();
var right = url.substring(slash + 1);
var rightA = right.split('/');
ret = leftA.concat(rightA);
}
if (ret[0].toLowerCase() === "eth")
ret.splice(0, 1);
ret.push(projectModel.projectTitle);
return ret;
}

4
mix/test/qml/TestMain.qml

@ -4,6 +4,7 @@ import org.ethereum.qml.TestService 1.0
import "../../qml"
import "js/TestDebugger.js" as TestDebugger
import "js/TestTutorial.js" as TestTutorial
import "js/TestProject.js" as TestProject
TestCase
{
@ -49,9 +50,9 @@ TestCase
function editContract(c)
{
mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText(c);
ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000))
fail("not compiled");
ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
}
function editHtml(c)
@ -74,5 +75,6 @@ TestCase
function test_dbg_transactionWithParameter() { TestDebugger.test_transactionWithParameter(); }
function test_dbg_constructorParameters() { TestDebugger.test_constructorParameters(); }
function test_dbg_arrayParametersAndStorage() { TestDebugger.test_arrayParametersAndStorage(); }
function test_project_contractRename() { TestProject.test_contractRename(); }
}

18
mix/test/qml/js/TestProject.js

@ -0,0 +1,18 @@
function test_contractRename()
{
newProject();
tryCompare(mainApplication.mainContent.projectNavigator.sections.itemAt(0).model.get(0), "name", "Contract");
editContract("contract Renamed {}");
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("Error running transaction");
wait(1000);
tryCompare(mainApplication.mainContent.projectNavigator.sections.itemAt(0).model.get(0), "name", "Renamed");
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.editTransaction(2);
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
tryCompare(transactionDialog, "contractId", "Renamed");
tryCompare(transactionDialog, "functionId", "Renamed");
transactionDialog.close();
mainApplication.projectModel.stateDialog.close();
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(2), "contract", "Renamed");
}
Loading…
Cancel
Save