Browse Source

Check gas used for contract deployment and dapp registration.

cl-refactor
yann300 10 years ago
parent
commit
62c988ac15
  1. 9
      mix/ClientModel.cpp
  2. 6
      mix/ClientModel.h
  3. 2
      mix/QBigInt.h
  4. 88
      mix/qml/DeploymentDialog.qml
  5. 67
      mix/qml/js/NetworkDeployment.js

9
mix/ClientModel.cpp

@ -192,11 +192,11 @@ QVariantMap ClientModel::contractAddresses() const
return res;
}
QVariantMap ClientModel::gasCosts() const
QVariantList ClientModel::gasCosts() const
{
QVariantMap res;
QVariantList res;
for (auto const& c: m_gasCosts)
res.insert(c.first, QVariant::fromValue(static_cast<int>(c.second)));
res.append(QVariant::fromValue(static_cast<int>(c)));
return res;
}
@ -299,6 +299,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
{
vector<Address> deployedContracts;
onStateReset();
m_gasCosts.clear();
for (TransactionSettings const& transaction: _sequence)
{
ContractCallDataEncoder encoder;
@ -364,7 +365,6 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
contractAddressesChanged();
}
gasCostsChanged();
m_gasCosts[transaction.contractId] = m_client->lastExecution().gasUsed;
}
else
{
@ -378,6 +378,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
}
callContract(contractAddressIter->second, encoder.encodedData(), transaction);
}
m_gasCosts.append(m_client->lastExecution().gasUsed);
}
onNewTransaction();
}

6
mix/ClientModel.h

@ -147,7 +147,7 @@ public:
/// @returns deployed contracts addresses
Q_PROPERTY(QVariantMap contractAddresses READ contractAddresses NOTIFY contractAddressesChanged)
/// @returns deployed contracts gas costs
Q_PROPERTY(QVariantMap gasCosts READ gasCosts NOTIFY gasCostsChanged)
Q_PROPERTY(QVariantList gasCosts READ gasCosts NOTIFY gasCostsChanged)
// @returns the last block
Q_PROPERTY(RecordLogEntry* lastBlock READ lastBlock CONSTANT)
/// ethereum.js RPC request entry point
@ -217,7 +217,7 @@ signals:
private:
RecordLogEntry* lastBlock() const;
QVariantMap contractAddresses() const;
QVariantMap gasCosts() const;
QVariantList gasCosts() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, std::map<Address, dev::eth::Account> const& _accounts, Secret const& _miner);
dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
@ -233,7 +233,7 @@ private:
std::unique_ptr<MixClient> m_client;
std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server;
std::map<QString, u256> m_gasCosts;
QList<u256> m_gasCosts;
std::map<QString, Address> m_contractAddresses;
std::map<Address, QString> m_contractNames;
std::map<QString, Address> m_stdContractAddresses;

2
mix/QBigInt.h

@ -79,7 +79,7 @@ public:
~QBigInt() {}
/// @returns the current used big integer.
BigIntVariant internalValue() { return m_internalValue; }
BigIntVariant internalValue() const { return m_internalValue; }
/// @returns a string representation of the big integer used. Invokable from QML.
Q_INVOKABLE QString value() const;
/// Set the value of the BigInteger used. Will use u256 type. Invokable from QML.

88
mix/qml/DeploymentDialog.qml

@ -24,7 +24,9 @@ Dialog {
property string packageBase64
property string eth: registrarAddr.text
property string currentAccount
property string gasToUse: "0x188132" //gasToUseInput.text
property string gasToUse: gasToUseInput.text
property string gasPrice
property variant gasTotal
property variant paramsModel: []
function close()
@ -43,7 +45,6 @@ Dialog {
id: 0
}];
console.log(packageHash);
TransactionHelper.rpcCall(requests, function(arg1, arg2)
{
modelAccounts.clear();
@ -70,16 +71,27 @@ Dialog {
{
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];
});
});
var gas = 0;
var gasCosts = clientModel.gasCosts;
for (var g in gasCosts)
gas += gasCosts[g];
gasToUse = gas;
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);
gasInt.setValue(NetworkDeploymentCode.gasUsed());
gasTotal = gasInt.multiply(gasPriceInt);
gasToUseInput.text = gasTotal.value();
});
}
}
function stopForInputError(inError)
@ -114,6 +126,16 @@ Dialog {
poolLog.start();
}
BigIntValue
{
id: gasInt
}
BigIntValue
{
id: gasPriceInt
}
Timer
{
id: poolLog
@ -295,11 +317,13 @@ Dialog {
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]);
}
}
model: ListModel {
@ -314,21 +338,41 @@ Dialog {
anchors.leftMargin: 20
id: balance;
}
BigIntValue
{
id: balanceInt
}
}
}
DefaultLabel
{
text: qsTr("Amount of gas to use for each contract deployment: ")
text: qsTr("Amount of gas to use for contract deployment: ")
}
DefaultTextField
{
text: "1000000"
Layout.preferredWidth: 350
id: gasToUseInput
}
DefaultLabel
{
text: qsTr("Amount of gas to use for dapp registration: ")
}
BigIntValue
{
id: deployGas;
}
DefaultTextField
{
Layout.preferredWidth: 350
id: gasToUseDeployInput
}
DefaultLabel
{
text: qsTr("Ethereum Application URL: ")
@ -345,6 +389,10 @@ Dialog {
id: applicationUrlEth
onTextChanged: {
appUrlFormatted.text = NetworkDeploymentCode.formatAppUrl(text).join('/');
NetworkDeploymentCode.checkPathCreationCost(function(pathCreationCost){
gasToUseDeployInput.text = pathCreationCost;
deployGas.setValue(pathCreationCost);
});
}
}
@ -379,6 +427,28 @@ Dialog {
id: runAction
tooltip: qsTr("Deploy contract(s) and Package resources files.")
onTriggered: {
if (contractRedeploy.checked)
{
console.log(gasTotal);
if (balanceInt <= gasTotal.add(deployGas))
{
errorDialog.text = qsTr("Not enough ether to deploy contract.");
errorDialog.open();
console.log("fff");
return;
}
}
else
{
if (balanceInt <= deployGas)
{
errorDialog.text = qsTr("Not enough ether to deploy contract.");
errorDialog.open();
console.log("mmmm");
return;
}
}
var inError = [];
var ethUrl = NetworkDeploymentCode.formatAppUrl(applicationUrlEth.text);
for (var k in ethUrl)

67
mix/qml/js/NetworkDeployment.js

@ -47,6 +47,7 @@ function startDeployProject(erasePrevious)
var ctrAddresses = {};
var state = retrieveState(projectModel.deployedState);
console.log(JSON.stringify(state));
if (!state)
{
var txt = qsTr("Unable to find state " + projectModel.deployedState);
@ -59,6 +60,23 @@ function startDeployProject(erasePrevious)
});
}
function checkPathCreationCost(callBack)
{
var dappUrl = formatAppUrl(deploymentDialog.applicationUrlEth);
checkEthPath(dappUrl, true, function(success) {
callBack((dappUrl.length - 1) * 100000 + 5000 /* 500: register content hash */);
});
}
function gasUsed()
{
var gas = 0;
var gasCosts = clientModel.gasCosts;
for (var g in gasCosts)
gas += gasCosts[g];
return gas;
}
function retrieveState(state)
{
for (var k = 0; k < projectModel.stateListModel.count; k++)
@ -113,7 +131,9 @@ function executeTr(trIndex, state, ctrAddresses, callBack)
executeTrNextStep(trIndex, state, ctrAddresses, callBack);
else
{
var rpcParams = { "from": deploymentDialog.currentAccount, "gas": deploymentDialog.gasToUse };
var gasCost = clientModel.encodeAbiString(clientModel.gasCosts[trIndex]);
console.log("gas " + gasCost);
var rpcParams = { "from": deploymentDialog.currentAccount, "gas": "0x" + gasCost };
var params = replaceParamToken(func.parameters, tr.parameters, ctrAddresses);
var encodedParams = clientModel.encodeParams(params, tr.contractId, tr.functionId);
@ -157,6 +177,19 @@ function executeTrNextStep(trIndex, state, ctrAddresses, callBack)
callBack();
}
function gasPrice(callBack)
{
var requests = [{
jsonrpc: "2.0",
method: "eth_gasPrice",
params: [],
id: jsonRpcRequestId
}];
rpcCall(requests, function (httpCall, response){
callBack(JSON.parse(response)[0].result);
});
}
function finalizeDeployment(deploymentId, addresses) {
deploymentStepChanged(qsTr("Packaging application ..."));
var deploymentDir = projectPath + deploymentId + "/";
@ -185,8 +218,8 @@ function finalizeDeployment(deploymentId, addresses) {
}
//write deployment js
var deploymentJs =
"// Autogenerated by Mix\n" +
"contracts = {};\n";
"// Autogenerated by Mix\n" +
"contracts = {};\n";
for (var c in codeModel.contracts) {
var contractAccessor = "contracts[\"" + codeModel.contracts[c].contract.name + "\"]";
deploymentJs += contractAccessor + " = {\n" +
@ -209,16 +242,18 @@ function finalizeDeployment(deploymentId, addresses) {
applicationUrlEth = formatAppUrl(applicationUrlEth);
deploymentStepChanged(qsTr("Registering application on the Ethereum network ..."));
checkEthPath(applicationUrlEth, function () {
checkEthPath(applicationUrlEth, false, function (success) {
if (!success)
return;
deploymentComplete();
deployResourcesDialog.text = qsTr("Register Web Application to finalize deployment.");
deployResourcesDialog.open();
});
}
function checkEthPath(dappUrl, callBack)
function checkEthPath(dappUrl, checkOnly, callBack)
{
if (dappUrl.length === 1)
if (dappUrl.length === 1 && !checkOnly)
registerContentHash(deploymentDialog.eth, callBack); // we directly create a dapp under the root registrar.
else
{
@ -240,19 +275,20 @@ function checkEthPath(dappUrl, callBack)
var errorTxt = qsTr("Path does not exists " + JSON.stringify(dappUrl) + ". Please register using Registration Dapp. Aborting.");
deploymentError(errorTxt);
console.log(errorTxt);
callBack(false);
}
else
{
dappUrl.splice(0, 1);
checkRegistration(dappUrl, addr, callBack);
checkRegistration(dappUrl, addr, callBack, checkOnly);
}
});
}
}
function checkRegistration(dappUrl, addr, callBack)
function checkRegistration(dappUrl, addr, callBack, checkOnly)
{
if (dappUrl.length === 1)
if (dappUrl.length === 1 && !checkOnly)
registerContentHash(addr, callBack); // We do not create the register for the last part, just registering the content hash.
else
{
@ -287,20 +323,28 @@ function checkRegistration(dappUrl, addr, callBack)
errorTxt = qsTr("Error when creating new owned regsitrar. Please use the regsitration Dapp. Aborting");
deploymentError(errorTxt);
console.log(errorTxt);
callBack(false);
}
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);
callBack(false);
}
else if (nextAddr.replace(/0+/g, "") !== "")
{
dappUrl.splice(0, 1);
checkRegistration(dappUrl, nextAddr, callBack);
checkRegistration(dappUrl, nextAddr, callBack, checkOnly);
}
else
{
if (checkOnly)
{
callBack(true);
return;
}
var txt = qsTr("Registering sub domain " + dappUrl[0] + " ...");
console.log(txt);
deploymentStepChanged(txt);
@ -424,6 +468,9 @@ function normalizeAddress(addr)
function formatAppUrl(url)
{
console.log(" 55 " + url);
if (url.toLowerCase().lastIndexOf("/") === url.length - 1)
url = url.substring(0, url.length - 1);
if (url.toLowerCase().indexOf("eth://") === 0)
url = url.substring(6);
if (url.toLowerCase().indexOf(projectModel.projectTitle + ".") === 0)

Loading…
Cancel
Save