From 9efb644ce414db9a348026ed4315f2a1b56f50a3 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 8 Jan 2015 17:02:18 +0100 Subject: [PATCH 01/14] started project model --- mix/AppContext.cpp | 11 ++- mix/AppContext.h | 2 + mix/CMakeLists.txt | 3 + mix/ConstantCompilationControl.cpp | 1 + mix/FileIo.cpp | 68 ++++++++++++++ mix/FileIo.h | 49 ++++++++++ mix/qml.qrc | 17 ++-- mix/qml/MainContent.qml | 10 ++- mix/qml/NewProjectDialog.qml | 92 +++++++++++++++++++ mix/qml/ProjectList.qml | 39 ++++++++ mix/qml/ProjectModel.qml | 140 +++++++++++++++++++++++++++++ mix/qml/StateList.qml | 14 +-- 12 files changed, 431 insertions(+), 15 deletions(-) create mode 100644 mix/FileIo.cpp create mode 100644 mix/FileIo.h create mode 100644 mix/qml/NewProjectDialog.qml create mode 100644 mix/qml/ProjectList.qml create mode 100644 mix/qml/ProjectModel.qml diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp index 8eae2b230..65ca4cb20 100644 --- a/mix/AppContext.cpp +++ b/mix/AppContext.cpp @@ -32,8 +32,10 @@ #include #include #include -#include "AppContext.h" #include "CodeModel.h" +#include "FileIo.h" +#include "AppContext.h" + using namespace dev; using namespace dev::eth; @@ -46,8 +48,13 @@ AppContext::AppContext(QQmlApplicationEngine* _engine) m_applicationEngine = _engine; //m_webThree = std::unique_ptr(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"})); m_codeModel = std::unique_ptr(new CodeModel(this)); - m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get()); + m_fileIo.reset(new FileIo()); m_applicationEngine->rootContext()->setContextProperty("appContext", this); + qmlRegisterType("org.ethereum.qml", 1, 0, "FileIo"); + qmlRegisterSingletonType(QUrl("qrc:/qml/ProjectModel.qml"), "org.ethereum.qml.ProjectModel", 1, 0, "ProjectModel"); + m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get()); + m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get()); + } AppContext::~AppContext() diff --git a/mix/AppContext.h b/mix/AppContext.h index a7fa8a017..02d3ef032 100644 --- a/mix/AppContext.h +++ b/mix/AppContext.h @@ -47,6 +47,7 @@ namespace mix { class CodeModel; +class FileIo; /** * @brief Provides access to application scope variable. */ @@ -73,6 +74,7 @@ private: QQmlApplicationEngine* m_applicationEngine; //owned by app std::unique_ptr m_webThree; std::unique_ptr m_codeModel; + std::unique_ptr m_fileIo; public slots: /// Delete the current instance when application quit. diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index 555f6290f..6cef4470c 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -46,3 +46,6 @@ eth_install_executable(${EXECUTABLE} QMLDIR ${CMAKE_CURRENT_SOURCE_DIR}/qml ) +#add qml files to project tree in Qt creator +file(GLOB_RECURSE QMLFILES "qml/*.*") +add_custom_target(dummy SOURCES ${QMLFILES}) diff --git a/mix/ConstantCompilationControl.cpp b/mix/ConstantCompilationControl.cpp index e2d69bf62..c9f21c11b 100644 --- a/mix/ConstantCompilationControl.cpp +++ b/mix/ConstantCompilationControl.cpp @@ -34,6 +34,7 @@ using namespace dev::mix; + ConstantCompilationControl::ConstantCompilationControl(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::Tab) { connect(_context->codeModel(), &CodeModel::compilationComplete, this, &ConstantCompilationControl::update); diff --git a/mix/FileIo.cpp b/mix/FileIo.cpp new file mode 100644 index 000000000..2d37422a9 --- /dev/null +++ b/mix/FileIo.cpp @@ -0,0 +1,68 @@ +/* + 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 . +*/ +/** @file FileIo.cpp + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include +#include "FileIo.h" + +using namespace dev::mix; + +void FileIo::makeDir(QString const& _path) +{ + QDir dirPath(_path); + if (!dirPath.exists()) + dirPath.mkpath(dirPath.path()); +} + +QString FileIo::readFile(QString const& _path) +{ + QFile file(_path); + if(file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QTextStream stream(&file); + QString data = stream.readAll(); + return data; + } + else + throw std::runtime_error(tr("Error reading file %1").arg(_path).toStdString()); +} + +void FileIo::writeFile(QString const& _path, QString const& _data) +{ + QFile file(_path); + if(file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QTextStream stream(&file); + stream << _data; + } + else + throw std::runtime_error(tr("Error writing file %1").arg(_path).toStdString()); +} + +void FileIo::copyFile(QString const& _sourcePath, QString const& _destPath) +{ + if (!QFile::copy(_sourcePath, _destPath)) + throw std::runtime_error(tr("Error copying file %1 to %2").arg(_sourcePath).arg(_destPath).toStdString()); +} diff --git a/mix/FileIo.h b/mix/FileIo.h new file mode 100644 index 000000000..ed1326037 --- /dev/null +++ b/mix/FileIo.h @@ -0,0 +1,49 @@ +/* + 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 . +*/ +/** @file FileIo.h + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include + +namespace dev +{ +namespace mix +{ + +///File services for QML +class FileIo : public QObject +{ + Q_OBJECT + +public: + /// Create a directory if it does not exist. Throws on failure. + Q_INVOKABLE void makeDir(QString const& _path); + /// Read file contents to a string. Throws on failure. + Q_INVOKABLE QString readFile(QString const& _path); + /// Write contents to a file. Throws on failure. + Q_INVOKABLE void writeFile(QString const& _path, QString const& _data); + /// Copy a file from _sourcePath to _destPath. Throws on failure. + Q_INVOKABLE void copyFile(QString const& _sourcePath, QString const& _destPath); +}; + +} +} diff --git a/mix/qml.qrc b/mix/qml.qrc index 7e731b6f4..a6215b243 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -1,16 +1,19 @@ - qml/BasicContent.qml qml/main.qml - qml/MainContent.qml - qml/TabStyle.qml - qml/Debugger.qml - qml/js/Debugger.js + qml/AlertMessageDialog.qml + qml/BasicContent.qml qml/BasicMessage.qml - qml/TransactionDialog.qml + qml/Debugger.qml + qml/MainContent.qml qml/ModalDialog.qml - qml/AlertMessageDialog.qml + qml/ProjectList.qml qml/StateDialog.qml qml/StateList.qml + qml/TabStyle.qml + qml/TransactionDialog.qml + qml/js/Debugger.js + qml/NewProjectDialog.qml + qml/ProjectModel.qml diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 7f9a27d58..6cd2f5baa 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -20,9 +20,17 @@ Rectangle { SplitView { orientation: Qt.Horizontal anchors.fill: parent + + ProjectList { + anchors.left: parent.left + width: parent.width * 0.2 + height: parent.height + Layout.minimumWidth: 20 + } + SplitView { //anchors.fill: parent - width: parent.width * 0.8 + width: parent.width * 0.6 orientation: Qt.Vertical Rectangle { anchors.top: parent.top diff --git a/mix/qml/NewProjectDialog.qml b/mix/qml/NewProjectDialog.qml new file mode 100644 index 000000000..b0853ba26 --- /dev/null +++ b/mix/qml/NewProjectDialog.qml @@ -0,0 +1,92 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.0 +import QtQuick.Dialogs 1.2 + +Window { + + modality: Qt.WindowModal + + width: 640 + height: 280 + + visible: false + + property alias projectTitle : titleField.text + property alias projectPath : pathField.text + signal accepted + + function open() { + visible = true; + titleField.focus = true; + } + + function close() { + visible = false; + } + + GridLayout { + id: dialogContent + columns: 2 + anchors.fill: parent + anchors.margins: 10 + rowSpacing: 10 + columnSpacing: 10 + + Label { + text: qsTr("Title") + } + TextField { + id: titleField + focus: true + Layout.fillWidth: true + } + + Label { + text: qsTr("Path") + } + RowLayout { + TextField { + id: pathField + Layout.fillWidth: true + } + Button { + text: qsTr("Browse") + onClicked: createProjectFileDialog.open() + } + } + + RowLayout + { + anchors.bottom: parent.bottom + anchors.right: parent.right; + + Button { + enabled: titleField.text != "" && pathField.text != "" + text: qsTr("Ok"); + onClicked: { + close(); + accepted(); + } + } + Button { + text: qsTr("Cancel"); + onClicked: close(); + } + } + } + + FileDialog { + id: createProjectFileDialog + visible: false + title: qsTr("Please choose a path for the project") + selectFolder: true + onAccepted: { + var u = createProjectFileDialog.fileUrl.toString(); + if (u.indexOf("file://") == 0) + u = u.substring(7, u.length) + pathField.text = u; + } + } +} diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml new file mode 100644 index 000000000..60f1de898 --- /dev/null +++ b/mix/qml/ProjectList.qml @@ -0,0 +1,39 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import org.ethereum.qml.ProjectModel 1.0 + +Item { + ListView { + model: ProjectModel.listModel + delegate: renderDelegate + } + + Component { + id: renderDelegate + Item { + id: wrapperItem + height: 20 + width: parent.width + RowLayout { + anchors.fill: parent + Text { + Layout.fillWidth: true + Layout.fillHeight: true + text: title + font.pointSize: 12 + verticalAlignment: Text.AlignBottom + } + } + } + } + + Action { + id: createProjectAction + text: qsTr("&New project") + shortcut: "Ctrl+N" + enabled: true; + onTriggered: ProjectModel.createProject(); + } +} diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml new file mode 100644 index 000000000..26896ea5f --- /dev/null +++ b/mix/qml/ProjectModel.qml @@ -0,0 +1,140 @@ +pragma Singleton + +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtQuick.Dialogs 1.1 +import Qt.labs.settings 1.0 + +Item { + id: projectModel + + signal projectClosed + signal projectLoaded + + property bool isEmpty: projectFile === "" + readonly property string projectFileName: ".mix" + + property bool haveUnsavedChanges: false + property string projectFile: "" + property var projectData: null + property var listModel: projectListModel + + function saveAll() { + saveProject(); + } + + function createProject() { + closeProject(); + newProjectDialog.open(); + } + + function closeProject() { + console.log("closing project"); + if (haveUnsavedChanges) + saveMessageDialog.open(); + else + doCloseProject(); + } + + function saveProject() { + if (!isEmpty) { + var json = JSON.stringify(projectData); + fileIo.writeFile(projectFile, json) + } + } + + function loadProject(path) { + if (!isEmpty) + closeProject(); + console.log("loading project at " + path); + var json = fileIo.readFile(path); + projectData = JSON.parse(json); + projectFile = path; + if (!projectData.files) + projectData.files = []; + + for(var i = 0; i < projectData.files; i++) { + var p = projectData.files[i]; + projectListModel.append({ + path: p, + name: p.substring(p.lastIndexOf("/") + 1, p.length) + }); + } + onProjectLoaded(); + } + + function doCloseProject() { + projectListModel.clear(); + projectFile = ""; + projectData = null; + projectClosed(); + } + + function doCreateProject(title, path) { + if (!isEmpty) + closeProject(); + console.log("creating project " + title + " at " + path); + if (path[path.length - 1] !== "/") + path += "/"; + var dirPath = path + title; + fileIo.makeDir(dirPath); + var projectFile = dirPath + "/" + projectFileName; + fileIo.writeFile(projectFile, ""); + loadProject(projectFile); + } + + NewProjectDialog { + id: newProjectDialog + visible: false + onAccepted: { + var title = newProjectDialog.projectTitle; + var path = newProjectDialog.projectPath; + projectModel.doCreateProject(title, path); + } + } + + MessageDialog { + id: saveMessageDialog + title: qsTr("Project") + text: qsTr("Do you want to save changes?") + standardButtons: StandardButton.Ok | StandardButton.Cancel + icon: StandardIcon.Question + onAccepted: { + projectModel.saveAll(); + projectModel.doCloseProject(); + } + onRejected: { + projectModel.doCloseProject(); + } + } + + ListModel { + id: projectListModel + } + + Component { + id: renderDelegate + Item { + id: wrapperItem + height: 20 + width: parent.width + RowLayout { + anchors.fill: parent + Text { + Layout.fillWidth: true + Layout.fillHeight: true + text: title + font.pointSize: 12 + verticalAlignment: Text.AlignBottom + } + } + } + } + + Settings { + id: projectSettings + property string lastProjectPath; + } +} diff --git a/mix/qml/StateList.qml b/mix/qml/StateList.qml index 152a35671..ceed6513b 100644 --- a/mix/qml/StateList.qml +++ b/mix/qml/StateList.qml @@ -3,6 +3,7 @@ import QtQuick.Controls.Styles 1.2 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.1 +import org.ethereum.qml.ProjectModel 1.0 Rectangle { color: "transparent" @@ -15,9 +16,12 @@ Rectangle { property var stateList: [] Connections { - target: appContext + target: ProjectModel + onProjectClosed: { + stateListModel.clear(); + } onProjectLoaded: { - var items = JSON.parse(_json); + var items = target.projectData.states; for(var i = 0; i < items.length; i++) { stateListModel.append(items[i]); stateList.push(items[i]) @@ -82,8 +86,8 @@ Rectangle { } function save() { - var json = JSON.stringify(stateList); - appContext.saveProject(json); + console.log(parent.id); + ProjectModel.saveProject(); } } @@ -124,7 +128,7 @@ Rectangle { Action { id: addStateAction text: "&Add State" - shortcut: "Ctrl+N" + shortcut: "Ctrl+T" enabled: codeModel.hasContract && !debugModel.running; onTriggered: stateListModel.addState(); } From 715e0a4745127beb035cfa1c39cb233244f665d3 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 9 Jan 2015 14:34:12 +0100 Subject: [PATCH 02/14] continue project model implementation --- mix/CodeEditorExtensionManager.cpp | 32 +------- mix/CodeEditorExtensionManager.h | 5 -- mix/FileIo.cpp | 29 ++++--- mix/FileIo.h | 20 +++-- mix/MixApplication.cpp | 1 + mix/qml.qrc | 3 + mix/qml/CodeEditor.qml | 81 +++++++++++++++++++ mix/qml/CodeEditorModel.qml | 13 +++ mix/qml/CodeEditorView.qml | 80 +++++++++++++++++++ mix/qml/MainContent.qml | 72 ++--------------- mix/qml/NewProjectDialog.qml | 12 +-- mix/qml/ProjectList.qml | 28 +++++-- mix/qml/ProjectModel.qml | 123 ++++++++++++++++++++--------- mix/qml/StateList.qml | 2 + mix/qml/main.qml | 53 +++++++++++++ 15 files changed, 384 insertions(+), 170 deletions(-) create mode 100644 mix/qml/CodeEditor.qml create mode 100644 mix/qml/CodeEditorModel.qml create mode 100644 mix/qml/CodeEditorView.qml diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index 48c928a1f..14795c223 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -51,15 +51,6 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) if (!_editor) return; - QVariant doc = _editor->property("textDocument"); - if (doc.canConvert()) - { - QQuickTextDocument* qqdoc = doc.value(); - if (qqdoc) - { - m_doc = qqdoc->textDocument(); - } - } } void CodeEditorExtensionManager::initExtensions() @@ -67,7 +58,6 @@ void CodeEditorExtensionManager::initExtensions() std::shared_ptr output = std::make_shared(m_appContext); std::shared_ptr debug = std::make_shared(m_appContext); std::shared_ptr stateList = std::make_shared(m_appContext); - QObject::connect(m_doc, &QTextDocument::contentsChange, this, &CodeEditorExtensionManager::onCodeChange); QObject::connect(debug.get(), &AssemblyDebuggerControl::runFailed, output.get(), &ConstantCompilationControl::displayError); QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); @@ -97,35 +87,21 @@ void CodeEditorExtensionManager::initExtension(std::shared_ptr _ext) m_features.append(_ext); } -void CodeEditorExtensionManager::setEditor(QQuickItem* _editor) -{ - this->loadEditor(_editor); - this->initExtensions(); - - auto args = QApplication::arguments(); - if (args.length() > 1) - { - QString path = args[1]; - QFile file(path); - if (file.exists() && file.open(QFile::ReadOnly)) - m_doc->setPlainText(file.readAll()); - } -} - void CodeEditorExtensionManager::onCodeChange() { - m_appContext->codeModel()->updateFormatting(m_doc); //update old formatting - m_appContext->codeModel()->registerCodeChange(m_doc->toPlainText()); +// m_appContext->codeModel()->updateFormatting(m_doc); //update old formatting +// m_appContext->codeModel()->registerCodeChange(m_doc->toPlainText()); } void CodeEditorExtensionManager::applyCodeHighlight() { - m_appContext->codeModel()->updateFormatting(m_doc); +// m_appContext->codeModel()->updateFormatting(m_doc); } void CodeEditorExtensionManager::setRightTabView(QQuickItem* _tabView) { m_rightTabView = _tabView; + initExtensions(); //TODO: this is not the right place for it } void CodeEditorExtensionManager::setTabView(QQuickItem* _tabView) diff --git a/mix/CodeEditorExtensionManager.h b/mix/CodeEditorExtensionManager.h index 46ee6569f..e910b62d3 100644 --- a/mix/CodeEditorExtensionManager.h +++ b/mix/CodeEditorExtensionManager.h @@ -43,7 +43,6 @@ class CodeEditorExtensionManager: public QObject { Q_OBJECT - Q_PROPERTY(QQuickItem* editor MEMBER m_editor WRITE setEditor) Q_PROPERTY(QQuickItem* tabView MEMBER m_tabView WRITE setTabView) Q_PROPERTY(QQuickItem* rightTabView MEMBER m_rightTabView WRITE setRightTabView) @@ -54,8 +53,6 @@ public: void initExtensions(); /// Initialize extension. void initExtension(std::shared_ptr); - /// Set current text editor. - void setEditor(QQuickItem*); /// Set current tab view void setTabView(QQuickItem*); /// Set current right tab view. @@ -66,11 +63,9 @@ private slots: void applyCodeHighlight(); private: - QQuickItem* m_editor; QVector> m_features; QQuickItem* m_tabView; QQuickItem* m_rightTabView; - QTextDocument* m_doc; AppContext* m_appContext; void loadEditor(QQuickItem* _editor); }; diff --git a/mix/FileIo.cpp b/mix/FileIo.cpp index 2d37422a9..4ecbc9c08 100644 --- a/mix/FileIo.cpp +++ b/mix/FileIo.cpp @@ -25,20 +25,23 @@ #include #include #include +#include #include "FileIo.h" using namespace dev::mix; -void FileIo::makeDir(QString const& _path) +void FileIo::makeDir(QString const& _url) { - QDir dirPath(_path); + QUrl url(_url); + QDir dirPath(url.path()); if (!dirPath.exists()) dirPath.mkpath(dirPath.path()); } -QString FileIo::readFile(QString const& _path) +QString FileIo::readFile(QString const& _url) { - QFile file(_path); + QUrl url(_url); + QFile file(url.path()); if(file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream stream(&file); @@ -46,23 +49,27 @@ QString FileIo::readFile(QString const& _path) return data; } else - throw std::runtime_error(tr("Error reading file %1").arg(_path).toStdString()); + error(tr("Error reading file %1").arg(_url)); + return QString(); } -void FileIo::writeFile(QString const& _path, QString const& _data) +void FileIo::writeFile(QString const& _url, QString const& _data) { - QFile file(_path); + QUrl url(_url); + QFile file(url.path()); if(file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream stream(&file); stream << _data; } else - throw std::runtime_error(tr("Error writing file %1").arg(_path).toStdString()); + error(tr("Error writing file %1").arg(_url)); } -void FileIo::copyFile(QString const& _sourcePath, QString const& _destPath) +void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl) { - if (!QFile::copy(_sourcePath, _destPath)) - throw std::runtime_error(tr("Error copying file %1 to %2").arg(_sourcePath).arg(_destPath).toStdString()); + QUrl sourceUrl(_sourceUrl); + QUrl destUrl(_destUrl); + if (!QFile::copy(sourceUrl.path(), destUrl.path())) + error(tr("Error copying file %1 to %2").arg(_sourceUrl).arg(_destUrl)); } diff --git a/mix/FileIo.h b/mix/FileIo.h index ed1326037..83352476b 100644 --- a/mix/FileIo.h +++ b/mix/FileIo.h @@ -34,15 +34,19 @@ class FileIo : public QObject { Q_OBJECT +signals: + /// Signalled in case of IO error + void error(QString const& _errorText); + public: - /// Create a directory if it does not exist. Throws on failure. - Q_INVOKABLE void makeDir(QString const& _path); - /// Read file contents to a string. Throws on failure. - Q_INVOKABLE QString readFile(QString const& _path); - /// Write contents to a file. Throws on failure. - Q_INVOKABLE void writeFile(QString const& _path, QString const& _data); - /// Copy a file from _sourcePath to _destPath. Throws on failure. - Q_INVOKABLE void copyFile(QString const& _sourcePath, QString const& _destPath); + /// Create a directory if it does not exist. Signals on failure. + Q_INVOKABLE void makeDir(QString const& _url); + /// Read file contents to a string. Signals on failure. + Q_INVOKABLE QString readFile(QString const& _url); + /// Write contents to a file. Signals on failure. + Q_INVOKABLE void writeFile(QString const& _url, QString const& _data); + /// Copy a file from _sourcePath to _destPath. Signals on failure. + Q_INVOKABLE void copyFile(QString const& _sourceUrl, QString const& _destUrl); }; } diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index 5cf71aa7d..b5226b90c 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -35,6 +35,7 @@ MixApplication::MixApplication(int _argc, char* _argv[]): qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff m_engine->load(QUrl("qrc:/qml/main.qml")); + //m_engine->load(QUrl("qrc:/qml/ProjectModel.qml")); m_appContext->loadProject(); } diff --git a/mix/qml.qrc b/mix/qml.qrc index a6215b243..53a2d1ae7 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -15,5 +15,8 @@ qml/js/Debugger.js qml/NewProjectDialog.qml qml/ProjectModel.qml + qml/CodeEditorModel.qml + qml/CodeEditor.qml + qml/CodeEditorView.qml diff --git a/mix/qml/CodeEditor.qml b/mix/qml/CodeEditor.qml new file mode 100644 index 000000000..77e60ee66 --- /dev/null +++ b/mix/qml/CodeEditor.qml @@ -0,0 +1,81 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.1 + +Component { + Item { + signal editorTextChanged + + function setText(text) { + codeEditor.text = text; + } + + function getText() { + return codeEditor.text; + } + + anchors.fill: parent + id: contentView + width: parent.width + height: parent.height * 0.7 + Rectangle { + id: lineColumn + property int rowHeight: codeEditor.font.pixelSize + 3 + color: "#202020" + width: 50 + height: parent.height + Column { + y: -codeEditor.flickableItem.contentY + 4 + width: parent.width + Repeater { + model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) + delegate: Text { + id: text + color: codeEditor.textColor + font: codeEditor.font + width: lineColumn.width - 4 + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + height: lineColumn.rowHeight + renderType: Text.NativeRendering + text: index + 1 + } + } + } + } + + TextArea { + id: codeEditor + textColor: "#EEE8D5" + style: TextAreaStyle { + backgroundColor: "#002B36" + } + + anchors.left: lineColumn.right + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + wrapMode: TextEdit.NoWrap + frameVisible: false + + height: parent.height + font.family: "Monospace" + font.pointSize: 12 + width: parent.width + + tabChangesFocus: false + Keys.onPressed: { + if (event.key === Qt.Key_Tab) { + codeEditor.insert(codeEditor.cursorPosition, "\t"); + event.accepted = true; + } + } + onTextChanged: { + editorTextChanged(); + } + + } + } +} diff --git a/mix/qml/CodeEditorModel.qml b/mix/qml/CodeEditorModel.qml new file mode 100644 index 000000000..c696272ce --- /dev/null +++ b/mix/qml/CodeEditorModel.qml @@ -0,0 +1,13 @@ +pragma Singleton + +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtQuick.Dialogs 1.1 + +Item { + id: codeEditorModel + + property var codeDocuments: [] +} diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml new file mode 100644 index 000000000..9edc81ed9 --- /dev/null +++ b/mix/qml/CodeEditorView.qml @@ -0,0 +1,80 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import org.ethereum.qml.ProjectModel 1.0 + +Item { + + property string currentDocumentId: "" + + function getDocumentText(documentId) { + for (i = 0; i < editorListModel.count; i++) { + if (editorListModel.get(i).documentId === documentId) { + return editors.itemAt(i).getDocumentText(); + } + } + return ""; + } + + function openDocument(document) { + loadDocument(document); + currentDocumentId = document.documentId; + } + + function loadDocument(document) { + for (var i = 0; i < editorListModel.count; i++) + if (editorListModel.get(i).documentId === document.documentId) + return; //already open + + editorListModel.append(document); + } + + function doLoadDocument(editor, document) { + var data = fileIo.readFile(document.path); + if (document.isContract) + editor.onEditorTextChanged.connect(function() { + codeModel.registerCodeChange(editor.getText()); + }); + editor.setText(data); + } + + Connections { + target: ProjectModel + onDocumentOpen: { + openDocument(document); + } + } + + CodeEditor { + id: codeEditor + } + + Repeater { + id: editors + model: editorListModel + delegate: Loader { + active: false; + asynchronous: true + anchors.fill: parent + sourceComponent: codeEditor + visible: (currentDocumentId === editorListModel.get(index).documentId) + onVisibleChanged: { + loadIfNotLoaded() + } + Component.onCompleted: { + loadIfNotLoaded() + } + onLoaded: { doLoadDocument(item, editorListModel.get(index)) } + + function loadIfNotLoaded () { + if(visible && !active) { + active = true; + } + } + } + } + ListModel { + id: editorListModel + } +} diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 6cd2f5baa..5376be49d 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -22,80 +22,21 @@ Rectangle { anchors.fill: parent ProjectList { - anchors.left: parent.left width: parent.width * 0.2 height: parent.height - Layout.minimumWidth: 20 + Layout.minimumWidth: 200 } SplitView { //anchors.fill: parent width: parent.width * 0.6 orientation: Qt.Vertical - Rectangle { - anchors.top: parent.top - id: contentView - width: parent.width - height: parent.height * 0.7 - - Item { - anchors.fill: parent - Rectangle { - id: lineColumn - property int rowHeight: codeEditor.font.pixelSize + 3 - color: "#202020" - width: 50 - height: parent.height - Column { - y: -codeEditor.flickableItem.contentY + 4 - width: parent.width - Repeater { - model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) - delegate: Text { - id: text - color: codeEditor.textColor - font: codeEditor.font - width: lineColumn.width - 4 - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - height: lineColumn.rowHeight - renderType: Text.NativeRendering - text: index + 1 - } - } - } - } - - TextArea { - id: codeEditor - textColor: "#EEE8D5" - style: TextAreaStyle { - backgroundColor: "#002B36" - } + CodeEditorView { + height: parent.height * 0.7 + anchors.top: parent.top + width: parent.width + } - anchors.left: lineColumn.right - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - wrapMode: TextEdit.NoWrap - frameVisible: false - - height: parent.height - font.family: "Monospace" - font.pointSize: 12 - width: parent.width - //anchors.centerIn: parent - tabChangesFocus: false - Keys.onPressed: { - if (event.key === Qt.Key_Tab) { - codeEditor.insert(codeEditor.cursorPosition, "\t"); - event.accepted = true; - } - } - } - } - - } Rectangle { anchors.bottom: parent.bottom id: contextualView @@ -126,7 +67,6 @@ Rectangle { CodeEditorExtensionManager { tabView: contextualTabs rightTabView: rightPaneTabs - editor: codeEditor } } } diff --git a/mix/qml/NewProjectDialog.qml b/mix/qml/NewProjectDialog.qml index b0853ba26..7bba58418 100644 --- a/mix/qml/NewProjectDialog.qml +++ b/mix/qml/NewProjectDialog.qml @@ -14,7 +14,7 @@ Window { visible: false property alias projectTitle : titleField.text - property alias projectPath : pathField.text + readonly property string projectPath : "file://" + pathField.text signal accepted function open() { @@ -83,10 +83,10 @@ Window { title: qsTr("Please choose a path for the project") selectFolder: true onAccepted: { - var u = createProjectFileDialog.fileUrl.toString(); - if (u.indexOf("file://") == 0) - u = u.substring(7, u.length) - pathField.text = u; - } + var u = createProjectFileDialog.fileUrl.toString(); + if (u.indexOf("file://") == 0) + u = u.substring(7, u.length) + pathField.text = u; + } } } diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml index 60f1de898..77810931c 100644 --- a/mix/qml/ProjectList.qml +++ b/mix/qml/ProjectList.qml @@ -6,8 +6,16 @@ import org.ethereum.qml.ProjectModel 1.0 Item { ListView { + id: projectList model: ProjectModel.listModel + anchors.fill: parent delegate: renderDelegate + highlight: Rectangle { + color: "lightsteelblue"; + } + highlightFollowsCurrentItem: true + focus: true + clip: true } Component { @@ -21,19 +29,23 @@ Item { Text { Layout.fillWidth: true Layout.fillHeight: true - text: title + text: name font.pointSize: 12 verticalAlignment: Text.AlignBottom } } + MouseArea { + id: mouseArea + z: 1 + hoverEnabled: false + anchors.fill: parent + + onClicked:{ + projectList.currentIndex = index; + ProjectModel.documentOpen(ProjectModel.listModel.get(index)); + } + } } } - Action { - id: createProjectAction - text: qsTr("&New project") - shortcut: "Ctrl+N" - enabled: true; - onTriggered: ProjectModel.createProject(); - } } diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index 26896ea5f..f8217353d 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -12,8 +12,9 @@ Item { signal projectClosed signal projectLoaded + signal documentOpen(var document) - property bool isEmpty: projectFile === "" + property bool isEmpty: (projectFile === "") readonly property string projectFileName: ".mix" property bool haveUnsavedChanges: false @@ -30,24 +31,29 @@ Item { newProjectDialog.open(); } + function browseProject() { + openProjectFileDialog.open(); + } + function closeProject() { - console.log("closing project"); - if (haveUnsavedChanges) - saveMessageDialog.open(); - else - doCloseProject(); + if (!isEmpty) { + console.log("closing project"); + if (haveUnsavedChanges) + saveMessageDialog.open(); + else + doCloseProject(); + } } function saveProject() { if (!isEmpty) { var json = JSON.stringify(projectData); - fileIo.writeFile(projectFile, json) + fileIo.writeFile(projectFile, json); } } function loadProject(path) { - if (!isEmpty) - closeProject(); + closeProject(); console.log("loading project at " + path); var json = fileIo.readFile(path); projectData = JSON.parse(json); @@ -55,14 +61,35 @@ Item { if (!projectData.files) projectData.files = []; - for(var i = 0; i < projectData.files; i++) { + for(var i = 0; i < projectData.files.length; i++) { var p = projectData.files[i]; - projectListModel.append({ - path: p, - name: p.substring(p.lastIndexOf("/") + 1, p.length) - }); + addFile(p); } - onProjectLoaded(); + projectSettings.lastProjectPath = projectFile; + projectLoaded(); + } + + function addExistingFile() { + addExistingFileDialog().open(); + } + + function addProjectFiles(files) { + for(var i = 0; i < files.length; i++) + addFile(files[i]); + } + + function addFile(file) { + var p = file; + var fileData = { + contract: false, + path: p, + name: p.substring(p.lastIndexOf("/") + 1, p.length), + documentId: p, + isText: true, + isContract: p.substring(p.length - 4, p.length) === ".sol", + }; + + projectListModel.append(fileData); } function doCloseProject() { @@ -73,25 +100,39 @@ Item { } function doCreateProject(title, path) { - if (!isEmpty) - closeProject(); + closeProject(); console.log("creating project " + title + " at " + path); if (path[path.length - 1] !== "/") path += "/"; var dirPath = path + title; fileIo.makeDir(dirPath); var projectFile = dirPath + "/" + projectFileName; - fileIo.writeFile(projectFile, ""); + + var indexFile = dirPath + "/index.html"; + var contractsFile = dirPath + "/contracts.sol"; + var projectData = { + files: [ indexFile, contractsFile ] + }; + + fileIo.writeFile(indexFile, ""); + fileIo.writeFile(contractsFile, "contract MyContract {}"); + var json = JSON.stringify(projectData); + fileIo.writeFile(projectFile, json); loadProject(projectFile); } + Component.onCompleted: { + if (projectSettings.lastProjectPath) + loadProject(projectSettings.lastProjectPath) + } + NewProjectDialog { id: newProjectDialog visible: false onAccepted: { var title = newProjectDialog.projectTitle; var path = newProjectDialog.projectPath; - projectModel.doCreateProject(title, path); + doCreateProject(title, path); } } @@ -114,27 +155,33 @@ Item { id: projectListModel } - Component { - id: renderDelegate - Item { - id: wrapperItem - height: 20 - width: parent.width - RowLayout { - anchors.fill: parent - Text { - Layout.fillWidth: true - Layout.fillHeight: true - text: title - font.pointSize: 12 - verticalAlignment: Text.AlignBottom - } - } - } - } - Settings { id: projectSettings property string lastProjectPath; } + + FileDialog { + id: openProjectFileDialog + visible: false + title: qsTr("Open a project") + selectFolder: true + onAccepted: { + var path = openProjectFileDialog.fileUrl.toString(); + path += "/" + projectFileName; + loadProject(path); + } + } + + FileDialog { + id: addExistingFileDialog + visible: false + title: qsTr("Add a file") + selectFolder: false + onAccepted: { + var paths = openProjectFileDialog.fileUrls; + addProjectFiles(paths); + } + } + + } diff --git a/mix/qml/StateList.qml b/mix/qml/StateList.qml index ceed6513b..67d81747a 100644 --- a/mix/qml/StateList.qml +++ b/mix/qml/StateList.qml @@ -21,6 +21,8 @@ Rectangle { stateListModel.clear(); } onProjectLoaded: { + if (!target.projectData.states) + target.projectData.states = []; var items = target.projectData.states; for(var i = 0; i < items.length; i++) { stateListModel.append(items[i]); diff --git a/mix/qml/main.qml b/mix/qml/main.qml index bba7c9088..290649048 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.1 import CodeEditorExtensionManager 1.0 +import org.ethereum.qml.ProjectModel 1.0 ApplicationWindow { id: mainApplication @@ -18,6 +19,12 @@ ApplicationWindow { menuBar: MenuBar { Menu { title: qsTr("File") + MenuItem { action: createProjectAction } + MenuItem { action: openProjectAction } + MenuItem { action: addExistingFileAction } + MenuItem { action: addNewJsFileAction } + MenuItem { action: addNewHtmlFileAction } + MenuItem { action: addNewContractAction } MenuItem { text: qsTr("Exit") onTriggered: Qt.quit(); @@ -62,5 +69,51 @@ ApplicationWindow { onTriggered: debugModel.resetState(); } + Action { + id: createProjectAction + text: qsTr("&New project") + shortcut: "Ctrl+N" + enabled: true; + onTriggered: ProjectModel.createProject(); + } + + Action { + id: openProjectAction + text: qsTr("&Open project") + shortcut: "Ctrl+O" + enabled: true; + onTriggered: ProjectModel.browseProject(); + } + + Action { + id: addNewJsFileAction + text: qsTr("New JavaScript file") + shortcut: "Ctrl+Alt+J" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.addJsFile(); + } + Action { + id: addNewHtmlFileAction + text: qsTr("New HTML file") + shortcut: "Ctrl+Alt+H" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.addHtmlFile(); + } + + Action { + id: addNewContractAction + text: qsTr("New contract") + shortcut: "Ctrl+Alt+C" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.addContract(); + } + + Action { + id: addExistingFileAction + text: qsTr("Add existing file") + shortcut: "Ctrl+Alt+A" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.addExistingFile(); + } } From a4a2e8c252ba54534e4f21b04b246a86a7d6de63 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sat, 10 Jan 2015 12:42:23 +0100 Subject: [PATCH 03/14] continue project model implementation --- mix/MixApplication.cpp | 2 +- mix/qml.qrc | 2 +- mix/qml/CodeEditorModel.qml | 13 ---- mix/qml/CodeEditorView.qml | 17 ++++- mix/qml/ProjectList.qml | 47 +++++++++--- mix/qml/ProjectModel.qml | 130 ++++++--------------------------- mix/qml/js/ProjectModel.js | 142 ++++++++++++++++++++++++++++++++++++ mix/qml/main.qml | 29 +++++++- 8 files changed, 239 insertions(+), 143 deletions(-) delete mode 100644 mix/qml/CodeEditorModel.qml create mode 100644 mix/qml/js/ProjectModel.js diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index b5226b90c..1a55b1e47 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -33,7 +33,7 @@ MixApplication::MixApplication(int _argc, char* _argv[]): QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()), m_appContext(new AppContext(m_engine.get())) { qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); - QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff + //QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff m_engine->load(QUrl("qrc:/qml/main.qml")); //m_engine->load(QUrl("qrc:/qml/ProjectModel.qml")); m_appContext->loadProject(); diff --git a/mix/qml.qrc b/mix/qml.qrc index 53a2d1ae7..0e25de765 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -15,8 +15,8 @@ qml/js/Debugger.js qml/NewProjectDialog.qml qml/ProjectModel.qml - qml/CodeEditorModel.qml qml/CodeEditor.qml qml/CodeEditorView.qml + qml/js/ProjectModel.js diff --git a/mix/qml/CodeEditorModel.qml b/mix/qml/CodeEditorModel.qml deleted file mode 100644 index c696272ce..000000000 --- a/mix/qml/CodeEditorModel.qml +++ /dev/null @@ -1,13 +0,0 @@ -pragma Singleton - -import QtQuick 2.0 -import QtQuick.Window 2.0 -import QtQuick.Layouts 1.0 -import QtQuick.Controls 1.0 -import QtQuick.Dialogs 1.1 - -Item { - id: codeEditorModel - - property var codeDocuments: [] -} diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 9edc81ed9..45d2cfb2a 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -11,7 +11,7 @@ Item { function getDocumentText(documentId) { for (i = 0; i < editorListModel.count; i++) { if (editorListModel.get(i).documentId === documentId) { - return editors.itemAt(i).getDocumentText(); + return editors.itemAt(i).getText(); } } return ""; @@ -41,9 +41,20 @@ Item { Connections { target: ProjectModel - onDocumentOpen: { + onDocumentOpened: { openDocument(document); } + onProjectSaved: { + for (var i = 0; i < editorListModel.count; i++) + fileIo.writeFile(editorListModel.get(i).path, editors.itemAt(i).item.getText()); + } + onProjectClosed: { + for (var i = 0; i < editorListModel.count; i++) { + editors.itemAt(i).visible = false; + } + editorListModel.clear(); + currentDocumentId = ""; + } } CodeEditor { @@ -58,7 +69,7 @@ Item { asynchronous: true anchors.fill: parent sourceComponent: codeEditor - visible: (currentDocumentId === editorListModel.get(index).documentId) + visible: (index >= 0 && index < editorListModel.count && currentDocumentId === editorListModel.get(index).documentId) onVisibleChanged: { loadIfNotLoaded() } diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml index 77810931c..1f578fc6a 100644 --- a/mix/qml/ProjectList.qml +++ b/mix/qml/ProjectList.qml @@ -5,19 +5,36 @@ import QtQuick.Controls 1.0 import org.ethereum.qml.ProjectModel 1.0 Item { - ListView { - id: projectList - model: ProjectModel.listModel + + ColumnLayout { anchors.fill: parent - delegate: renderDelegate - highlight: Rectangle { - color: "lightsteelblue"; + Text { + Layout.fillWidth: true + color: "blue" + text: ProjectModel.projectData ? ProjectModel.projectData.title : "" + horizontalAlignment: Text.AlignHCenter + visible: !ProjectModel.isEmpty; } - highlightFollowsCurrentItem: true - focus: true - clip: true - } + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + id: projectList + model: ProjectModel.listModel + + delegate: renderDelegate + highlight: Rectangle { + color: "lightsteelblue"; + } + highlightFollowsCurrentItem: true + focus: true + clip: true + onCurrentIndexChanged: { + if (currentIndex >= 0 && currentIndex < ProjectModel.listModel.count) + ProjectModel.openDocument(ProjectModel.listModel.get(currentIndex).documentId); + } + } + } Component { id: renderDelegate Item { @@ -42,10 +59,16 @@ Item { onClicked:{ projectList.currentIndex = index; - ProjectModel.documentOpen(ProjectModel.listModel.get(index)); + } + } + + Connections { + target: ProjectModel + onProjectLoaded: { + projectList.currentIndex = 0; } } } } - } + diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index f8217353d..976f70316 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -7,119 +7,33 @@ import QtQuick.Controls 1.0 import QtQuick.Dialogs 1.1 import Qt.labs.settings 1.0 +import "js/ProjectModel.js" as ProjectModelCode + Item { id: projectModel signal projectClosed signal projectLoaded - signal documentOpen(var document) + signal documentOpened(var document) + signal projectSaved - property bool isEmpty: (projectFile === "") + property bool isEmpty: (projectData === null) readonly property string projectFileName: ".mix" property bool haveUnsavedChanges: false - property string projectFile: "" + property string projectPath: "" property var projectData: null property var listModel: projectListModel - function saveAll() { - saveProject(); - } - - function createProject() { - closeProject(); - newProjectDialog.open(); - } - - function browseProject() { - openProjectFileDialog.open(); - } - - function closeProject() { - if (!isEmpty) { - console.log("closing project"); - if (haveUnsavedChanges) - saveMessageDialog.open(); - else - doCloseProject(); - } - } - - function saveProject() { - if (!isEmpty) { - var json = JSON.stringify(projectData); - fileIo.writeFile(projectFile, json); - } - } - - function loadProject(path) { - closeProject(); - console.log("loading project at " + path); - var json = fileIo.readFile(path); - projectData = JSON.parse(json); - projectFile = path; - if (!projectData.files) - projectData.files = []; - - for(var i = 0; i < projectData.files.length; i++) { - var p = projectData.files[i]; - addFile(p); - } - projectSettings.lastProjectPath = projectFile; - projectLoaded(); - } - - function addExistingFile() { - addExistingFileDialog().open(); - } - - function addProjectFiles(files) { - for(var i = 0; i < files.length; i++) - addFile(files[i]); - } - - function addFile(file) { - var p = file; - var fileData = { - contract: false, - path: p, - name: p.substring(p.lastIndexOf("/") + 1, p.length), - documentId: p, - isText: true, - isContract: p.substring(p.length - 4, p.length) === ".sol", - }; - - projectListModel.append(fileData); - } - - function doCloseProject() { - projectListModel.clear(); - projectFile = ""; - projectData = null; - projectClosed(); - } - - function doCreateProject(title, path) { - closeProject(); - console.log("creating project " + title + " at " + path); - if (path[path.length - 1] !== "/") - path += "/"; - var dirPath = path + title; - fileIo.makeDir(dirPath); - var projectFile = dirPath + "/" + projectFileName; - - var indexFile = dirPath + "/index.html"; - var contractsFile = dirPath + "/contracts.sol"; - var projectData = { - files: [ indexFile, contractsFile ] - }; - - fileIo.writeFile(indexFile, ""); - fileIo.writeFile(contractsFile, "contract MyContract {}"); - var json = JSON.stringify(projectData); - fileIo.writeFile(projectFile, json); - loadProject(projectFile); - } + //interface + function saveAll() { ProjectModelCode.saveAll(); } + function createProject() { ProjectModelCode.createProject(); } + function browseProject() { ProjectModelCode.browseProject(); } + function closeProject() { ProjectModelCode.closeProject(); } + function saveProject() { ProjectModelCode.saveProject(); } + function loadProject(path) { ProjectModelCode.loadProject(path); } + function addExistingFile() { ProjectModelCode.addExistingFile(); } + function openDocument(documentId) { ProjectModelCode.openDocument(documentId); } Component.onCompleted: { if (projectSettings.lastProjectPath) @@ -132,7 +46,7 @@ Item { onAccepted: { var title = newProjectDialog.projectTitle; var path = newProjectDialog.projectPath; - doCreateProject(title, path); + ProjectModelCode.doCreateProject(title, path); } } @@ -144,10 +58,10 @@ Item { icon: StandardIcon.Question onAccepted: { projectModel.saveAll(); - projectModel.doCloseProject(); + ProjectModelCode.doCloseProject(); } onRejected: { - projectModel.doCloseProject(); + ProjectModelCode.doCloseProject(); } } @@ -167,8 +81,8 @@ Item { selectFolder: true onAccepted: { var path = openProjectFileDialog.fileUrl.toString(); - path += "/" + projectFileName; - loadProject(path); + path += "/"; + loadProject(path); } } @@ -179,9 +93,7 @@ Item { selectFolder: false onAccepted: { var paths = openProjectFileDialog.fileUrls; - addProjectFiles(paths); + ProjectModelCode.doAddExistingFiles(paths); } } - - } diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js new file mode 100644 index 000000000..7bcfb5cd7 --- /dev/null +++ b/mix/qml/js/ProjectModel.js @@ -0,0 +1,142 @@ +/* + 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 . +*/ +/** @file ProjectModel.js + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +function saveAll() { + saveProject(); + projectSaved(); +} + +function createProject() { + newProjectDialog.open(); +} + +function browseProject() { + openProjectFileDialog.open(); +} + +function closeProject() { + if (!isEmpty) { + if (haveUnsavedChanges) + saveMessageDialog.open(); + else + doCloseProject(); + } +} + +function saveProject() { + if (!isEmpty) { + var json = JSON.stringify(projectData); + var projectFile = projectPath + projectFileName; + fileIo.writeFile(projectFile, json); + } +} + +function loadProject(path) { + closeProject(); + console.log("loading project at " + path); + var projectFile = path + projectFileName; + var json = fileIo.readFile(projectFile); + projectData = JSON.parse(json); + if (!projectData.title) { + var parts = path.split("/"); + projectData.title = parts[parts.length - 2]; + } + projectPath = path; + if (!projectData.files) + projectData.files = []; + + for(var i = 0; i < projectData.files.length; i++) { + addFile(projectData.files[i]); + } + projectSettings.lastProjectPath = path; + projectLoaded(); +} + +function addExistingFile() { + addExistingFileDialog().open(); +} + +function addProjectFiles(files) { + for(var i = 0; i < files.length; i++) + addFile(files[i]); +} + +function addFile(fileName) { + var p = projectPath + fileName; + var fileData = { + contract: false, + path: p, + name: fileName, + documentId: fileName, + isText: true, + isContract: fileName.substring(fileName.length - 4, fileName.length) === ".sol", + }; + + projectListModel.append(fileData); +} + +function openDocument(documentId) { + for (var i = 0; i < projectListModel.count; i++) + if (projectListModel.get(i).documentId === documentId) + documentOpened(projectListModel.get(i)); +} + +function doCloseProject() { + console.log("closing project"); + projectListModel.clear(); + projectPath = ""; + projectData = null; + projectClosed(); +} + +function doCreateProject(title, path) { + closeProject(); + console.log("creating project " + title + " at " + path); + if (path[path.length - 1] !== "/") + path += "/"; + var dirPath = path + title + "/"; + fileIo.makeDir(dirPath); + var projectFile = dirPath + projectFileName; + + var indexFile = dirPath + "index.html"; + var contractsFile = dirPath + "contracts.sol"; + var projectData = { + title: title, + files: [ "contracts.sol", "index.html" ] + }; + + fileIo.writeFile(indexFile, ""); + fileIo.writeFile(contractsFile, "contract MyContract {}"); + var json = JSON.stringify(projectData); + fileIo.writeFile(projectFile, json); + loadProject(dirPath); +} + +function doAddExistingFiles(files) { + for(var i = 0; i < files.length; i++) { + var sourcePath = files[i]; + var sourceFileName = sourcePath.substring(sourcePath.lastIndexOf("/") + 1, sourcePath.length); + var destPath = projectPath + sourceFileName; + fileIo.copyFile(sourcePath, destPath); + addFile(sourceFileName); + } +} diff --git a/mix/qml/main.qml b/mix/qml/main.qml index c6cec939d..4bea535c2 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -21,14 +21,12 @@ ApplicationWindow { title: qsTr("File") MenuItem { action: createProjectAction } MenuItem { action: openProjectAction } + MenuItem { action: saveAllFilesAction } MenuItem { action: addExistingFileAction } MenuItem { action: addNewJsFileAction } MenuItem { action: addNewHtmlFileAction } MenuItem { action: addNewContractAction } - MenuItem { - text: qsTr("Exit") - onTriggered: Qt.quit(); - } + MenuItem { action: exitAppAction } } Menu { title: qsTr("Debug") @@ -54,6 +52,13 @@ ApplicationWindow { id: messageDialog } + Action { + id: exitAppAction + text: qsTr("Exit") + shortcut: "Ctrl+Q" + onTriggered: Qt.quit(); + } + Action { id: debugRunAction text: "&Run" @@ -116,4 +121,20 @@ ApplicationWindow { enabled: !ProjectModel.isEmpty onTriggered: ProjectModel.addExistingFile(); } + + Action { + id: saveAllFilesAction + text: qsTr("Save all") + shortcut: "Ctrl+S" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.saveAll(); + } + + Action { + id: closeProjectAction + text: qsTr("Close project") + shortcut: "Ctrl+W" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.closeProject(); + } } From 23c82cccac0068842d5c114a04f16fcdb34ca8d1 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sun, 11 Jan 2015 15:27:57 +0100 Subject: [PATCH 04/14] Removed dependency on WebThreeDirect form Web3Server Mix Client Model --- libdevcore/Exceptions.h | 2 + libweb3jsonrpc/WebThreeStubServer.cpp | 637 +-------------------- libweb3jsonrpc/WebThreeStubServer.h | 105 +--- libweb3jsonrpc/WebThreeStubServerBase.cpp | 664 ++++++++++++++++++++++ libweb3jsonrpc/WebThreeStubServerBase.h | 138 +++++ libwebthree/WebThree.h | 72 ++- mix/AppContext.cpp | 39 +- mix/AppContext.h | 12 +- mix/AssemblyDebuggerControl.cpp | 169 +----- mix/AssemblyDebuggerControl.h | 42 +- mix/AssemblyDebuggerModel.cpp | 119 ---- mix/AssemblyDebuggerModel.h | 76 --- mix/ClientModel.cpp | 222 ++++++++ mix/ClientModel.h | 113 ++++ mix/CodeEditorExtensionManager.cpp | 3 +- mix/CodeModel.cpp | 2 +- mix/CodeModel.h | 8 +- mix/ConstantCompilationControl.cpp | 2 +- mix/ContractCallDataEncoder.cpp | 5 +- mix/ContractCallDataEncoder.h | 8 +- mix/DebuggingStateWrapper.h | 40 +- mix/FileIo.cpp | 5 + mix/FileIo.h | 4 + mix/MixApplication.cpp | 5 +- mix/MixClient.cpp | 336 +++++++++++ mix/MixClient.h | 125 ++++ mix/QFunctionDefinition.cpp | 4 +- mix/QFunctionDefinition.h | 3 + mix/Web3Server.cpp | 55 ++ mix/Web3Server.h | 56 ++ mix/qml.qrc | 1 + mix/qml/CodeEditorView.qml | 2 +- mix/qml/ProjectModel.qml | 11 +- mix/qml/StateDialog.qml | 2 +- mix/qml/StateList.qml | 8 +- mix/qml/WebPreview.qml | 81 +++ mix/qml/js/ProjectModel.js | 2 +- mix/qml/main.qml | 6 +- 38 files changed, 1947 insertions(+), 1237 deletions(-) create mode 100644 libweb3jsonrpc/WebThreeStubServerBase.cpp create mode 100644 libweb3jsonrpc/WebThreeStubServerBase.h delete mode 100644 mix/AssemblyDebuggerModel.cpp delete mode 100644 mix/AssemblyDebuggerModel.h create mode 100644 mix/ClientModel.cpp create mode 100644 mix/ClientModel.h create mode 100644 mix/MixClient.cpp create mode 100644 mix/MixClient.h create mode 100644 mix/Web3Server.cpp create mode 100644 mix/Web3Server.h create mode 100644 mix/qml/WebPreview.qml diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index 5d03c195f..061f181b3 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -22,6 +22,7 @@ #pragma once #include +#include #include #include #include "CommonData.h" @@ -40,6 +41,7 @@ struct NoNetworking: virtual Exception {}; struct NoUPnPDevice: virtual Exception {}; struct RootNotFound: virtual Exception {}; struct FileError: virtual Exception {}; +struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): m_f("Interface " + _f + " not supported.") {} virtual const char* what() const noexcept { return m_f.c_str(); } private: std::string m_f; }; // error information to be added to exceptions typedef boost::error_info errinfo_invalidSymbol; diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 874c14331..f728a29f5 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -21,198 +21,19 @@ * @date 2014 */ -#include -#include -#include -#include "WebThreeStubServer.h" -#include -#include -#include -#include -#include #include +#include #include -#include -#include -#include +#include "WebThreeStubServer.h" using namespace std; using namespace dev; using namespace dev::eth; -static Json::Value toJson(dev::eth::BlockInfo const& _bi) -{ - Json::Value res; - res["hash"] = boost::lexical_cast(_bi.hash); - res["parentHash"] = toJS(_bi.parentHash); - res["sha3Uncles"] = toJS(_bi.sha3Uncles); - res["miner"] = toJS(_bi.coinbaseAddress); - res["stateRoot"] = toJS(_bi.stateRoot); - res["transactionsRoot"] = toJS(_bi.transactionsRoot); - res["difficulty"] = toJS(_bi.difficulty); - res["number"] = (int)_bi.number; - res["gasLimit"] = (int)_bi.gasLimit; - res["timestamp"] = (int)_bi.timestamp; - res["extraData"] = jsFromBinary(_bi.extraData); - res["nonce"] = toJS(_bi.nonce); - return res; -} - -static Json::Value toJson(dev::eth::Transaction const& _t) -{ - Json::Value res; - res["hash"] = toJS(_t.sha3()); - res["input"] = jsFromBinary(_t.data()); - res["to"] = toJS(_t.receiveAddress()); - res["from"] = toJS(_t.sender()); - res["gas"] = (int)_t.gas(); - res["gasPrice"] = toJS(_t.gasPrice()); - res["nonce"] = toJS(_t.nonce()); - res["value"] = toJS(_t.value()); - return res; -} - -static Json::Value toJson(dev::eth::LogEntry const& _e) -{ - Json::Value res; - - res["data"] = jsFromBinary(_e.data); - res["address"] = toJS(_e.address); - for (auto const& t: _e.topics) - res["topics"].append(toJS(t)); - return res; -} - -static Json::Value toJson(dev::eth::LogEntries const& _es) // commented to avoid warning. Uncomment once in use @ poC-7. -{ - Json::Value res; - for (dev::eth::LogEntry const& e: _es) - res.append(toJson(e)); - return res; -} - -static Json::Value toJson(std::map const& _storage) -{ - Json::Value res(Json::objectValue); - for (auto i: _storage) - res[toJS(i.first)] = toJS(i.second); - return res; -} - -static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to avoid warning. Uncomment once in use @ PoC-7. -{ - dev::eth::LogFilter filter; - if (!_json.isObject() || _json.empty()) - return filter; - - if (_json["earliest"].isInt()) - filter.withEarliest(_json["earliest"].asInt()); - if (_json["latest"].isInt()) - filter.withLatest(_json["lastest"].asInt()); - if (_json["max"].isInt()) - filter.withMax(_json["max"].asInt()); - if (_json["skip"].isInt()) - filter.withSkip(_json["skip"].asInt()); - if (!_json["address"].empty()) - { - if (_json["address"].isArray()) - { - for (auto i : _json["address"]) - if (i.isString()) - filter.address(jsToAddress(i.asString())); - } - else if (_json["address"].isString()) - filter.address(jsToAddress(_json["address"].asString())); - } - if (!_json["topics"].empty()) - { - if (_json["topics"].isArray()) - { - for (auto i: _json["topics"]) - if (i.isString()) - filter.topic(jsToU256(i.asString())); - } - else if(_json["topics"].isString()) - filter.topic(jsToU256(_json["topics"].asString())); - } - return filter; -} - -static shh::Message toMessage(Json::Value const& _json) -{ - shh::Message ret; - if (_json["from"].isString()) - ret.setFrom(jsToPublic(_json["from"].asString())); - if (_json["to"].isString()) - ret.setTo(jsToPublic(_json["to"].asString())); - if (_json["payload"].isString()) - ret.setPayload(jsToBytes(_json["payload"].asString())); - return ret; -} - -static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, Secret _from) -{ - unsigned ttl = 50; - unsigned workToProve = 50; - shh::BuildTopic bt; - - if (_json["ttl"].isInt()) - ttl = _json["ttl"].asInt(); - if (_json["workToProve"].isInt()) - workToProve = _json["workToProve"].asInt(); - if (!_json["topic"].empty()) - { - if (_json["topic"].isString()) - bt.shift(jsToBytes(_json["topic"].asString())); - else if (_json["topic"].isArray()) - for (auto i: _json["topic"]) - if (i.isString()) - bt.shift(jsToBytes(i.asString())); - } - return _m.seal(_from, bt, ttl, workToProve); -} - -static pair toWatch(Json::Value const& _json) -{ - shh::BuildTopicMask bt; - Public to; - - if (_json["to"].isString()) - to = jsToPublic(_json["to"].asString()); - - if (!_json["topic"].empty()) - { - if (_json["topic"].isString()) - bt.shift(jsToBytes(_json["topic"].asString())); - else if (_json["topic"].isArray()) - for (auto i: _json["topic"]) - if (i.isString()) - bt.shift(jsToBytes(i.asString())); - } - return make_pair(bt.toTopicMask(), to); -} - -static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m) -{ - Json::Value res; - res["hash"] = toJS(_h); - res["expiry"] = (int)_e.expiry(); - res["sent"] = (int)_e.sent(); - res["ttl"] = (int)_e.ttl(); - res["workProved"] = (int)_e.workProved(); - for (auto const& t: _e.topics()) - res["topics"].append(toJS(t)); - res["payload"] = toJS(_m.payload()); - res["from"] = toJS(_m.from()); - res["to"] = toJS(_m.to()); - return res; -} - WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, std::vector const& _accounts): - AbstractWebThreeStubServer(_conn), + WebThreeStubServerBase(_conn, _accounts), m_web3(_web3) { - setAccounts(_accounts); auto path = getDataDir() + "/.web3"; boost::filesystem::create_directories(path); ldb::Options o; @@ -220,181 +41,27 @@ WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, ldb::DB::Open(o, path, &m_db); } -void WebThreeStubServer::setAccounts(std::vector const& _accounts) -{ - m_accounts.clear(); - for (auto i: _accounts) - m_accounts[i.address()] = i.secret(); -} - -void WebThreeStubServer::setIdentities(std::vector const& _ids) -{ - m_ids.clear(); - for (auto i: _ids) - m_ids[i.pub()] = i.secret(); -} - -dev::eth::Interface* WebThreeStubServer::client() const +dev::eth::Interface* WebThreeStubServer::client() { return m_web3.ethereum(); } -std::shared_ptr WebThreeStubServer::face() const +std::shared_ptr WebThreeStubServer::face() { return m_web3.whisper(); } -std::string WebThreeStubServer::web3_sha3(std::string const& _param1) -{ - return toJS(sha3(jsToBytes(_param1))); -} - -Json::Value WebThreeStubServer::eth_accounts() -{ - Json::Value ret(Json::arrayValue); - for (auto i: m_accounts) - ret.append(toJS(i.first)); - return ret; -} - -std::string WebThreeStubServer::shh_addToGroup(std::string const& _group, std::string const& _who) -{ - (void)_group; - (void)_who; - return ""; -} - -std::string WebThreeStubServer::eth_balanceAt(string const& _address) -{ - return toJS(client()->balanceAt(jsToAddress(_address), client()->getDefault())); -} - -Json::Value WebThreeStubServer::eth_blockByHash(std::string const& _hash) -{ - return toJson(client()->blockInfo(jsToFixed<32>(_hash))); -} - -Json::Value WebThreeStubServer::eth_blockByNumber(int const& _number) -{ - return toJson(client()->blockInfo(client()->hashFromNumber(_number))); -} - -static TransactionSkeleton toTransaction(Json::Value const& _json) -{ - TransactionSkeleton ret; - if (!_json.isObject() || _json.empty()) - return ret; - - if (_json["from"].isString()) - ret.from = jsToAddress(_json["from"].asString()); - if (_json["to"].isString()) - ret.to = jsToAddress(_json["to"].asString()); - if (!_json["value"].empty()) - { - if (_json["value"].isString()) - ret.value = jsToU256(_json["value"].asString()); - else if (_json["value"].isInt()) - ret.value = u256(_json["value"].asInt()); - } - if (!_json["gas"].empty()) - { - if (_json["gas"].isString()) - ret.gas = jsToU256(_json["gas"].asString()); - else if (_json["gas"].isInt()) - ret.gas = u256(_json["gas"].asInt()); - } - if (!_json["gasPrice"].empty()) - { - if (_json["gasPrice"].isString()) - ret.gasPrice = jsToU256(_json["gasPrice"].asString()); - else if (_json["gasPrice"].isInt()) - ret.gas = u256(_json["gas"].asInt()); - } - if (!_json["data"].empty()) - { - if (_json["data"].isString()) // ethereum.js has preconstructed the data array - ret.data = jsToBytes(_json["data"].asString()); - else if (_json["data"].isArray()) // old style: array of 32-byte-padded values. TODO: remove PoC-8 - for (auto i: _json["data"]) - dev::operator +=(ret.data, padded(jsToBytes(i.asString()), 32)); - } - - if (_json["code"].isString()) - ret.data = jsToBytes(_json["code"].asString()); - return ret; -} - -std::string WebThreeStubServer::eth_call(Json::Value const& _json) -{ - std::string ret; - TransactionSkeleton t = toTransaction(_json); - if (!t.from && m_accounts.size()) - { - auto b = m_accounts.begin()->first; - for (auto a: m_accounts) - if (client()->balanceAt(a.first) > client()->balanceAt(b)) - b = a.first; - t.from = b; - } - if (!m_accounts.count(t.from)) - return ret; - if (!t.gasPrice) - t.gasPrice = 10 * dev::eth::szabo; - if (!t.gas) - t.gas = min(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice); - ret = toJS(client()->call(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice)); - return ret; -} - -bool WebThreeStubServer::eth_changed(int const& _id) -{ - return client()->checkWatch(_id); -} - -std::string WebThreeStubServer::eth_codeAt(string const& _address) +dev::WebThreeNetworkFace* WebThreeStubServer::network() { - return jsFromBinary(client()->codeAt(jsToAddress(_address), client()->getDefault())); + return &m_web3; } -std::string WebThreeStubServer::eth_coinbase() +dev::WebThreeStubDatabaseFace* WebThreeStubServer::db() { - return toJS(client()->address()); + return this; } -double WebThreeStubServer::eth_countAt(string const& _address) -{ - return (double)(uint64_t)client()->countAt(jsToAddress(_address), client()->getDefault()); -} - -int WebThreeStubServer::eth_defaultBlock() -{ - return client()->getDefault(); -} - -std::string WebThreeStubServer::eth_gasPrice() -{ - return toJS(10 * dev::eth::szabo); -} - -std::string WebThreeStubServer::db_get(std::string const& _name, std::string const& _key) -{ - bytes k = sha3(_name).asBytes() + sha3(_key).asBytes(); - string ret; - m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret); - return toJS(dev::asBytes(ret)); -} - -Json::Value WebThreeStubServer::eth_filterLogs(int const& _id) -{ - return toJson(client()->logs(_id)); -} - -Json::Value WebThreeStubServer::eth_logs(Json::Value const& _json) -{ - return toJson(client()->logs(toLogFilter(_json))); -} - -std::string WebThreeStubServer::db_getString(std::string const& _name, std::string const& _key) +std::string WebThreeStubServer::get(std::string const& _name, std::string const& _key) { bytes k = sha3(_name).asBytes() + sha3(_key).asBytes(); string ret; @@ -402,289 +69,9 @@ std::string WebThreeStubServer::db_getString(std::string const& _name, std::stri return ret; } -bool WebThreeStubServer::shh_haveIdentity(std::string const& _id) -{ - return m_ids.count(jsToPublic(_id)) > 0; -} - -bool WebThreeStubServer::eth_listening() -{ - return m_web3.isNetworkStarted(); -} - -bool WebThreeStubServer::eth_mining() -{ - return client()->isMining(); -} - -int WebThreeStubServer::eth_newFilter(Json::Value const& _json) -{ - unsigned ret = -1; - ret = client()->installWatch(toLogFilter(_json)); - return ret; -} - -int WebThreeStubServer::eth_newFilterString(std::string const& _filter) -{ - unsigned ret = -1; - if (_filter.compare("chain") == 0) - ret = client()->installWatch(dev::eth::ChainChangedFilter); - else if (_filter.compare("pending") == 0) - ret = client()->installWatch(dev::eth::PendingChangedFilter); - return ret; -} - -std::string WebThreeStubServer::shh_newGroup(std::string const& _id, std::string const& _who) -{ - (void)_id; - (void)_who; - return ""; -} - -std::string WebThreeStubServer::shh_newIdentity() -{ -// cnote << this << m_ids; - KeyPair kp = KeyPair::create(); - m_ids[kp.pub()] = kp.secret(); - return toJS(kp.pub()); -} - -Json::Value WebThreeStubServer::eth_compilers() -{ - Json::Value ret(Json::arrayValue); - ret.append("lll"); - ret.append("solidity"); - ret.append("serpent"); - return ret; -} - -std::string WebThreeStubServer::eth_lll(std::string const& _code) -{ - string res; - vector errors; - res = toJS(dev::eth::compileLLL(_code, true, &errors)); - cwarn << "LLL compilation errors: " << errors; - return res; -} - -std::string WebThreeStubServer::eth_serpent(std::string const& _code) -{ - string res; - try - { - res = toJS(dev::asBytes(::compile(_code))); - } - catch (string err) - { - cwarn << "Solidity compilation error: " << err; - } - catch (...) - { - cwarn << "Uncought serpent compilation exception"; - } - return res; -} - -std::string WebThreeStubServer::eth_solidity(std::string const& _code) -{ - string res; - dev::solidity::CompilerStack compiler; - try - { - res = toJS(compiler.compile(_code, true)); - } - catch (dev::Exception const& exception) - { - ostringstream error; - solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); - cwarn << "Solidity compilation error: " << error.str(); - } - catch (...) - { - cwarn << "Uncought solidity compilation exception"; - } - return res; -} - -int WebThreeStubServer::eth_number() -{ - return client()->number() + 1; -} - -int WebThreeStubServer::eth_peerCount() -{ - return m_web3.peerCount(); -} - -bool WebThreeStubServer::shh_post(Json::Value const& _json) -{ - shh::Message m = toMessage(_json); - Secret from; - - if (m.from() && m_ids.count(m.from())) - { - cwarn << "Silently signing message from identity" << m.from().abridged() << ": User validation hook goes here."; - // TODO: insert validification hook here. - from = m_ids[m.from()]; - } - - face()->inject(toSealed(_json, m, from)); - return true; -} - -bool WebThreeStubServer::db_put(std::string const& _name, std::string const& _key, std::string const& _value) +void WebThreeStubServer::put(std::string const& _name, std::string const& _key, std::string const& _value) { bytes k = sha3(_name).asBytes() + sha3(_key).asBytes(); - bytes v = jsToBytes(_value); - m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size())); - return true; -} - -bool WebThreeStubServer::db_putString(std::string const& _name, std::string const& _key, std::string const& _value) -{ - bytes k = sha3(_name).asBytes() + sha3(_key).asBytes(); - string v = _value; - m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size())); - return true; -} - -bool WebThreeStubServer::eth_setCoinbase(std::string const& _address) -{ - client()->setAddress(jsToAddress(_address)); - return true; -} - -bool WebThreeStubServer::eth_setDefaultBlock(int const& _block) -{ - client()->setDefault(_block); - return true; -} - -bool WebThreeStubServer::eth_setListening(bool const& _listening) -{ - if (_listening) - m_web3.startNetwork(); - else - m_web3.stopNetwork(); - return true; -} - -bool WebThreeStubServer::eth_setMining(bool const& _mining) -{ - if (_mining) - client()->startMining(); - else - client()->stopMining(); - return true; -} - -Json::Value WebThreeStubServer::shh_changed(int const& _id) -{ - Json::Value ret(Json::arrayValue); - auto pub = m_shhWatches[_id]; - if (!pub || m_ids.count(pub)) - for (h256 const& h: face()->checkWatch(_id)) - { - auto e = face()->envelope(h); - shh::Message m; - if (pub) - { - cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here."; - m = e.open(m_ids[pub]); - if (!m) - continue; - } - else - m = e.open(); - ret.append(toJson(h, e, m)); - } - - return ret; -} - -int WebThreeStubServer::shh_newFilter(Json::Value const& _json) -{ - auto w = toWatch(_json); - auto ret = face()->installWatch(w.first); - m_shhWatches.insert(make_pair(ret, w.second)); - return ret; -} - -bool WebThreeStubServer::shh_uninstallFilter(int const& _id) -{ - face()->uninstallWatch(_id); - return true; -} - -std::string WebThreeStubServer::eth_stateAt(string const& _address, string const& _storage) -{ - return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), client()->getDefault())); -} - -Json::Value WebThreeStubServer::eth_storageAt(string const& _address) -{ - return toJson(client()->storageAt(jsToAddress(_address))); -} - -std::string WebThreeStubServer::eth_transact(Json::Value const& _json) -{ - std::string ret; - TransactionSkeleton t = toTransaction(_json); - if (!t.from && m_accounts.size()) - { - auto b = m_accounts.begin()->first; - for (auto a: m_accounts) - if (client()->balanceAt(a.first) > client()->balanceAt(b)) - b = a.first; - t.from = b; - } - if (!m_accounts.count(t.from)) - return ret; - if (!t.gasPrice) - t.gasPrice = 10 * dev::eth::szabo; - if (!t.gas) - t.gas = min(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice); - if (authenticate(t)) - { - if (t.to) - // TODO: from qethereum, insert validification hook here. - client()->transact(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice); - else - ret = toJS(client()->transact(m_accounts[t.from].secret(), t.value, t.data, t.gas, t.gasPrice)); - client()->flushTransactions(); - } - return ret; -} - -bool WebThreeStubServer::authenticate(TransactionSkeleton const& _t) const -{ - cwarn << "Silently signing transaction from address" << _t.from.abridged() << ": User validation hook goes here."; - return true; -} - -Json::Value WebThreeStubServer::eth_transactionByHash(std::string const& _hash, int const& _i) -{ - return toJson(client()->transaction(jsToFixed<32>(_hash), _i)); -} - -Json::Value WebThreeStubServer::eth_transactionByNumber(int const& _number, int const& _i) -{ - return toJson(client()->transaction(client()->hashFromNumber(_number), _i)); -} - -Json::Value WebThreeStubServer::eth_uncleByHash(std::string const& _hash, int const& _i) -{ - return toJson(client()->uncle(jsToFixed<32>(_hash), _i)); -} - -Json::Value WebThreeStubServer::eth_uncleByNumber(int const& _number, int const& _i) -{ - return toJson(client()->uncle(client()->hashFromNumber(_number), _i)); -} - -bool WebThreeStubServer::eth_uninstallFilter(int const& _id) -{ - client()->uninstallWatch(_id); - return true; + m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)_value.data(), _value.size())); } diff --git a/libweb3jsonrpc/WebThreeStubServer.h b/libweb3jsonrpc/WebThreeStubServer.h index 0f81fce9d..b76fc17e9 100644 --- a/libweb3jsonrpc/WebThreeStubServer.h +++ b/libweb3jsonrpc/WebThreeStubServer.h @@ -28,111 +28,32 @@ #include #pragma warning(pop) -#include -#include -#include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#include "abstractwebthreestubserver.h" -#pragma GCC diagnostic pop - -namespace ldb = leveldb; +#include "WebThreeStubServerBase.h" namespace dev { class WebThreeDirect; -class KeyPair; -class TransactionSkeleton; -namespace eth -{ -class Interface; -} -namespace shh -{ -class Interface; -} } /** - * @brief JSON-RPC api implementation - * @todo filters should work on unsigned instead of int - * unsigned are not supported in json-rpc-cpp and there are bugs with double in json-rpc-cpp version 0.2.1 - * @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols. - * @todo modularise everything so additional subprotocols don't need to change this file. + * @brief JSON-RPC api implementation for WebThreeDirect */ -class WebThreeStubServer: public AbstractWebThreeStubServer +class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace { public: WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector const& _accounts); - virtual std::string web3_sha3(std::string const& _param1); - virtual Json::Value eth_accounts(); - virtual std::string eth_balanceAt(std::string const& _address); - virtual Json::Value eth_blockByHash(std::string const& _hash); - virtual Json::Value eth_blockByNumber(int const& _number); - virtual std::string eth_call(Json::Value const& _json); - virtual bool eth_changed(int const& _id); - virtual std::string eth_codeAt(std::string const& _address); - virtual std::string eth_coinbase(); - virtual Json::Value eth_compilers(); - virtual double eth_countAt(std::string const& _address); - virtual int eth_defaultBlock(); - virtual std::string eth_gasPrice(); - virtual Json::Value eth_filterLogs(int const& _id); - virtual Json::Value eth_logs(Json::Value const& _json); - virtual bool eth_listening(); - virtual bool eth_mining(); - virtual int eth_newFilter(Json::Value const& _json); - virtual int eth_newFilterString(std::string const& _filter); - virtual int eth_number(); - virtual int eth_peerCount(); - virtual bool eth_setCoinbase(std::string const& _address); - virtual bool eth_setDefaultBlock(int const& _block); - virtual bool eth_setListening(bool const& _listening); - virtual std::string eth_lll(std::string const& _s); - virtual std::string eth_serpent(std::string const& _s); - virtual bool eth_setMining(bool const& _mining); - virtual std::string eth_solidity(std::string const& _code); - virtual std::string eth_stateAt(std::string const& _address, std::string const& _storage); - virtual Json::Value eth_storageAt(std::string const& _address); - virtual std::string eth_transact(Json::Value const& _json); - virtual Json::Value eth_transactionByHash(std::string const& _hash, int const& _i); - virtual Json::Value eth_transactionByNumber(int const& _number, int const& _i); - virtual Json::Value eth_uncleByHash(std::string const& _hash, int const& _i); - virtual Json::Value eth_uncleByNumber(int const& _number, int const& _i); - virtual bool eth_uninstallFilter(int const& _id); - - virtual std::string db_get(std::string const& _name, std::string const& _key); - virtual std::string db_getString(std::string const& _name, std::string const& _key); - virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value); - virtual bool db_putString(std::string const& _name, std::string const& _key, std::string const& _value); - - virtual std::string shh_addToGroup(std::string const& _group, std::string const& _who); - virtual Json::Value shh_changed(int const& _id); - virtual bool shh_haveIdentity(std::string const& _id); - virtual int shh_newFilter(Json::Value const& _json); - virtual std::string shh_newGroup(std::string const& _id, std::string const& _who); - virtual std::string shh_newIdentity(); - virtual bool shh_post(Json::Value const& _json); - virtual bool shh_uninstallFilter(int const& _id); - - void setAccounts(std::vector const& _accounts); - void setIdentities(std::vector const& _ids); - std::map const& ids() const { return m_ids; } - -protected: - virtual bool authenticate(dev::TransactionSkeleton const& _t) const; +private: + dev::eth::Interface* client() override; + std::shared_ptr face() override; + dev::WebThreeNetworkFace* network() override; + dev::WebThreeStubDatabaseFace* db() override; + std::string get(std::string const& _name, std::string const& _key) override; + void put(std::string const& _name, std::string const& _key, std::string const& _value) override; private: - dev::eth::Interface* client() const; - std::shared_ptr face() const; dev::WebThreeDirect& m_web3; - std::map m_accounts; - - ldb::ReadOptions m_readOptions; - ldb::WriteOptions m_writeOptions; - ldb::DB* m_db; - - std::map m_ids; - std::map m_shhWatches; + leveldb::ReadOptions m_readOptions; + leveldb::WriteOptions m_writeOptions; + leveldb::DB* m_db; }; diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp new file mode 100644 index 000000000..565db9555 --- /dev/null +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -0,0 +1,664 @@ +/* + 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 . +*/ +/** @file WebThreeStubServerBase.cpp + * @authors: + * Gav Wood + * Marek Kotewicz + * @date 2014 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "WebThreeStubServerBase.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +static Json::Value toJson(dev::eth::BlockInfo const& _bi) +{ + Json::Value res; + res["hash"] = boost::lexical_cast(_bi.hash); + res["parentHash"] = toJS(_bi.parentHash); + res["sha3Uncles"] = toJS(_bi.sha3Uncles); + res["miner"] = toJS(_bi.coinbaseAddress); + res["stateRoot"] = toJS(_bi.stateRoot); + res["transactionsRoot"] = toJS(_bi.transactionsRoot); + res["difficulty"] = toJS(_bi.difficulty); + res["number"] = (int)_bi.number; + res["gasLimit"] = (int)_bi.gasLimit; + res["timestamp"] = (int)_bi.timestamp; + res["extraData"] = jsFromBinary(_bi.extraData); + res["nonce"] = toJS(_bi.nonce); + return res; +} + +static Json::Value toJson(dev::eth::Transaction const& _t) +{ + Json::Value res; + res["hash"] = toJS(_t.sha3()); + res["input"] = jsFromBinary(_t.data()); + res["to"] = toJS(_t.receiveAddress()); + res["from"] = toJS(_t.sender()); + res["gas"] = (int)_t.gas(); + res["gasPrice"] = toJS(_t.gasPrice()); + res["nonce"] = toJS(_t.nonce()); + res["value"] = toJS(_t.value()); + return res; +} + +static Json::Value toJson(dev::eth::LogEntry const& _e) +{ + Json::Value res; + + res["data"] = jsFromBinary(_e.data); + res["address"] = toJS(_e.address); + for (auto const& t: _e.topics) + res["topics"].append(toJS(t)); + return res; +} + +static Json::Value toJson(dev::eth::LogEntries const& _es) // commented to avoid warning. Uncomment once in use @ poC-7. +{ + Json::Value res; + for (dev::eth::LogEntry const& e: _es) + res.append(toJson(e)); + return res; +} + +static Json::Value toJson(std::map const& _storage) +{ + Json::Value res(Json::objectValue); + for (auto i: _storage) + res[toJS(i.first)] = toJS(i.second); + return res; +} + +static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to avoid warning. Uncomment once in use @ PoC-7. +{ + dev::eth::LogFilter filter; + if (!_json.isObject() || _json.empty()) + return filter; + + if (_json["earliest"].isInt()) + filter.withEarliest(_json["earliest"].asInt()); + if (_json["latest"].isInt()) + filter.withLatest(_json["lastest"].asInt()); + if (_json["max"].isInt()) + filter.withMax(_json["max"].asInt()); + if (_json["skip"].isInt()) + filter.withSkip(_json["skip"].asInt()); + if (!_json["address"].empty()) + { + if (_json["address"].isArray()) + { + for (auto i : _json["address"]) + if (i.isString()) + filter.address(jsToAddress(i.asString())); + } + else if (_json["address"].isString()) + filter.address(jsToAddress(_json["address"].asString())); + } + if (!_json["topics"].empty()) + { + if (_json["topics"].isArray()) + { + for (auto i: _json["topics"]) + if (i.isString()) + filter.topic(jsToU256(i.asString())); + } + else if(_json["topics"].isString()) + filter.topic(jsToU256(_json["topics"].asString())); + } + return filter; +} + +static shh::Message toMessage(Json::Value const& _json) +{ + shh::Message ret; + if (_json["from"].isString()) + ret.setFrom(jsToPublic(_json["from"].asString())); + if (_json["to"].isString()) + ret.setTo(jsToPublic(_json["to"].asString())); + if (_json["payload"].isString()) + ret.setPayload(jsToBytes(_json["payload"].asString())); + return ret; +} + +static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, Secret _from) +{ + unsigned ttl = 50; + unsigned workToProve = 50; + shh::BuildTopic bt; + + if (_json["ttl"].isInt()) + ttl = _json["ttl"].asInt(); + if (_json["workToProve"].isInt()) + workToProve = _json["workToProve"].asInt(); + if (!_json["topic"].empty()) + { + if (_json["topic"].isString()) + bt.shift(jsToBytes(_json["topic"].asString())); + else if (_json["topic"].isArray()) + for (auto i: _json["topic"]) + if (i.isString()) + bt.shift(jsToBytes(i.asString())); + } + return _m.seal(_from, bt, ttl, workToProve); +} + +static pair toWatch(Json::Value const& _json) +{ + shh::BuildTopicMask bt; + Public to; + + if (_json["to"].isString()) + to = jsToPublic(_json["to"].asString()); + + if (!_json["topic"].empty()) + { + if (_json["topic"].isString()) + bt.shift(jsToBytes(_json["topic"].asString())); + else if (_json["topic"].isArray()) + for (auto i: _json["topic"]) + if (i.isString()) + bt.shift(jsToBytes(i.asString())); + } + return make_pair(bt.toTopicMask(), to); +} + +static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m) +{ + Json::Value res; + res["hash"] = toJS(_h); + res["expiry"] = (int)_e.expiry(); + res["sent"] = (int)_e.sent(); + res["ttl"] = (int)_e.ttl(); + res["workProved"] = (int)_e.workProved(); + for (auto const& t: _e.topics()) + res["topics"].append(toJS(t)); + res["payload"] = toJS(_m.payload()); + res["from"] = toJS(_m.from()); + res["to"] = toJS(_m.to()); + return res; +} + +WebThreeStubServerBase::WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts): + AbstractWebThreeStubServer(_conn) +{ + setAccounts(_accounts); +} + +void WebThreeStubServerBase::setAccounts(std::vector const& _accounts) +{ + m_accounts.clear(); + for (auto i: _accounts) + m_accounts[i.address()] = i.secret(); +} + +void WebThreeStubServerBase::setIdentities(std::vector const& _ids) +{ + m_ids.clear(); + for (auto i: _ids) + m_ids[i.pub()] = i.secret(); +} + +std::string WebThreeStubServerBase::web3_sha3(std::string const& _param1) +{ + return toJS(sha3(jsToBytes(_param1))); +} + +Json::Value WebThreeStubServerBase::eth_accounts() +{ + Json::Value ret(Json::arrayValue); + for (auto i: m_accounts) + ret.append(toJS(i.first)); + return ret; +} + +std::string WebThreeStubServerBase::shh_addToGroup(std::string const& _group, std::string const& _who) +{ + (void)_group; + (void)_who; + return ""; +} + +std::string WebThreeStubServerBase::eth_balanceAt(string const& _address) +{ + return toJS(client()->balanceAt(jsToAddress(_address), client()->getDefault())); +} + +Json::Value WebThreeStubServerBase::eth_blockByHash(std::string const& _hash) +{ + return toJson(client()->blockInfo(jsToFixed<32>(_hash))); +} + +Json::Value WebThreeStubServerBase::eth_blockByNumber(int const& _number) +{ + return toJson(client()->blockInfo(client()->hashFromNumber(_number))); +} + +static TransactionSkeleton toTransaction(Json::Value const& _json) +{ + TransactionSkeleton ret; + if (!_json.isObject() || _json.empty()) + return ret; + + if (_json["from"].isString()) + ret.from = jsToAddress(_json["from"].asString()); + if (_json["to"].isString()) + ret.to = jsToAddress(_json["to"].asString()); + if (!_json["value"].empty()) + { + if (_json["value"].isString()) + ret.value = jsToU256(_json["value"].asString()); + else if (_json["value"].isInt()) + ret.value = u256(_json["value"].asInt()); + } + if (!_json["gas"].empty()) + { + if (_json["gas"].isString()) + ret.gas = jsToU256(_json["gas"].asString()); + else if (_json["gas"].isInt()) + ret.gas = u256(_json["gas"].asInt()); + } + if (!_json["gasPrice"].empty()) + { + if (_json["gasPrice"].isString()) + ret.gasPrice = jsToU256(_json["gasPrice"].asString()); + else if (_json["gasPrice"].isInt()) + ret.gas = u256(_json["gas"].asInt()); + } + if (!_json["data"].empty()) + { + if (_json["data"].isString()) // ethereum.js has preconstructed the data array + ret.data = jsToBytes(_json["data"].asString()); + else if (_json["data"].isArray()) // old style: array of 32-byte-padded values. TODO: remove PoC-8 + for (auto i: _json["data"]) + dev::operator +=(ret.data, padded(jsToBytes(i.asString()), 32)); + } + + if (_json["code"].isString()) + ret.data = jsToBytes(_json["code"].asString()); + return ret; +} + +std::string WebThreeStubServerBase::eth_call(Json::Value const& _json) +{ + std::string ret; + TransactionSkeleton t = toTransaction(_json); + if (!t.from && m_accounts.size()) + { + auto b = m_accounts.begin()->first; + for (auto a: m_accounts) + if (client()->balanceAt(a.first) > client()->balanceAt(b)) + b = a.first; + t.from = b; + } + if (!m_accounts.count(t.from)) + return ret; + if (!t.gasPrice) + t.gasPrice = 10 * dev::eth::szabo; + if (!t.gas) + t.gas = min(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice); + ret = toJS(client()->call(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice)); + return ret; +} + +bool WebThreeStubServerBase::eth_changed(int const& _id) +{ + return client()->checkWatch(_id); +} + +std::string WebThreeStubServerBase::eth_codeAt(string const& _address) +{ + return jsFromBinary(client()->codeAt(jsToAddress(_address), client()->getDefault())); +} + +std::string WebThreeStubServerBase::eth_coinbase() +{ + return toJS(client()->address()); +} + +double WebThreeStubServerBase::eth_countAt(string const& _address) +{ + return (double)(uint64_t)client()->countAt(jsToAddress(_address), client()->getDefault()); +} + +int WebThreeStubServerBase::eth_defaultBlock() +{ + return client()->getDefault(); +} + +std::string WebThreeStubServerBase::eth_gasPrice() +{ + return toJS(10 * dev::eth::szabo); +} + +std::string WebThreeStubServerBase::db_get(std::string const& _name, std::string const& _key) +{ + string ret = db()->get(_name, _key); + return toJS(dev::asBytes(ret)); +} + +Json::Value WebThreeStubServerBase::eth_filterLogs(int const& _id) +{ + return toJson(client()->logs(_id)); +} + +Json::Value WebThreeStubServerBase::eth_logs(Json::Value const& _json) +{ + return toJson(client()->logs(toLogFilter(_json))); +} + +std::string WebThreeStubServerBase::db_getString(std::string const& _name, std::string const& _key) +{ + return db()->get(_name, _key);; +} + +bool WebThreeStubServerBase::shh_haveIdentity(std::string const& _id) +{ + return m_ids.count(jsToPublic(_id)) > 0; +} + +bool WebThreeStubServerBase::eth_listening() +{ + return network()->isNetworkStarted(); +} + +bool WebThreeStubServerBase::eth_mining() +{ + return client()->isMining(); +} + +int WebThreeStubServerBase::eth_newFilter(Json::Value const& _json) +{ + unsigned ret = -1; + ret = client()->installWatch(toLogFilter(_json)); + return ret; +} + +int WebThreeStubServerBase::eth_newFilterString(std::string const& _filter) +{ + unsigned ret = -1; + if (_filter.compare("chain") == 0) + ret = client()->installWatch(dev::eth::ChainChangedFilter); + else if (_filter.compare("pending") == 0) + ret = client()->installWatch(dev::eth::PendingChangedFilter); + return ret; +} + +std::string WebThreeStubServerBase::shh_newGroup(std::string const& _id, std::string const& _who) +{ + (void)_id; + (void)_who; + return ""; +} + +std::string WebThreeStubServerBase::shh_newIdentity() +{ +// cnote << this << m_ids; + KeyPair kp = KeyPair::create(); + m_ids[kp.pub()] = kp.secret(); + return toJS(kp.pub()); +} + +Json::Value WebThreeStubServerBase::eth_compilers() +{ + Json::Value ret(Json::arrayValue); + ret.append("lll"); + ret.append("solidity"); + ret.append("serpent"); + return ret; +} + +std::string WebThreeStubServerBase::eth_lll(std::string const& _code) +{ + string res; + vector errors; + res = toJS(dev::eth::compileLLL(_code, true, &errors)); + cwarn << "LLL compilation errors: " << errors; + return res; +} + +std::string WebThreeStubServerBase::eth_serpent(std::string const& _code) +{ + string res; + try + { + res = toJS(dev::asBytes(::compile(_code))); + } + catch (string err) + { + cwarn << "Solidity compilation error: " << err; + } + catch (...) + { + cwarn << "Uncought serpent compilation exception"; + } + return res; +} + +std::string WebThreeStubServerBase::eth_solidity(std::string const& _code) +{ + string res; + dev::solidity::CompilerStack compiler; + try + { + res = toJS(compiler.compile(_code, true)); + } + catch (dev::Exception const& exception) + { + ostringstream error; + solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); + cwarn << "Solidity compilation error: " << error.str(); + } + catch (...) + { + cwarn << "Uncought solidity compilation exception"; + } + return res; +} + +int WebThreeStubServerBase::eth_number() +{ + return client()->number() + 1; +} + +int WebThreeStubServerBase::eth_peerCount() +{ + return network()->peerCount(); +} + +bool WebThreeStubServerBase::shh_post(Json::Value const& _json) +{ + shh::Message m = toMessage(_json); + Secret from; + + if (m.from() && m_ids.count(m.from())) + { + cwarn << "Silently signing message from identity" << m.from().abridged() << ": User validation hook goes here."; + // TODO: insert validification hook here. + from = m_ids[m.from()]; + } + + face()->inject(toSealed(_json, m, from)); + return true; +} + +bool WebThreeStubServerBase::db_put(std::string const& _name, std::string const& _key, std::string const& _value) +{ + string v = asString(jsToBytes(_value)); + db()->put(_name, _key, v); + return true; +} + +bool WebThreeStubServerBase::db_putString(std::string const& _name, std::string const& _key, std::string const& _value) +{ + db()->put(_name, _key,_value); + return true; +} + +bool WebThreeStubServerBase::eth_setCoinbase(std::string const& _address) +{ + client()->setAddress(jsToAddress(_address)); + return true; +} + +bool WebThreeStubServerBase::eth_setDefaultBlock(int const& _block) +{ + client()->setDefault(_block); + return true; +} + +bool WebThreeStubServerBase::eth_setListening(bool const& _listening) +{ + if (_listening) + network()->startNetwork(); + else + network()->stopNetwork(); + return true; +} + +bool WebThreeStubServerBase::eth_setMining(bool const& _mining) +{ + if (_mining) + client()->startMining(); + else + client()->stopMining(); + return true; +} + +Json::Value WebThreeStubServerBase::shh_changed(int const& _id) +{ + Json::Value ret(Json::arrayValue); + auto pub = m_shhWatches[_id]; + if (!pub || m_ids.count(pub)) + for (h256 const& h: face()->checkWatch(_id)) + { + auto e = face()->envelope(h); + shh::Message m; + if (pub) + { + cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here."; + m = e.open(m_ids[pub]); + if (!m) + continue; + } + else + m = e.open(); + ret.append(toJson(h, e, m)); + } + + return ret; +} + +int WebThreeStubServerBase::shh_newFilter(Json::Value const& _json) +{ + auto w = toWatch(_json); + auto ret = face()->installWatch(w.first); + m_shhWatches.insert(make_pair(ret, w.second)); + return ret; +} + +bool WebThreeStubServerBase::shh_uninstallFilter(int const& _id) +{ + face()->uninstallWatch(_id); + return true; +} + +std::string WebThreeStubServerBase::eth_stateAt(string const& _address, string const& _storage) +{ + return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), client()->getDefault())); +} + +Json::Value WebThreeStubServerBase::eth_storageAt(string const& _address) +{ + return toJson(client()->storageAt(jsToAddress(_address))); +} + +std::string WebThreeStubServerBase::eth_transact(Json::Value const& _json) +{ + std::string ret; + TransactionSkeleton t = toTransaction(_json); + if (!t.from && m_accounts.size()) + { + auto b = m_accounts.begin()->first; + for (auto a: m_accounts) + if (client()->balanceAt(a.first) > client()->balanceAt(b)) + b = a.first; + t.from = b; + } + if (!m_accounts.count(t.from)) + return ret; + if (!t.gasPrice) + t.gasPrice = 10 * dev::eth::szabo; + if (!t.gas) + t.gas = min(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice); + if (authenticate(t)) + { + if (t.to) + // TODO: from qethereum, insert validification hook here. + client()->transact(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice); + else + ret = toJS(client()->transact(m_accounts[t.from].secret(), t.value, t.data, t.gas, t.gasPrice)); + client()->flushTransactions(); + } + return ret; +} + +bool WebThreeStubServerBase::authenticate(TransactionSkeleton const& _t) const +{ + cwarn << "Silently signing transaction from address" << _t.from.abridged() << ": User validation hook goes here."; + return true; +} + +Json::Value WebThreeStubServerBase::eth_transactionByHash(std::string const& _hash, int const& _i) +{ + return toJson(client()->transaction(jsToFixed<32>(_hash), _i)); +} + +Json::Value WebThreeStubServerBase::eth_transactionByNumber(int const& _number, int const& _i) +{ + return toJson(client()->transaction(client()->hashFromNumber(_number), _i)); +} + +Json::Value WebThreeStubServerBase::eth_uncleByHash(std::string const& _hash, int const& _i) +{ + return toJson(client()->uncle(jsToFixed<32>(_hash), _i)); +} + +Json::Value WebThreeStubServerBase::eth_uncleByNumber(int const& _number, int const& _i) +{ + return toJson(client()->uncle(client()->hashFromNumber(_number), _i)); +} + +bool WebThreeStubServerBase::eth_uninstallFilter(int const& _id) +{ + client()->uninstallWatch(_id); + return true; +} + diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h new file mode 100644 index 000000000..6868207a6 --- /dev/null +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -0,0 +1,138 @@ +/* + 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 . +*/ +/** @file WebThreeStubServer.h + * @authors: + * Gav Wood + * Marek Kotewicz + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include "abstractwebthreestubserver.h" +#pragma GCC diagnostic pop + + +namespace dev +{ +class WebThreeNetworkFace; +class KeyPair; +class TransactionSkeleton; +namespace eth +{ +class Interface; +} +namespace shh +{ +class Interface; +} + +class WebThreeStubDatabaseFace +{ +public: + virtual std::string get(std::string const& _name, std::string const& _key) = 0; + virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) = 0; +}; + +/** + * @brief JSON-RPC api implementation + * @todo filters should work on unsigned instead of int + * unsigned are not supported in json-rpc-cpp and there are bugs with double in json-rpc-cpp version 0.2.1 + * @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols. + * @todo modularise everything so additional subprotocols don't need to change this file. + */ +class WebThreeStubServerBase: public AbstractWebThreeStubServer +{ +public: + WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts); + + virtual std::string web3_sha3(std::string const& _param1); + virtual Json::Value eth_accounts(); + virtual std::string eth_balanceAt(std::string const& _address); + virtual Json::Value eth_blockByHash(std::string const& _hash); + virtual Json::Value eth_blockByNumber(int const& _number); + virtual std::string eth_call(Json::Value const& _json); + virtual bool eth_changed(int const& _id); + virtual std::string eth_codeAt(std::string const& _address); + virtual std::string eth_coinbase(); + virtual Json::Value eth_compilers(); + virtual double eth_countAt(std::string const& _address); + virtual int eth_defaultBlock(); + virtual std::string eth_gasPrice(); + virtual Json::Value eth_filterLogs(int const& _id); + virtual Json::Value eth_logs(Json::Value const& _json); + virtual bool eth_listening(); + virtual bool eth_mining(); + virtual int eth_newFilter(Json::Value const& _json); + virtual int eth_newFilterString(std::string const& _filter); + virtual int eth_number(); + virtual int eth_peerCount(); + virtual bool eth_setCoinbase(std::string const& _address); + virtual bool eth_setDefaultBlock(int const& _block); + virtual bool eth_setListening(bool const& _listening); + virtual std::string eth_lll(std::string const& _s); + virtual std::string eth_serpent(std::string const& _s); + virtual bool eth_setMining(bool const& _mining); + virtual std::string eth_solidity(std::string const& _code); + virtual std::string eth_stateAt(std::string const& _address, std::string const& _storage); + virtual Json::Value eth_storageAt(std::string const& _address); + virtual std::string eth_transact(Json::Value const& _json); + virtual Json::Value eth_transactionByHash(std::string const& _hash, int const& _i); + virtual Json::Value eth_transactionByNumber(int const& _number, int const& _i); + virtual Json::Value eth_uncleByHash(std::string const& _hash, int const& _i); + virtual Json::Value eth_uncleByNumber(int const& _number, int const& _i); + virtual bool eth_uninstallFilter(int const& _id); + + virtual std::string db_get(std::string const& _name, std::string const& _key); + virtual std::string db_getString(std::string const& _name, std::string const& _key); + virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value); + virtual bool db_putString(std::string const& _name, std::string const& _key, std::string const& _value); + + virtual std::string shh_addToGroup(std::string const& _group, std::string const& _who); + virtual Json::Value shh_changed(int const& _id); + virtual bool shh_haveIdentity(std::string const& _id); + virtual int shh_newFilter(Json::Value const& _json); + virtual std::string shh_newGroup(std::string const& _id, std::string const& _who); + virtual std::string shh_newIdentity(); + virtual bool shh_post(Json::Value const& _json); + virtual bool shh_uninstallFilter(int const& _id); + + void setAccounts(std::vector const& _accounts); + void setIdentities(std::vector const& _ids); + std::map const& ids() const { return m_ids; } + +protected: + virtual bool authenticate(dev::TransactionSkeleton const& _t) const; + +protected: + virtual dev::eth::Interface* client() = 0; + virtual std::shared_ptr face() = 0; + virtual dev::WebThreeNetworkFace* network() = 0; + virtual dev::WebThreeStubDatabaseFace* db() = 0; + + std::map m_accounts; + + std::map m_ids; + std::map m_shhWatches; +}; + +} //namespace dev diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index ec7bf2406..682fdc0b6 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -38,8 +38,6 @@ namespace dev { -class InterfaceNotSupported: public Exception { public: InterfaceNotSupported(std::string _f): m_f(_f) {} virtual const char* what() const noexcept { return ("Interface " + m_f + " not supported.").c_str(); } private: std::string m_f; }; - enum WorkState { Active = 0, @@ -51,6 +49,48 @@ namespace eth { class Interface; } namespace shh { class Interface; } namespace bzz { class Interface; } + +class WebThreeNetworkFace +{ +public: + /// Get information on the current peer set. + virtual std::vector peers() = 0; + + /// Same as peers().size(), but more efficient. + virtual size_t peerCount() const = 0; + + /// Connect to a particular peer. + virtual void connect(std::string const& _seedHost, unsigned short _port) = 0; + + /// Save peers + virtual dev::bytes saveNodes() = 0; + + /// Restore peers + virtual void restoreNodes(bytesConstRef _saved) = 0; + + /// Sets the ideal number of peers. + virtual void setIdealPeerCount(size_t _n) = 0; + + virtual bool haveNetwork() const = 0; + + virtual void setNetworkPreferences(p2p::NetworkPreferences const& _n) = 0; + + virtual p2p::NodeId id() const = 0; + + /// Gets the nodes. + virtual p2p::Nodes nodes() const = 0; + + /// Start the network subsystem. + virtual void startNetwork() = 0; + + /// Stop the network subsystem. + virtual void stopNetwork() = 0; + + /// Is network working? there may not be any peers yet. + virtual bool isNetworkStarted() const = 0; +}; + + /** * @brief Main API hub for interfacing with Web 3 components. This doesn't do any local multiplexing, so you can only have one * running on any given machine for the provided DB path. @@ -61,7 +101,7 @@ namespace bzz { class Interface; } * * Provides a baseline for the multiplexed multi-protocol session class, WebThree. */ -class WebThreeDirect +class WebThreeDirect : public WebThreeNetworkFace { public: /// Constructor for private instance. If there is already another process on the machine using @a _dbPath, then this will throw an exception. @@ -84,40 +124,40 @@ public: // Network stuff: /// Get information on the current peer set. - std::vector peers(); + std::vector peers() override; /// Same as peers().size(), but more efficient. - size_t peerCount() const; + size_t peerCount() const override; /// Connect to a particular peer. - void connect(std::string const& _seedHost, unsigned short _port = 30303); + void connect(std::string const& _seedHost, unsigned short _port = 30303) override; /// Save peers - dev::bytes saveNodes(); + dev::bytes saveNodes() override; /// Restore peers - void restoreNodes(bytesConstRef _saved); + void restoreNodes(bytesConstRef _saved) override; /// Sets the ideal number of peers. - void setIdealPeerCount(size_t _n); + void setIdealPeerCount(size_t _n) override; - bool haveNetwork() const { return m_net.isStarted(); } + bool haveNetwork() const override { return m_net.isStarted(); } - void setNetworkPreferences(p2p::NetworkPreferences const& _n); + void setNetworkPreferences(p2p::NetworkPreferences const& _n) override; - p2p::NodeId id() const { return m_net.id(); } + p2p::NodeId id() const override { return m_net.id(); } /// Gets the nodes. - p2p::Nodes nodes() const { return m_net.nodes(); } + p2p::Nodes nodes() const override { return m_net.nodes(); } /// Start the network subsystem. - void startNetwork() { m_net.start(); } + void startNetwork() override { m_net.start(); } /// Stop the network subsystem. - void stopNetwork() { m_net.stop(); } + void stopNetwork() override { m_net.stop(); } /// Is network working? there may not be any peers yet. - bool isNetworkStarted() { return m_net.isStarted(); } + bool isNetworkStarted() const override { return m_net.isStarted(); } private: std::string m_clientVersion; ///< Our end-application client's name/version. diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp index 049cb04ea..4bede3c49 100644 --- a/mix/AppContext.cpp +++ b/mix/AppContext.cpp @@ -27,13 +27,10 @@ #include #include #include -#include -#include -#include -#include #include #include "CodeModel.h" #include "FileIo.h" +#include "ClientModel.h" #include "AppContext.h" @@ -47,7 +44,8 @@ AppContext::AppContext(QQmlApplicationEngine* _engine) { m_applicationEngine = _engine; //m_webThree = std::unique_ptr(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"})); - m_codeModel = std::unique_ptr(new CodeModel(this)); + m_codeModel.reset(new CodeModel(this)); + m_clientModel.reset(new ClientModel(this)); m_fileIo.reset(new FileIo()); m_applicationEngine->rootContext()->setContextProperty("appContext", this); qmlRegisterType("org.ethereum.qml", 1, 0, "FileIo"); @@ -61,21 +59,6 @@ AppContext::~AppContext() { } -void AppContext::loadProject() -{ - QString path = QStandardPaths::locate(QStandardPaths::DataLocation, c_projectFileName); - if (!path.isEmpty()) - { - QFile file(path); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - QTextStream stream(&file); - QString json = stream.readAll(); - emit projectLoaded(json); - } - } -} - QQmlApplicationEngine* AppContext::appEngine() { return m_applicationEngine; @@ -93,19 +76,3 @@ void AppContext::displayMessageDialog(QString _title, QString _message) dialogWin->findChild("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message); QMetaObject::invokeMethod(dialogWin, "open"); } - -void AppContext::saveProject(QString const& _json) -{ - QDir dirPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); - QString path = QDir(dirPath).filePath(c_projectFileName); - if (!path.isEmpty()) - { - dirPath.mkpath(dirPath.path()); - QFile file(path); - if (file.open(QIODevice::WriteOnly | QIODevice::Text)) - { - QTextStream stream(&file); - stream << _json; - } - } -} diff --git a/mix/AppContext.h b/mix/AppContext.h index 3db3414d9..dec3b319e 100644 --- a/mix/AppContext.h +++ b/mix/AppContext.h @@ -47,6 +47,7 @@ namespace mix { class CodeModel; +class ClientModel; class FileIo; /** * @brief Provides access to application scope variable. @@ -63,24 +64,25 @@ public: QQmlApplicationEngine* appEngine(); /// Get code model CodeModel* codeModel() { return m_codeModel.get(); } + /// Get client model + ClientModel* clientModel() { return m_clientModel.get(); } /// Display an alert message. void displayMessageDialog(QString _title, QString _message); - /// Load project settings - void loadProject(); + signals: - void projectLoaded(QString const& _json); + /// Triggered once components have been loaded + void appLoaded(); private: QQmlApplicationEngine* m_applicationEngine; //owned by app std::unique_ptr m_webThree; std::unique_ptr m_codeModel; + std::unique_ptr m_clientModel; std::unique_ptr m_fileIo; public slots: /// Delete the current instance when application quit. void quitApplication() {} - /// Write json to a settings file - void saveProject(QString const& _json); }; } diff --git a/mix/AssemblyDebuggerControl.cpp b/mix/AssemblyDebuggerControl.cpp index 136353dc6..b503757d0 100644 --- a/mix/AssemblyDebuggerControl.cpp +++ b/mix/AssemblyDebuggerControl.cpp @@ -17,53 +17,19 @@ * display opcode debugging. */ -#include #include #include #include -#include -#include -#include -#include "AssemblyDebuggerModel.h" -#include "AssemblyDebuggerControl.h" #include "AppContext.h" -#include "DebuggingStateWrapper.h" -#include "QContractDefinition.h" -#include "QVariableDeclaration.h" -#include "ContractCallDataEncoder.h" -#include "CodeModel.h" +#include "ClientModel.h" +#include "AssemblyDebuggerControl.h" -using namespace dev::eth; using namespace dev::mix; -/// @todo Move this to QML -dev::u256 fromQString(QString const& _s) -{ - return dev::jsToU256(_s.toStdString()); -} - -/// @todo Move this to QML -QString toQString(dev::u256 _value) -{ - std::ostringstream s; - s << _value; - return QString::fromStdString(s.str()); -} - AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context): - Extension(_context, ExtensionDisplayBehavior::ModalDialog), m_running(false) + Extension(_context, ExtensionDisplayBehavior::ModalDialog) { - qRegisterMetaType("QVariableDefinition*"); - qRegisterMetaType("QVariableDefinitionList*"); - qRegisterMetaType>("QList"); - qRegisterMetaType>("QList"); - qRegisterMetaType("QVariableDeclaration*"); - qRegisterMetaType("AssemblyDebuggerData"); - - connect(this, &AssemblyDebuggerControl::dataAvailable, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection); - m_modelDebugger = std::unique_ptr(new AssemblyDebuggerModel); - - _context->appEngine()->rootContext()->setContextProperty("debugModel", this); + connect(_context->clientModel(), &ClientModel::showDebuggerWindow, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection); } QString AssemblyDebuggerControl::contentUrl() const @@ -80,132 +46,7 @@ void AssemblyDebuggerControl::start() const { } -void AssemblyDebuggerControl::debugDeployment() -{ - executeSequence(std::vector(), 0); -} - -void AssemblyDebuggerControl::debugState(QVariantMap _state) -{ - u256 balance = fromQString(_state.value("balance").toString()); - QVariantList transactions = _state.value("transactions").toList(); - - std::vector transactionSequence; - - for (auto const& t: transactions) - { - QVariantMap transaction = t.toMap(); - - QString functionId = transaction.value("functionId").toString(); - u256 value = fromQString(transaction.value("value").toString()); - u256 gas = fromQString(transaction.value("gas").toString()); - u256 gasPrice = fromQString(transaction.value("gasPrice").toString()); - QVariantMap params = transaction.value("parameters").toMap(); - TransactionSettings transactionSettings(functionId, value, gas, gasPrice); - - for (auto p = params.cbegin(); p != params.cend(); ++p) - transactionSettings.parameterValues.insert(std::make_pair(p.key(), fromQString(p.value().toString()))); - - transactionSequence.push_back(transactionSettings); - } - executeSequence(transactionSequence, balance); -} - -void AssemblyDebuggerControl::executeSequence(std::vector const& _sequence, u256 _balance) -{ - if (m_running) - throw (std::logic_error("debugging already running")); - auto compilerRes = m_ctx->codeModel()->code(); - std::shared_ptr contractDef = compilerRes->sharedContract(); - m_running = true; - - emit runStarted(); - emit stateChanged(); - - //run sequence - QtConcurrent::run([=]() - { - try - { - bytes contractCode = compilerRes->bytes(); - std::vector transactonData; - QFunctionDefinition* f; - ContractCallDataEncoder c; - //encode data for all transactions - for (auto const& t: _sequence) - { - f = nullptr; - for (int tf = 0; tf < contractDef->functionsList().size(); tf++) - { - if (contractDef->functionsList().at(tf)->name() == t.functionId) - { - f = contractDef->functionsList().at(tf); - break; - } - } - if (!f) - throw std::runtime_error("function " + t.functionId.toStdString() + " not found"); - - c.encode(f->index()); - for (int p = 0; p < f->parametersList().size(); p++) - { - QVariableDeclaration* var = (QVariableDeclaration*)f->parametersList().at(p); - u256 value = 0; - auto v = t.parameterValues.find(var->name()); - if (v != t.parameterValues.cend()) - value = v->second; - c.encode(var, value); - } - transactonData.emplace_back(c.encodedData()); - } - - //run contract creation first - m_modelDebugger->resetState(_balance); - DebuggingContent debuggingContent = m_modelDebugger->deployContract(contractCode); - Address address = debuggingContent.contractAddress; - for (unsigned i = 0; i < _sequence.size(); ++i) - debuggingContent = m_modelDebugger->callContract(address, transactonData.at(i), _sequence.at(i)); - - if (f) - debuggingContent.returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue); - - //we need to wrap states in a QObject before sending to QML. - QList wStates; - for (int i = 0; i < debuggingContent.machineStates.size(); i++) - { - QPointer s(new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes())); - s->setState(debuggingContent.machineStates.at(i)); - wStates.append(s); - } - //collect states for last transaction - AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode); - emit dataAvailable(debuggingContent.returnParameters, wStates, code); - emit runComplete(); - } - catch(boost::exception const&) - { - emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); - } - - catch(std::exception const& e) - { - emit runFailed(e.what()); - } - m_running = false; - emit stateChanged(); - }); -} - -void AssemblyDebuggerControl::showDebugger(QList const& _returnParam, QList const& _wStates, AssemblyDebuggerData const& _code) +void AssemblyDebuggerControl::showDebugger() { - m_appEngine->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates)); - m_appEngine->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code))); - m_appEngine->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code))); - m_appEngine->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam))); this->addContentOn(this); } - -void AssemblyDebuggerControl::showDebugError(QString const& _error) -{ - m_ctx->displayMessageDialog(QApplication::tr("Debugger"), _error); -} diff --git a/mix/AssemblyDebuggerControl.h b/mix/AssemblyDebuggerControl.h index b4dff38f5..dbe9b0676 100644 --- a/mix/AssemblyDebuggerControl.h +++ b/mix/AssemblyDebuggerControl.h @@ -20,14 +20,7 @@ #pragma once #include -#include #include "Extension.h" -#include "AssemblyDebuggerModel.h" - -using AssemblyDebuggerData = std::tuple, dev::mix::QQMLMap*>; - -Q_DECLARE_METATYPE(AssemblyDebuggerData) -Q_DECLARE_METATYPE(dev::mix::DebuggingContent) class AppContext; @@ -37,7 +30,7 @@ namespace mix { /** - * @brief Extension which display transaction creation or transaction call debugging. handle: F5 to deploy contract, F6 to reset state. + * @brief Extension which display transaction creation or transaction call debugging. */ class AssemblyDebuggerControl: public Extension { @@ -50,40 +43,9 @@ public: QString title() const override; QString contentUrl() const override; - Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged) - -private: - void executeSequence(std::vector const& _sequence, u256 _balance); - - std::unique_ptr m_modelDebugger; - std::atomic m_running; - -public slots: - /// Run the contract constructor and show debugger window. - void debugDeployment(); - /// Setup state, run transaction sequence, show debugger for the last transaction - /// @param _state JS object with state configuration - void debugState(QVariantMap _state); - private slots: /// Update UI with machine states result. Display a modal dialog. - void showDebugger(QList const& _returnParams = QList(), QList const& _wStates = QList(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); - /// Update UI with transaction run error. - void showDebugError(QString const& _error); - -signals: - /// Transaction execution started - void runStarted(); - /// Transaction execution completed successfully - void runComplete(); - /// Transaction execution completed with error - /// @param _message Error message - void runFailed(QString const& _message); - /// Execution state changed - void stateChanged(); - - /// Emited when machine states are available. - void dataAvailable(QList const& _returnParams = QList(), QList const& _wStates = QList(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); + void showDebugger(); }; } diff --git a/mix/AssemblyDebuggerModel.cpp b/mix/AssemblyDebuggerModel.cpp deleted file mode 100644 index e822d0a3f..000000000 --- a/mix/AssemblyDebuggerModel.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - 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 . -*/ -/** @file AssemblyDebuggerModel.cpp - * @author Yann yann@ethdev.com - * @date 2014 - * used as a model to debug contract assembly code. - */ - -#include -#include -#include -#include -#include -#include -#include "AssemblyDebuggerModel.h" -#include "AppContext.h" -#include "DebuggingStateWrapper.h" - -using namespace std; -using namespace dev; -using namespace dev::eth; - -namespace dev -{ -namespace mix -{ - -AssemblyDebuggerModel::AssemblyDebuggerModel(): - m_userAccount(KeyPair::create()) -{ - resetState(10000000 * ether); -} - -DebuggingContent AssemblyDebuggerModel::executeTransaction(bytesConstRef const& _rawTransaction) -{ - QList machineStates; - eth::Executive execution(m_executiveState, LastHashes(), 0); - execution.setup(_rawTransaction); - std::vector levels; - bytes code; - bytesConstRef data; - bool firstIteration = true; - auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) - { - VM& vm = *(VM*)voidVM; - ExtVM const& ext = *(ExtVM const*)voidExt; - - if (firstIteration) - { - code = ext.code; - data = ext.data; - firstIteration = false; - } - - if (levels.size() < ext.depth) - levels.push_back(&machineStates.back()); - else - levels.resize(ext.depth); - - machineStates.append(DebuggingState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), - vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); - }; - - execution.go(onOp); - execution.finalize(); - - DebuggingContent d; - d.returnValue = execution.out().toVector(); - d.machineStates = machineStates; - d.executionCode = code; - d.executionData = data; - d.contentAvailable = true; - d.message = "ok"; - return d; -} - -DebuggingContent AssemblyDebuggerModel::deployContract(bytes const& _code) -{ - u256 gasPrice = 10000000000000; - u256 gas = 1000000; - u256 amount = 100; - Transaction _tr(amount, gasPrice, min(gas, m_executiveState.gasLimitRemaining()), _code, m_executiveState.transactionsFrom(dev::toAddress(m_userAccount.secret())), m_userAccount.secret()); - bytes b = _tr.rlp(); - dev::bytesConstRef bytesRef = &b; - DebuggingContent d = executeTransaction(bytesRef); - h256 th = sha3(rlpList(_tr.sender(), _tr.nonce())); - d.contractAddress = right160(th); - return d; -} - -DebuggingContent AssemblyDebuggerModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) -{ - Transaction tr = Transaction(_tr.value, _tr.gasPrice, min(_tr.gas, m_executiveState.gasLimitRemaining()), _contract, _data, m_executiveState.transactionsFrom(dev::toAddress(m_userAccount.secret())), m_userAccount.secret()); - bytes b = tr.rlp(); - dev::bytesConstRef bytesRef = &b; - DebuggingContent d = executeTransaction(bytesRef); - d.contractAddress = tr.receiveAddress(); - return d; -} - -void AssemblyDebuggerModel::resetState(u256 _balance) -{ - m_executiveState = eth::State(Address(), m_overlayDB, BaseState::Empty); - m_executiveState.addBalance(m_userAccount.address(), _balance); -} - -} -} diff --git a/mix/AssemblyDebuggerModel.h b/mix/AssemblyDebuggerModel.h deleted file mode 100644 index c12e711c6..000000000 --- a/mix/AssemblyDebuggerModel.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - 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 . -*/ -/** @file AssemblyDebuggerModel.h - * @author Yann yann@ethdev.com - * @date 2014 - * Used as a model to debug contract assembly code. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include "DebuggingStateWrapper.h" - -namespace dev -{ -namespace mix -{ - -/// Backend transaction config class -struct TransactionSettings -{ - TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice): - functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {} - - /// Contract function name - QString functionId; - /// Transaction value - u256 value; - /// Gas - u256 gas; - /// Gas price - u256 gasPrice; - /// Mapping from contract function parameter name to value - std::map parameterValues; -}; - - -/** - * @brief Long-life object for managing all executions. - */ -class AssemblyDebuggerModel -{ -public: - AssemblyDebuggerModel(); - /// Call function in a already deployed contract. - DebuggingContent callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); - /// Deploy the contract described by _code. - DebuggingContent deployContract(bytes const& _code); - /// Reset state to the empty state with given balance. - void resetState(u256 _balance); - -private: - KeyPair m_userAccount; - OverlayDB m_overlayDB; - eth::State m_executiveState; - DebuggingContent executeTransaction(dev::bytesConstRef const& _rawTransaction); -}; - -} -} diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp new file mode 100644 index 000000000..6859b0279 --- /dev/null +++ b/mix/ClientModel.cpp @@ -0,0 +1,222 @@ +/* + 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 . +*/ +/** @file ClientModel.cpp + * @author Yann yann@ethdev.com + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include +#include +#include "ClientModel.h" +#include "AppContext.h" +#include "DebuggingStateWrapper.h" +#include "QContractDefinition.h" +#include "QVariableDeclaration.h" +#include "ContractCallDataEncoder.h" +#include "CodeModel.h" +#include "ClientModel.h" + +using namespace dev::eth; +using namespace dev::mix; + +/// @todo Move this to QML +dev::u256 fromQString(QString const& _s) +{ + return dev::jsToU256(_s.toStdString()); +} + +/// @todo Move this to QML +QString toQString(dev::u256 _value) +{ + std::ostringstream s; + s << _value; + return QString::fromStdString(s.str()); +} + +ClientModel::ClientModel(AppContext* _context): + m_context(_context), m_running(false) +{ + qRegisterMetaType("QVariableDefinition*"); + qRegisterMetaType("QVariableDefinitionList*"); + qRegisterMetaType>("QList"); + qRegisterMetaType>("QList"); + qRegisterMetaType("QVariableDeclaration*"); + qRegisterMetaType("AssemblyDebuggerData"); + + connect(this, &ClientModel::dataAvailable, this, &ClientModel::showDebugger, Qt::QueuedConnection); + m_client.reset(new MixClient()); + + _context->appEngine()->rootContext()->setContextProperty("clientModel", this); +} + +void ClientModel::debugDeployment() +{ + executeSequence(std::vector(), 10000000 * ether); +} + +void ClientModel::debugState(QVariantMap _state) +{ + u256 balance = fromQString(_state.value("balance").toString()); + QVariantList transactions = _state.value("transactions").toList(); + + std::vector transactionSequence; + + for (auto const& t: transactions) + { + QVariantMap transaction = t.toMap(); + + QString functionId = transaction.value("functionId").toString(); + u256 value = fromQString(transaction.value("value").toString()); + u256 gas = fromQString(transaction.value("gas").toString()); + u256 gasPrice = fromQString(transaction.value("gasPrice").toString()); + QVariantMap params = transaction.value("parameters").toMap(); + TransactionSettings transactionSettings(functionId, value, gas, gasPrice); + + for (auto p = params.cbegin(); p != params.cend(); ++p) + transactionSettings.parameterValues.insert(std::make_pair(p.key(), fromQString(p.value().toString()))); + + transactionSequence.push_back(transactionSettings); + } + executeSequence(transactionSequence, balance); +} + +void ClientModel::executeSequence(std::vector const& _sequence, u256 _balance) +{ + if (m_running) + throw (std::logic_error("debugging already running")); + auto compilerRes = m_context->codeModel()->code(); + std::shared_ptr contractDef = compilerRes->sharedContract(); + m_running = true; + + emit runStarted(); + emit stateChanged(); + + //run sequence + QtConcurrent::run([=]() + { + try + { + bytes contractCode = compilerRes->bytes(); + std::vector transactonData; + QFunctionDefinition* f; + ContractCallDataEncoder c; + //encode data for all transactions + for (auto const& t: _sequence) + { + f = nullptr; + for (int tf = 0; tf < contractDef->functionsList().size(); tf++) + { + if (contractDef->functionsList().at(tf)->name() == t.functionId) + { + f = contractDef->functionsList().at(tf); + break; + } + } + if (!f) + throw std::runtime_error("function " + t.functionId.toStdString() + " not found"); + + c.encode(f); + for (int p = 0; p < f->parametersList().size(); p++) + { + QVariableDeclaration* var = (QVariableDeclaration*)f->parametersList().at(p); + u256 value = 0; + auto v = t.parameterValues.find(var->name()); + if (v != t.parameterValues.cend()) + value = v->second; + c.encode(var, value); + } + transactonData.emplace_back(c.encodedData()); + } + + //run contract creation first + m_client->resetState(_balance); + ExecutionResult debuggingContent = deployContract(contractCode); + Address address = debuggingContent.contractAddress; + for (unsigned i = 0; i < _sequence.size(); ++i) + debuggingContent = callContract(address, transactonData.at(i), _sequence.at(i)); + + QList returnParameters; + + if (f) + returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue); + + //we need to wrap states in a QObject before sending to QML. + QList wStates; + for (unsigned i = 0; i < debuggingContent.machineStates.size(); i++) + { + QPointer s(new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes())); + s->setState(debuggingContent.machineStates[i]); + wStates.append(s); + } + //collect states for last transaction + AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode); + emit dataAvailable(returnParameters, wStates, code); + emit runComplete(); + } + catch(boost::exception const&) + { + emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); + } + + catch(std::exception const& e) + { + emit runFailed(e.what()); + } + m_running = false; + emit stateChanged(); + }); +} + +void ClientModel::showDebugger(QList const& _returnParam, QList const& _wStates, AssemblyDebuggerData const& _code) +{ + m_context->appEngine()->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates)); + m_context->appEngine()->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code))); + m_context->appEngine()->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code))); + m_context->appEngine()->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam))); + showDebuggerWindow(); +} + +void ClientModel::showDebugError(QString const& _error) +{ + //TODO: change that to a signal + m_context->displayMessageDialog(tr("Debugger"), _error); +} + +ExecutionResult ClientModel::deployContract(bytes const& _code) +{ + u256 gasPrice = 10000000000000; + u256 gas = 125000; + u256 amount = 100; + + Address contractAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice); + ExecutionResult r = m_client->lastExecutionResult(); + r.contractAddress = contractAddress; + return r; +} + +ExecutionResult ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) +{ + //bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; + m_client->transact(m_client->userAccount().secret(), _tr.value, _contract, _data, _tr.gas, _tr.gasPrice); + ExecutionResult r = m_client->lastExecutionResult(); + r.contractAddress = _contract; + return r; +} + diff --git a/mix/ClientModel.h b/mix/ClientModel.h new file mode 100644 index 000000000..509a5995b --- /dev/null +++ b/mix/ClientModel.h @@ -0,0 +1,113 @@ +/* + 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 . +*/ +/** @file ClientModel.h + * @author Yann yann@ethdev.com + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include +#include "DebuggingStateWrapper.h" +#include "MixClient.h" + +using AssemblyDebuggerData = std::tuple, dev::mix::QQMLMap*>; + +Q_DECLARE_METATYPE(AssemblyDebuggerData) +Q_DECLARE_METATYPE(dev::mix::ExecutionResult) + +namespace dev +{ +namespace mix +{ + +class AppContext; + +/// Backend transaction config class +struct TransactionSettings +{ + TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice): + functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {} + + /// Contract function name + QString functionId; + /// Transaction value + u256 value; + /// Gas + u256 gas; + /// Gas price + u256 gasPrice; + /// Mapping from contract function parameter name to value + std::map parameterValues; +}; + + +/** + * @brief Ethereum state control + */ +class ClientModel: public QObject +{ + Q_OBJECT + +public: + ClientModel(AppContext* _context); + + Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged) + +public slots: + /// Run the contract constructor and show debugger window. + void debugDeployment(); + /// Setup state, run transaction sequence, show debugger for the last transaction + /// @param _state JS object with state configuration + void debugState(QVariantMap _state); + +private slots: + /// Update UI with machine states result. Display a modal dialog. + void showDebugger(QList const& _returnParams = QList(), QList const& _wStates = QList(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); + /// Update UI with transaction run error. + void showDebugError(QString const& _error); + +signals: + /// Transaction execution started + void runStarted(); + /// Transaction execution completed successfully + void runComplete(); + /// Transaction execution completed with error + /// @param _message Error message + void runFailed(QString const& _message); + /// Execution state changed + void stateChanged(); + /// Show debugger window request + void showDebuggerWindow(); + + /// Emited when machine states are available. + void dataAvailable(QList const& _returnParams = QList(), QList const& _wStates = QList(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); + +private: + void executeSequence(std::vector const& _sequence, u256 _balance); + ExecutionResult deployContract(bytes const& _code); + ExecutionResult callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); + + AppContext* m_context; + std::atomic m_running; + std::unique_ptr m_client; +}; + +} +} diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index 14795c223..f5ceb333e 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -31,6 +31,7 @@ #include "AppContext.h" #include "MixApplication.h" #include "CodeModel.h" +#include "ClientModel.h" #include "CodeHighlighter.h" #include "CodeEditorExtensionManager.h" @@ -58,7 +59,7 @@ void CodeEditorExtensionManager::initExtensions() std::shared_ptr output = std::make_shared(m_appContext); std::shared_ptr debug = std::make_shared(m_appContext); std::shared_ptr stateList = std::make_shared(m_appContext); - QObject::connect(debug.get(), &AssemblyDebuggerControl::runFailed, output.get(), &ConstantCompilationControl::displayError); + QObject::connect(m_appContext->clientModel(), &ClientModel::runFailed, output.get(), &ConstantCompilationControl::displayError); QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); initExtension(output); diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 7d8b0442a..87152360e 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -162,7 +162,7 @@ void CodeModel::onCompilationComplete(CompilationResult*_newResult) m_result.reset(_newResult); emit compilationComplete(); emit stateChanged(); - if (m_result->successfull()) + if (m_result->successful()) emit codeChanged(); } diff --git a/mix/CodeModel.h b/mix/CodeModel.h index c66703b22..e1a2a0fbd 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -69,7 +69,7 @@ class CompilationResult: public QObject public: /// Empty compilation result constructor CompilationResult(); - /// Successfull compilation result constructor + /// Successful compilation result constructor CompilationResult(solidity::CompilerStack const& _compiler); /// Failed compilation result constructor CompilationResult(CompilationResult const& _prev, QString const& _compilerMessage); @@ -78,9 +78,9 @@ public: QContractDefinition* contract() { return m_contract.get(); } /// @returns contract definition std::shared_ptr sharedContract() { return m_contract; } - /// Indicates if the compilation was successfull - bool successfull() const { return m_successful; } - /// @returns compiler error message in case of unsuccessfull compilation + /// Indicates if the compilation was successful + bool successful() const { return m_successful; } + /// @returns compiler error message in case of unsuccessful compilation QString compilerMessage() const { return m_compilerMessage; } /// @returns contract bytecode dev::bytes const& bytes() const { return m_bytes; } diff --git a/mix/ConstantCompilationControl.cpp b/mix/ConstantCompilationControl.cpp index c9f21c11b..6352f070d 100644 --- a/mix/ConstantCompilationControl.cpp +++ b/mix/ConstantCompilationControl.cpp @@ -61,7 +61,7 @@ void ConstantCompilationControl::update() QObject* status = m_view->findChild("status", Qt::FindChildrenRecursively); QObject* content = m_view->findChild("content", Qt::FindChildrenRecursively); - if (result->successfull()) + if (result->successful()) { status->setProperty("text", "succeeded"); status->setProperty("color", "green"); diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 757f7243c..f2cd5d587 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -27,6 +27,7 @@ #include #include "QVariableDeclaration.h" #include "QVariableDefinition.h" +#include "QFunctionDefinition.h" #include "ContractCallDataEncoder.h" using namespace dev; using namespace dev::solidity; @@ -37,9 +38,9 @@ bytes ContractCallDataEncoder::encodedData() return m_encodedData; } -void ContractCallDataEncoder::encode(int _functionIndex) +void ContractCallDataEncoder::encode(QFunctionDefinition const* _function) { - bytes i = jsToBytes(std::to_string(_functionIndex)); + bytes i = _function->hash().asBytes(); m_encodedData.insert(m_encodedData.end(), i.begin(), i.end()); } diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index fd67a7b7a..49410a7cd 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -30,6 +30,10 @@ namespace dev namespace mix { +class QFunctionDefinition; +class QVariableDeclaration; +class QVariableDefinition; + /** * @brief Encode/Decode data to be sent to a transaction or to be displayed in a view. */ @@ -43,8 +47,8 @@ public: void encode(QVariableDeclaration const* _dec, u256 _value); /// Encode variable in order to be sent as parameter. void encode(QVariableDeclaration const* _dec, bool _value); - /// Encode index of the function to call. - void encode(int _functionIndex); + /// Encode hash of the function to call. + void encode(QFunctionDefinition const* _function); /// Decode variable in order to be sent to QML view. QList decode(QList _dec, bytes _value); /// Get all encoded data encoded by encode function. diff --git a/mix/DebuggingStateWrapper.h b/mix/DebuggingStateWrapper.h index bf3efe34d..6d1ce4220 100644 --- a/mix/DebuggingStateWrapper.h +++ b/mix/DebuggingStateWrapper.h @@ -28,45 +28,13 @@ #include #include #include "QVariableDefinition.h" +#include "MixClient.h" namespace dev { namespace mix { -/** - * @brief Store information about a machine state. - */ -struct DebuggingState -{ - uint64_t steps; - dev::Address cur; - dev::u256 curPC; - dev::eth::Instruction inst; - dev::bigint newMemSize; - dev::u256 gas; - dev::u256s stack; - dev::bytes memory; - dev::bigint gasCost; - std::map storage; - std::vector levels; -}; - -/** - * @brief Store information about a machine states. - */ -struct DebuggingContent -{ - QList machineStates; - bytes executionCode; - bytesConstRef executionData; - Address contractAddress; - bool contentAvailable; - QString message; - bytes returnValue; - QList returnParameters; -}; - /** * @brief Contains the line nb of the assembly code and the corresponding index in the code bytes array. */ @@ -151,14 +119,14 @@ public: /// Get all previous steps. QStringList levels(); /// Get the current processed machine state. - DebuggingState state() { return m_state; } + MachineState state() { return m_state; } /// Set the current processed machine state. - void setState(DebuggingState _state) { m_state = _state; } + void setState(MachineState _state) { m_state = _state; } /// Convert all machine state in human readable code. static std::tuple, QQMLMap*> getHumanReadableCode(bytes const& _code); private: - DebuggingState m_state; + MachineState m_state; bytes m_code; bytes m_data; }; diff --git a/mix/FileIo.cpp b/mix/FileIo.cpp index 4ecbc9c08..a91dfc167 100644 --- a/mix/FileIo.cpp +++ b/mix/FileIo.cpp @@ -73,3 +73,8 @@ void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl) if (!QFile::copy(sourceUrl.path(), destUrl.path())) error(tr("Error copying file %1 to %2").arg(_sourceUrl).arg(_destUrl)); } + +QString FileIo::getHomePath() const +{ + return QDir::homePath(); +} diff --git a/mix/FileIo.h b/mix/FileIo.h index 83352476b..3c251cfbc 100644 --- a/mix/FileIo.h +++ b/mix/FileIo.h @@ -33,6 +33,7 @@ namespace mix class FileIo : public QObject { Q_OBJECT + Q_PROPERTY(QString homePath READ getHomePath CONSTANT); signals: /// Signalled in case of IO error @@ -47,6 +48,9 @@ public: Q_INVOKABLE void writeFile(QString const& _url, QString const& _data); /// Copy a file from _sourcePath to _destPath. Signals on failure. Q_INVOKABLE void copyFile(QString const& _sourceUrl, QString const& _destUrl); + +private: + QString getHomePath() const; }; } diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index 1a55b1e47..2cdc4b30d 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -33,10 +33,9 @@ MixApplication::MixApplication(int _argc, char* _argv[]): QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()), m_appContext(new AppContext(m_engine.get())) { qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); - //QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff + QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff m_engine->load(QUrl("qrc:/qml/main.qml")); - //m_engine->load(QUrl("qrc:/qml/ProjectModel.qml")); - m_appContext->loadProject(); + m_appContext->appLoaded(); } MixApplication::~MixApplication() diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp new file mode 100644 index 000000000..5d635ce1f --- /dev/null +++ b/mix/MixClient.cpp @@ -0,0 +1,336 @@ +/* + 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 . +*/ +/** @file MixClient.cpp + * @author Yann yann@ethdev.com + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "MixClient.h" + +using namespace dev; +using namespace dev::eth; +using namespace dev::mix; + +MixClient::MixClient(): + m_userAccount(KeyPair::create()) +{ + resetState(10000000 * ether); +} + +void MixClient::resetState(u256 _balance) +{ + WriteGuard l(x_state); + m_state = eth::State(Address(), m_stateDB, BaseState::Empty); + m_state.addBalance(m_userAccount.address(), _balance); +} + +void MixClient::executeTransaction(bytesConstRef _rlp, State& _state) +{ + Executive execution(_state, LastHashes(), 0); + execution.setup(_rlp); + bytes code; + bytesConstRef data; + bool firstIteration = true; + std::vector machineStates; + std::vector levels; + auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) + { + VM& vm = *(VM*)voidVM; + ExtVM const& ext = *(ExtVM const*)voidExt; + + if (firstIteration) + { + code = ext.code; + data = ext.data; + firstIteration = false; + } + + if (levels.size() < ext.depth) + levels.push_back(&machineStates.back()); + else + levels.resize(ext.depth); + + machineStates.push_back(MachineState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), + vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); + }; + + execution.go(onOp); + execution.finalize(); + + ExecutionResult d; + d.returnValue = execution.out().toVector(); + d.machineStates = machineStates; + d.executionCode = code; + d.executionData = data; + d.contentAvailable = true; + d.message = "ok"; + m_lastExecutionResult = d; +} + +void MixClient::validateBlock(int _block) const +{ + //TODO: throw exception here if _block != 0 + (void)_block; +} + +void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +{ + WriteGuard l(x_state); + u256 n = m_state.transactionsFrom(toAddress(_secret)); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + bytes rlp = t.rlp(); + executeTransaction(&rlp, m_state); +} + +Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) +{ + WriteGuard l(x_state); + u256 n = m_state.transactionsFrom(toAddress(_secret)); + eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); + bytes rlp = t.rlp(); + executeTransaction(&rlp, m_state); + return right160(sha3(rlpList(t.sender(), t.nonce()))); +} + +void MixClient::inject(bytesConstRef _rlp) +{ + WriteGuard l(x_state); + executeTransaction(_rlp, m_state); +} + +void MixClient::flushTransactions() +{ +} + +bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +{ + bytes out; + u256 n; + State temp; + { + ReadGuard lr(x_state); + temp = m_state; + n = temp.transactionsFrom(toAddress(_secret)); + } + Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + bytes rlp = t.rlp(); + WriteGuard lw(x_state); //TODO: lock is required only for last executoin state + executeTransaction(&rlp, temp); + return m_lastExecutionResult.returnValue; +} + +u256 MixClient::balanceAt(Address _a, int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + return m_state.balance(_a); +} + +u256 MixClient::countAt(Address _a, int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + return m_state.transactionsFrom(_a); +} + +u256 MixClient::stateAt(Address _a, u256 _l, int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + return m_state.storage(_a, _l); +} + +bytes MixClient::codeAt(Address _a, int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + return m_state.code(_a); +} + +std::map MixClient::storageAt(Address _a, int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + return m_state.storage(_a); +} + +eth::LogEntries MixClient::logs(unsigned _watchId) const +{ + (void)_watchId; + return LogEntries(); +} + +eth::LogEntries MixClient::logs(eth::LogFilter const& _filter) const +{ + (void)_filter; + return LogEntries(); +} + +unsigned MixClient::installWatch(eth::LogFilter const& _filter) +{ + (void)_filter; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::installWatch")); +} + +unsigned MixClient::installWatch(h256 _filterId) +{ + (void)_filterId; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::installWatch")); +} + +void MixClient::uninstallWatch(unsigned _watchId) +{ + (void)_watchId; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uninstallWatch")); +} + +bool MixClient::peekWatch(unsigned _watchId) const +{ + (void)_watchId; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::peekWatch")); +} + +bool MixClient::checkWatch(unsigned _watchId) +{ + (void)_watchId; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::checkWatch")); +} + +h256 MixClient::hashFromNumber(unsigned _number) const +{ + (void)_number; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::hashFromNumber")); +} + +eth::BlockInfo MixClient::blockInfo(h256 _hash) const +{ + (void)_hash; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockInfo")); +} + +eth::BlockDetails MixClient::blockDetails(h256 _hash) const +{ + (void)_hash; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockDetails")); +} + +eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const +{ + (void)_blockHash; + (void)_i; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::transaction")); +} + +eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const +{ + (void)_blockHash; + (void)_i; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uncle")); +} + +unsigned MixClient::number() const +{ + return 0; +} + +eth::Transactions MixClient::pending() const +{ + return eth::Transactions(); +} + +eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const +{ + (void)_txi; + (void)_block; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff")); +} + +eth::StateDiff MixClient::diff(unsigned _txi, int _block) const +{ + (void)_txi; + (void)_block; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff")); +} + +Addresses MixClient::addresses(int _block) const +{ + validateBlock(_block); + ReadGuard l(x_state); + Addresses ret; + for (auto const& i: m_state.addresses()) + ret.push_back(i.first); + return ret; +} + +u256 MixClient::gasLimitRemaining() const +{ + ReadGuard l(x_state); + return m_state.gasLimitRemaining(); +} + +void MixClient::setAddress(Address _us) +{ + WriteGuard l(x_state); + m_state.setAddress(_us); +} + +Address MixClient::address() const +{ + ReadGuard l(x_state); + return m_state.address(); +} + +void MixClient::setMiningThreads(unsigned _threads) +{ + (void)_threads; + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::setMiningThreads")); +} + +unsigned MixClient::miningThreads() const +{ + return 0; +} + +void MixClient::startMining() +{ + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::startMining")); +} + +void MixClient::stopMining() +{ + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::stopMining")); +} + +bool MixClient::isMining() +{ + return false; +} + +eth::MineProgress MixClient::miningProgress() const +{ + return eth::MineProgress(); +} diff --git a/mix/MixClient.h b/mix/MixClient.h new file mode 100644 index 000000000..4cbaad6e8 --- /dev/null +++ b/mix/MixClient.h @@ -0,0 +1,125 @@ +/* + 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 . +*/ +/** @file MixClient.h + * @author Yann yann@ethdev.com + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include + +namespace dev +{ +namespace mix +{ + +/** + * @brief Store information about a machine state. + */ +struct MachineState +{ + uint64_t steps; + dev::Address cur; + dev::u256 curPC; + dev::eth::Instruction inst; + dev::bigint newMemSize; + dev::u256 gas; + dev::u256s stack; + dev::bytes memory; + dev::bigint gasCost; + std::map storage; + std::vector levels; +}; + +/** + * @brief Store information about a machine states. + */ +struct ExecutionResult +{ + std::vector machineStates; + bytes executionCode; + bytesConstRef executionData; + Address contractAddress; + bool contentAvailable; + std::string message; + bytes returnValue; +}; + +class MixClient: public dev::eth::Interface +{ +public: + MixClient(); + /// Reset state to the empty state with given balance. + void resetState(u256 _balance); + const KeyPair& userAccount() const { return m_userAccount; } + const ExecutionResult lastExecutionResult() const { ReadGuard l(x_state); return m_lastExecutionResult; } + + //dev::eth::Interface + void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; + Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override; + void inject(bytesConstRef _rlp) override; + void flushTransactions() override; + bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; + u256 balanceAt(Address _a, int _block) const override; + u256 countAt(Address _a, int _block) const override; + u256 stateAt(Address _a, u256 _l, int _block) const override; + bytes codeAt(Address _a, int _block) const override; + std::map storageAt(Address _a, int _block) const override; + eth::LogEntries logs(unsigned _watchId) const override; + eth::LogEntries logs(eth::LogFilter const& _filter) const override; + unsigned installWatch(eth::LogFilter const& _filter) override; + unsigned installWatch(h256 _filterId) override; + void uninstallWatch(unsigned _watchId) override; + bool peekWatch(unsigned _watchId) const override; + bool checkWatch(unsigned _watchId) override; + h256 hashFromNumber(unsigned _number) const override; + eth::BlockInfo blockInfo(h256 _hash) const override; + eth::BlockDetails blockDetails(h256 _hash) const override; + eth::Transaction transaction(h256 _blockHash, unsigned _i) const override; + eth::BlockInfo uncle(h256 _blockHash, unsigned _i) const override; + unsigned number() const override; + eth::Transactions pending() const override; + eth::StateDiff diff(unsigned _txi, h256 _block) const override; + eth::StateDiff diff(unsigned _txi, int _block) const override; + Addresses addresses(int _block) const override; + u256 gasLimitRemaining() const override; + void setAddress(Address _us) override; + Address address() const override; + void setMiningThreads(unsigned _threads) override; + unsigned miningThreads() const override; + void startMining() override; + void stopMining() override; + bool isMining() override; + eth::MineProgress miningProgress() const override; + +private: + void executeTransaction(bytesConstRef _rlp, eth::State& _state); + void validateBlock(int _block) const; + + KeyPair m_userAccount; + eth::State m_state; + OverlayDB m_stateDB; + mutable boost::shared_mutex x_state; + ExecutionResult m_lastExecutionResult; +}; + +} + +} diff --git a/mix/QFunctionDefinition.cpp b/mix/QFunctionDefinition.cpp index 7149809af..97ce0ff58 100644 --- a/mix/QFunctionDefinition.cpp +++ b/mix/QFunctionDefinition.cpp @@ -20,12 +20,14 @@ */ #include +#include #include "QVariableDeclaration.h" #include "QFunctionDefinition.h" + using namespace dev::solidity; using namespace dev::mix; -QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDefinition const* _f, int _index): QBasicNodeDefinition(_f), m_index(_index) +QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDefinition const* _f, int _index): QBasicNodeDefinition(_f), m_index(_index), m_hash(dev::sha3(_f->getCanonicalSignature())) { std::vector> parameters = _f->getParameterList().getParameters(); for (unsigned i = 0; i < parameters.size(); i++) diff --git a/mix/QFunctionDefinition.h b/mix/QFunctionDefinition.h index b2d0cd0b7..7f606c8a1 100644 --- a/mix/QFunctionDefinition.h +++ b/mix/QFunctionDefinition.h @@ -49,9 +49,12 @@ public: QList returnParameters() const { return m_returnParameters; } /// Get the index of this function on the contract ABI. int index() const { return m_index; } + /// Get the hash of this function declaration on the contract ABI. + FixedHash<4> hash() const { return m_hash; } private: int m_index; + FixedHash<4> m_hash; QList m_parameters; QList m_returnParameters; void initQParameters(); diff --git a/mix/Web3Server.cpp b/mix/Web3Server.cpp new file mode 100644 index 000000000..469ca907d --- /dev/null +++ b/mix/Web3Server.cpp @@ -0,0 +1,55 @@ +/* + 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 . +*/ +/** @file Web3Server.h.cpp + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + + +#include +#include "Web3Server.h" + +using namespace dev::mix; + +Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts, dev::eth::Interface* _client): + WebThreeStubServerBase(_conn, _accounts), + m_client(_client) +{ +} + +std::shared_ptr Web3Server::face() +{ + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::shh::Interface")); +} + +dev::WebThreeNetworkFace* Web3Server::network() +{ + BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::WebThreeNetworkFace")); +} + +std::string Web3Server::get(std::string const& _name, std::string const& _key) +{ + std::string k(_name + "/" + _key); + return m_db[k]; +} + +void Web3Server::put(std::string const& _name, std::string const& _key, std::string const& _value) +{ + std::string k(_name + "/" + _key); + m_db[k] = _value; +} diff --git a/mix/Web3Server.h b/mix/Web3Server.h new file mode 100644 index 000000000..c603b48a2 --- /dev/null +++ b/mix/Web3Server.h @@ -0,0 +1,56 @@ +/* + 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 . +*/ +/** @file Web3Server.h + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include +#include +#include + +namespace dev +{ + +namespace mix +{ + +class Web3Server: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace +{ +public: + Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts, dev::eth::Interface* _client); + +private: + dev::eth::Interface* client() override { return m_client; } + std::shared_ptr face() override; + dev::WebThreeNetworkFace* network() override; + dev::WebThreeStubDatabaseFace* db() override { return this; } + + std::string get(std::string const& _name, std::string const& _key) override; + void put(std::string const& _name, std::string const& _key, std::string const& _value) override; + +private: + dev::eth::Interface* m_client; + std::map m_db; +}; + +} + +} diff --git a/mix/qml.qrc b/mix/qml.qrc index 0e25de765..28b82865f 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -18,5 +18,6 @@ qml/CodeEditor.qml qml/CodeEditorView.qml qml/js/ProjectModel.js + qml/WebPreview.qml diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 45d2cfb2a..4e95bd49c 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -44,7 +44,7 @@ Item { onDocumentOpened: { openDocument(document); } - onProjectSaved: { + onProjectSaving: { for (var i = 0; i < editorListModel.count; i++) fileIo.writeFile(editorListModel.get(i).path, editors.itemAt(i).item.getText()); } diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index 976f70316..cc5ecccd9 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -15,7 +15,7 @@ Item { signal projectClosed signal projectLoaded signal documentOpened(var document) - signal projectSaved + signal projectSaving(var projectData) property bool isEmpty: (projectData === null) readonly property string projectFileName: ".mix" @@ -35,9 +35,12 @@ Item { function addExistingFile() { ProjectModelCode.addExistingFile(); } function openDocument(documentId) { ProjectModelCode.openDocument(documentId); } - Component.onCompleted: { - if (projectSettings.lastProjectPath) - loadProject(projectSettings.lastProjectPath) + Connections { + target: appContext + onAppLoaded: { + if (projectSettings.lastProjectPath) + loadProject(projectSettings.lastProjectPath) + } } NewProjectDialog { diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index 862e6f854..208c766f9 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -122,7 +122,7 @@ Window { var item = { value: "0", functionId: "", - gas: "1000000000000", + gas: "125000", gasPrice: "100000" }; diff --git a/mix/qml/StateList.qml b/mix/qml/StateList.qml index de59fec4a..12f0318d1 100644 --- a/mix/qml/StateList.qml +++ b/mix/qml/StateList.qml @@ -29,6 +29,9 @@ Rectangle { stateList.push(items[i]) } } + onProjectSaving: { + projectData.states = stateList; + } } ListView { @@ -78,7 +81,7 @@ Rectangle { function runState(index) { var item = stateList[index]; - debugModel.debugState(item); + clientModel.debugState(item); } function deleteState(index) { @@ -88,7 +91,6 @@ Rectangle { } function save() { - console.log(parent.id); ProjectModel.saveProject(); } } @@ -131,7 +133,7 @@ Rectangle { id: addStateAction text: "&Add State" shortcut: "Ctrl+T" - enabled: codeModel.hasContract && !debugModel.running; + enabled: codeModel.hasContract && !clientModel.running; onTriggered: stateListModel.addState(); } } diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml new file mode 100644 index 000000000..77e60ee66 --- /dev/null +++ b/mix/qml/WebPreview.qml @@ -0,0 +1,81 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.1 + +Component { + Item { + signal editorTextChanged + + function setText(text) { + codeEditor.text = text; + } + + function getText() { + return codeEditor.text; + } + + anchors.fill: parent + id: contentView + width: parent.width + height: parent.height * 0.7 + Rectangle { + id: lineColumn + property int rowHeight: codeEditor.font.pixelSize + 3 + color: "#202020" + width: 50 + height: parent.height + Column { + y: -codeEditor.flickableItem.contentY + 4 + width: parent.width + Repeater { + model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) + delegate: Text { + id: text + color: codeEditor.textColor + font: codeEditor.font + width: lineColumn.width - 4 + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + height: lineColumn.rowHeight + renderType: Text.NativeRendering + text: index + 1 + } + } + } + } + + TextArea { + id: codeEditor + textColor: "#EEE8D5" + style: TextAreaStyle { + backgroundColor: "#002B36" + } + + anchors.left: lineColumn.right + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + wrapMode: TextEdit.NoWrap + frameVisible: false + + height: parent.height + font.family: "Monospace" + font.pointSize: 12 + width: parent.width + + tabChangesFocus: false + Keys.onPressed: { + if (event.key === Qt.Key_Tab) { + codeEditor.insert(codeEditor.cursorPosition, "\t"); + event.accepted = true; + } + } + onTextChanged: { + editorTextChanged(); + } + + } + } +} diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 7bcfb5cd7..a547457b0 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -22,7 +22,6 @@ function saveAll() { saveProject(); - projectSaved(); } function createProject() { @@ -44,6 +43,7 @@ function closeProject() { function saveProject() { if (!isEmpty) { + projectSaving(projectData); var json = JSON.stringify(projectData); var projectFile = projectPath + projectFileName; fileIo.writeFile(projectFile, json); diff --git a/mix/qml/main.qml b/mix/qml/main.qml index 4bea535c2..f59c5874c 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -63,15 +63,15 @@ ApplicationWindow { id: debugRunAction text: "&Run" shortcut: "F5" - enabled: codeModel.hasContract && !debugModel.running; - onTriggered: debugModel.debugDeployment(); + enabled: codeModel.hasContract && !clientModel.running; + onTriggered: clientModel.debugDeployment(); } Action { id: debugResetStateAction text: "Reset &State" shortcut: "F6" - onTriggered: debugModel.resetState(); + onTriggered: clientModel.resetState(); } Action { From 2ae6a42e25dc3bc52093fe160c1d69c86c0bc9f8 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sun, 11 Jan 2015 19:22:58 +0100 Subject: [PATCH 05/14] Additional commands for project model --- mix/ContractCallDataEncoder.cpp | 2 +- mix/FileIo.cpp | 15 ++++++ mix/FileIo.h | 6 ++- mix/qml/NewProjectDialog.qml | 3 +- mix/qml/ProjectList.qml | 65 ++++++++++++++++++++++-- mix/qml/ProjectModel.qml | 9 +++- mix/qml/js/ProjectModel.js | 88 ++++++++++++++++++++++++++++----- mix/qml/main.qml | 13 +++-- 8 files changed, 176 insertions(+), 25 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index f2cd5d587..0e628c3c0 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -92,7 +92,7 @@ int ContractCallDataEncoder::padding(QString type) else if (type.indexOf("bool") != -1) return 1; else if ((type.indexOf("address") != -1)) - return 20; + return 32; else return 0; } diff --git a/mix/FileIo.cpp b/mix/FileIo.cpp index a91dfc167..7eaa0ca7b 100644 --- a/mix/FileIo.cpp +++ b/mix/FileIo.cpp @@ -78,3 +78,18 @@ QString FileIo::getHomePath() const { return QDir::homePath(); } + +void FileIo::moveFile(QString const& _sourceUrl, QString const& _destUrl) +{ + QUrl sourceUrl(_sourceUrl); + QUrl destUrl(_destUrl); + if (!QFile::rename(sourceUrl.path(), destUrl.path())) + error(tr("Error moving file %1 to %2").arg(_sourceUrl).arg(_destUrl)); +} + +bool FileIo::fileExists(QString const& _url) +{ + QUrl url(_url); + QFile file(url.path()); + return file.exists(); +} diff --git a/mix/FileIo.h b/mix/FileIo.h index 3c251cfbc..e635ffa25 100644 --- a/mix/FileIo.h +++ b/mix/FileIo.h @@ -33,7 +33,7 @@ namespace mix class FileIo : public QObject { Q_OBJECT - Q_PROPERTY(QString homePath READ getHomePath CONSTANT); + Q_PROPERTY(QString homePath READ getHomePath CONSTANT) signals: /// Signalled in case of IO error @@ -48,6 +48,10 @@ public: Q_INVOKABLE void writeFile(QString const& _url, QString const& _data); /// Copy a file from _sourcePath to _destPath. Signals on failure. Q_INVOKABLE void copyFile(QString const& _sourceUrl, QString const& _destUrl); + /// Move (rename) a file from _sourcePath to _destPath. Signals on failure. + Q_INVOKABLE void moveFile(QString const& _sourceUrl, QString const& _destUrl); + /// Check if file exists + Q_INVOKABLE bool fileExists(QString const& _url); private: QString getHomePath() const; diff --git a/mix/qml/NewProjectDialog.qml b/mix/qml/NewProjectDialog.qml index 7bba58418..be9880457 100644 --- a/mix/qml/NewProjectDialog.qml +++ b/mix/qml/NewProjectDialog.qml @@ -9,7 +9,7 @@ Window { modality: Qt.WindowModal width: 640 - height: 280 + height: 120 visible: false @@ -89,4 +89,5 @@ Window { pathField.text = u; } } + Component.onCompleted: pathField.text = fileIo.homePath } diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml index 1f578fc6a..077aae28e 100644 --- a/mix/qml/ProjectList.qml +++ b/mix/qml/ProjectList.qml @@ -5,7 +5,7 @@ import QtQuick.Controls 1.0 import org.ethereum.qml.ProjectModel 1.0 Item { - + property bool renameMode: false; ColumnLayout { anchors.fill: parent Text { @@ -16,9 +16,10 @@ Item { visible: !ProjectModel.isEmpty; } ListView { + id: projectList Layout.fillWidth: true Layout.fillHeight: true - id: projectList + model: ProjectModel.listModel delegate: renderDelegate @@ -34,6 +35,21 @@ Item { ProjectModel.openDocument(ProjectModel.listModel.get(currentIndex).documentId); } } + Menu { + id: contextMenu + MenuItem { + text: qsTr("Rename") + onTriggered: { + renameMode = true; + } + } + MenuItem { + text: qsTr("Delete") + onTriggered: { + ProjectModel.removeDocument(projectList.model.get(projectList.currentIndex).documentId); + } + } + } } Component { id: renderDelegate @@ -43,7 +59,9 @@ Item { width: parent.width RowLayout { anchors.fill: parent + visible: !(index === projectList.currentIndex) || !renameMode Text { + id: nameText Layout.fillWidth: true Layout.fillHeight: true text: name @@ -51,17 +69,56 @@ Item { verticalAlignment: Text.AlignBottom } } + + TextInput { + id: textInput + text: nameText.text + visible: (index === projectList.currentIndex) && renameMode + MouseArea { + id: textMouseArea + anchors.fill: parent + hoverEnabled: true + z:2 + onClicked: { + console.log("clicked"); + textInput.forceActiveFocus(); + } + } + + onVisibleChanged: { + if (visible) { + selectAll(); + forceActiveFocus(); + } + } + + onAccepted: close(true); + onCursorVisibleChanged: { + if (!cursorVisible) + close(false); + } + onFocusChanged: { + if (!focus) + close(false); + } + function close(accept) { + renameMode = false; + if (accept) + ProjectModel.renameDocument(projectList.model.get(projectList.currentIndex).documentId, textInput.text); + } + } MouseArea { id: mouseArea z: 1 hoverEnabled: false anchors.fill: parent - + acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked:{ projectList.currentIndex = index; + if (mouse.button === Qt.RightButton && !projectList.model.get(index).isContract) + contextMenu.popup(); } } - Connections { target: ProjectModel onProjectLoaded: { diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index cc5ecccd9..5337e2780 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -15,6 +15,8 @@ Item { signal projectClosed signal projectLoaded signal documentOpened(var document) + signal documentRemoved(var documentId) + signal documentUpdated(var documentId) //renamed signal projectSaving(var projectData) property bool isEmpty: (projectData === null) @@ -33,7 +35,12 @@ Item { function saveProject() { ProjectModelCode.saveProject(); } function loadProject(path) { ProjectModelCode.loadProject(path); } function addExistingFile() { ProjectModelCode.addExistingFile(); } + function newHtmlFile() { ProjectModelCode.newHtmlFile(); } + function newJsFile() { ProjectModelCode.newJsFile(); } + //function newContract() { ProjectModelCode.newContract(); } function openDocument(documentId) { ProjectModelCode.openDocument(documentId); } + function renameDocument(documentId, newName) { ProjectModelCode.renameDocument(documentId, newName); } + function removeDocument(documentId) { ProjectModelCode.removeDocument(documentId); } Connections { target: appContext @@ -95,7 +102,7 @@ Item { title: qsTr("Add a file") selectFolder: false onAccepted: { - var paths = openProjectFileDialog.fileUrls; + var paths = addExistingFileDialog.fileUrls; ProjectModelCode.doAddExistingFiles(paths); } } diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index a547457b0..7cf2b3018 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -72,7 +72,7 @@ function loadProject(path) { } function addExistingFile() { - addExistingFileDialog().open(); + addExistingFileDialog.open(); } function addProjectFiles(files) { @@ -82,22 +82,31 @@ function addProjectFiles(files) { function addFile(fileName) { var p = projectPath + fileName; + var extension = fileName.substring(fileName.length - 4, fileName.length); + var isContract = extension === ".sol"; var fileData = { contract: false, path: p, - name: fileName, + name: isContract ? "Contract" : fileName, documentId: fileName, - isText: true, - isContract: fileName.substring(fileName.length - 4, fileName.length) === ".sol", + isText: isContract || extension === ".html" || extension === ".js", + isContract: fileData, }; projectListModel.append(fileData); } -function openDocument(documentId) { +function findDocument(documentId) +{ for (var i = 0; i < projectListModel.count; i++) if (projectListModel.get(i).documentId === documentId) - documentOpened(projectListModel.get(i)); + return i; + console.error("Cant find document " + documentId); + return -1; +} + +function openDocument(documentId) { + documentOpened(projectListModel.get(findDocument(documentId))); } function doCloseProject() { @@ -117,15 +126,15 @@ function doCreateProject(title, path) { fileIo.makeDir(dirPath); var projectFile = dirPath + projectFileName; - var indexFile = dirPath + "index.html"; - var contractsFile = dirPath + "contracts.sol"; + var indexFile = "index.html"; + var contractsFile = "contracts.sol"; var projectData = { title: title, - files: [ "contracts.sol", "index.html" ] + files: [ indexFile, contractsFile ] }; - - fileIo.writeFile(indexFile, ""); - fileIo.writeFile(contractsFile, "contract MyContract {}"); + //TODO: copy from template + fileIo.writeFile(dirPath + indexFile, ""); + fileIo.writeFile(dirPath + contractsFile, "contract MyContract {\n }\n"); var json = JSON.stringify(projectData); fileIo.writeFile(projectFile, json); loadProject(dirPath); @@ -134,9 +143,62 @@ function doCreateProject(title, path) { function doAddExistingFiles(files) { for(var i = 0; i < files.length; i++) { var sourcePath = files[i]; + console.log(sourcePath); var sourceFileName = sourcePath.substring(sourcePath.lastIndexOf("/") + 1, sourcePath.length); + console.log(sourceFileName); var destPath = projectPath + sourceFileName; - fileIo.copyFile(sourcePath, destPath); + console.log(destPath); + if (sourcePath !== destPath) + fileIo.copyFile(sourcePath, destPath); addFile(sourceFileName); } } + +function renameDocument(documentId, newName) { + var i = findDocument(documentId); + var document = projectListModel.get(i); + if (!document.isContract) { + var sourcePath = document.path; + var destPath = projectPath + newName; + fileIo.moveFile(sourcePath, destPath); + document.path = destPath; + document.name = newName; + projectListModel.set(i, document); + documentUpdated(documentId); + } +} + +function removeDocument(documentId) { + var i = findDocument(documentId); + var document = projectListModel.get(i); + if (!document.isContract) { + projectListModel.remove(i); + documentUpdated(documentId); + } +} + +function newHtmlFile() { + createAndAddFile("page", "html", "\n"); +} + +function newJsFile() { + createAndAddFile("script", "js", "function foo() {\n}\n"); +} + +function createAndAddFile(name, extension, content) { + var fileName = generateFileName(name, extension); + var filePath = projectPath + fileName; + fileIo.writeFile(filePath, content); + addFile(fileName); +} + +function generateFileName(name, extension) { + var i = 1; + do { + var fileName = name + i + "." + extension; + var filePath = projectPath + fileName; + i++; + } while (fileIo.fileExists(filePath)); + return fileName +} + diff --git a/mix/qml/main.qml b/mix/qml/main.qml index f59c5874c..94d061639 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -21,11 +21,16 @@ ApplicationWindow { title: qsTr("File") MenuItem { action: createProjectAction } MenuItem { action: openProjectAction } + MenuSeparator {} MenuItem { action: saveAllFilesAction } + MenuSeparator {} MenuItem { action: addExistingFileAction } MenuItem { action: addNewJsFileAction } MenuItem { action: addNewHtmlFileAction } - MenuItem { action: addNewContractAction } + MenuSeparator {} + //MenuItem { action: addNewContractAction } + MenuItem { action: closeProjectAction } + MenuSeparator {} MenuItem { action: exitAppAction } } Menu { @@ -95,7 +100,7 @@ ApplicationWindow { text: qsTr("New JavaScript file") shortcut: "Ctrl+Alt+J" enabled: !ProjectModel.isEmpty - onTriggered: ProjectModel.addJsFile(); + onTriggered: ProjectModel.newJsFile(); } Action { @@ -103,7 +108,7 @@ ApplicationWindow { text: qsTr("New HTML file") shortcut: "Ctrl+Alt+H" enabled: !ProjectModel.isEmpty - onTriggered: ProjectModel.addHtmlFile(); + onTriggered: ProjectModel.newHtmlFile(); } Action { @@ -111,7 +116,7 @@ ApplicationWindow { text: qsTr("New contract") shortcut: "Ctrl+Alt+C" enabled: !ProjectModel.isEmpty - onTriggered: ProjectModel.addContract(); + onTriggered: ProjectModel.newContract(); } Action { From aa389db06fb5ba3beb64b788dc753fb0c8b3cf69 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sun, 11 Jan 2015 19:50:22 +0100 Subject: [PATCH 06/14] style fixed --- mix/FileIo.cpp | 4 ++-- mix/FileIo.h | 2 +- mix/qml/NewProjectDialog.qml | 4 ++-- mix/qml/StateDialog.qml | 4 ++-- mix/qml/TransactionDialog.qml | 10 +++++----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mix/FileIo.cpp b/mix/FileIo.cpp index 7eaa0ca7b..fed9909e6 100644 --- a/mix/FileIo.cpp +++ b/mix/FileIo.cpp @@ -42,7 +42,7 @@ QString FileIo::readFile(QString const& _url) { QUrl url(_url); QFile file(url.path()); - if(file.open(QIODevice::ReadOnly | QIODevice::Text)) + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream stream(&file); QString data = stream.readAll(); @@ -57,7 +57,7 @@ void FileIo::writeFile(QString const& _url, QString const& _data) { QUrl url(_url); QFile file(url.path()); - if(file.open(QIODevice::WriteOnly | QIODevice::Text)) + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream stream(&file); stream << _data; diff --git a/mix/FileIo.h b/mix/FileIo.h index e635ffa25..08d49e099 100644 --- a/mix/FileIo.h +++ b/mix/FileIo.h @@ -30,7 +30,7 @@ namespace mix { ///File services for QML -class FileIo : public QObject +class FileIo: public QObject { Q_OBJECT Q_PROPERTY(QString homePath READ getHomePath CONSTANT) diff --git a/mix/qml/NewProjectDialog.qml b/mix/qml/NewProjectDialog.qml index be9880457..406dc02b2 100644 --- a/mix/qml/NewProjectDialog.qml +++ b/mix/qml/NewProjectDialog.qml @@ -13,8 +13,8 @@ Window { visible: false - property alias projectTitle : titleField.text - readonly property string projectPath : "file://" + pathField.text + property alias projectTitle: titleField.text + readonly property string projectPath: "file://" + pathField.text signal accepted function open() { diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index 208c766f9..cde7cb3ef 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -11,8 +11,8 @@ Window { visible: false - property alias stateTitle : titleField.text - property alias stateBalance : balanceField.text + property alias stateTitle: titleField.text + property alias stateBalance: balanceField.text property int stateIndex property var stateTransactions: [] signal accepted diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 54528f97f..3a32e2af5 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -10,11 +10,11 @@ Window { visible: false property int transactionIndex - property alias transactionParams : paramsModel; - property alias gas : gasField.text; - property alias gasPrice : gasPriceField.text; - property alias transactionValue : valueField.text; - property alias functionId : functionComboBox.currentText; + property alias transactionParams: paramsModel; + property alias gas: gasField.text; + property alias gasPrice: gasPriceField.text; + property alias transactionValue: valueField.text; + property alias functionId: functionComboBox.currentText; property var itemParams; signal accepted; From 6e4d57817a20d66e1b6fd96966739e138dec9161 Mon Sep 17 00:00:00 2001 From: "U-SVZ13\\Arkady" Date: Sun, 11 Jan 2015 20:07:59 +0100 Subject: [PATCH 07/14] fixed msvc build --- libweb3jsonrpc/WebThreeStubServerBase.h | 2 +- mix/AppContext.cpp | 3 +-- mix/ClientModel.cpp | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 6868207a6..8648665be 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -121,7 +121,7 @@ public: std::map const& ids() const { return m_ids; } protected: - virtual bool authenticate(dev::TransactionSkeleton const& _t) const; + virtual bool authenticate(dev::TransactionSkeleton const& _t) const = 0; protected: virtual dev::eth::Interface* client() = 0; diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp index 4bede3c49..4c8e61510 100644 --- a/mix/AppContext.cpp +++ b/mix/AppContext.cpp @@ -27,12 +27,11 @@ #include #include #include -#include #include "CodeModel.h" #include "FileIo.h" #include "ClientModel.h" #include "AppContext.h" - +#include using namespace dev; using namespace dev::eth; diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 6859b0279..9a90d52e3 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -33,6 +33,7 @@ #include "CodeModel.h" #include "ClientModel.h" +using namespace dev; using namespace dev::eth; using namespace dev::mix; From eaa4312f3fccc90defd1f49b86f71fb9fb5ec9c3 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sun, 11 Jan 2015 20:14:42 +0100 Subject: [PATCH 08/14] compiling ethereum.js is part of the cmake --- cmake/EthDependencies.cmake | 9 +++++++++ libjsqrc/CMakeLists.txt | 10 ++++++++++ libjsqrc/compilejs.sh | 7 +++++++ 3 files changed, 26 insertions(+) create mode 100644 libjsqrc/compilejs.sh diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 136c86799..05389f24c 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -113,6 +113,15 @@ if (NOT HEADLESS) message(" - macdeployqt path: ${MACDEPLOYQT_APP}") endif() +# TODO check node && npm version + find_program(ETH_NODE node) + string(REGEX REPLACE "node" "" ETH_NODE_DIRECTORY ${ETH_NODE}) + message(" - nodejs location : ${ETH_NODE}") + + find_program(ETH_NPM npm) + message(" - npm location : ${ETH_NPM}") + string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM}) + endif() #HEADLESS # use multithreaded boost libraries, with -mt suffix diff --git a/libjsqrc/CMakeLists.txt b/libjsqrc/CMakeLists.txt index a6dbf023b..2635bc558 100644 --- a/libjsqrc/CMakeLists.txt +++ b/libjsqrc/CMakeLists.txt @@ -12,4 +12,14 @@ qt5_add_resources(JSQRC js.qrc) add_library(jsqrc STATIC ${JSQRC}) target_link_libraries(jsqrc Qt5::Core) +if (ETH_NODE AND ETH_NPM) + add_custom_target(ethereumjs) + add_custom_command(TARGET ethereumjs + POST_BUILD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND bash compilejs.sh ${ETH_NPM_DIRECTORY} ${ETH_NODE_DIRECTORY} + ) + add_dependencies(jsqrc ethereumjs) +endif() + install( TARGETS jsqrc ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) diff --git a/libjsqrc/compilejs.sh b/libjsqrc/compilejs.sh new file mode 100644 index 000000000..ba99e2415 --- /dev/null +++ b/libjsqrc/compilejs.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cd ethereumjs +export PATH=$PATH:$1:$2 +npm install +npm run-script build + From 134e81919cc741e70baab3e5735fcfdbe8e58922 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sun, 11 Jan 2015 21:22:34 +0100 Subject: [PATCH 09/14] order of status message --- cmake/EthDependencies.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 05389f24c..9b00a182f 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -119,8 +119,8 @@ if (NOT HEADLESS) message(" - nodejs location : ${ETH_NODE}") find_program(ETH_NPM npm) - message(" - npm location : ${ETH_NPM}") string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM}) + message(" - npm location : ${ETH_NPM}") endif() #HEADLESS From 81dbf3ec5f9fabcd2f1912c69841bbf85c935ffd Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 12 Jan 2015 11:48:24 +0100 Subject: [PATCH 10/14] fix for eth_changed --- libqethereum/QEthereum.cpp | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index daf773a22..44e882178 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -134,24 +134,7 @@ void QWebThree::onDataProcessed(QString _json, QString _addInfo) if (!_addInfo.compare("internal")) return; - if (!_addInfo.compare("eth_changed")) - { - QJsonArray resultsArray = QJsonDocument::fromJson(_json.toUtf8()).array(); - for (int i = 0; i < resultsArray.size(); i++) - { - QJsonObject elem = resultsArray[i].toObject(); - if (elem.contains("result") && elem["result"].toBool() == true) - { - QJsonObject res; - res["_event"] = _addInfo; - res["_id"] = (int)m_watches[i]; // we can do that couse poll is synchronous - response(QString::fromUtf8(QJsonDocument(res).toJson())); - } - } - return; - } - - if (!_addInfo.compare("shh_changed")) + if (!_addInfo.compare("shh_changed") || !_addInfo.compare("eth_changed")) { QJsonArray resultsArray = QJsonDocument::fromJson(_json.toUtf8()).array(); for (int i = 0; i < resultsArray.size(); i++) @@ -161,13 +144,18 @@ void QWebThree::onDataProcessed(QString _json, QString _addInfo) { QJsonObject res; res["_event"] = _addInfo; - res["_id"] = (int)m_shhWatches[i]; + + if (!_addInfo.compare("shh_changed")) + res["_id"] = (int)m_shhWatches[i]; // we can do that couse poll is synchronous + else + res["_id"] = (int)m_watches[i]; + res["data"] = elem["result"].toArray(); response(QString::fromUtf8(QJsonDocument(res).toJson())); } } } - + QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object(); if ((!_addInfo.compare("eth_newFilter") || !_addInfo.compare("eth_newFilterString")) && f.contains("result")) From 5e0b71730c9d691b5fb994ce95ef670918c0d1d5 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 12 Jan 2015 12:46:52 +0100 Subject: [PATCH 11/14] More convenient function type construction. --- libsolidity/ExpressionCompiler.cpp | 2 +- libsolidity/GlobalContext.cpp | 45 +++++++----------------------- libsolidity/Scanner.cpp | 20 +------------ libsolidity/Token.cpp | 17 +++++++++++ libsolidity/Token.h | 2 ++ libsolidity/Types.cpp | 29 ++++++++++--------- libsolidity/Types.h | 6 ++++ 7 files changed, 53 insertions(+), 68 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 4fdea3326..1c02f4f32 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -250,7 +250,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); }; - appendExternalFunctionCall(FunctionType({}, {}, Location::EXTERNAL), {}, options); + appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL), {}, options); break; } case Location::SUICIDE: diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index 633331a90..26a52fd10 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -34,54 +34,29 @@ namespace solidity { GlobalContext::GlobalContext(): -// TODO: make this cleaner. m_magicVariables(vector>{make_shared("block", make_shared(MagicType::Kind::BLOCK)), make_shared("msg", make_shared(MagicType::Kind::MSG)), make_shared("tx", make_shared(MagicType::Kind::TX)), make_shared("suicide", - make_shared(TypePointers({std::make_shared(0, - IntegerType::Modifier::ADDRESS)}), - TypePointers(), - FunctionType::Location::SUICIDE)), + make_shared(vector{"address"}, vector{}, FunctionType::Location::SUICIDE)), make_shared("sha3", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - FunctionType::Location::SHA3)), + make_shared(vector{"hash"}, vector{"hash"}, FunctionType::Location::SHA3)), make_shared("log0", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers(), - FunctionType::Location::LOG0)), + make_shared(vector{"hash"},vector{}, FunctionType::Location::LOG0)), make_shared("log1", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH), std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers(), - FunctionType::Location::LOG1)), + make_shared(vector{"hash", "hash"},vector{}, FunctionType::Location::LOG1)), make_shared("log2", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH), std::make_shared(256, IntegerType::Modifier::HASH), std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers(), - FunctionType::Location::LOG2)), + make_shared(vector{"hash", "hash", "hash"},vector{}, FunctionType::Location::LOG2)), make_shared("log3", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH), std::make_shared(256, IntegerType::Modifier::HASH), std::make_shared(256, IntegerType::Modifier::HASH), std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers(), - FunctionType::Location::LOG3)), + make_shared(vector{"hash", "hash", "hash", "hash"},vector{}, FunctionType::Location::LOG3)), make_shared("log4", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH), std::make_shared(256, IntegerType::Modifier::HASH), std::make_shared(256, IntegerType::Modifier::HASH), std::make_shared(256, IntegerType::Modifier::HASH), std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers(), - FunctionType::Location::LOG4)), + make_shared(vector{"hash", "hash", "hash", "hash", "hash"},vector{}, FunctionType::Location::LOG4)), make_shared("sha256", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - FunctionType::Location::SHA256)), + make_shared(vector{"hash"}, vector{"hash"}, FunctionType::Location::SHA256)), make_shared("ecrecover", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH), - std::make_shared(8, IntegerType::Modifier::HASH), - std::make_shared(256, IntegerType::Modifier::HASH), - std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers({std::make_shared(0, IntegerType::Modifier::ADDRESS)}), - FunctionType::Location::ECRECOVER)), + make_shared(vector{"hash", "hash8", "hash", "hash"}, vector{"address"}, FunctionType::Location::ECRECOVER)), make_shared("ripemd160", - make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers({std::make_shared(160, IntegerType::Modifier::HASH)}), - FunctionType::Location::RIPEMD160))}) + make_shared(vector{"hash"}, vector{"hash160"}, FunctionType::Location::RIPEMD160))}) { } diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 6e3d04bc5..b283ca10e 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -700,24 +700,6 @@ Token::Value Scanner::scanNumber(char _charSeen) return Token::NUMBER; } - -// ---------------------------------------------------------------------------- -// Keyword Matcher - - -static Token::Value keywordOrIdentifierToken(string const& _input) -{ - // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored - // and keywords to be put inside the keywords variable. -#define KEYWORD(name, string, precedence) {string, Token::name}, -#define TOKEN(name, string, precedence) - static const map keywords({TOKEN_LIST(TOKEN, KEYWORD)}); -#undef KEYWORD -#undef TOKEN - auto it = keywords.find(_input); - return it == keywords.end() ? Token::IDENTIFIER : it->second; -} - Token::Value Scanner::scanIdentifierOrKeyword() { solAssert(isIdentifierStart(m_char), ""); @@ -727,7 +709,7 @@ Token::Value Scanner::scanIdentifierOrKeyword() while (isIdentifierPart(m_char)) addLiteralCharAndAdvance(); literal.complete(); - return keywordOrIdentifierToken(m_nextToken.literal); + return Token::fromIdentifierOrKeyword(m_nextToken.literal); } char CharStream::advanceAndGet(size_t _chars) diff --git a/libsolidity/Token.cpp b/libsolidity/Token.cpp index 093bd9c1d..7dc56c327 100644 --- a/libsolidity/Token.cpp +++ b/libsolidity/Token.cpp @@ -40,8 +40,11 @@ // You should have received a copy of the GNU General Public License // along with cpp-ethereum. If not, see . +#include #include +using namespace std; + namespace dev { namespace solidity @@ -77,6 +80,20 @@ char const Token::m_tokenType[] = { TOKEN_LIST(KT, KK) }; + +Token::Value Token::fromIdentifierOrKeyword(const std::string& _name) +{ + // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored + // and keywords to be put inside the keywords variable. +#define KEYWORD(name, string, precedence) {string, Token::name}, +#define TOKEN(name, string, precedence) + static const map keywords({TOKEN_LIST(TOKEN, KEYWORD)}); +#undef KEYWORD +#undef TOKEN + auto it = keywords.find(_name); + return it == keywords.end() ? Token::IDENTIFIER : it->second; +} + #undef KT #undef KK diff --git a/libsolidity/Token.h b/libsolidity/Token.h index f2ffd076a..2d4441d08 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -386,6 +386,8 @@ public: return m_precedence[tok]; } + static Token::Value fromIdentifierOrKeyword(std::string const& _name); + private: static char const* const m_name[NUM_TOKENS]; static char const* const m_string[NUM_TOKENS]; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index eda022ccb..b3eae2025 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -35,7 +35,7 @@ namespace solidity shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) { - solAssert(Token::isElementaryTypeName(_typeToken), ""); + solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) { @@ -204,18 +204,12 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe } const MemberList IntegerType::AddressMemberList = - MemberList({{"balance", - make_shared(256)}, - {"callstring32", - make_shared(TypePointers({make_shared(32)}), - TypePointers(), FunctionType::Location::BARE)}, - {"callstring32string32", - make_shared(TypePointers({make_shared(32), - make_shared(32)}), - TypePointers(), FunctionType::Location::BARE)}, - {"send", - make_shared(TypePointers({make_shared(256)}), - TypePointers(), FunctionType::Location::SEND)}}); + MemberList({{"balance", make_shared(256)}, + {"callstring32", make_shared(vector{"string32"}, + vector{}, FunctionType::Location::BARE)}, + {"callstring32string32", make_shared(vector{"string32", "string32"}, + vector{}, FunctionType::Location::BARE)}, + {"send", make_shared(vector{"uint"}, vector{}, FunctionType::Location::SEND)}}); shared_ptr IntegerConstantType::fromLiteral(string const& _literal) { @@ -625,6 +619,15 @@ string FunctionType::getCanonicalSignature() const return ret + ")"; } +TypePointers FunctionType::parseElementaryTypeVector(vector const& _types) +{ + TypePointers pointers; + pointers.reserve(_types.size()); + for (string const& type: _types) + pointers.push_back(Type::fromElementaryTypeName(Token::fromIdentifierOrKeyword(type))); + return pointers; +} + bool MappingType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 335c58a31..031d45ea5 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -352,6 +352,10 @@ public: virtual Category getCategory() const override { return Category::FUNCTION; } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); + FunctionType(std::vector const& _parameterTypes, + std::vector const& _returnParameterTypes, + Location _location = Location::INTERNAL): + FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), _location) {} FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, Location _location = Location::INTERNAL): m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), @@ -371,6 +375,8 @@ public: std::string getCanonicalSignature() const; private: + static TypePointers parseElementaryTypeVector(std::vector const& _types); + TypePointers m_parameterTypes; TypePointers m_returnParameterTypes; Location m_location; From 4f52200c7e514b8022b823d3c45af91aa1ea06c5 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 12 Jan 2015 13:13:45 +0100 Subject: [PATCH 12/14] fixed style and build --- libweb3jsonrpc/WebThreeStubServerBase.h | 2 +- mix/ClientModel.cpp | 1 - mix/CodeEditorExtensionManager.cpp | 10 ++-------- mix/CodeEditorExtensionManager.h | 1 - mix/ContractCallDataEncoder.cpp | 4 ++-- 5 files changed, 5 insertions(+), 13 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 451e2f9ee..168ab288a 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -121,7 +121,7 @@ public: std::map const& ids() const { return m_ids; } protected: - virtual bool authenticate(dev::TransactionSkeleton const& _t) const = 0; + virtual bool authenticate(dev::TransactionSkeleton const& _t) const; protected: virtual dev::eth::Interface* client() = 0; diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 9a90d52e3..bcbed1fac 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -214,7 +214,6 @@ ExecutionResult ClientModel::deployContract(bytes const& _code) ExecutionResult ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) { - //bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; m_client->transact(m_client->userAccount().secret(), _tr.value, _contract, _data, _tr.gas, _tr.gasPrice); ExecutionResult r = m_client->lastExecutionResult(); r.contractAddress = _contract; diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index f5ceb333e..a123c3b7a 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -88,21 +88,15 @@ void CodeEditorExtensionManager::initExtension(std::shared_ptr _ext) m_features.append(_ext); } -void CodeEditorExtensionManager::onCodeChange() -{ -// m_appContext->codeModel()->updateFormatting(m_doc); //update old formatting -// m_appContext->codeModel()->registerCodeChange(m_doc->toPlainText()); -} - void CodeEditorExtensionManager::applyCodeHighlight() { -// m_appContext->codeModel()->updateFormatting(m_doc); + //TODO: reimplement } void CodeEditorExtensionManager::setRightTabView(QQuickItem* _tabView) { m_rightTabView = _tabView; - initExtensions(); //TODO: this is not the right place for it + initExtensions(); //TODO: move this to a proper place } void CodeEditorExtensionManager::setTabView(QQuickItem* _tabView) diff --git a/mix/CodeEditorExtensionManager.h b/mix/CodeEditorExtensionManager.h index e910b62d3..2f2a315b9 100644 --- a/mix/CodeEditorExtensionManager.h +++ b/mix/CodeEditorExtensionManager.h @@ -59,7 +59,6 @@ public: void setRightTabView(QQuickItem*); private slots: - void onCodeChange(); void applyCodeHighlight(); private: diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 0e628c3c0..7a38db22b 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -40,8 +40,8 @@ bytes ContractCallDataEncoder::encodedData() void ContractCallDataEncoder::encode(QFunctionDefinition const* _function) { - bytes i = _function->hash().asBytes(); - m_encodedData.insert(m_encodedData.end(), i.begin(), i.end()); + bytes hash = _function->hash().asBytes(); + m_encodedData.insert(m_encodedData.end(), hash.begin(), hash.end()); } void ContractCallDataEncoder::encode(QVariableDeclaration const* _dec, bool _value) From 18f7839fd599f33fc39eaf550d5f031f9afb3235 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 12 Jan 2015 13:29:16 +0100 Subject: [PATCH 13/14] Define strings = vector --- libdevcore/Common.h | 3 +++ libsolidity/GlobalContext.cpp | 20 ++++++++++---------- libsolidity/Types.cpp | 12 ++++++------ libsolidity/Types.h | 8 ++++---- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 0967596e2..9fefea45a 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -80,6 +80,9 @@ using StringMap = std::map; using u256Map = std::map; using HexMap = std::map; +// String types. +using strings = std::vector; + // Fixed-length string types. using string32 = std::array; diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index 26a52fd10..92ca9548a 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -38,25 +38,25 @@ m_magicVariables(vector>{make_shared< make_shared("msg", make_shared(MagicType::Kind::MSG)), make_shared("tx", make_shared(MagicType::Kind::TX)), make_shared("suicide", - make_shared(vector{"address"}, vector{}, FunctionType::Location::SUICIDE)), + make_shared(strings{"address"}, strings{}, FunctionType::Location::SUICIDE)), make_shared("sha3", - make_shared(vector{"hash"}, vector{"hash"}, FunctionType::Location::SHA3)), + make_shared(strings{"hash"}, strings{"hash"}, FunctionType::Location::SHA3)), make_shared("log0", - make_shared(vector{"hash"},vector{}, FunctionType::Location::LOG0)), + make_shared(strings{"hash"},strings{}, FunctionType::Location::LOG0)), make_shared("log1", - make_shared(vector{"hash", "hash"},vector{}, FunctionType::Location::LOG1)), + make_shared(strings{"hash", "hash"},strings{}, FunctionType::Location::LOG1)), make_shared("log2", - make_shared(vector{"hash", "hash", "hash"},vector{}, FunctionType::Location::LOG2)), + make_shared(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::LOG2)), make_shared("log3", - make_shared(vector{"hash", "hash", "hash", "hash"},vector{}, FunctionType::Location::LOG3)), + make_shared(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::LOG3)), make_shared("log4", - make_shared(vector{"hash", "hash", "hash", "hash", "hash"},vector{}, FunctionType::Location::LOG4)), + make_shared(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::LOG4)), make_shared("sha256", - make_shared(vector{"hash"}, vector{"hash"}, FunctionType::Location::SHA256)), + make_shared(strings{"hash"}, strings{"hash"}, FunctionType::Location::SHA256)), make_shared("ecrecover", - make_shared(vector{"hash", "hash8", "hash", "hash"}, vector{"address"}, FunctionType::Location::ECRECOVER)), + make_shared(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRECOVER)), make_shared("ripemd160", - make_shared(vector{"hash"}, vector{"hash160"}, FunctionType::Location::RIPEMD160))}) + make_shared(strings{"hash"}, strings{"hash160"}, FunctionType::Location::RIPEMD160))}) { } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index b3eae2025..7ca1dc6d5 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -205,11 +205,11 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe const MemberList IntegerType::AddressMemberList = MemberList({{"balance", make_shared(256)}, - {"callstring32", make_shared(vector{"string32"}, - vector{}, FunctionType::Location::BARE)}, - {"callstring32string32", make_shared(vector{"string32", "string32"}, - vector{}, FunctionType::Location::BARE)}, - {"send", make_shared(vector{"uint"}, vector{}, FunctionType::Location::SEND)}}); + {"callstring32", make_shared(strings{"string32"}, strings{}, + FunctionType::Location::BARE)}, + {"callstring32string32", make_shared(strings{"string32", "string32"}, + strings{}, FunctionType::Location::BARE)}, + {"send", make_shared(strings{"uint"}, strings{}, FunctionType::Location::SEND)}}); shared_ptr IntegerConstantType::fromLiteral(string const& _literal) { @@ -619,7 +619,7 @@ string FunctionType::getCanonicalSignature() const return ret + ")"; } -TypePointers FunctionType::parseElementaryTypeVector(vector const& _types) +TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) { TypePointers pointers; pointers.reserve(_types.size()); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 031d45ea5..deabe1160 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -352,10 +352,10 @@ public: virtual Category getCategory() const override { return Category::FUNCTION; } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); - FunctionType(std::vector const& _parameterTypes, - std::vector const& _returnParameterTypes, + FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes, Location _location = Location::INTERNAL): - FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), _location) {} + FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), + _location) {} FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, Location _location = Location::INTERNAL): m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), @@ -375,7 +375,7 @@ public: std::string getCanonicalSignature() const; private: - static TypePointers parseElementaryTypeVector(std::vector const& _types); + static TypePointers parseElementaryTypeVector(strings const& _types); TypePointers m_parameterTypes; TypePointers m_returnParameterTypes; From 28aefa7ddb042e9c33b0997a45ba6ea9b5d2dfaa Mon Sep 17 00:00:00 2001 From: "U-SVZ13\\Arkady" Date: Mon, 12 Jan 2015 13:32:53 +0100 Subject: [PATCH 14/14] fixed msvc build --- libweb3jsonrpc/WebThreeStubServerBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 168ab288a..24a6a9962 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -36,7 +36,7 @@ namespace dev { class WebThreeNetworkFace; class KeyPair; -class TransactionSkeleton; +struct TransactionSkeleton; namespace eth { class Interface;