Browse Source

Merge pull request #1828 from yann300/bugFix

Mix - basic transaction creation
cl-refactor
Arkadiy Paronyan 10 years ago
parent
commit
c7b53adec0
  1. 74
      mix/ClientModel.cpp
  2. 17
      mix/ClientModel.h
  3. 62
      mix/qml/QAddressView.qml
  4. 4
      mix/qml/StateDialog.qml
  5. 22
      mix/qml/StateListModel.qml
  6. 29
      mix/qml/StructView.qml
  7. 128
      mix/qml/TransactionDialog.qml
  8. 5
      mix/qml/js/TransactionHelper.js

74
mix/ClientModel.cpp

@ -194,7 +194,7 @@ QVariantMap ClientModel::contractAddresses() const
{
QVariantMap res;
for (auto const& c: m_contractAddresses)
res.insert(c.first, QString::fromStdString(toJS(c.second)));
res.insert(c.first.first, QString::fromStdString(toJS(c.second)));
return res;
}
@ -256,7 +256,9 @@ void ClientModel::setupState(QVariantMap _state)
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
QString sender = transaction.value("sender").toString();
bool isStdContract = (transaction.value("stdContract").toBool());
bool isStdContract = transaction.value("stdContract").toBool();
bool isContractCreation = transaction.value("isContractCreation").toBool();
bool isFunctionCall = transaction.value("isFunctionCall").toBool();
if (isStdContract)
{
if (contractId.isEmpty()) //TODO: This is to support old project files, remove later
@ -272,7 +274,7 @@ void ClientModel::setupState(QVariantMap _state)
{
if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later
contractId = m_codeModel->contracts().keys()[0];
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()));
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall);
transactionSettings.parameterValues = transaction.value("parameters").toMap();
if (contractId == functionId || functionId == "Constructor")
@ -308,6 +310,14 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
m_gasCosts.clear();
for (TransactionSettings const& transaction: _sequence)
{
std::pair<QString, int> ctrInstance = resolvePair(transaction.contractId);
QString address = resolveToken(ctrInstance, deployedContracts);
if (!transaction.isFunctionCall)
{
callAddress(Address(address.toStdString()), bytes(), transaction);
onNewTransaction();
continue;
}
ContractCallDataEncoder encoder;
if (!transaction.stdContractUrl.isEmpty())
{
@ -322,7 +332,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
else
{
//encode data
CompiledContract const& compilerRes = m_codeModel->contract(transaction.contractId);
CompiledContract const& compilerRes = m_codeModel->contract(ctrInstance.first);
QFunctionDefinition const* f = nullptr;
bytes contractCode = compilerRes.bytes();
shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract();
@ -348,32 +358,29 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
{
QSolidityType const* type = p->type();
QVariant value = transaction.parameterValues.value(p->name());
if (type->type().type == SolidityType::Type::Address && value.toString().startsWith("<") && value.toString().endsWith(">"))
if (type->type().type == SolidityType::Type::Address)
{
QStringList nb = value.toString().remove("<").remove(">").split(" - ");
value = QVariant(QString::fromStdString("0x" + dev::toHex(deployedContracts.at(nb.back().toInt()).ref())));
std::pair<QString, int> ctrParamInstance = resolvePair(value.toString());
value = QVariant(resolveToken(ctrParamInstance, deployedContracts));
}
encoder.encode(value, type->type());
}
if (transaction.functionId.isEmpty() || transaction.functionId == transaction.contractId)
if (transaction.functionId.isEmpty() || transaction.functionId == ctrInstance.first)
{
bytes param = encoder.encodedData();
contractCode.insert(contractCode.end(), param.begin(), param.end());
Address newAddress = deployContract(contractCode, transaction);
deployedContracts.push_back(newAddress);
auto contractAddressIter = m_contractAddresses.find(transaction.contractId);
if (contractAddressIter == m_contractAddresses.end() || newAddress != contractAddressIter->second)
{
m_contractAddresses[transaction.contractId] = newAddress;
m_contractNames[newAddress] = transaction.contractId;
contractAddressesChanged();
}
std::pair<QString, int> contractToken = retrieveToken(transaction.contractId, deployedContracts);
m_contractAddresses[contractToken] = newAddress;
m_contractNames[newAddress] = contractToken.first;
contractAddressesChanged();
gasCostsChanged();
}
else
{
auto contractAddressIter = m_contractAddresses.find(transaction.contractId);
auto contractAddressIter = m_contractAddresses.find(ctrInstance);
if (contractAddressIter == m_contractAddresses.end())
{
emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId);
@ -381,7 +388,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
emit runStateChanged();
return;
}
callContract(contractAddressIter->second, encoder.encodedData(), transaction);
callAddress(contractAddressIter->second, encoder.encodedData(), transaction);
}
m_gasCosts.append(m_client->lastExecution().gasUsed);
}
@ -405,6 +412,37 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
});
}
std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId)
{
std::pair<QString, int> ret;
ret.first = _contractId;
ret.second = -1;
if (_contractId.startsWith("<") && _contractId.endsWith(">"))
{
QStringList values = ret.first.remove("<").remove(">").split(" - ");
ret.first = values[0];
ret.second = values[1].toUInt();
}
return ret;
}
QString ClientModel::resolveToken(std::pair<QString, int> const& _value, vector<Address> const& _contracts)
{
if (_value.second != -1)
return QString::fromStdString("0x" + dev::toHex(_contracts.at(_value.second).ref()));
else
return _value.first;
}
std::pair<QString, int> ClientModel::retrieveToken(QString const& _value, vector<Address> const& _contracts)
{
std::pair<QString, int> ret;
ret.first = _value;
ret.second = _contracts.size() - 1;
return ret;
}
void ClientModel::showDebugger()
{
ExecutionResult last = m_client->lastExecution();
@ -609,7 +647,7 @@ Address ClientModel::deployContract(bytes const& _code, TransactionSettings cons
return newAddress;
}
void ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
void ClientModel::callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
{
m_client->submitTransaction(_tr.sender, _tr.value, _contract, _data, _tr.gas, _tr.gasPrice, _tr.gasAuto);
}

17
mix/ClientModel.h

@ -53,10 +53,10 @@ struct SolidityType;
struct TransactionSettings
{
TransactionSettings() {}
TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender):
contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender) {}
TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender, bool _isContractCreation, bool _isFunctionCall):
contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender), isContractCreation(_isContractCreation), isFunctionCall(_isFunctionCall) {}
TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl):
contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl) {}
contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCreation(true), isFunctionCall(true) {}
/// Contract name
QString contractId;
@ -76,6 +76,10 @@ struct TransactionSettings
QString stdContractUrl;
/// Sender
Secret sender;
/// Tr deploys a contract
bool isContractCreation;
/// Tr call a ctr function
bool isFunctionCall;
};
@ -222,11 +226,14 @@ private:
QVariantList gasCosts() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, std::unordered_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);
void callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
void onNewTransaction();
void onStateReset();
void showDebuggerForTransaction(ExecutionResult const& _t);
QVariant formatValue(SolidityType const& _type, dev::u256 const& _value);
QString resolveToken(std::pair<QString, int> const& _value, std::vector<Address> const& _contracts);
std::pair<QString, int> retrieveToken(QString const& _value, std::vector<Address> const& _contracts);
std::pair<QString, int> resolvePair(QString const& _contractId);
QVariant formatStorageValue(SolidityType const& _type, std::unordered_map<dev::u256, dev::u256> const& _storage, unsigned _offset, dev::u256 const& _slot);
std::atomic<bool> m_running;
@ -237,7 +244,7 @@ private:
std::unique_ptr<Web3Server> m_web3Server;
std::shared_ptr<eth::FixedAccountHolder> m_ethAccounts;
QList<u256> m_gasCosts;
std::map<QString, Address> m_contractAddresses;
std::map<std::pair<QString, int>, Address> m_contractAddresses;
std::map<Address, QString> m_contractNames;
std::map<QString, Address> m_stdContractAddresses;
std::map<Address, QString> m_stdContractNames;

62
mix/qml/QAddressView.qml

@ -8,6 +8,10 @@ Item
property alias accountRef: ctrModel
property string subType
property bool readOnly
property alias currentIndex: trCombobox.currentIndex
property alias currentText: textinput.text
property variant accounts
signal indexChanged()
id: editRoot
height: 20
width: 320
@ -17,6 +21,51 @@ Item
id: boldFont
}
function currentValue() {
return currentText;
}
function currentType()
{
return accountRef.get(trCombobox.currentIndex).type;
}
function current()
{
return accountRef.get(trCombobox.currentIndex);
}
function load()
{
accountRef.clear();
accountRef.append({"itemid": " - "});
if (subType === "contract" || subType === "address")
{
var trCr = 0;
for (var k = 0; k < transactionsModel.count; k++)
{
if (k >= transactionIndex)
break;
var tr = transactionsModel.get(k);
if (tr.functionId === tr.contractId /*&& (dec[1] === tr.contractId || item.subType === "address")*/)
{
accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" });
trCr++;
}
}
}
if (subType === "address")
{
for (k = 0; k < accounts.length; k++)
{
if (accounts[k].address === undefined)
accounts[k].address = clientModel.address(accounts[k].secret);
accountRef.append({ "itemid": accounts[k].name, "value": "0x" + accounts[k].address, "type": "address" });
}
}
}
function init()
{
trCombobox.visible = !readOnly
@ -35,6 +84,18 @@ Item
}
}
function select(address)
{
for (var k = 0; k < accountRef.count; k++)
{
if (accountRef.get(k).value === address)
{
trCombobox.currentIndex = k;
break;
}
}
}
Rectangle {
anchors.fill: parent
radius: 4
@ -96,6 +157,7 @@ Item
{
textinput.text = "";
}
indexChanged();
}
}
}

4
mix/qml/StateDialog.qml

@ -436,7 +436,7 @@ Dialog {
model: transactionsModel
headerVisible: false
TableViewColumn {
role: "name"
role: "label"
title: qsTr("Name")
width: 150
delegate: Item {
@ -476,7 +476,7 @@ Dialog {
text: {
if (styleData.row >= 0)
return transactionsModel.get(
styleData.row).functionId
styleData.row).label
else
return ""
}

22
mix/qml/StateListModel.qml

@ -46,6 +46,7 @@ Item {
t.sender = defaultAccount; //support for old project
var r = {
type: t.type,
contractId: t.contractId,
functionId: t.functionId,
url: t.url,
@ -55,8 +56,21 @@ Item {
gasAuto: t.gasAuto,
stdContract: t.stdContract ? true : false,
parameters: {},
sender: t.sender
sender: t.sender,
isContractCreation: t.isContractCreation,
label: t.label,
isFunctionCall: t.isFunctionCall
};
if (r.isFunctionCall === undefined)
r.isFunctionCall = true;
if (!r.label)
r.label = r.contractId + " - " + r.functionId;
if (r.isContractCreation === undefined)
r.isContractCreation = r.functionId === r.contractId;
for (var key in t.parameters)
r.parameters[key] = t.parameters[key];
@ -100,6 +114,7 @@ Item {
function toPlainTransactionItem(t) {
var r = {
type: t.type,
contractId: t.contractId,
functionId: t.functionId,
url: t.url,
@ -109,7 +124,10 @@ Item {
gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit },
stdContract: t.stdContract,
sender: t.sender,
parameters: {}
parameters: {},
isContractCreation: t.isContractCreation,
label: t.label,
isFunctionCall: t.isFunctionCall
};
for (var key in t.parameters)
r.parameters[key] = t.parameters[key];

29
mix/qml/StructView.qml

@ -75,38 +75,13 @@ Column
item.readOnly = context === "variable";
if (ptype.category === QSolidityType.Address)
{
item.accounts = accounts
item.value = getValue();
if (context === "parameter")
{
var dec = modelData.type.name.split(" ");
item.subType = dec[0];
item.accountRef.append({"itemid": " - "});
if (item.subType === "contract" || item.subType === "address")
{
var trCr = 0;
for (var k = 0; k < transactionsModel.count; k++)
{
if (k >= transactionIndex)
break;
var tr = transactionsModel.get(k);
if (tr.functionId === tr.contractId && (dec[1] === tr.contractId || item.subType === "address"))
{
item.accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" });
trCr++;
}
}
}
if (item.subType === "address")
{
for (k = 0; k < accounts.length; k++)
{
if (accounts[k].address === undefined)
accounts[k].address = clientModel.address(accounts[k].secret);
item.accountRef.append({ "itemid": accounts[k].name, "value": "0x" + accounts[k].address, "type": "address" });
}
}
item.load();
}
item.init();
}

128
mix/qml/TransactionDialog.qml

@ -66,29 +66,48 @@ Dialog {
contractIndex = 0; //@todo suggest unused contract
contractComboBox.currentIndex = contractIndex;
loadFunctions(contractComboBox.currentValue());
recipients.accounts = senderComboBox.model;
recipients.subType = "address";
recipients.load();
recipients.init();
recipients.select(contractId);
if (item.isContractCreation)
loadFunctions(contractComboBox.currentValue());
else
loadFunctions(contractFromToken(recipients.currentValue()))
selectFunction(functionId);
trType.checked = item.isContractCreation
trType.init();
paramsModel = [];
if (functionId !== contractComboBox.currentValue())
if (item.isContractCreation)
loadCtorParameters();
else
loadParameters();
else {
var contract = codeModel.contracts[contractId];
if (contract) {
var params = contract.contract.constructor.parameters;
for (var p = 0; p < params.length; p++)
loadParameter(params[p]);
}
}
initTypeLoader();
visible = true;
valueField.focus = true;
}
function loadCtorParameters(contractId)
{
paramsModel = [];
console.log(contractId);
var contract = codeModel.contracts[contractId];
if (contract) {
var params = contract.contract.constructor.parameters;
for (var p = 0; p < params.length; p++)
loadParameter(params[p]);
}
initTypeLoader();
}
function loadFunctions(contractId)
{
functionsModel.clear();
functionsModel.append({ text: " - " });
var contract = codeModel.contracts[contractId];
if (contract) {
var functions = codeModel.contracts[contractId].contract.functions;
@ -96,9 +115,6 @@ Dialog {
functionsModel.append({ text: functions[f].name });
}
}
//append constructor
functionsModel.append({ text: contractId });
}
function selectContract(contractName)
@ -136,7 +152,7 @@ Dialog {
function loadParameters() {
paramsModel = []
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
var contract = codeModel.contracts[contractComboBox.currentValue()];
var contract = codeModel.contracts[contractFromToken(recipients.currentValue())];
if (contract) {
var func = contract.contract.functions[functionComboBox.currentIndex];
if (func) {
@ -193,10 +209,37 @@ Dialog {
item.functionId = transactionDialog.functionId;
}
item.isContractCreation = trType.checked;
item.isFunctionCall = item.functionId !== " - ";
if (!item.isContractCreation)
{
item.contractId = recipients.currentText;
item.label = item.contractId + " " + item.functionId;
if (recipients.current().type === "address")
{
item.functionId = "";
item.isFunctionCall = false;
}
}
else
{
item.functionId = item.contractId;
item.label = qsTr("Deploy") + " " + item.contractId;
}
item.sender = senderComboBox.model[senderComboBox.currentIndex].secret;
item.parameters = paramValues;
return item;
}
function contractFromToken(token)
{
if (token.indexOf('<') === 0)
return token.replace("<", "").replace(">", "").split(" - ")[0];
return token;
}
contentItem: Rectangle {
color: transactionDialogStyle.generic.backgroundColor
ColumnLayout {
@ -238,6 +281,59 @@ Dialog {
}
}
RowLayout
{
id: rowIsContract
Layout.fillWidth: true
height: 150
CheckBox {
id: trType
onCheckedChanged:
{
init();
}
function init()
{
rowFunction.visible = !checked;
rowContract.visible = checked;
rowRecipient.visible = !checked;
paramLabel.visible = checked;
paramScroll.visible = checked;
functionComboBox.enabled = !checked;
if (checked)
loadCtorParameters(contractComboBox.currentValue());
}
text: qsTr("is contract creation")
checked: true
}
}
RowLayout
{
id: rowRecipient
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Recipient")
}
QAddressView
{
id: recipients
onIndexChanged:
{
rowFunction.visible = current().type === "contract";
paramLabel.visible = current().type === "contract";
paramScroll.visible = current().type === "contract";
if (!rowIsContract.checked)
loadFunctions(contractFromToken(recipients.currentValue()))
}
}
}
RowLayout
{
id: rowContract
@ -260,7 +356,7 @@ Dialog {
id: contractsModel
}
onCurrentIndexChanged: {
loadFunctions(currentValue());
loadCtorParameters(currentValue());
}
}
}

5
mix/qml/js/TransactionHelper.js

@ -9,7 +9,10 @@ function defaultTransaction()
gasAuto: true,
gasPrice: createEther("100000", QEther.Wei),
parameters: {},
stdContract: false
stdContract: false,
isContractCreation: true,
label: "",
isFunctionCall: true
};
}

Loading…
Cancel
Save