Browse Source

Contract Deployment:

- add files hash
 - add test action (to be removed
cl-refactor
yann300 10 years ago
parent
commit
86d446d2ba
  1. 39
      mix/FileIo.cpp
  2. 2
      mix/FileIo.h
  3. 128
      mix/qml/DeploymentDialog.qml
  4. 2
      mix/qml/TransactionDialog.qml
  5. 189
      mix/qml/js/ProjectModel.js

39
mix/FileIo.cpp

@ -20,6 +20,8 @@
* Ethereum IDE client. * Ethereum IDE client.
*/ */
#include <json/json.h>
#include <QJsonObject>
#include <QDebug> #include <QDebug>
#include <QDirIterator> #include <QDirIterator>
#include <QDir> #include <QDir>
@ -106,23 +108,18 @@ bool FileIo::fileExists(QString const& _url)
return file.exists(); 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); QUrl folder(_deploymentFolder);
QString path(folder.path()); QString path(folder.path());
QDir deployDir = QDir(path); QDir deployDir = QDir(path);
dev::RLPStream str; 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)) for (auto item: deployDir.entryInfoList(QDir::Files))
{ {
QFile qFile(item.filePath()); QFile qFile(item.filePath());
@ -133,14 +130,34 @@ QString FileIo::compress(QString const& _manifest, QString const& _deploymentFol
str.append(bytes(fileBytes.begin(), fileBytes.end())); str.append(bytes(fileBytes.begin(), fileBytes.end()));
QByteArray contentBytes = QString().toUtf8(); 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(); QByteArray _a = qFile.readAll();
str.append(bytes(_a.begin(), _a.end())); 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(); 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(); bytes dapp = str.out();
dev::h256 h = dev::sha3(dapp); dev::h256 h = dev::sha3(dapp);
QString ret = QString::fromStdString(toHex(h.ref())); QString ret = QString::fromStdString(toHex(h.ref()));

2
mix/FileIo.h

@ -54,7 +54,7 @@ public:
/// Check if file exists /// Check if file exists
Q_INVOKABLE bool fileExists(QString const& _url); Q_INVOKABLE bool fileExists(QString const& _url);
/// Compress a folder, @returns sha3 of the compressed file. /// 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: private:
QString getHomePath() const; QString getHomePath() const;

128
mix/qml/DeploymentDialog.qml

@ -6,8 +6,10 @@ import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0 import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper import "js/TransactionHelper.js" as TransactionHelper
import "js/ProjectModel.js" as ProjectModelCode import "js/ProjectModel.js" as ProjectModelCode
import "js/QEtherHelper.js" as QEtherHelper
import "." import "."
Window { Window {
id: modalDeploymentDialog id: modalDeploymentDialog
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
@ -16,6 +18,12 @@ Window {
visible: false visible: false
property alias applicationUrlEth: applicationUrlEth.text property alias applicationUrlEth: applicationUrlEth.text
property alias applicationUrlHttp: applicationUrlHttp.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 color: Style.generic.layout.backgroundColor
function close() function close()
@ -30,6 +38,16 @@ Window {
visible = true; visible = true;
} }
function pad(h)
{
// TODO move this to QHashType class
while (h.length < 64)
{
h = '0' + h;
}
return h;
}
GridLayout GridLayout
{ {
columns: 2 columns: 2
@ -89,5 +107,115 @@ Window {
text: qsTr("Close"); text: qsTr("Close");
onClicked: 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);
}
}
} }
} }

2
mix/qml/TransactionDialog.qml

@ -405,7 +405,7 @@ Window {
return boolViewComp; return boolViewComp;
else if (type.indexOf("string") !== -1) else if (type.indexOf("string") !== -1)
return stringViewComp; return stringViewComp;
else if (type.indexOf("hash") !== -1) else if (type.indexOf("hash") !== -1 || type.indexOf("address") !== -1)
return hashViewComp; return hashViewComp;
else else
return null; return null;

189
mix/qml/js/ProjectModel.js

@ -23,8 +23,6 @@ Qt.include("QEtherHelper.js")
var htmlTemplate = "<html>\n<head>\n<script>\n</script>\n</head>\n<body>\n<script>\n</script>\n</body>\n</html>"; 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"; var contractTemplate = "contract Contract {\n}\n";
var registrarContract = "6fcdee8688e44aebdcddf28a8d87318d38f695ff" /*"0000000000000000000000000000000000000a28"*/
var hintContract = "c4040ef9635e7503bbbc74b73a9385ac78733d09"
function saveAll() { function saveAll() {
saveProject(); saveProject();
@ -337,9 +335,6 @@ function finalizeDeployment(deploymentId, addresses) {
//create a dir for frontend files and copy them //create a dir for frontend files and copy them
var deploymentDir = projectPath + deploymentId + "/"; var deploymentDir = projectPath + deploymentId + "/";
fileIo.makeDir(deploymentDir); fileIo.makeDir(deploymentDir);
var manifest = {
entries: []
};
for (var i = 0; i < projectListModel.count; i++) { for (var i = 0; i < projectListModel.count; i++) {
var doc = projectListModel.get(i); var doc = projectListModel.get(i);
if (doc.isContract) if (doc.isContract)
@ -362,12 +357,6 @@ function finalizeDeployment(deploymentId, addresses) {
} }
else else
fileIo.copyFile(doc.path, deploymentDir + doc.fileName); fileIo.copyFile(doc.path, deploymentDir + doc.fileName);
var jsonFile = {
path: '/' + doc.fileName,
file: '/' + doc.fileName
}
manifest.entries.push(jsonFile);
} }
//write deployment js //write deployment js
var deploymentJs = var deploymentJs =
@ -389,89 +378,168 @@ function finalizeDeployment(deploymentId, addresses) {
deploymentAddresses = addresses; deploymentAddresses = addresses;
saveProject(); saveProject();
var hash = fileIo.compress(JSON.stringify(manifest), deploymentDir); var hash = fileIo.compress(deploymentDir);
var applicationUrlEth = deploymentDialog.applicationUrlEth; var applicationUrlEth = deploymentDialog.applicationUrlEth;
var applicationUrlHttp = deploymentDialog.applicationUrlHttp;
applicationUrlEth = formatAppUrl(applicationUrlEth); applicationUrlEth = formatAppUrl(applicationUrlEth);
checkRegistration(applicationUrlEth, registrarContract, hash, function () { checkRegistration(applicationUrlEth, deploymentDialog.root, hash, function () {
deploymentComplete(); 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) function checkRegistration(dappUrl, addr, hash, callBack)
{ {
console.log("CALL TO " + addr);
var requests = []; var requests = [];
var data = ""; var data = "";
console.log(JSON.stringify(dappUrl));
if (dappUrl.length > 0) if (dappUrl.length > 0)
{ {
//checking path (addr). //checking path (register).
var str = createString(dappUrl[0]); var str = createString(dappUrl[0]);
data = "6be16bed" + str.encodeValueAsString(); data = "0x6be16bed" + str.encodeValueAsString();
console.log("checking if path exists (register) => " + data); console.log("checking if path exists (register) => " + data);
console.log("adrr : " + '0x' + addr + " param " + data);
requests.push({ requests.push({
jsonrpc: "2.0", jsonrpc: "2.0",
method: "eth_call", method: "eth_call",
params: [ { "to": addr, "data": data } ], params: [ { "to": '0x' + addr, "data": data } ],
id: jsonRpcRequestId++ 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 else
{ {
//finalize (setContentHash).
finalize = true;
var paramTitle = createString(projectModel.projectTitle); var paramTitle = createString(projectModel.projectTitle);
var paramHash = createHash(hash);
data = "5d574e32" + paramTitle.encodeValueAsString() + paramHash.encodeValueAsString();
console.log("finalize (setRegister) => " + data);
requests.push({ requests.push({
jsonrpc: "2.0", //owner()
method: "eth_transact", jsonrpc: "2.0",
params: [ { "to": addr, "data": data } ], method: "eth_call",
id: jsonRpcRequestId++ params: [ { "to": '0x' + addr, "data": "0xec7b9200" + paramTitle.encodeValueAsString() } ],
}); id: jsonRpcRequestId++
});
var paramWebUrl = createString(deploymentDialog.applicationUrlHttp);
var dataHint = "4983e19c" + paramHash.encodeValueAsString() + paramWebUrl.encodeValueAsString();
requests.push({ requests.push({
//accounts
jsonrpc: "2.0", jsonrpc: "2.0",
method: "eth_transact", method: "eth_accounts",
params: [ { "to": hintContract, "data": dataHint } ], params: null,
id: jsonRpcRequestId++ id: jsonRpcRequestId++
}); });
}
var jsonRpcUrl = "http://localhost:8080"; rpcCall(requests, function (httpRequest, response) {
var rpcRequest = JSON.stringify(requests); requests = [];
var httpRequest = new XMLHttpRequest(); var res = JSON.parse(response);
httpRequest.open("POST", jsonRpcUrl, true); var currentOwner = res[0].result;
httpRequest.setRequestHeader("Content-type", "application/json"); var noOwner = currentOwner.replace('0x', '').replace(/0/g, '') === '';
httpRequest.setRequestHeader("Content-length", rpcRequest.length); var bOwner = false;
httpRequest.setRequestHeader("Connection", "close");
httpRequest.onreadystatechange = function() { if (noOwner)
if (httpRequest.readyState === XMLHttpRequest.DONE) { {
if (httpRequest.status === 200) { requests.push({
console.log(httpRequest.responseText); //reserve()
if (dappUrl.length > 0) 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 (normalizeAddress(res[1].result[u]) === currentOwner)
if (address === "") bOwner = true;
deploymentError(qsTr("This Eth Dapp path has not been registered")); }
else
{ if (!bOwner)
dappUrl.splice(0, 1); {
checkRegistration(dappUrl, address, hash, callBack); 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) function formatAppUrl(url)
@ -499,3 +567,4 @@ function formatAppUrl(url)
return ret; return ret;
} }
} }

Loading…
Cancel
Save