Arkadiy Paronyan
10 years ago
12 changed files with 415 additions and 365 deletions
@ -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; |
|||
} |
@ -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…
Reference in new issue