446 lines
11 KiB

import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import org.ethereum.qml.QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper
import "js/TransactionHelper.js" as TransactionHelper
Item {
property alias model: stateListModel
property var stateList: []
property alias stateDialog: stateDialog
property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project
function fromPlainStateItem(s) {
if (!s.accounts)
s.accounts = [stateListModel.newAccount("1000000", QEther.Ether, defaultAccount)]; //support for old project
if (!s.contracts)
s.contracts = [];
var ret = {};
ret.title = s.title;
ret.transactions = s.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem); //support old projects by filtering std contracts
if (s.blocks)
ret.blocks = s.blocks.map(fromPlainBlockItem);
ret.accounts = s.accounts.map(fromPlainAccountItem);
ret.contracts = s.contracts.map(fromPlainAccountItem);
ret.miner = s.miner;
// support old projects
if (!ret.blocks)
{
ret.blocks = [{
hash: "",
number: -1,
transactions: [],
status: "pending"
}]
for (var j in ret.transactions)
ret.blocks[0].transactions.push(fromPlainTransactionItem(toPlainTransactionItem(ret.transactions[j])))
}
return ret;
}
function fromPlainAccountItem(t)
{
return {
name: t.name,
address: t.address,
secret: t.secret,
balance: QEtherHelper.createEther(t.balance.value, t.balance.unit),
storage: t.storage,
code: t.code,
};
}
function fromPlainTransactionItem(t) {
if (!t.sender)
t.sender = defaultAccount; //support for old project
var r = {
type: t.type,
contractId: t.contractId,
functionId: t.functionId,
url: t.url,
value: QEtherHelper.createEther(t.value.value, t.value.unit),
gas: QEtherHelper.createBigInt(t.gas.value),
gasPrice: QEtherHelper.createEther(t.gasPrice.value, t.gasPrice.unit),
gasAuto: t.gasAuto,
parameters: {},
sender: t.sender,
isContractCreation: t.isContractCreation,
label: t.label,
isFunctionCall: t.isFunctionCall,
saveStatus: t.saveStatus
};
if (r.saveStatus === undefined)
r.saveStatus = true
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];
return r;
}
function fromPlainBlockItem(b)
{
var r = {
hash: b.hash,
number: b.number,
transactions: b.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem), //support old projects by filtering std contracts
status: b.status
}
return r;
}
function toPlainStateItem(s) {
return {
title: s.title,
blocks: s.blocks.map(toPlainBlockItem),
transactions: s.transactions.map(toPlainTransactionItem),
accounts: s.accounts.map(toPlainAccountItem),
contracts: s.contracts.map(toPlainAccountItem),
miner: s.miner
};
}
function getParamType(param, params)
{
for (var k in params)
{
if (params[k].declaration.name === param)
return params[k].declaration.type;
}
return '';
}
function toPlainBlockItem(b)
{
var r = {
hash: b.hash,
number: b.number,
transactions: b.transactions.map(toPlainTransactionItem),
status: b.status
}
return r;
}
function toPlainAccountItem(t)
{
return {
name: t.name,
secret: t.secret,
balance: {
value: t.balance.value,
unit: t.balance.unit
},
address: t.address,
storage: t.storage,
code: t.code,
};
}
function toPlainTransactionItem(t) {
var r = {
type: t.type,
contractId: t.contractId,
functionId: t.functionId,
url: t.url,
value: { value: t.value.value, unit: t.value.unit },
gas: { value: t.gas.value() },
gasAuto: t.gasAuto,
gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit },
sender: t.sender,
parameters: {},
isContractCreation: t.isContractCreation,
label: t.label,
isFunctionCall: t.isFunctionCall,
saveStatus: t.saveStatus
};
for (var key in t.parameters)
r.parameters[key] = t.parameters[key];
return r;
}
Connections {
target: projectModel
onProjectClosed: {
stateListModel.clear();
stateList = [];
codeModel.reset();
}
onProjectLoading: stateListModel.loadStatesFromProject(projectData);
onProjectFileSaving: {
projectData.states = []
for(var i = 0; i < stateListModel.count; i++)
{
projectData.states.push(toPlainStateItem(stateList[i]));
stateListModel.set(i, stateList[i]);
}
projectData.defaultStateIndex = stateListModel.defaultStateIndex;
stateListModel.data = projectData
}
onNewProject: {
var state = toPlainStateItem(stateListModel.createDefaultState());
state.title = qsTr("Default");
projectData.states = [ state ];
projectData.defaultStateIndex = 0;
stateListModel.loadStatesFromProject(projectData);
}
}
Connections {
target: codeModel
onNewContractCompiled: {
stateListModel.addNewContracts();
}
onContractRenamed: {
stateListModel.renameContracts(_oldName, _newName);
}
}
StateDialog {
id: stateDialog
onAccepted: {
var item = stateDialog.getItem();
saveState(item);
}
function saveState(item)
{
stateList[stateDialog.stateIndex].accounts = item.accounts
stateList[stateDialog.stateIndex].contracts = item.contracts
stateListModel.get(stateDialog.stateIndex).accounts = item.accounts
stateListModel.get(stateDialog.stateIndex).contracts = item.contracts
stateListModel.accountsValidated(item.accounts)
stateListModel.contractsValidated(item.contracts)
stateListModel.get(stateDialog.stateIndex).miner = item.miner
stateList[stateDialog.stateIndex].miner = item.miner
if (item.defaultState)
{
stateListModel.defaultStateIndex = stateDialog.stateIndex
stateListModel.defaultStateChanged()
}
}
}
ListModel {
id: stateListModel
property int defaultStateIndex: 0
property variant data
signal accountsValidated(var _accounts)
signal contractsValidated(var _contracts)
signal defaultStateChanged;
signal stateListModelReady;
signal stateRun(int index)
signal stateDeleted(int index)
function defaultTransactionItem()
{
return TransactionHelper.defaultTransaction();
}
function newAccount(_balance, _unit, _secret)
{
if (!_secret)
_secret = clientModel.newSecret();
var address = clientModel.address(_secret);
var name = qsTr("Account") + "-" + address.substring(0, 4);
var amount = QEtherHelper.createEther(_balance, _unit)
return { name: name, secret: _secret, balance: amount, address: address };
}
function duplicateState(index)
{
var state = stateList[index]
var item = fromPlainStateItem(toPlainStateItem(state))
item.title = qsTr("Copy of") + " " + state.title
appendState(item)
save()
}
function createEmptyBlock()
{
return {
hash: "",
number: -1,
transactions: [],
status: "pending"
}
}
function createDefaultState() {
var item = {
title: "",
transactions: [],
accounts: [],
contracts: [],
blocks: [{ status: "pending", number: -1, hash: "", transactions: []}]
};
var account = newAccount("1000000", QEther.Ether, defaultAccount)
item.accounts.push(account);
item.miner = account;
//add constructors, //TODO: order by dependencies
for(var c in codeModel.contracts) {
var ctorTr = defaultTransactionItem();
ctorTr.functionId = c;
ctorTr.contractId = c;
ctorTr.label = ctorTr.contractId + "." + ctorTr.contractId + "()"
ctorTr.sender = item.accounts[0].secret;
item.transactions.push(ctorTr);
item.blocks[0].transactions.push(ctorTr)
}
return item;
}
function renameContracts(oldName, newName) {
var changed = false;
for(var c in codeModel.contracts) {
for (var s = 0; s < stateListModel.count; s++) {
var state = stateList[s];
for (var t = 0; t < state.transactions.length; t++) {
var transaction = state.transactions[t];
if (transaction.contractId === oldName) {
transaction.contractId = newName;
if (transaction.functionId === oldName)
transaction.functionId = newName;
changed = true;
state.transactions[t] = transaction;
}
}
stateListModel.set(s, state);
stateList[s] = state;
}
}
if (changed)
save();
}
function addNewContracts() {
//add new contracts to empty states
var changed = false;
for (var c in codeModel.contracts) {
for (var s = 0; s < stateListModel.count; s++) {
var state = stateList[s];
if (state.transactions.length === 0) {
//append this contract
var ctorTr = defaultTransactionItem();
ctorTr.functionId = c;
ctorTr.contractId = c;
ctorTr.label = ctorTr.contractId + "." + ctorTr.contractId + "()";
ctorTr.sender = state.accounts[0].secret;
state.transactions.push(ctorTr);
changed = true;
stateListModel.set(s, state);
stateList[s] = state;
}
}
}
if (changed)
save();
}
function addState() {
var item = createDefaultState();
stateDialog.open(stateListModel.count, item, false);
}
function appendState(item)
{
stateListModel.append(item);
stateList.push(item);
}
function editState(index) {
stateDialog.open(index, stateList[index], defaultStateIndex === index);
}
function getState(index) {
return stateList[index];
}
function debugDefaultState() {
if (defaultStateIndex >= 0 && defaultStateIndex < stateList.length)
runState(defaultStateIndex);
}
function runState(index) {
var item = stateList[index];
clientModel.setupScenario(item);
stateRun(index);
}
function deleteState(index) {
stateListModel.remove(index);
stateList.splice(index, 1);
if (index === defaultStateIndex)
{
defaultStateIndex = 0;
defaultStateChanged();
}
else if (defaultStateIndex > index)
defaultStateIndex--;
save();
stateDeleted(index);
}
function save() {
projectModel.saveProject();
}
function defaultStateName()
{
if (stateList.length > 0)
return stateList[defaultStateIndex].title;
else
return ""
}
function reloadStateFromProject(index)
{
if (data)
{
var item = fromPlainStateItem(data.states[index])
stateListModel.set(index, item)
stateList[index] = item
return item
}
}
function loadStatesFromProject(projectData)
{
data = projectData
if (!projectData.states)
projectData.states = [];
if (projectData.defaultStateIndex !== undefined)
defaultStateIndex = projectData.defaultStateIndex;
else
defaultStateIndex = 0;
var items = projectData.states;
stateListModel.clear();
stateList = [];
for(var i = 0; i < items.length; i++) {
var item = fromPlainStateItem(items[i]);
stateListModel.append(item);
stateList.push(item);
}
stateListModelReady();
}
}
}