From 86d446d2ba778d89c533fb99ce3b50184138a3bc Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 20 Feb 2015 18:10:21 +0100 Subject: [PATCH] Contract Deployment: - add files hash - add test action (to be removed --- mix/FileIo.cpp | 39 +++++-- mix/FileIo.h | 2 +- mix/qml/DeploymentDialog.qml | 128 +++++++++++++++++++++++ mix/qml/TransactionDialog.qml | 2 +- mix/qml/js/ProjectModel.js | 189 +++++++++++++++++++++++----------- 5 files changed, 287 insertions(+), 73 deletions(-) diff --git a/mix/FileIo.cpp b/mix/FileIo.cpp index 8f57c24a6..02d8b9bef 100644 --- a/mix/FileIo.cpp +++ b/mix/FileIo.cpp @@ -20,6 +20,8 @@ * Ethereum IDE client. */ +#include +#include #include #include #include @@ -106,23 +108,18 @@ bool FileIo::fileExists(QString const& _url) return file.exists(); } -QString FileIo::compress(QString const& _manifest, QString const& _deploymentFolder) +QString FileIo::compress(QString const& _deploymentFolder) { + + Json::Value manifest; + Json::Value entries(Json::arrayValue); + QUrl folder(_deploymentFolder); QString path(folder.path()); QDir deployDir = QDir(path); dev::RLPStream str; - QByteArray manifestBytes = "swarm.json"; - str.append(bytes(manifestBytes.begin(), manifestBytes.end())); - - QByteArray manifestcontentBytes = "application/json"; - str.append(bytes(manifestcontentBytes.begin(), manifestcontentBytes.end())); - - QByteArray b = _manifest.toUtf8(); - str.append(bytes(b.begin(), b.end())); - for (auto item: deployDir.entryInfoList(QDir::Files)) { QFile qFile(item.filePath()); @@ -133,14 +130,34 @@ QString FileIo::compress(QString const& _manifest, QString const& _deploymentFol str.append(bytes(fileBytes.begin(), fileBytes.end())); QByteArray contentBytes = QString().toUtf8(); - str.append(bytes(contentBytes.begin(), contentBytes.end())); + bytes b = bytes(contentBytes.begin(), contentBytes.end()); + str.append(b); QByteArray _a = qFile.readAll(); str.append(bytes(_a.begin(), _a.end())); + + Json::Value f; + f["path"] = "/"; //TODO: Manage relative sub folder + f["file"] = "/" + i.fileName().toStdString(); + f["hash"] = toHex(dev::sha3(b).ref()); + entries.append(f); } qFile.close(); } + manifest["entries"] = entries; + + QByteArray manifestBytes = "swarm.json"; + str.append(bytes(manifestBytes.begin(), manifestBytes.end())); + + QByteArray manifestcontentBytes = "application/json"; + str.append(bytes(manifestcontentBytes.begin(), manifestcontentBytes.end())); + + std::stringstream jsonStr; + jsonStr << manifest; + QByteArray b = QString::fromStdString(jsonStr.str()).toUtf8(); + str.append(bytes(b.begin(), b.end())); + bytes dapp = str.out(); dev::h256 h = dev::sha3(dapp); QString ret = QString::fromStdString(toHex(h.ref())); diff --git a/mix/FileIo.h b/mix/FileIo.h index 5bfd17aa9..d8899015b 100644 --- a/mix/FileIo.h +++ b/mix/FileIo.h @@ -54,7 +54,7 @@ public: /// Check if file exists Q_INVOKABLE bool fileExists(QString const& _url); /// Compress a folder, @returns sha3 of the compressed file. - Q_INVOKABLE QString compress(QString const& _manifest, QString const& _deploymentFolder); + Q_INVOKABLE QString compress(QString const& _deploymentFolder); private: QString getHomePath() const; diff --git a/mix/qml/DeploymentDialog.qml b/mix/qml/DeploymentDialog.qml index 88dfb90b2..a2b8b3623 100644 --- a/mix/qml/DeploymentDialog.qml +++ b/mix/qml/DeploymentDialog.qml @@ -6,8 +6,10 @@ 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/QEtherHelper.js" as QEtherHelper import "." + Window { id: modalDeploymentDialog modality: Qt.ApplicationModal @@ -16,6 +18,12 @@ Window { visible: false property alias applicationUrlEth: applicationUrlEth.text property alias applicationUrlHttp: applicationUrlHttp.text + property string root: "42f6279a5b6d350e1ce2a9ebef05657c79275c6a"; + property string eth: "31f6aee7f26e9d3320753c112ed34bcfc3c989b8"; + property string wallet: "c4040ef9635e7503bbbc74b73a9385ac78733d09"; + property string urlHintContract: "29a2e6d3c56ef7713a4e7229c3d1a23406f0161a" + + color: Style.generic.layout.backgroundColor function close() @@ -30,6 +38,16 @@ Window { visible = true; } + function pad(h) + { + // TODO move this to QHashType class + while (h.length < 64) + { + h = '0' + h; + } + return h; + } + GridLayout { columns: 2 @@ -89,5 +107,115 @@ Window { text: qsTr("Close"); onClicked: close(); } + + Button { + text: qsTr("Check Ownership"); + onClicked: { + var requests = []; + var ethStr = QEtherHelper.createString("eth"); + + var ethHash = QEtherHelper.createHash(eth); + + requests.push({ //owner + jsonrpc: "2.0", + method: "eth_call", + params: [ { "to": '0x' + modalDeploymentDialog.root, "data": "0xec7b9200" + ethStr.encodeValueAsString() } ], + id: 3 + }); + + requests.push({ //register + jsonrpc: "2.0", + method: "eth_call", + params: [ { "to": '0x' + modalDeploymentDialog.root, "data": "0x6be16bed" + ethStr.encodeValueAsString() } ], + id: 4 + }); + + var jsonRpcUrl = "http://localhost:8080"; + var rpcRequest = JSON.stringify(requests); + var httpRequest = new XMLHttpRequest(); + httpRequest.open("POST", jsonRpcUrl, true); + httpRequest.setRequestHeader("Content-type", "application/json"); + httpRequest.setRequestHeader("Content-length", rpcRequest.length); + httpRequest.setRequestHeader("Connection", "close"); + httpRequest.onreadystatechange = function() { + if (httpRequest.readyState === XMLHttpRequest.DONE) { + if (httpRequest.status === 200) { + console.log(httpRequest.responseText); + } else { + var errorText = qsTr("path registration failed ") + httpRequest.status; + console.log(errorText); + } + } + } + httpRequest.send(rpcRequest); + } + } + + + Button { + text: qsTr("Generate registrar init"); + onClicked: { + console.log("registering eth/wallet") + var jsonRpcRequestId = 0; + + var requests = []; + var ethStr = QEtherHelper.createString("eth"); + var ethHash = QEtherHelper.createHash(eth); + requests.push({ //reserve + jsonrpc: "2.0", + method: "eth_transact", + params: [ { "to": '0x' + modalDeploymentDialog.root, "data": "0x1c83171b" + ethStr.encodeValueAsString() } ], + id: jsonRpcRequestId++ + }); + + console.log("0x7d2e3ce9" + ethStr.encodeValueAsString() + pad(eth)); + console.log(ethStr.encodeValueAsString()); + console.log(pad(eth)); + + requests.push({ //setRegister + jsonrpc: "2.0", + method: "eth_transact", + params: [ { "to": '0x' + modalDeploymentDialog.root, "data": "0x96077307" + ethStr.encodeValueAsString() + pad(eth) /*ethHash.encodeValueAsString()*/ } ], + id: jsonRpcRequestId++ + }); + + var walletStr = QEtherHelper.createString("wallet"); + var walletHash = QEtherHelper.createHash(wallet); + + requests.push({ //reserve + jsonrpc: "2.0", + method: "eth_transact", + params: [ { "to": '0x' + modalDeploymentDialog.eth, "data": "0x1c83171b" + walletStr.encodeValueAsString() } ], + id: jsonRpcRequestId++ + }); + + + requests.push({ //setRegister + jsonrpc: "2.0", + method: "eth_transact", + params: [ { "to": '0x' + modalDeploymentDialog.eth, "data": "0x96077307" + walletStr.encodeValueAsString() + pad(wallet) } ], + id: jsonRpcRequestId++ + }); + + var jsonRpcUrl = "http://localhost:8080"; + var rpcRequest = JSON.stringify(requests); + var httpRequest = new XMLHttpRequest(); + httpRequest.open("POST", jsonRpcUrl, true); + httpRequest.setRequestHeader("Content-type", "application/json"); + httpRequest.setRequestHeader("Content-length", rpcRequest.length); + httpRequest.setRequestHeader("Connection", "close"); + httpRequest.onreadystatechange = function() { + if (httpRequest.readyState === XMLHttpRequest.DONE) { + if (httpRequest.status === 200) { + console.log(httpRequest.responseText); + } else { + var errorText = qsTr("path registration failed ") + httpRequest.status; + console.log(errorText); + } + } + } + httpRequest.send(rpcRequest); + } + } } } diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index e5a8bc746..434f8a850 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -405,7 +405,7 @@ Window { return boolViewComp; else if (type.indexOf("string") !== -1) return stringViewComp; - else if (type.indexOf("hash") !== -1) + else if (type.indexOf("hash") !== -1 || type.indexOf("address") !== -1) return hashViewComp; else return null; diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 0af32c083..6426e39ef 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -23,8 +23,6 @@ Qt.include("QEtherHelper.js") var htmlTemplate = "\n\n\n\n\n\n\n"; var contractTemplate = "contract Contract {\n}\n"; -var registrarContract = "6fcdee8688e44aebdcddf28a8d87318d38f695ff" /*"0000000000000000000000000000000000000a28"*/ -var hintContract = "c4040ef9635e7503bbbc74b73a9385ac78733d09" function saveAll() { saveProject(); @@ -337,9 +335,6 @@ function finalizeDeployment(deploymentId, addresses) { //create a dir for frontend files and copy them var deploymentDir = projectPath + deploymentId + "/"; fileIo.makeDir(deploymentDir); - var manifest = { - entries: [] - }; for (var i = 0; i < projectListModel.count; i++) { var doc = projectListModel.get(i); if (doc.isContract) @@ -362,12 +357,6 @@ function finalizeDeployment(deploymentId, addresses) { } else fileIo.copyFile(doc.path, deploymentDir + doc.fileName); - - var jsonFile = { - path: '/' + doc.fileName, - file: '/' + doc.fileName - } - manifest.entries.push(jsonFile); } //write deployment js var deploymentJs = @@ -389,89 +378,168 @@ function finalizeDeployment(deploymentId, addresses) { deploymentAddresses = addresses; saveProject(); - var hash = fileIo.compress(JSON.stringify(manifest), deploymentDir); + var hash = fileIo.compress(deploymentDir); var applicationUrlEth = deploymentDialog.applicationUrlEth; - var applicationUrlHttp = deploymentDialog.applicationUrlHttp; applicationUrlEth = formatAppUrl(applicationUrlEth); - checkRegistration(applicationUrlEth, registrarContract, hash, function () { + checkRegistration(applicationUrlEth, deploymentDialog.root, hash, function () { deploymentComplete(); }); } +function rpcCall(requests, callBack) +{ + var jsonRpcUrl = "http://localhost:8080"; + var rpcRequest = JSON.stringify(requests); + var httpRequest = new XMLHttpRequest(); + httpRequest.open("POST", jsonRpcUrl, true); + httpRequest.setRequestHeader("Content-type", "application/json"); + httpRequest.setRequestHeader("Content-length", rpcRequest.length); + httpRequest.setRequestHeader("Connection", "close"); + httpRequest.onreadystatechange = function() { + if (httpRequest.readyState === XMLHttpRequest.DONE) { + if (httpRequest.status !== 200) + { + var errorText = qsTr("Deployment error: Error while registering Dapp ") + httpRequest.status; + console.log(errorText); + deploymentError(errorText); + return; + } + console.log(httpRequest.responseText); + callBack(httpRequest.status, httpRequest.responseText) + } + } + httpRequest.send(rpcRequest); +} + + function checkRegistration(dappUrl, addr, hash, callBack) { + console.log("CALL TO " + addr); var requests = []; var data = ""; + console.log(JSON.stringify(dappUrl)); if (dappUrl.length > 0) { - //checking path (addr). + //checking path (register). var str = createString(dappUrl[0]); - data = "6be16bed" + str.encodeValueAsString(); + data = "0x6be16bed" + str.encodeValueAsString(); console.log("checking if path exists (register) => " + data); + console.log("adrr : " + '0x' + addr + " param " + data); requests.push({ jsonrpc: "2.0", method: "eth_call", - params: [ { "to": addr, "data": data } ], + params: [ { "to": '0x' + addr, "data": data } ], id: jsonRpcRequestId++ }); + + rpcCall(requests, function (httpRequest, response) { + var address = JSON.parse(response)[0].result.replace('0x', ''); + if (address === "") + { + var errorTxt = qsTr("Path does not exists " + JSON.stringify(dappUrl) + " cannot continue"); + deploymentError(errorTxt); + console.log(errorTxt); + return; + } + + dappUrl.splice(0, 1); + checkRegistration(dappUrl, address, hash, callBack); + }); } else { - //finalize (setContentHash). - finalize = true; var paramTitle = createString(projectModel.projectTitle); - var paramHash = createHash(hash); - data = "5d574e32" + paramTitle.encodeValueAsString() + paramHash.encodeValueAsString(); - console.log("finalize (setRegister) => " + data); requests.push({ - jsonrpc: "2.0", - method: "eth_transact", - params: [ { "to": addr, "data": data } ], - id: jsonRpcRequestId++ - }); + //owner() + jsonrpc: "2.0", + method: "eth_call", + params: [ { "to": '0x' + addr, "data": "0xec7b9200" + paramTitle.encodeValueAsString() } ], + id: jsonRpcRequestId++ + }); - var paramWebUrl = createString(deploymentDialog.applicationUrlHttp); - var dataHint = "4983e19c" + paramHash.encodeValueAsString() + paramWebUrl.encodeValueAsString(); requests.push({ + //accounts jsonrpc: "2.0", - method: "eth_transact", - params: [ { "to": hintContract, "data": dataHint } ], + method: "eth_accounts", + params: null, id: jsonRpcRequestId++ }); - } - var jsonRpcUrl = "http://localhost:8080"; - var rpcRequest = JSON.stringify(requests); - var httpRequest = new XMLHttpRequest(); - httpRequest.open("POST", jsonRpcUrl, true); - httpRequest.setRequestHeader("Content-type", "application/json"); - httpRequest.setRequestHeader("Content-length", rpcRequest.length); - httpRequest.setRequestHeader("Connection", "close"); - httpRequest.onreadystatechange = function() { - if (httpRequest.readyState === XMLHttpRequest.DONE) { - if (httpRequest.status === 200) { - console.log(httpRequest.responseText); - if (dappUrl.length > 0) + rpcCall(requests, function (httpRequest, response) { + requests = []; + var res = JSON.parse(response); + var currentOwner = res[0].result; + var noOwner = currentOwner.replace('0x', '').replace(/0/g, '') === ''; + var bOwner = false; + + if (noOwner) + { + requests.push({ + //reserve() + jsonrpc: "2.0", + method: "eth_transact", + params: [ { "to": '0x' + addr, "data": "0x1c83171b" + paramTitle.encodeValueAsString() } ], + id: jsonRpcRequestId++ + }); + } + else + { + currentOwner = normalizeAddress(currentOwner); + for (var u in res[1].result) { - var address = JSON.parse(httpRequest.responseText)[0].result.replace('0x', ''); - if (address === "") - deploymentError(qsTr("This Eth Dapp path has not been registered")); - else - { - dappUrl.splice(0, 1); - checkRegistration(dappUrl, address, hash, callBack); - } + if (normalizeAddress(res[1].result[u]) === currentOwner) + bOwner = true; + } + + if (!bOwner) + { + var errorTxt = qsTr("Current user is not the owner of this path. Cannot continue") + deploymentError(errorTxt); + console.log(errorTxt); + return; } - else - callBack(); - } else { - var errorText = qsTr("Deployment error: Error while registering Dapp ") + httpRequest.status; - console.log(errorText); - deploymentError(errorText); } - } + + + requests.push({ + //setContent() + jsonrpc: "2.0", + method: "eth_transact", + params: [ { "to": '0x' + addr, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + hash } ], + id: jsonRpcRequestId++ + }); + console.log("reserve and register"); + rpcCall(requests, function (httpRequest, response) { + requests = []; + var paramUrlHttp = createString(deploymentDialog.applicationUrlHttp); + requests.push({ + //urlHint => suggestUrl + jsonrpc: "2.0", + method: "eth_transact", + params: [ { "to": '0x' + deploymentDialog.urlHintContract, "data": "0x4983e19c" + hash + paramUrlHttp.encodeValueAsString() } ], + id: jsonRpcRequestId++ + }); + + rpcCall(requests, function (httpRequest, response) { + callBack(); + }); + }); + }); } - httpRequest.send(rpcRequest); +} + +function normalizeAddress(addr) +{ + addr = addr.replace('0x', ''); + var i = 0; + for (var k in addr) + { + if (addr[k] !== "0") + break; + else + i++; + } + return addr.substring(i); } function formatAppUrl(url) @@ -499,3 +567,4 @@ function formatAppUrl(url) return ret; } } +