diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index ab15f55ca..7bc25fd3f 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -81,7 +81,7 @@ ClientModel::ClientModel(): qRegisterMetaType("QInstruction"); qRegisterMetaType("QCode"); qRegisterMetaType("QCallData"); - qRegisterMetaType("RecordLogEntry*"); + qRegisterMetaType("RecordLogEntry*"); } ClientModel::~ClientModel() @@ -236,6 +236,7 @@ void ClientModel::setupScenario(QVariantMap _scenario) QVariantList blocks = _scenario.value("blocks").toList(); QVariantList stateAccounts = _scenario.value("accounts").toList(); + QVariantList stateContracts = _scenario.value("contracts").toList(); m_accounts.clear(); m_accountsSecret.clear(); @@ -258,6 +259,19 @@ void ClientModel::setupScenario(QVariantMap _scenario) } m_ethAccounts->setAccounts(m_accountsSecret); + for (auto const& c: stateContracts) + { + QVariantMap contract = c.toMap(); + Address address = Address(fromHex(contract.value("address").toString().toStdString())); + Account account(qvariant_cast(contract.value("balance"))->toU256Wei(), Account::ContractConception); + bytes code = fromHex(contract.value("code").toString().toStdString()); + account.setCode(std::move(code)); + QVariantMap storageMap = contract.value("storage").toMap(); + for(auto s = storageMap.cbegin(); s != storageMap.cend(); ++s) + account.setStorage(fromBigEndian(fromHex(s.key().toStdString())), fromBigEndian(fromHex(s.value().toString().toStdString()))); + m_accounts[address] = account; + } + bool trToExecute = false; for (auto const& b: blocks) { @@ -891,7 +905,7 @@ void ClientModel::onNewTransaction() QVariantMap accountBalances; for (auto const& ctr : m_contractAddresses) { - u256 wei = m_client->balanceAt(ctr.second, PendingBlock); + u256 wei = m_client->balanceAt(ctr.second, PendingBlock); accountBalances.insert("0x" + QString::fromStdString(ctr.second.hex()), QEther(wei, QEther::Wei).format()); } for (auto const& account : m_accounts) diff --git a/mix/qml/Application.qml b/mix/qml/Application.qml index 5ae12b816..188d87be9 100644 --- a/mix/qml/Application.qml +++ b/mix/qml/Application.qml @@ -108,12 +108,14 @@ ApplicationWindow { title: qsTr("Deploy") MenuItem { action: mineAction } MenuSeparator {} - MenuItem { action: editStatesAction } - MenuSeparator {} MenuItem { action: deployViaRpcAction } MenuSeparator {} MenuItem { action: toggleRunOnLoadAction } } + Menu { + title: qsTr("Scenario") + MenuItem { action: editStatesAction } + } Menu { title: qsTr("Debug") MenuItem { action: debugRunAction } @@ -184,7 +186,7 @@ ApplicationWindow { Action { id: editStatesAction - text: qsTr("Edit States") + text: qsTr("Edit Scenarii") shortcut: "Ctrl+Alt+E" onTriggered: stateList.open(); } diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index 383cb85e8..532e348d7 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -22,6 +22,7 @@ ColumnLayout property int blockIndex property variant scenario property string labelColor: "#414141" + property int scenarioIndex signal txSelected(var txIndex) function calculateHeight() @@ -105,13 +106,14 @@ ColumnLayout anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 14 - visible: false + visible: number === -2 MouseArea { anchors.fill: parent onClicked: { // load edit block panel + projectModel.stateListModel.editState(scenarioIndex) } } } diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 41c65553a..25793a35f 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -16,6 +16,7 @@ ColumnLayout { property alias trDialog: transactionDialog property alias blockChainRepeater: blockChainRepeater property variant model + property int scenarioIndex property var states: ({}) spacing: 0 property int previousWidth @@ -26,6 +27,25 @@ ColumnLayout { signal rebuilding signal accountAdded(string address, string amount) + Connections + { + target: projectModel.stateListModel + onAccountsValidated: + { + if (rebuild.accountsSha3 !== codeModel.sha3(JSON.stringify(_accounts))) + rebuild.needRebuild("AccountsChanged") + else + rebuild.notNeedRebuild("AccountsChanged") + } + onContractsValidated: + { + if (rebuild.contractsSha3 !== codeModel.sha3(JSON.stringify(_contracts))) + rebuild.needRebuild("ContractsChanged") + else + rebuild.notNeedRebuild("ContractsChanged") + } + } + Connections { target: codeModel @@ -94,13 +114,15 @@ ColumnLayout { return states[record] } - function load(scenario) + function load(scenario, index) { if (!scenario) return; if (model) rebuild.startBlinking() model = scenario + scenarioIndex = index + genesis.scenarioIndex = index states = [] blockModel.clear() for (var b in model.blocks) @@ -138,6 +160,20 @@ ColumnLayout { width: parent.width spacing: 20 + Block + { + id: genesis + scenario: blockChainPanel.model + scenarioIndex: scenarioIndex + Layout.preferredWidth: blockChainScrollView.width + Layout.preferredHeight: 60 + blockIndex: -1 + transactions: [] + status: "" + number: -2 + trHeight: 60 + } + Repeater // List of blocks { id: blockChainRepeater @@ -264,6 +300,8 @@ ColumnLayout { roundRight: true property variant contractsHex: ({}) property variant txSha3: ({}) + property variant accountsSha3 + property variant contractsSha3 property variant txChanged: [] property var blinkReasons: [] @@ -352,10 +390,22 @@ ColumnLayout { ensureNotFuturetime.start() takeCodeSnapshot() takeTxSnaphot() + takeAccountsSnapshot() + takeContractsSnapShot() blinkReasons = [] clientModel.setupScenario(model); } + function takeContractsSnapShot() + { + contractsSha3 = codeModel.sha3(JSON.stringify(model.contracts)) + } + + function takeAccountsSnapshot() + { + accountsSha3 = codeModel.sha3(JSON.stringify(model.accounts)) + } + function takeCodeSnapshot() { contractsHex = {} diff --git a/mix/qml/DeployContractStep.qml b/mix/qml/DeployContractStep.qml index 39881879c..9bdcd765c 100644 --- a/mix/qml/DeployContractStep.qml +++ b/mix/qml/DeployContractStep.qml @@ -490,7 +490,8 @@ Rectangle { id: clearDeployAction onTriggered: { worker.forceStopPooling() - fileIo.deleteDir(projectModel.deploymentDir) + if (projectModel.deploymentDir && projectModel.deploymentDir !== "") + fileIo.deleteDir(projectModel.deploymentDir) projectModel.cleanDeploymentStatus() deploymentDialog.steps.reset() } diff --git a/mix/qml/ScenarioExecution.qml b/mix/qml/ScenarioExecution.qml index 00c09f4cf..9b4bcd39f 100644 --- a/mix/qml/ScenarioExecution.qml +++ b/mix/qml/ScenarioExecution.qml @@ -70,7 +70,7 @@ Rectangle { onLoaded: { watchers.clear() - blockChain.load(scenario) + blockChain.load(scenario, loader.selectedScenarioIndex) } } @@ -91,7 +91,8 @@ Rectangle { updateWatchers(blockIndex, txIndex) } - function updateWatchers(blockIndex, txIndex){ + function updateWatchers(blockIndex, txIndex) + { var tx = blockChain.model.blocks[blockIndex].transactions[txIndex] var state = blockChain.getState(tx.recordIndex) watchers.updateWidthTx(tx, state, blockIndex, txIndex) diff --git a/mix/qml/ScenarioLoader.qml b/mix/qml/ScenarioLoader.qml index 5b8cb2d70..24b5962c1 100644 --- a/mix/qml/ScenarioLoader.qml +++ b/mix/qml/ScenarioLoader.qml @@ -20,6 +20,7 @@ ColumnLayout signal loaded(variant scenario) signal renamed(variant scenario) signal deleted() + property alias selectedScenarioIndex: scenarioList.currentIndex spacing: 0 function init() { @@ -80,15 +81,14 @@ ColumnLayout } } - Label - { - anchors.left: editImg.right - text: "X" - height: parent.height - color: "#cccccc" + Image { + source: "qrc:/qml/img/delete_sign.png" + height: parent.height - 16 + fillMode: Image.PreserveAspectFit id: deleteImg + anchors.left: editImg.right anchors.top: parent.top - anchors.topMargin: 7 + anchors.topMargin: 8 visible: projectModel.stateListModel.count > 1 MouseArea { @@ -98,13 +98,37 @@ ColumnLayout if (projectModel.stateListModel.count > 1) { projectModel.stateListModel.deleteState(scenarioList.currentIndex) - scenarioList.currentIndex = 0 - deleted() + scenarioList.init() + } + } + } + } + + Label + { + + MouseArea + { + anchors.fill: parent + onClicked: + { + if (projectModel.stateListModel.count > 1) + { + projectModel.stateListModel.deleteState(scenarioList.currentIndex) + scenarioList.init() } } } } + Connections + { + target: projectModel.stateListModel + onStateDeleted: { + scenarioList.init() + } + } + ComboBox { id: scenarioList @@ -122,6 +146,12 @@ ColumnLayout restoreScenario.restore() } + function init() + { + scenarioList.currentIndex = 0 + deleted() + } + function load() { var state = projectModel.stateListModel.getState(currentIndex) diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index 404a524f4..56be75a09 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -18,12 +18,8 @@ Dialog { title: qsTr("Edit State") visible: false - property alias stateTitle: titleField.text property alias isDefault: defaultCheckBox.checked - property alias model: transactionsModel - property alias transactionDialog: transactionDialog property alias minerComboBox: comboMiner - property alias newAccAction: newAccountAction property int stateIndex property var stateTransactions: [] property var stateAccounts: [] @@ -36,16 +32,6 @@ Dialog { function open(index, item, setDefault) { stateIndex = index - stateTitle = item.title - transactionsModel.clear() - - stateTransactions = [] - var transactions = item.transactions - for (var t = 0; t < transactions.length; t++) { - transactionsModel.append(item.transactions[t]) - stateTransactions.push(item.transactions[t]) - } - accountsModel.clear() stateAccounts = [] var miner = 0 @@ -66,8 +52,8 @@ Dialog { visible = true isDefault = setDefault - titleField.focus = true - defaultCheckBox.enabled = !isDefault + console.log(isDefault) + defaultCheckBox.checked = isDefault comboMiner.model = stateAccounts comboMiner.currentIndex = miner forceActiveFocus() @@ -84,8 +70,6 @@ Dialog { function getItem() { var item = { - title: stateDialog.stateTitle, - transactions: stateTransactions, accounts: stateAccounts, contracts: stateContracts } @@ -95,6 +79,7 @@ Dialog { break } } + item.defaultState = defaultCheckBox.checked return item } @@ -111,21 +96,6 @@ Dialog { ColumnLayout { id: dialogContent anchors.top: parent.top - RowLayout { - Layout.fillWidth: true - DefaultLabel { - Layout.preferredWidth: 85 - text: qsTr("Title") - } - DefaultTextField { - id: titleField - Layout.fillWidth: true - } - } - - CommonSeparator { - Layout.fillWidth: true - } RowLayout { Layout.fillWidth: true @@ -258,30 +228,6 @@ Dialog { Layout.preferredWidth: 85 text: qsTr("Accounts") } - - Button { - id: newAccountButton - anchors.top: accountsLabel.bottom - anchors.topMargin: 10 - iconSource: "qrc:/qml/img/plus.png" - action: newAccountAction - } - - Action { - id: newAccountAction - tooltip: qsTr("Add new Account") - onTriggered: { - add() - } - - function add() { - var account = stateListModel.newAccount( - "1000000", QEther.Ether) - stateAccounts.push(account) - accountsModel.append(account) - return account - } - } } MessageDialog { @@ -313,21 +259,8 @@ Dialog { id: deleteAccountAction tooltip: qsTr("Delete Account") onTriggered: { - if (transactionsModel.isUsed( - stateAccounts[styleData.row].secret)) - alertAlreadyUsed.open() - else { - if (stateAccounts[styleData.row].name - === comboMiner.currentText) - comboMiner.currentIndex = 0 - stateAccounts.splice( - styleData.row, - 1) - accountsModel.remove( - styleData.row) - comboMiner.model = stateAccounts //TODO: filter accounts wo private keys - comboMiner.update() - } + stateAccounts.splice(styleData.row, 1) + accountsView.model.remove(styleData.row) } } @@ -405,115 +338,17 @@ Dialog { Layout.fillWidth: true } - RowLayout { - Layout.fillWidth: true - - Rectangle { - Layout.preferredWidth: 85 - DefaultLabel { - id: transactionsLabel - Layout.preferredWidth: 85 - text: qsTr("Transactions") - } - - Button { - anchors.top: transactionsLabel.bottom - anchors.topMargin: 10 - iconSource: "qrc:/qml/img/plus.png" - action: newTrAction - } - - Action { - id: newTrAction - tooltip: qsTr("Create a new transaction") - onTriggered: transactionsModel.addTransaction() - } - } - - TableView { - id: transactionsView - Layout.fillWidth: true - model: transactionsModel - headerVisible: false - TableViewColumn { - role: "label" - title: qsTr("Name") - width: 150 - delegate: Item { - RowLayout { - height: 30 - width: parent.width - Button { - iconSource: "qrc:/qml/img/delete_sign.png" - action: deleteTransactionAction - } - - Action { - id: deleteTransactionAction - tooltip: qsTr("Delete") - onTriggered: transactionsModel.deleteTransaction( - styleData.row) - } - - Button { - iconSource: "qrc:/qml/img/edit.png" - action: editAction - visible: styleData.row - >= 0 ? !transactionsModel.get( - styleData.row).stdContract : false - width: 10 - height: 10 - Action { - id: editAction - tooltip: qsTr("Edit") - onTriggered: transactionsModel.editTransaction( - styleData.row) - } - } - - DefaultLabel { - Layout.preferredWidth: 150 - text: { - if (styleData.row >= 0) - return transactionsModel.get( - styleData.row).label - else - return "" - } - } - } - } - } - rowDelegate: Rectangle { - color: styleData.alternate ? "transparent" : "#f0f0f0" - height: 30 - } - } - } } RowLayout { anchors.bottom: parent.bottom anchors.right: parent.right - Button { - text: qsTr("Delete") - enabled: !modalStateDialog.isDefault - onClicked: { - projectModel.stateListModel.deleteState(stateIndex) - close() - } - } Button { text: qsTr("OK") onClicked: { - if (titleField.text === "") - alertDialog.open() - else - { - close() - accepted() - } + close() + accepted() } } Button { @@ -522,21 +357,6 @@ Dialog { } } - MessageDialog - { - id: alertDialog - text: qsTr("Please provide a name.") - } - - ListModel { - id: accountsModel - - function removeAccount(_i) { - accountsModel.remove(_i) - stateAccounts.splice(_i, 1) - } - } - ListModel { id: contractsModel @@ -547,50 +367,11 @@ Dialog { } ListModel { - id: transactionsModel - - function editTransaction(index) { - transactionDialog.stateAccounts = stateAccounts - transactionDialog.open(index, - transactionsModel.get(index)) - } - - function addTransaction() { - // Set next id here to work around Qt bug - // https://bugreports.qt-project.org/browse/QTBUG-41327 - // Second call to signal handler would just edit the item that was just created, no harm done - var item = TransactionHelper.defaultTransaction() - transactionDialog.stateAccounts = stateAccounts - transactionDialog.open(transactionsModel.count, item) - } - - function deleteTransaction(index) { - stateTransactions.splice(index, 1) - transactionsModel.remove(index) - } - - function isUsed(secret) { - for (var i in stateTransactions) { - if (stateTransactions[i].sender === secret) - return true - } - return false - } - } + id: accountsModel - TransactionDialog { - id: transactionDialog - onAccepted: { - var item = transactionDialog.getItem() - if (transactionDialog.transactionIndex < transactionsModel.count) { - transactionsModel.set( - transactionDialog.transactionIndex, - item) - stateTransactions[transactionDialog.transactionIndex] = item - } else { - transactionsModel.append(item) - stateTransactions.push(item) - } + function removeAccount(_i) { + accountsModel.remove(_i) + stateAccounts.splice(_i, 1) } } } diff --git a/mix/qml/StateList.qml b/mix/qml/StateList.qml index 39567feac..3558350b7 100644 --- a/mix/qml/StateList.qml +++ b/mix/qml/StateList.qml @@ -27,7 +27,7 @@ Dialog { frameVisible: false TableViewColumn { role: "title" - title: qsTr("State") + title: qsTr("Scenario") width: list.width } } @@ -71,25 +71,12 @@ Dialog { Layout.fillHeight: true onClicked: list.model.deleteState(styleData.row); } - ToolButton { - text: qsTr("Run"); - Layout.fillHeight: true - onClicked: list.model.runState(styleData.row); - } } } } Row { - Action { - id: addStateAction - text: qsTr("Add State") - shortcut: "Ctrl+T" - enabled: codeModel.hasContract && !clientModel.running; - onTriggered: list.model.addState(); - } - Action { id: closeAction text: qsTr("Close") diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index efa52f853..b55781396 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -222,20 +222,19 @@ Item { function saveState(item) { - if (stateDialog.stateIndex < stateListModel.count) { - if (stateDialog.isDefault) - stateListModel.defaultStateIndex = stateIndex; - stateList[stateDialog.stateIndex] = item; - stateListModel.set(stateDialog.stateIndex, item); - } else { - if (stateDialog.isDefault) - stateListModel.defaultStateIndex = 0; - stateList.push(item); - stateListModel.append(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() } - if (stateDialog.isDefault) - stateListModel.defaultStateChanged(); - stateListModel.save(); } } @@ -243,6 +242,8 @@ Item { 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) @@ -367,6 +368,8 @@ Item { } function editState(index) { + console.log(index) + console.log(defaultStateIndex) stateDialog.open(index, stateList[index], defaultStateIndex === index); }