Browse Source

Merge branch 'develop' into new_jsonrpc

cl-refactor
Marek Kotewicz 10 years ago
parent
commit
812f7ee04b
  1. 2
      README.md
  2. 1
      alethzero/CMakeLists.txt
  3. 139
      alethzero/DappHost.cpp
  4. 58
      alethzero/DappHost.h
  5. 203
      alethzero/DappLoader.cpp
  6. 94
      alethzero/DappLoader.h
  7. 71
      alethzero/MainWin.cpp
  8. 14
      alethzero/MainWin.h
  9. 4
      alethzero/Transact.cpp
  10. 28
      alethzero/WebPage.cpp
  11. 40
      alethzero/WebPage.h
  12. 18
      libnatspec/NatspecExpressionEvaluator.cpp
  13. 9
      libnatspec/NatspecExpressionEvaluator.h
  14. 102
      libnatspec/natspec.js
  15. 2
      libnatspec/natspec.qrc
  16. 3
      libnatspec/natspecjs/.gitignore
  17. 8
      libnatspec/natspecjs/.travis.yml
  18. 47
      libnatspec/natspecjs/README.md
  19. 3570
      libnatspec/natspecjs/dist/natspec.js
  20. 1
      libnatspec/natspecjs/dist/natspec.js.map
  21. 2
      libnatspec/natspecjs/dist/natspec.min.js
  22. 13
      libnatspec/natspecjs/example/example.html
  23. 186
      libnatspec/natspecjs/natspec.js
  24. 24
      libnatspec/natspecjs/package.json
  25. 149
      libnatspec/natspecjs/test/test.js
  26. 4
      libsolidity/Compiler.h
  27. 2
      libsolidity/Parser.h
  28. 3
      mix/AppContext.cpp
  29. 9
      mix/CodeEditorExtensionManager.cpp
  30. 2
      mix/CodeEditorExtensionManager.h
  31. 2
      mix/CodeModel.h
  32. 2
      mix/FileIo.cpp
  33. 156
      mix/SortFilterProxyModel.cpp
  34. 97
      mix/SortFilterProxyModel.h
  35. 302
      mix/qml/LogsPane.qml
  36. 29
      mix/qml/LogsPaneStyle.qml
  37. 32
      mix/qml/MainContent.qml
  38. 10
      mix/qml/ProjectModel.qml
  39. 5
      mix/qml/StateListModel.qml
  40. 170
      mix/qml/StatusPane.qml
  41. 7
      mix/qml/WebPreview.qml
  42. BIN
      mix/qml/img/broom.png
  43. BIN
      mix/qml/img/copy.png
  44. 21
      mix/qml/js/ProjectModel.js
  45. 1
      mix/qml/qmldir
  46. 4
      mix/res.qrc
  47. 32
      pullSubtree.sh
  48. 7
      solc/CommandLineInterface.cpp
  49. 41
      test/TestHelper.cpp
  50. 8
      test/TestHelper.h
  51. 18
      test/bcUncleTestFiller.json
  52. 14
      test/blockchain.cpp
  53. 19
      test/natspec.cpp
  54. 64
      test/vm.cpp

2
README.md

@ -1,5 +1,7 @@
## Ethereum C++ Client.
[![Join the chat at https://gitter.im/ethereum/cpp-ethereum](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/cpp-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
By Gav Wood et al, 2013, 2014, 2015.
| Linux | OSX | Windows

1
alethzero/CMakeLists.txt

@ -56,6 +56,7 @@ target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} jsqrc)
target_link_libraries(${EXECUTABLE} natspec)
target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES})
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
target_link_libraries(${EXECUTABLE} serpent)

139
alethzero/DappHost.cpp

@ -0,0 +1,139 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file DappHost.cpp
* @author Arkadiy Paronyan <arkadiy@ethdev.org>
* @date 2015
*/
#include "DappHost.h"
#include <QUrl>
#include <microhttpd.h>
#include <boost/algorithm/string.hpp>
#include <libdevcore/Common.h>
using namespace dev;
DappHost::DappHost(int _port, int _threads):
m_port(_port), m_threads(_threads), m_running(false), m_daemon(nullptr)
{
startListening();
}
DappHost::~DappHost()
{
stopListening();
}
void DappHost::startListening()
{
if(!this->m_running)
{
this->m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, this->m_port, nullptr, nullptr, &DappHost::callback, this, MHD_OPTION_THREAD_POOL_SIZE, this->m_threads, MHD_OPTION_END);
if (this->m_daemon != nullptr)
this->m_running = true;
}
}
void DappHost::stopListening()
{
if(this->m_running)
{
MHD_stop_daemon(this->m_daemon);
this->m_running = false;
}
}
void DappHost::sendOptionsResponse(MHD_Connection* _connection)
{
MHD_Response *result = MHD_create_response_from_data(0, NULL, 0, 1);
MHD_add_response_header(result, "Allow", "GET, OPTIONS");
MHD_add_response_header(result, "Access-Control-Allow-Headers", "origin, content-type, accept");
MHD_add_response_header(result, "DAV", "1");
MHD_queue_response(_connection, MHD_HTTP_OK, result);
MHD_destroy_response(result);
}
void DappHost::sendNotAllowedResponse(MHD_Connection* _connection)
{
MHD_Response *result = MHD_create_response_from_data(0, NULL, 0, 1);
MHD_add_response_header(result, "Allow", "GET, OPTIONS");
MHD_queue_response(_connection, MHD_HTTP_METHOD_NOT_ALLOWED, result);
MHD_destroy_response(result);
}
void DappHost::sendResponse(std::string const& _url, MHD_Connection* _connection)
{
QUrl requestUrl(QString::fromStdString(_url));
QString path = requestUrl.path().toLower();
if (path.isEmpty())
path = "/";
bytesConstRef response;
unsigned code = MHD_HTTP_NOT_FOUND;
std::string contentType;
while (!path.isEmpty())
{
auto iter = m_entriesByPath.find(path);
if (iter != m_entriesByPath.end())
{
ManifestEntry const* entry = iter->second;
auto contentIter = m_dapp.content.find(entry->hash);
if (contentIter == m_dapp.content.end())
break;
response = bytesConstRef(contentIter->second.data(), contentIter->second.size());
code = entry->httpStatus != 0 ? entry->httpStatus : MHD_HTTP_OK;
contentType = entry->contentType;
break;
}
path.truncate(path.length() - 1);
path = path.mid(0, path.lastIndexOf('/'));
}
MHD_Response *result = MHD_create_response_from_data(response.size(), const_cast<byte*>(response.data()), 0, 1);
if (!contentType.empty())
MHD_add_response_header(result, "Content-Type", contentType.c_str());
MHD_queue_response(_connection, code, result);
MHD_destroy_response(result);
}
int DappHost::callback(void* _cls, MHD_Connection* _connection, char const* _url, char const* _method, char const* _version, char const* _uploadData, size_t* _uploadDataSize, void** _conCls)
{
(void)_version;
(void)_uploadData;
(void)_uploadDataSize;
(void)_conCls;
DappHost* host = static_cast<DappHost*>(_cls);
if (std::string("GET") == _method)
host->sendResponse(std::string(_url), _connection);
else if (std::string("OPTIONS") == _method)
host->sendOptionsResponse(_connection);
else
host->sendNotAllowedResponse(_connection);
return MHD_YES;
}
QUrl DappHost::hostDapp(Dapp&& _dapp)
{
m_dapp = std::move(_dapp);
m_entriesByPath.clear();
for (ManifestEntry const& entry: m_dapp.manifest.entries)
m_entriesByPath[QString::fromStdString(entry.path)] = &entry;
return QUrl(QString("http://localhost:%1/").arg(m_port));
}

58
alethzero/DappHost.h

@ -0,0 +1,58 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file DappHost.h
* @author Arkadiy Paronyan <arkadiy@ethdev.org>
* @date 2015
*/
#pragma once
#include <map>
#include <QUrl>
#include <QString>
#include "DappLoader.h"
struct MHD_Daemon;
struct MHD_Connection;
/// DApp web server. Servers web content, resolves paths by hashes
class DappHost
{
public:
/// @param _port Network pork to listen for incoming connections
/// @param _threads Max number of threads to process requests
DappHost(int _port, int _threads = 10);
virtual ~DappHost();
/// Load and host a dapp. Previsous dapp in discarded. Synchronous
QUrl hostDapp(Dapp&& _dapp);
private:
void startListening();
void stopListening();
void sendOptionsResponse(MHD_Connection* _connection);
void sendNotAllowedResponse(MHD_Connection* _connection);
void sendResponse(std::string const& _url, MHD_Connection* _connection);
static int callback(void* _cls, MHD_Connection* _connection, char const* _url, char const* _method, char const* _version, char const* _uploadData, size_t* _uploadDataSize, void** _conCls);
int m_port;
int m_threads;
bool m_running;
MHD_Daemon* m_daemon;
Dapp m_dapp;
std::map<QString, ManifestEntry const*> m_entriesByPath;
};

203
alethzero/DappLoader.cpp

@ -0,0 +1,203 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file DappLoader.cpp
* @author Arkadiy Paronyan <arkadiy@ethdev.org>
* @date 2015
*/
#include <algorithm>
#include <json/json.h>
#include <QUrl>
#include <QStringList>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/SHA3.h>
#include <libethcore/CommonJS.h>
#include <libethereum/Client.h>
#include <libwebthree/WebThree.h>
#include "DappLoader.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::crypto;
Address c_registrar = Address("0000000000000000000000000000000000000a28");
Address c_urlHint = Address("0000000000000000000000000000000000000a29");
QString contentsOfQResource(std::string const& res);
DappLoader::DappLoader(QObject* _parent, WebThreeDirect* _web3):
QObject(_parent), m_web3(_web3)
{
connect(&m_net, &QNetworkAccessManager::finished, this, &DappLoader::downloadComplete);
}
DappLocation DappLoader::resolveAppUri(QString const& _uri)
{
QUrl url(_uri);
if (!url.scheme().isEmpty() && url.scheme() != "eth")
throw dev::Exception(); //TODO:
QStringList parts = url.host().split('.', QString::SkipEmptyParts);
QStringList domainParts;
std::reverse(parts.begin(), parts.end());
parts.append(url.path().split('/', QString::SkipEmptyParts));
Address address = c_registrar;
Address lastAddress;
int partIndex = 0;
h256 contentHash;
while (address && partIndex < parts.length())
{
lastAddress = address;
string32 name = { 0 };
QByteArray utf8 = parts[partIndex].toUtf8();
std::copy(utf8.data(), utf8.data() + utf8.size(), name.data());
address = abiOut<Address>(web3()->ethereum()->call(address, abiIn("addr(string32)", name)));
domainParts.append(parts[partIndex]);
if (!address)
{
//we have the address of the last part, try to get content hash
contentHash = abiOut<h256>(web3()->ethereum()->call(lastAddress, abiIn("content(string32)", name)));
if (!contentHash)
throw dev::Exception() << errinfo_comment("Can't resolve address");
}
++partIndex;
}
string32 contentUrl = abiOut<string32>(web3()->ethereum()->call(c_urlHint, abiIn("url(hash256)", contentHash)));
QString domain = domainParts.join('/');
parts.erase(parts.begin(), parts.begin() + partIndex);
QString path = parts.join('/');
QString contentUrlString = QString::fromUtf8(std::string(contentUrl.data(), contentUrl.size()).c_str());
if (!contentUrlString.startsWith("http://") || !contentUrlString.startsWith("https://"))
contentUrlString = "http://" + contentUrlString;
return DappLocation { domain, path, contentUrlString, contentHash };
}
void DappLoader::downloadComplete(QNetworkReply* _reply)
{
try
{
//try to interpret as rlp
QByteArray data = _reply->readAll();
_reply->deleteLater();
h256 expected = m_uriHashes[_reply->request().url()];
bytes package(reinterpret_cast<unsigned char const*>(data.constData()), reinterpret_cast<unsigned char const*>(data.constData() + data.size()));
Secp256k1 dec;
dec.decrypt(expected, package);
h256 got = sha3(package);
if (got != expected)
{
//try base64
data = QByteArray::fromBase64(data);
package = bytes(reinterpret_cast<unsigned char const*>(data.constData()), reinterpret_cast<unsigned char const*>(data.constData() + data.size()));
dec.decrypt(expected, package);
got = sha3(package);
if (got != expected)
throw dev::Exception() << errinfo_comment("Dapp content hash does not match");
}
RLP rlp(package);
loadDapp(rlp);
}
catch (...)
{
qWarning() << tr("Error downloading DApp: ") << boost::current_exception_diagnostic_information().c_str();
emit dappError();
}
}
void DappLoader::loadDapp(RLP const& _rlp)
{
Dapp dapp;
unsigned len = _rlp.itemCountStrict();
dapp.manifest = loadManifest(_rlp[0].toString());
for (unsigned c = 1; c < len; ++c)
{
bytesConstRef content = _rlp[c].toBytesConstRef();
h256 hash = sha3(content);
auto entry = std::find_if(dapp.manifest.entries.cbegin(), dapp.manifest.entries.cend(), [=](ManifestEntry const& _e) { return _e.hash == hash; });
if (entry != dapp.manifest.entries.cend())
{
if (entry->path == "/deployment.js")
{
//inject web3 code
QString code;
code += contentsOfQResource(":/js/bignumber.min.js");
code += "\n";
code += contentsOfQResource(":/js/webthree.js");
code += "\n";
code += contentsOfQResource(":/js/setup.js");
code += "\n";
QByteArray res = code.toLatin1();
bytes b(res.data(), res.data() + res.size());
b.insert(b.end(), content.begin(), content.end());
dapp.content[hash] = b;
}
else
dapp.content[hash] = content.toBytes();
}
else
throw dev::Exception() << errinfo_comment("Dapp content hash does not match");
}
emit dappReady(dapp);
}
Manifest DappLoader::loadManifest(std::string const& _manifest)
{
/// https://github.com/ethereum/go-ethereum/wiki/URL-Scheme
Manifest manifest;
Json::Reader jsonReader;
Json::Value root;
jsonReader.parse(_manifest, root, false);
Json::Value entries = root["entries"];
for (Json::ValueIterator it = entries.begin(); it != entries.end(); ++it)
{
Json::Value const& entryValue = *it;
std::string path = entryValue["path"].asString();
if (path.size() == 0 || path[0] != '/')
path = "/" + path;
std::string contentType = entryValue["contentType"].asString();
std::string strHash = entryValue["hash"].asString();
if (strHash.length() == 64)
strHash = "0x" + strHash;
h256 hash = jsToFixed<32>(strHash);
unsigned httpStatus = entryValue["status"].asInt();
manifest.entries.push_back(ManifestEntry{ path, hash, contentType, httpStatus });
}
return manifest;
}
void DappLoader::loadDapp(QString const& _uri)
{
DappLocation location = resolveAppUri(_uri);
QUrl uri(location.contentUri);
QNetworkRequest request(uri);
m_uriHashes[uri] = location.contentHash;
m_net.get(request);
}

94
alethzero/DappLoader.h

@ -0,0 +1,94 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file DappLoader.h
* @author Arkadiy Paronyan <arkadiy@ethdev.org>
* @date 2015
*/
#pragma once
#include <map>
#include <string>
#include <vector>
#include <QObject>
#include <QString>
#include <QUrl>
#include <QNetworkAccessManager>
#include <libdevcore/FixedHash.h>
namespace dev
{
class WebThreeDirect;
class RLP;
}
struct ManifestEntry
{
std::string path;
dev::h256 hash;
std::string contentType;
unsigned httpStatus;
};
struct Manifest
{
std::vector<ManifestEntry> entries;
};
struct Dapp
{
Manifest manifest;
std::map<dev::h256, dev::bytes> content;
};
struct DappLocation
{
QString canonDomain;
QString path;
QString contentUri;
dev::h256 contentHash;
};
///Downloads, unpacks and prepares DApps for hosting
class DappLoader: public QObject
{
Q_OBJECT
public:
DappLoader(QObject* _parent, dev::WebThreeDirect* _web3);
///Load a new DApp. Resolves a name with a name reg contract. Asynchronous. dappReady is emitted once everything is read, dappError othervise
///@param _uri Eth name path
void loadDapp(QString const& _uri);
signals:
void dappReady(Dapp& _dapp);
void dappError();
private slots:
void downloadComplete(QNetworkReply* _reply);
private:
dev::WebThreeDirect* web3() const { return m_web3; }
DappLocation resolveAppUri(QString const& _uri);
void loadDapp(dev::RLP const& _rlp);
Manifest loadManifest(std::string const& _manifest);
dev::WebThreeDirect* m_web3;
QNetworkAccessManager m_net;
std::map<QUrl, dev::h256> m_uriHashes;
};

71
alethzero/MainWin.cpp

@ -19,7 +19,6 @@
* @date 2014
*/
#define QWEBENGINEINSPECTOR 1
#include <fstream>
// Make sure boost/asio.hpp is included before windows.h.
@ -68,6 +67,9 @@
#include "OurWebThreeStubServer.h"
#include "Transact.h"
#include "Debugger.h"
#include "DappLoader.h"
#include "DappHost.h"
#include "WebPage.h"
#include "ui_Main.h"
using namespace std;
using namespace dev;
@ -116,7 +118,9 @@ Address c_newConfig = Address("c6d9d2cd449a754c494264e1809c50e34d64562b");
Main::Main(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Main),
m_transact(this, this)
m_transact(this, this),
m_dappLoader(nullptr),
m_webPage(nullptr)
{
QtWebEngine::initialize();
setWindowFlags(Qt::Window);
@ -166,10 +170,12 @@ Main::Main(QWidget *parent) :
m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening();
WebPage* webPage= new WebPage(this);
m_webPage = webPage;
connect(webPage, &WebPage::consoleMessage, [this](QString const& _msg) { Main::addConsoleMessage(_msg, QString()); });
ui->webView->setPage(m_webPage);
connect(ui->webView, &QWebEngineView::loadFinished, [this]()
{
// f->disconnect();
// f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership);
auto f = ui->webView->page();
f->runJavaScript(contentsOfQResource(":/js/bignumber.min.js"));
f->runJavaScript(contentsOfQResource(":/js/webthree.js"));
@ -181,6 +187,9 @@ Main::Main(QWidget *parent) :
ui->tabWidget->setTabText(0, ui->webView->title());
});
m_dappHost.reset(new DappHost(8081));
m_dappLoader = new DappLoader(this, web3());
connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded);
// ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true);
// QWebEngineInspector* inspector = new QWebEngineInspector();
// inspector->setPage(page);
@ -425,13 +434,7 @@ void Main::eval(QString const& _js)
s = "<span style=\"color: #840\">" + jsonEv.toString().toHtmlEscaped() + "</span>";
else
s = "<span style=\"color: #888\">unknown type</span>";
m_consoleHistory.push_back(qMakePair(_js, s));
s = "<html><body style=\"margin: 0;\">" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%");
for (auto const& i: m_consoleHistory)
s += "<div style=\"border-bottom: 1 solid #eee; width: 100%\"><span style=\"float: left; width: 1em; color: #888; font-weight: bold\">&gt;</span><span style=\"color: #35d\">" + i.first.toHtmlEscaped() + "</span></div>"
"<div style=\"border-bottom: 1 solid #eee; width: 100%\"><span style=\"float: left; width: 1em\">&nbsp;</span><span>" + i.second + "</span></div>";
s += "</div></body></html>";
ui->jsConsole->setHtml(s);
addConsoleMessage(_js, s);
};
ui->webView->page()->runJavaScript("JSON.stringify(___RET)", f2);
};
@ -439,6 +442,17 @@ void Main::eval(QString const& _js)
ui->webView->page()->runJavaScript(c, f);
}
void Main::addConsoleMessage(QString const& _js, QString const& _s)
{
m_consoleHistory.push_back(qMakePair(_js, _s));
QString r = "<html><body style=\"margin: 0;\">" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%");
for (auto const& i: m_consoleHistory)
r += "<div style=\"border-bottom: 1 solid #eee; width: 100%\"><span style=\"float: left; width: 1em; color: #888; font-weight: bold\">&gt;</span><span style=\"color: #35d\">" + i.first.toHtmlEscaped() + "</span></div>"
"<div style=\"border-bottom: 1 solid #eee; width: 100%\"><span style=\"float: left; width: 1em\">&nbsp;</span><span>" + i.second + "</span></div>";
r += "</div></body></html>";
ui->jsConsole->setHtml(r);
}
static Public stringToPublic(QString const& _a)
{
string sn = _a.toStdString();
@ -780,15 +794,28 @@ void Main::on_jitvm_triggered()
void Main::on_urlEdit_returnPressed()
{
QString s = ui->urlEdit->text();
QRegExp r("([a-z]+://)?([^/]*)(.*)");
if (r.exactMatch(s))
if (r.cap(2).isEmpty())
s = (r.cap(1).isEmpty() ? "file://" : r.cap(1)) + r.cap(3);
QUrl url(s);
if (url.scheme().isEmpty() || url.scheme() == "eth")
{
try
{
//try do resolve dapp url
m_dappLoader->loadDapp(s);
}
catch (...)
{
qWarning() << boost::current_exception_diagnostic_information().c_str();
}
}
if (url.scheme().isEmpty())
if (url.path().indexOf('/') < url.path().indexOf('.'))
url.setScheme("file");
else
s = (r.cap(1).isEmpty() ? "http://" : r.cap(1)) + lookup(r.cap(2)) + r.cap(3);
else{}
qDebug() << s;
ui->webView->setUrl(s);
url.setScheme("http");
else {}
qDebug() << url.toString();
ui->webView->page()->setUrl(url);
}
void Main::on_nameReg_textChanged()
@ -1835,3 +1862,9 @@ void Main::refreshWhispers()
ui->whispers->addItem(item);
}
}
void Main::dappLoaded(Dapp& _dapp)
{
QUrl url = m_dappHost->hostDapp(std::move(_dapp));
ui->webView->page()->setUrl(url);
}

14
alethzero/MainWin.h

@ -54,8 +54,11 @@ namespace jsonrpc {
class HttpServer;
}
class QQuickView;
class QWebEnginePage;
class OurWebThreeStubServer;
class DappLoader;
class DappHost;
struct Dapp;
using WatchHandler = std::function<void(dev::eth::LocalisedLogEntries const&)>;
@ -99,6 +102,7 @@ public slots:
private slots:
void eval(QString const& _js);
void addConsoleMessage(QString const& _js, QString const& _s);
// Application
void on_about_triggered();
@ -172,6 +176,9 @@ private slots:
void refreshBlockChain();
void addNewId(QString _ids);
// Dapps
void dappLoaded(Dapp& _dapp); //qt does not support rvalue refs for signals
signals:
void poll();
@ -234,8 +241,6 @@ private:
QString m_privateChain;
dev::Address m_nameReg;
QNetworkAccessManager m_webCtrl;
QList<QPair<QString, QString>> m_consoleHistory;
QMutex m_logLock;
QString m_logHistory;
@ -248,4 +253,7 @@ private:
NatspecHandler m_natSpecDB;
Transact m_transact;
std::unique_ptr<DappHost> m_dappHost;
DappLoader* m_dappLoader;
QWebEnginePage* m_webPage;
};

4
alethzero/Transact.cpp

@ -175,7 +175,7 @@ void Transact::rejigData()
m_data = fromHex(src);
else if (sourceIsSolidity(src))
{
dev::solidity::CompilerStack compiler;
dev::solidity::CompilerStack compiler(true);
try
{
// compiler.addSources(dev::solidity::StandardSources);
@ -287,7 +287,7 @@ void Transact::on_send_clicked()
if (sourceIsSolidity(src))
try
{
dev::solidity::CompilerStack compiler;
dev::solidity::CompilerStack compiler(true);
m_data = compiler.compile(src, ui->optimize->isChecked());
for (string const& s: compiler.getContractNames())
{

28
alethzero/WebPage.cpp

@ -0,0 +1,28 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file WebPage.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com>
* @date 2015
*/
#include "WebPage.h"
void WebPage::javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID)
{
QString prefix = _level == QWebEnginePage::ErrorMessageLevel ? "error" : _level == QWebEnginePage::WarningMessageLevel ? "warning" : "";
emit consoleMessage(QString("%1(%2:%3):%4").arg(prefix).arg(_sourceID).arg(_lineNumber).arg(_message));
}

40
alethzero/WebPage.h

@ -0,0 +1,40 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file WebPage.h
* @author Arkadiy Paronyan arkadiy@ethdev.com>
* @date 2015
*/
#pragma once
#include <QString>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic" //QtWebEngineWidgets/qwebenginecertificateerror.h:78:348: error: extra ';'
#include <QtWebEngineWidgets/QWebEnginePage>
#pragma GCC diagnostic pop
class WebPage: public QWebEnginePage
{
Q_OBJECT
public:
WebPage(QObject* _parent): QWebEnginePage(_parent) { }
signals:
void consoleMessage(QString const& _msg);
protected:
void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID) override;
};

18
libnatspec/NatspecExpressionEvaluator.cpp

@ -35,25 +35,23 @@ static QString contentsOfQResource(string const& _res)
return in.readAll();
}
NatspecExpressionEvaluator::NatspecExpressionEvaluator(QString const& _abi, QString const& _method, QString const& _params)
NatspecExpressionEvaluator::NatspecExpressionEvaluator(QString const& _abi, QString const& _transaction, QString const& _method)
: m_abi(_abi), m_transaction(_transaction), m_method(_method)
{
Q_INIT_RESOURCE(natspec);
QJSValue result = m_engine.evaluate(contentsOfQResource(":/natspec/natspec.js"));
if (result.isError())
BOOST_THROW_EXCEPTION(FileError());
m_engine.evaluate("globals.abi = " + _abi);
m_engine.evaluate("globals.method = " + _method);
m_engine.evaluate("globals.params = " + _params);
m_engine.evaluate("var natspec = require('natspec')");
}
QString NatspecExpressionEvaluator::evalExpression(QString const& _expression)
{
QJSValue result = m_engine.evaluate("evaluateExpression(\"" + _expression + "\")");
if (result.isError())
{
cerr << "Could not evaluate expression: \"" << _expression.toStdString() << "\"" << endl;
return _expression;
}
QString call = "";
if (!m_abi.isEmpty() && !m_transaction.isEmpty() && !m_method.isEmpty())
call = ", {abi:" + m_abi + ", transaction:" + m_transaction + ", method: '" + m_method + "' }";
QJSValue result = m_engine.evaluate("natspec.evaluateExpressionSafe(\"" + _expression + "\"" + call + ")");
return result.toString();
}

9
libnatspec/NatspecExpressionEvaluator.h

@ -34,12 +34,10 @@ class NatspecExpressionEvaluator
public:
/// Construct natspec expression evaluator
/// @params abi - contract's abi in json format, passed as string
/// @params transaction - json object containing transaction data
/// @params method - name of the contract's method for which we evaluate the natspec.
/// If we want to use raw string, it should be passed with quotation marks eg. "\"helloWorld\""
/// If we pass string "helloWorld", the value of the object with name "helloWorld" will be used
/// @params params - array of method input params, passed as string, objects in array should be
/// javascript valid objects
NatspecExpressionEvaluator(QString const& _abi = "[]", QString const& _method = "", QString const& _params = "[]");
NatspecExpressionEvaluator(QString const& _abi = "[]", QString const& _transaction = "{}", QString const& _method = "");
/// Should be called to evaluate natspec expression
/// @params expression - natspec expression
@ -48,4 +46,7 @@ public:
private:
QJSEngine m_engine;
QString m_abi;
QString m_transaction;
QString m_method;
};

102
libnatspec/natspec.js

@ -1,102 +0,0 @@
/**
* This plugin exposes 'evaluateExpression' method which should be used
* to evaluate natspec description
*/
/// Object which should be used by NatspecExpressionEvaluator
/// abi - abi of the contract that will be used
/// method - name of the method that is called
/// params - input params of the method that will be called
var globals = {
abi: [],
method: "",
params: []
};
/// Helper method
/// Should be called to copy values from object to global context
var copyToContext = function (obj, context) {
var keys = Object.keys(obj);
keys.forEach(function (key) {
context[key] = obj[key];
});
}
/// Helper method
/// Should be called to get method with given name from the abi
/// @param contract's abi
/// @param name of the method that we are looking for
var getMethodWithName = function(abi, name) {
for (var i = 0; i < abi.length; i++) {
if (abi[i].name === name) {
return abi[i];
}
}
//console.warn('could not find method with name: ' + name);
return undefined;
};
/// Function called to get all contract's storage values
/// @returns hashmap with contract properties which are used
/// TODO: check if this function will be used
var getContractProperties = function (address, abi) {
return {};
};
/// Function called to get all contract's methods
/// @returns hashmap with used contract's methods
/// TODO: check if this function will be used
var getContractMethods = function (address, abi) {
//return web3.eth.contract(address, abi); // commented out web3 usage
return {};
};
/// Function called to get all contract method input variables
/// @returns hashmap with all contract's method input variables
var getMethodInputParams = function (method, params) {
return method.inputs.reduce(function (acc, current, index) {
acc[current.name] = params[index];
return acc;
}, {});
};
/// Should be called to evaluate single expression
/// Is internally using javascript's 'eval' method
/// Should be checked if it is safe
var evaluateExpression = function (expression) {
var self = this;
//var storage = getContractProperties(address, abi);
//var methods = getContractMethods(address, abi);
var method = getMethodWithName(globals.abi, globals.method);
if (method) {
var input = getMethodInputParams(method, globals.params);
copyToContext(input, self);
}
// TODO: test if it is safe
var evaluatedExpression = "";
// match everything in `` quotes
var pattern = /\`(?:\\.|[^`\\])*\`/gim
var match;
var lastIndex = 0;
while ((match = pattern.exec(expression)) !== null) {
var startIndex = pattern.lastIndex - match[0].length;
var toEval = match[0].slice(1, match[0].length - 1);
evaluatedExpression += expression.slice(lastIndex, startIndex);
evaluatedExpression += eval(toEval).toString();
lastIndex = pattern.lastIndex;
}
evaluatedExpression += expression.slice(lastIndex);
return evaluatedExpression;
};

2
libnatspec/natspec.qrc

@ -1,5 +1,5 @@
<RCC>
<qresource prefix="/natspec">
<file>natspec.js</file>
<file alias="natspec.js">natspecjs/dist/natspec.min.js</file>
</qresource>
</RCC>

3
libnatspec/natspecjs/.gitignore

@ -0,0 +1,3 @@
# VIM stuff
*.swp
node_modules/

8
libnatspec/natspecjs/.travis.yml

@ -0,0 +1,8 @@
language: node_js
node_js:
- "0.11"
- "0.10"
before_script:
- npm install
after_script:
- npm run-script test-coveralls

47
libnatspec/natspecjs/README.md

@ -0,0 +1,47 @@
# natspec.js
Javascript Library used to evaluate natspec expressions
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url]
[travis-image]: https://travis-ci.org/ethereum/natspec.js.svg
[travis-url]: https://travis-ci.org/ethereum/natspec.js
[coveralls-image]: https://coveralls.io/repos/ethereum/natspec.js/badge.svg?branch=master
[coveralls-url]: https://coveralls.io/r/ethereum/natspec.js?branch=master
## Usage
It exposes global object `natspec` with method `evaluateExpression`.
```javascript
var natspec = require('natspec');
var natspecExpression = "Will multiply `a` by 7 and return `a * 7`.";
var call = {
method: 'multiply',
abi: abi,
transaction: transaction
};
var evaluatedExpression = natspec.evaluateExpression(natspecExpression, call);
console.log(evaluatedExpression); // "Will multiply 4 by 7 and return 28."
```
More examples are available [here](https://github.com/ethereum/natspec.js/blob/master/test/test.js).
## Building
```bash
npm run-script build
```
## Testing (mocha)
```bash
npm test
```
## Wiki
* [Ethereum Natural Specification Format](https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format)
* [Natspec Example](https://github.com/ethereum/wiki/wiki/Natspec-Example)

3570
libnatspec/natspecjs/dist/natspec.js

File diff suppressed because it is too large

1
libnatspec/natspecjs/dist/natspec.js.map

File diff suppressed because one or more lines are too long

2
libnatspec/natspecjs/dist/natspec.min.js

File diff suppressed because one or more lines are too long

13
libnatspec/natspecjs/example/example.html

@ -0,0 +1,13 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="../dist/natspec.js"></script>
<script type="text/javascript">
var natspec = require('natspec');
</script>
</head>
<body>
</body>
</html>

186
libnatspec/natspecjs/natspec.js

@ -0,0 +1,186 @@
/*
This file is part of natspec.js.
natspec.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
natspec.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with natspec.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file natspec.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var abi = require('./node_modules/ethereum.js/lib/abi.js');
/**
* This object should be used to evaluate natspec expression
* It has one method evaluateExpression which shoul be used
*/
var natspec = (function () {
/**
* Helper method
* Should be called to copy values from object to global context
*
* @method copyToContext
* @param {Object} object from which we want to copy properties
* @param {Object} object to which we copy
*/
var copyToContext = function (obj, context) {
Object.keys(obj).forEach(function (key) {
context[key] = obj[key];
});
}
/**
* Should be used to generate codes, which will be evaluated
*
* @method generateCode
* @param {Object} object from which code will be generated
* @return {String} javascript code which is used to initalized variables
*/
var generateCode = function (obj) {
return Object.keys(obj).reduce(function (acc, key) {
return acc + "var " + key + " = context['" + key + "'];\n";
}, "");
};
/**
* Helper method
* Should be called to get method with given name from the abi
*
* @method getMethodWithName
* @param {Array} contract's abi
* @param {String} name of the method that we are looking for
* @return {Object} abi for method with name
*/
var getMethodWithName = function(abi, name) {
return abi.filter(function (method) {
return method.name === name;
})[0];
};
/**
* Should be used to get all contract method input variables
*
* @method getMethodInputParams
* @param {Object} abi for certain method
* @param {Object} transaction object
* @return {Object} object with all contract's method input variables
*/
var getMethodInputParams = function (method, transaction) {
// do it with output formatter (cause we have to decode)
var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10));
return method.inputs.reduce(function (acc, current, index) {
acc[current.name] = params[index];
return acc;
}, {});
};
/**
* Should be called when we want to evaluate natspec expression
* Replaces all natspec 'subexpressions' with evaluated value
*
* @method mapExpressionToEvaluate
* @param {String} expression to evaluate
* @param {Function} callback which is called to evaluate te expression
* @return {String} evaluated expression
*/
var mapExpressionsToEvaluate = function (expression, cb) {
var evaluatedExpression = "";
// match everything in `` quotes
var pattern = /\`(?:\\.|[^`\\])*\`/gim
var match;
var lastIndex = 0;
try {
while ((match = pattern.exec(expression)) !== null) {
var startIndex = pattern.lastIndex - match[0].length;
var toEval = match[0].slice(1, match[0].length - 1);
evaluatedExpression += expression.slice(lastIndex, startIndex);
var evaluatedPart = cb(toEval);
evaluatedExpression += evaluatedPart;
lastIndex = pattern.lastIndex;
}
evaluatedExpression += expression.slice(lastIndex);
}
catch (err) {
throw new Error("Natspec evaluation failed, wrong input params");
}
return evaluatedExpression;
};
/**
* Should be called to evaluate single expression
* Is internally using javascript's 'eval' method
*
* @method evaluateExpression
* @param {String} expression which should be evaluated
* @param {Object} [call] object containing contract abi, transaction, called method
* @return {String} evaluated expression
* @throws exception if method is not found or we are trying to evaluate input params that does not exists
*/
var evaluateExpression = function (expression, call) {
//var self = this;
var context = {};
if (!!call) {
try {
var method = getMethodWithName(call.abi, call.method);
var params = getMethodInputParams(method, call.transaction);
copyToContext(params, context);
}
catch (err) {
throw new Error("Natspec evaluation failed, method does not exist");
}
}
var code = generateCode(context);
var evaluatedExpression = mapExpressionsToEvaluate(expression, function (toEval) {
var fn = new Function("context", code + "return " + toEval + ";");
return fn(context).toString();
});
return evaluatedExpression;
};
/**
* Safe version of evaluateExpression
* Instead of throwing an exception it returns it as a string
*
* @method evaluateExpressionSafe
* @param {String} expression which should be evaluated
* @param {Object} [call] object containing contract abi, transaction, called method
* @return {String} evaluated expression
*/
var evaluateExpressionSafe = function (expression, call) {
try {
return evaluateExpression(expression, call);
}
catch (err) {
return err.message;
}
};
return {
evaluateExpression: evaluateExpression,
evaluateExpressionSafe: evaluateExpressionSafe
};
})();
module.exports = natspec;

24
libnatspec/natspecjs/package.json

@ -0,0 +1,24 @@
{
"name": "natspec.js",
"version": "0.0.1",
"description": "Javascript Library used to evaluate natspec expressions",
"main": "natspec.js",
"scripts": {
"build": "cd dist && browserify -r ../natspec.js:natspec -i crypto -o natspec.js && uglifyjs natspec.js --source-map natspec.js.map -c -m -o natspec.min.js",
"test": "mocha",
"test-coveralls": "istanbul cover _mocha -- -R spec && cat coverage/lcov.info | coveralls --verbose"
},
"author": "",
"dependencies": {
"ethereum.js": "ethereum/ethereum.js#master"
},
"devDependencies": {
"browserify": "^9.0.3",
"chai": "^2.1.0",
"coveralls": "^2.11.2",
"istanbul": "^0.3.6",
"mocha": "^2.1.0",
"uglify-js": "^2.4.16"
},
"license": "LGPL-3.0"
}

149
libnatspec/natspecjs/test/test.js

@ -0,0 +1,149 @@
var chai = require('chai');
var natspec = require('../natspec.js');
var assert = chai.assert;
describe('natspec', function () {
it('should evaluate simple expression', function () {
// given
var expression = "`x = 1` + `y = 2` will be equal `x + y`";
// when
var result = natspec.evaluateExpression(expression);
var result2 = natspec.evaluateExpressionSafe(expression);
// then
assert.equal(result, "1 + 2 will be equal 3");
assert.equal(result2, "1 + 2 will be equal 3");
});
it('should evalute expression using input params', function () {
//given
var expression = "Will multiply `a` by 7 and return `a * 7`.";
var method = 'multiply';
var abi = [{
"name": "multiply",
"constant": false,
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}],
"outputs": [{
"name": "d",
"type": "uint256"
}]
}];
var transaction = {
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": "0x8521742d3f456bd237e312d6e30724960f72517a",
"data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a"
}],
"id": 6
};
var call = {
method: method,
abi: abi,
transaction: transaction
};
// when
var result = natspec.evaluateExpression(expression, call);
var result2 = natspec.evaluateExpressionSafe(expression, call);
// then
assert.equal(result, "Will multiply 122 by 7 and return 854.");
assert.equal(result2, "Will multiply 122 by 7 and return 854.");
});
it('should evalute expression using input params', function () {
//given
var expression = "Will multiply `a` by 7 and return `a * 7`.";
var method = 'multiply';
var abi = [{
"name": "multiply",
"constant": false,
"type": "function",
"inputs": [{
"name": "b",
"type": "uint256"
}],
"outputs": [{
"name": "d",
"type": "uint256"
}]
}];
var transaction = {
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": "0x8521742d3f456bd237e312d6e30724960f72517a",
"data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a"
}],
"id": 6
};
var call = {
method: method,
abi: abi,
transaction: transaction
};
// when
var exceptionThrow = function () { natspec.evaluateExpression(expression, call);}
var result = natspec.evaluateExpressionSafe(expression, call);
// then
assert.equal(result, "Natspec evaluation failed, wrong input params");
assert.throws(exceptionThrow, "Natspec evaluation failed, wrong input params");
});
it('should evalute expression using input params', function () {
//given
var expression = "Will multiply `a` by 7 and return `a * 7`.";
var method = 'multiply2';
var abi = [{
"name": "multiply",
"constant": false,
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}],
"outputs": [{
"name": "d",
"type": "uint256"
}]
}];
var transaction = {
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": "0x8521742d3f456bd237e312d6e30724960f72517a",
"data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a"
}],
"id": 6
};
var call = {
method: method,
abi: abi,
transaction: transaction
};
// when
var exceptionThrow = function () { natspec.evaluateExpression(expression, call);}
var result = natspec.evaluateExpressionSafe(expression, call);
// then
assert.equal(result, "Natspec evaluation failed, method does not exist");
assert.throws(exceptionThrow, "Natspec evaluation failed, method does not exist");
});
});

4
libsolidity/Compiler.h

@ -94,8 +94,8 @@ private:
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
unsigned m_modifierDepth = 0;
FunctionDefinition const* m_currentFunction;
unsigned m_stackCleanupForReturn; ///< this number of stack elements need to be removed before jump to m_returnTag
FunctionDefinition const* m_currentFunction = nullptr;
unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag
// arguments for base constructors, filled in derived-to-base order
std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments;
};

2
libsolidity/Parser.h

@ -34,6 +34,8 @@ class Scanner;
class Parser
{
public:
Parser() {}
ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
std::shared_ptr<std::string const> const& getSourceName() const;

3
mix/AppContext.cpp

@ -37,6 +37,7 @@
#include "QVariableDefinition.h"
#include "HttpServer.h"
#include "AppContext.h"
#include "SortFilterProxyModel.h"
using namespace dev;
using namespace dev::eth;
@ -74,6 +75,7 @@ void AppContext::load()
qmlRegisterType<QBoolType>("org.ethereum.qml.QBoolType", 1, 0, "QBoolType");
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration");
qmlRegisterType<RecordLogEntry>("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry");
qmlRegisterType<SortFilterProxyModel>("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel");
QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml"));
QObject* projectModel = projectModelComponent.create();
if (projectModelComponent.isError())
@ -86,6 +88,7 @@ void AppContext::load()
m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
QWindow *window = qobject_cast<QWindow*>(m_applicationEngine->rootObjects().at(0));
window->setIcon(QIcon(":/res/mix_256x256x32.png"));

9
mix/CodeEditorExtensionManager.cpp

@ -51,14 +51,6 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
return;
}
void CodeEditorExtensionManager::initExtensions()
{
std::shared_ptr<StatusPane> output = std::make_shared<StatusPane>(m_appContext);
QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight);
initExtension(output);
}
void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)
{
if (!_ext->contentUrl().isEmpty())
@ -93,5 +85,4 @@ void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView)
void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView)
{
m_headerView = _headerView;
initExtensions(); //TODO: move this to a proper place
}

2
mix/CodeEditorExtensionManager.h

@ -49,8 +49,6 @@ class CodeEditorExtensionManager: public QObject
public:
CodeEditorExtensionManager();
~CodeEditorExtensionManager();
/// Initialize all extensions.
void initExtensions();
/// Initialize extension.
void initExtension(std::shared_ptr<Extension>);
/// Set current tab view

2
mix/CodeModel.h

@ -164,6 +164,8 @@ public:
/// Find a contract by document id
/// @returns CompiledContract object or null if not found
Q_INVOKABLE CompiledContract* contractByDocumentId(QString _documentId) const;
/// Reset code model
Q_INVOKABLE void reset() { reset(QVariantMap()); }
signals:
/// Emited on compilation state change

2
mix/FileIo.cpp

@ -158,6 +158,7 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder)
}
rlpStr.appendList(k);
manifest["entries"] = entries;
std::stringstream jsonStr;
jsonStr << manifest;
QByteArray b = QString::fromStdString(jsonStr.str()).toUtf8();
@ -166,7 +167,6 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder)
for (unsigned int k = 0; k < files.size(); k++)
rlpStr.append(files.at(k));
manifest["entries"] = entries;
bytes dapp = rlpStr.out();
dev::h256 dappHash = dev::sha3(dapp);
//encrypt

156
mix/SortFilterProxyModel.cpp

@ -0,0 +1,156 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/**
* @author Yann <yann@ethdev.com>
* @date 2015
* Proxy used to filter a QML TableView.
*/
#include "SortFilterProxyModel.h"
#include <QtDebug>
#include <QtQml>
using namespace dev::mix;
SortFilterProxyModel::SortFilterProxyModel(QObject* _parent) : QSortFilterProxyModel(_parent)
{
connect(this, &SortFilterProxyModel::rowsInserted, this, &SortFilterProxyModel::countChanged);
connect(this, &SortFilterProxyModel::rowsRemoved, this, &SortFilterProxyModel::countChanged);
}
int SortFilterProxyModel::count() const
{
return rowCount();
}
QObject* SortFilterProxyModel::source() const
{
return sourceModel();
}
void SortFilterProxyModel::setSource(QObject* _source)
{
setSourceModel(qobject_cast<QAbstractItemModel*>(_source));
}
QByteArray SortFilterProxyModel::sortRole() const
{
return roleNames().value(QSortFilterProxyModel::sortRole());
}
void SortFilterProxyModel::setSortRole(QByteArray const& _role)
{
QSortFilterProxyModel::setSortRole(roleKey(_role));
}
void SortFilterProxyModel::setSortOrder(Qt::SortOrder _order)
{
QSortFilterProxyModel::sort(0, _order);
}
QString SortFilterProxyModel::filterString() const
{
return filterRegExp().pattern();
}
void SortFilterProxyModel::setFilterString(QString const& _filter)
{
setFilterRegExp(QRegExp(_filter, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax())));
}
SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const
{
return static_cast<FilterSyntax>(filterRegExp().patternSyntax());
}
void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax _syntax)
{
setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(_syntax)));
}
QJSValue SortFilterProxyModel::get(int _idx) const
{
QJSEngine *engine = qmlEngine(this);
QJSValue value = engine->newObject();
if (_idx >= 0 && _idx < count())
{
QHash<int, QByteArray> roles = roleNames();
QHashIterator<int, QByteArray> it(roles);
while (it.hasNext())
{
it.next();
value.setProperty(QString::fromUtf8(it.value()), data(index(_idx, 0), it.key()).toString());
}
}
return value;
}
int SortFilterProxyModel::roleKey(QByteArray const& _role) const
{
QHash<int, QByteArray> roles = roleNames();
QHashIterator<int, QByteArray> it(roles);
while (it.hasNext())
{
it.next();
if (it.value() == _role)
return it.key();
}
return -1;
}
QHash<int, QByteArray> SortFilterProxyModel::roleNames() const
{
if (QAbstractItemModel* source = sourceModel())
return source->roleNames();
return QHash<int, QByteArray>();
}
bool SortFilterProxyModel::filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const
{
QAbstractItemModel* model = sourceModel();
QModelIndex sourceIndex = model->index(_sourceRow, 0, _sourceParent);
if (!sourceIndex.isValid())
return true;
QString keyType = model->data(sourceIndex, roleKey(type.toUtf8())).toString();
QString keyContent = model->data(sourceIndex, roleKey(content.toUtf8())).toString();
return keyType.contains(m_filterType) && keyContent.contains(m_filterContent);
}
void SortFilterProxyModel::setFilterType(QString const& _type)
{
m_filterType = QRegExp(_type, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax()));
setFilterRegExp(_type);
}
QString SortFilterProxyModel::filterType() const
{
return m_filterType.pattern();
}
void SortFilterProxyModel::setFilterContent(QString const& _content)
{
m_filterContent = QRegExp(_content, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax()));
setFilterRegExp(_content);
}
QString SortFilterProxyModel::filterContent() const
{
return m_filterContent.pattern();
}

97
mix/SortFilterProxyModel.h

@ -0,0 +1,97 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/**
* @author Yann <yann@ethdev.com>
* @date 2015
* Proxy used to filter a QML TableView.
*/
#pragma once
#include <QtCore/qsortfilterproxymodel.h>
#include <QtQml/qjsvalue.h>
namespace dev
{
namespace mix
{
class SortFilterProxyModel: public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(QObject* source READ source WRITE setSource)
Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole)
Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder)
Q_PROPERTY(QString filterContent READ filterContent WRITE setFilterContent)
Q_PROPERTY(QString filterType READ filterType WRITE setFilterType)
Q_PROPERTY(QString filterString READ filterString WRITE setFilterString)
Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax)
Q_ENUMS(FilterSyntax)
public:
explicit SortFilterProxyModel(QObject* _parent = 0);
QObject* source() const;
void setSource(QObject* _source);
QByteArray sortRole() const;
void setSortRole(QByteArray const& _role);
void setSortOrder(Qt::SortOrder _order);
QString filterContent() const;
void setFilterContent(QString const& _content);
QString filterType() const;
void setFilterType(QString const& _type);
QString filterString() const;
void setFilterString(QString const& _filter);
enum FilterSyntax {
RegExp,
Wildcard,
FixedString
};
FilterSyntax filterSyntax() const;
void setFilterSyntax(FilterSyntax _syntax);
int count() const;
Q_INVOKABLE QJSValue get(int _index) const;
signals:
void countChanged();
protected:
int roleKey(QByteArray const& _role) const;
QHash<int, QByteArray> roleNames() const;
bool filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const;
private:
QRegExp m_filterType;
QRegExp m_filterContent;
const QString type = "type";
const QString content = "content";
};
}
}

302
mix/qml/LogsPane.qml

@ -0,0 +1,302 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.SortFilterProxyModel 1.0
import "."
Rectangle
{
function push(_level, _type, _content)
{
_content = _content.replace(/\n/g, " ")
logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level });
}
anchors.fill: parent
radius: 5
color: LogsPaneStyle.generic.layout.backgroundColor
border.color: LogsPaneStyle.generic.layout.borderColor
border.width: LogsPaneStyle.generic.layout.borderWidth
ColumnLayout {
z: 2
height: parent.height
width: parent.width
spacing: 0
Row
{
id: rowAction
Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight
height: LogsPaneStyle.generic.layout.headerHeight
anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin
anchors.left: parent.left
spacing: LogsPaneStyle.generic.layout.headerButtonSpacing
Button
{
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
action: clearAction
iconSource: "qrc:/qml/img/broom.png"
}
Action {
id: clearAction
enabled: logsModel.count > 0
tooltip: qsTr("Clear")
onTriggered: {
logsModel.clear()
}
}
Button
{
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
action: copytoClipBoardAction
iconSource: "qrc:/qml/img/copy.png"
}
Action {
id: copytoClipBoardAction
enabled: logsModel.count > 0
tooltip: qsTr("Copy to Clipboard")
onTriggered: {
var content = "";
for (var k = 0; k < logsModel.count; k++)
{
var log = logsModel.get(k);
content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n";
}
appContext.toClipboard(content);
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height - 10
color : "#808080"
}
ToolButton {
id: javascriptButton
checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("javascript")
}
tooltip: qsTr("JavaScript")
style:
ButtonStyle {
label:
Item {
DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
color: LogsPaneStyle.generic.layout.logLabelColor
anchors.centerIn: parent
text: qsTr("JavaScript")
}
}
}
}
ToolButton {
id: runButton
checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("run")
}
tooltip: qsTr("Run")
style:
ButtonStyle {
label:
Item {
DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
color: LogsPaneStyle.generic.layout.logLabelColor
anchors.centerIn: parent
text: qsTr("Run")
}
}
}
}
ToolButton {
id: stateButton
checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("state")
}
tooltip: qsTr("State")
style:
ButtonStyle {
label:
Item {
DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
color: "#5391d8"
anchors.centerIn: parent
text: qsTr("State")
}
}
}
}
ToolButton {
id: compilationButton
checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
checked: false
onCheckedChanged: {
proxyModel.toogleFilter("compilation")
}
tooltip: qsTr("Compilation")
style:
ButtonStyle {
label:
Item {
DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
color: "#5391d8"
anchors.centerIn: parent
text: qsTr("Compilation")
}
}
}
}
DefaultTextField
{
id: searchBox
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
width: LogsPaneStyle.generic.layout.headerInputWidth
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
font.italic: true
onTextChanged: {
proxyModel.search(text);
}
}
}
ListModel {
id: logsModel
}
TableView {
id: logsTable
clip: true
Layout.fillWidth: true
Layout.preferredHeight: parent.height - rowAction.height
headerVisible : false
onDoubleClicked:
{
var log = logsModel.get((logsTable.currentRow));
if (log)
appContext.toClipboard(log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content);
}
model: SortFilterProxyModel {
id: proxyModel
source: logsModel
property var roles: ["-", "javascript", "run", "state"]
Component.onCompleted: {
filterType = regEx(proxyModel.roles);
}
function search(_value)
{
filterContent = _value;
}
function toogleFilter(_value)
{
var count = roles.length;
for (var i in roles)
{
if (roles[i] === _value)
{
roles.splice(i, 1);
break;
}
}
if (count === roles.length)
roles.push(_value);
filterType = regEx(proxyModel.roles);
}
function regEx(_value)
{
return "(?:" + roles.join('|') + ")";
}
filterType: "(?:javascript|run|state)"
filterContent: ""
filterSyntax: SortFilterProxyModel.RegExp
filterCaseSensitivity: Qt.CaseInsensitive
}
TableViewColumn
{
role: "date"
title: qsTr("date")
width: LogsPaneStyle.generic.layout.dateWidth
delegate: itemDelegate
}
TableViewColumn
{
role: "type"
title: qsTr("type")
width: LogsPaneStyle.generic.layout.typeWidth
delegate: itemDelegate
}
TableViewColumn
{
role: "content"
title: qsTr("content")
width: LogsPaneStyle.generic.layout.contentWidth
delegate: itemDelegate
}
rowDelegate: Item {
Rectangle {
width: logsTable.width - 4
height: 17
color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor
}
}
}
Component {
id: itemDelegate
DefaultLabel {
text: styleData.value;
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-1)
color: {
if (proxyModel.get(styleData.row).level === "error")
return "red";
else if (proxyModel.get(styleData.row).level === "warning")
return "orange";
else
return "#808080";
}
}
}
}
}

29
mix/qml/LogsPaneStyle.qml

@ -0,0 +1,29 @@
pragma Singleton
import QtQuick 2.0
QtObject {
function absoluteSize(rel)
{
return systemPointSize + rel;
}
property QtObject generic: QtObject {
property QtObject layout: QtObject {
property string backgroundColor: "#f7f7f7"
property string borderColor: "#5391d8"
property int borderWidth: 1
property int headerHeight: 35
property int headerButtonSpacing: 5
property int leftMargin: 10
property int headerButtonHeight: 30
property string logLabelColor: "#5391d8"
property string logLabelFont: "sans serif"
property int headerInputWidth: 200
property int dateWidth: 150
property int typeWidth: 80
property int contentWidth: 700
property string logAlternateColor: "#f0f0f0"
}
}
}

32
mix/qml/MainContent.qml

@ -35,8 +35,8 @@ Rectangle {
onCompilationComplete: {
if (firstCompile) {
firstCompile = false;
if (runOnProjectLoad)
startQuickDebugging();
if (runOnProjectLoad)
startQuickDebugging();
}
}
}
@ -102,7 +102,6 @@ Rectangle {
}
CodeEditorExtensionManager {
headerView: headerPaneTabs;
}
Settings {
@ -116,6 +115,7 @@ Rectangle {
ColumnLayout
{
id: mainColumn
anchors.fill: parent
spacing: 0
Rectangle {
@ -133,21 +133,15 @@ Rectangle {
}
id: headerPaneContainer
anchors.fill: parent
TabView {
id: headerPaneTabs
tabsVisible: false
antialiasing: true
StatusPane
{
anchors.fill: parent
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {}
frame: Rectangle { color: "transparent" }
}
webPreview: webPreview
}
}
}
Rectangle{
Rectangle {
Layout.fillWidth: true
height: 1
color: "#8c8c8c"
@ -168,9 +162,9 @@ Rectangle {
{
anchors.fill: parent
handleDelegate: Rectangle {
width: 1
height: 1
color: "#8c8c8c"
width: 1
height: 1
color: "#8c8c8c"
}
orientation: Qt.Horizontal
@ -180,16 +174,18 @@ Rectangle {
Layout.minimumWidth: 250
Layout.fillHeight: true
}
Rectangle {
id: contentView
Layout.fillHeight: true
Layout.fillWidth: true
SplitView {
handleDelegate: Rectangle {
handleDelegate: Rectangle {
width: 1
height: 1
color: "#8c8c8c"
}
}
id: codeWebSplitter
anchors.fill: parent
orientation: Qt.Vertical

10
mix/qml/ProjectModel.qml

@ -64,7 +64,7 @@ Item {
Connections {
target: appContext
onAppLoaded: {
if (projectSettings.lastProjectPath)
if (projectSettings.lastProjectPath && projectSettings.lastProjectPath !== "")
projectModel.loadProject(projectSettings.lastProjectPath)
}
}
@ -130,6 +130,14 @@ Item {
id: projectStateListModel
}
Connections
{
target: projectModel
onProjectClosed: {
projectSettings.lastProjectPath = "";
}
}
Settings {
id: projectSettings
property string lastProjectPath;

5
mix/qml/StateListModel.qml

@ -134,6 +134,7 @@ Item {
onProjectClosed: {
stateListModel.clear();
stateList = [];
codeModel.reset();
}
onProjectLoading: stateListModel.loadStatesFromProject(projectData);
onProjectSaving: {
@ -148,6 +149,7 @@ Item {
state.title = qsTr("Default");
projectData.states = [ state ];
projectData.defaultStateIndex = 0;
stateListModel.loadStatesFromProject(projectData);
}
}
@ -264,6 +266,7 @@ Item {
defaultStateIndex--;
save();
}
function save() {
@ -284,6 +287,8 @@ Item {
else
defaultStateIndex = 0;
var items = projectData.states;
stateListModel.clear();
stateList = [];
for(var i = 0; i < items.length; i++) {
var item = fromPlainStateItem(items[i]);
stateListModel.append(item);

170
mix/qml/StatusPane.qml

@ -8,6 +8,7 @@ import "."
Rectangle {
id: statusHeader
objectName: "statusPane"
property variant webPreview
function updateStatus(message)
{
@ -15,7 +16,6 @@ Rectangle {
{
status.state = "";
status.text = qsTr("Compile successfully.");
logslink.visible = false;
debugImg.state = "active";
}
else
@ -23,39 +23,76 @@ Rectangle {
status.state = "error";
var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true);
status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail;
logslink.visible = true;
debugImg.state = "";
errorMessage(status.text, "Compilation");
}
debugRunActionIcon.enabled = codeModel.hasContract;
}
function infoMessage(text)
function infoMessage(text, type)
{
status.state = "";
status.text = text
logslink.visible = false;
logPane.push("info", type, text);
}
function errorMessage(text)
function warningMessage(text, type)
{
status.state = "warning";
status.text = text
logPane.push("warning", type, text);
}
function errorMessage(text, type)
{
status.state = "error";
status.text = text
logslink.visible = false;
logPane.push("error", type, text);
}
Connections {
target: webPreview
onJavaScriptMessage:
{
if (_level === 0)
infoMessage(_content, "JavaScript")
else
{
var message = _sourceId.substring(_sourceId.lastIndexOf("/") + 1) + " - " + qsTr("line") + " " + _lineNb + " - " + _content;
if (_level === 1)
warningMessage(message, "JavaScript")
else
errorMessage(message, "JavaScript")
}
}
}
Connections {
target:clientModel
onRunStarted: infoMessage(qsTr("Running transactions..."));
onRunFailed: errorMessage(qsTr("Error running transactions: " + _message));
onRunComplete: infoMessage(qsTr("Run complete"));
onNewBlock: infoMessage(qsTr("New block created"));
onRunStarted: infoMessage(qsTr("Running transactions..."), "Run");
onRunFailed: errorMessage(format(_message), "Run");
onRunComplete: infoMessage(qsTr("Run complete"), "Run");
onNewBlock: infoMessage(qsTr("New block created"), "State");
function format(_message)
{
var formatted = _message.match(/(?:<dev::eth::)(.+)(?:>)/);
if (formatted.length > 1)
formatted = formatted[1] + ": ";
else
return _message;
var exceptionInfos = _message.match(/(?:tag_)(.+)/g);
for (var k in exceptionInfos)
formatted += " " + exceptionInfos[k].replace("*]", "").replace("tag_", "").replace("=", "");
return formatted;
}
}
Connections {
target:projectModel
onDeploymentStarted: infoMessage(qsTr("Running deployment..."));
onDeploymentError: errorMessage(error);
onDeploymentComplete: infoMessage(qsTr("Deployment complete"));
onDeploymentStepChanged: infoMessage(message);
onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "Deployment");
onDeploymentError: errorMessage(error, "Deployment");
onDeploymentComplete: infoMessage(qsTr("Deployment complete"), "Deployment");
onDeploymentStepChanged: infoMessage(message, "Deployment");
}
Connections {
target: codeModel
@ -74,6 +111,24 @@ Rectangle {
width: 500
height: 30
color: "#fcfbfc"
states: [
State {
name: "logsOpened"
PropertyChanges {
target: statusContainer
border.color: "#5391d8"
border.width: 1
}
},
State {
name: "logsClosed"
PropertyChanges {
target: statusContainer
border.color: "#5391d8"
border.width: 0
}
}
]
Text {
anchors.verticalCenter: parent.verticalCenter
@ -98,6 +153,17 @@ Rectangle {
target: statusContainer
color: "#fffcd5"
}
},
State {
name: "warning"
PropertyChanges {
target: status
color: "orange"
}
PropertyChanges {
target: statusContainer
color: "#fffcd5"
}
}
]
onTextChanged:
@ -127,30 +193,73 @@ Rectangle {
color: "transparent"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
logsContainer.toggle();
}
}
}
Action {
id: toolTipInfo
tooltip: ""
}
}
Button
{
id: logslink
anchors.left: statusContainer.right
anchors.leftMargin: 9
visible: false
anchors.verticalCenter: parent.verticalCenter
action: displayLogAction
iconSource: "qrc:/qml/img/search_filled.png"
}
Rectangle
{
function toggle()
{
if (logsContainer.state === "opened")
{
statusContainer.state = "logsClosed";
logsContainer.state = "closed"
}
else
{
statusContainer.state = "logsOpened";
logsContainer.state = "opened";
logsContainer.focus = true;
forceActiveFocus();
}
}
Action {
id: displayLogAction
tooltip: qsTr("Display Log")
onTriggered: {
mainContent.displayCompilationErrorIfAny();
id: logsContainer
width: 1000
height: 0
anchors.topMargin: 10
anchors.top: statusContainer.bottom
anchors.horizontalCenter: parent.horizontalCenter
visible: false
radius: 5
Component.onCompleted:
{
var top = logsContainer;
while (top.parent)
top = top.parent
var coordinates = logsContainer.mapToItem(top, 0, 0)
logsContainer.parent = top;
logsContainer.x = coordinates.x
logsContainer.y = coordinates.y
}
LogsPane
{
id: logPane
}
states: [
State {
name: "opened";
PropertyChanges { target: logsContainer; height: 500; visible: true }
},
State {
name: "closed";
PropertyChanges { target: logsContainer; height: 0; visible: false }
}
]
transitions: Transition {
NumberAnimation { properties: "height"; easing.type: Easing.InOutQuad; duration: 200 }
NumberAnimation { properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 }
}
}
}
@ -168,7 +277,6 @@ Rectangle {
{
color: "transparent"
anchors.fill: parent
Button
{
anchors.right: parent.right

7
mix/qml/WebPreview.qml

@ -12,6 +12,7 @@ Item {
id: webPreview
property string pendingPageUrl: ""
property bool initialized: false
signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content)
function setPreviewUrl(url) {
if (!initialized)
@ -198,7 +199,6 @@ Item {
{
setPreviewUrl(text);
}
focus: true
}
@ -216,7 +216,9 @@ Item {
anchors.verticalCenter: parent.verticalCenter
width: 21
height: 21
focus: true
}
CheckBox {
id: autoReloadOnSave
checked: true
@ -227,6 +229,7 @@ Item {
text: qsTr("Auto reload on save")
}
}
focus: true
}
}
}
@ -240,7 +243,7 @@ Item {
id: webView
experimental.settings.localContentCanAccessRemoteUrls: true
onJavaScriptConsoleMessage: {
console.log(sourceID + ":" + lineNumber + ":" + message);
webPreview.javaScriptMessage(level, sourceID, lineNumber, message);
}
onLoadingChanged: {
if (!loading) {

BIN
mix/qml/img/broom.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

BIN
mix/qml/img/copy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

21
mix/qml/js/ProjectModel.js

@ -105,7 +105,6 @@ function loadProject(path) {
contractSources[doc.documentId] = fileIo.readFile(doc.path);
}
codeModel.reset(contractSources);
}
function addFile(fileName) {
@ -377,8 +376,6 @@ function finalizeDeployment(deploymentId, addresses) {
else
insertAt += 6;
html = html.substr(0, insertAt) +
"<script src=\"bignumber.min.js\"></script>" +
"<script src=\"ethereum.js\"></script>" +
"<script src=\"deployment.js\"></script>" +
html.substr(insertAt);
fileIo.writeFile(deploymentDir + doc.fileName, html);
@ -388,9 +385,8 @@ function finalizeDeployment(deploymentId, addresses) {
}
//write deployment js
var deploymentJs =
"// Autogenerated by Mix\n" +
"web3 = require(\"web3\");\n" +
"contracts = {};\n";
"// Autogenerated by Mix\n" +
"contracts = {};\n";
for (var c in codeModel.contracts) {
var contractAccessor = "contracts[\"" + codeModel.contracts[c].contract.name + "\"]";
deploymentJs += contractAccessor + " = {\n" +
@ -400,9 +396,6 @@ function finalizeDeployment(deploymentId, addresses) {
contractAccessor + ".contract = web3.eth.contract(" + contractAccessor + ".address, " + contractAccessor + ".interface);\n";
}
fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs);
//copy scripts
fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js");
fileIo.copyFile("qrc:///js/webthree.js", deploymentDir + "ethereum.js");
deploymentAddresses = addresses;
saveProject();
@ -435,7 +428,7 @@ function checkEthPath(dappUrl, callBack)
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ],
params: [ { "gas": 150000, "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
@ -472,7 +465,7 @@ function checkRegistration(dappUrl, addr, callBack)
//getOwner()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ],
params: [ { "gas" : 2000, "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ],
id: jsonRpcRequestId++
});
@ -537,7 +530,7 @@ function checkRegistration(dappUrl, addr, callBack)
//setRegister()
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "from": deploymentDialog.currentAccount, "gas": 2000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ],
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ],
id: jsonRpcRequestId++
});
@ -570,7 +563,7 @@ function registerContentHash(registrar, callBack)
//setContent()
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "from": deploymentDialog.currentAccount, "gas": 2000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + deploymentDialog.packageHash } ],
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + deploymentDialog.packageHash } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
@ -587,7 +580,7 @@ function registerToUrlHint()
//urlHint => suggestUrl
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 2000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ],
params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 30000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ],
id: jsonRpcRequestId++
});

1
mix/qml/qmldir

@ -5,3 +5,4 @@ singleton DebuggerPaneStyle 1.0 DebuggerPaneStyle.qml
singleton StateStyle 1.0 StateStyle.qml
singleton StatusPaneStyle 1.0 StatusPaneStyle.qml
singleton WebPreviewStyle 1.0 WebPreviewStyle.qml
singleton LogsPaneStyle 1.0 LogsPaneStyle.qml

4
mix/res.qrc

@ -111,5 +111,9 @@
<file>qml/img/exit.png</file>
<file>qml/img/run.png</file>
<file>qml/img/note.png</file>
<file>qml/LogsPane.qml</file>
<file>qml/img/copy.png</file>
<file>qml/img/broom.png</file>
<file>qml/LogsPaneStyle.qml</file>
</qresource>
</RCC>

32
pullSubtree.sh

@ -0,0 +1,32 @@
# usage
# ./pullsubtree [repository branch] [repository2 branch2]
#
# example
# ./pullSubtree evmjit master
# ./pullSubtree ethereumjs develop
# ./pullSubtree evmjit master ethereumjs master
evmjit_repo="https://github.com/ethereum/evmjit"
evmjit_location="evmjit"
ethereumjs_repo="https://github.com/ethereum/ethereum.js"
ethereumjs_location="libjsqrc/ethereumjs"
natspecjs_repo="https://github.com/ethereum/natspec.js"
natspecjs_location="libnatspec/natspecjs"
while [ "$1" != "" ]; do
case $1 in
evmjit | ethereumjs | natspecjs )
REPO="${1}_repo"
REPO=${!REPO}
LOCATION="${1}_location"
LOCATION=${!LOCATION}
shift
BRANCH=$1
git subtree pull --prefix=${LOCATION} ${REPO} ${BRANCH} --squash
;;
esac
shift
done

7
solc/CommandLineInterface.cpp

@ -345,18 +345,11 @@ bool CommandLineInterface::processInput()
void CommandLineInterface::handleAst(string const& _argStr)
{
string title;
string suffix;
if (_argStr == g_argAstStr)
{
title = "Syntax trees:";
suffix = ".ast";
}
else if (_argStr == g_argAstJson)
{
title = "JSON AST:";
suffix = ".json";
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal argStr for AST"));

41
test/TestHelper.cpp

@ -181,46 +181,35 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost)
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());
// export post state
json_spirit::mObject postState;
for (auto const& a: _statePost.addresses())
{
json_spirit::mObject o;
o["balance"] = toString(_statePost.balance(a.first));
o["nonce"] = toString(_statePost.transactionsFrom(a.first));
{
json_spirit::mObject store;
for (auto const& s: _statePost.storage(a.first))
store["0x"+toHex(toCompactBigEndian(s.first))] = "0x"+toHex(toCompactBigEndian(s.second));
o["storage"] = store;
}
o["code"] = "0x" + toHex(_statePost.code(a.first));
postState[toString(a.first)] = o;
}
m_TestObject["post"] = json_spirit::mValue(postState);
m_TestObject["post"] = fillJsonWithState(_statePost);
m_TestObject["postStateRoot"] = toHex(_statePost.rootHash().asBytes());
// export pre state
json_spirit::mObject preState;
m_TestObject["pre"] = fillJsonWithState(m_statePre);
}
json_spirit::mObject fillJsonWithState(State _state)
{
// export pre state
json_spirit::mObject oState;
for (auto const& a: m_statePre.addresses())
for (auto const& a: _state.addresses())
{
json_spirit::mObject o;
o["balance"] = toString(m_statePre.balance(a.first));
o["nonce"] = toString(m_statePre.transactionsFrom(a.first));
o["balance"] = toString(_state.balance(a.first));
o["nonce"] = toString(_state.transactionsFrom(a.first));
{
json_spirit::mObject store;
for (auto const& s: m_statePre.storage(a.first))
for (auto const& s: _state.storage(a.first))
store["0x"+toHex(toCompactBigEndian(s.first))] = "0x"+toHex(toCompactBigEndian(s.second));
o["storage"] = store;
}
o["code"] = "0x" + toHex(m_statePre.code(a.first));
o["code"] = "0x" + toHex(_state.code(a.first));
preState[toString(a.first)] = o;
oState[toString(a.first)] = o;
}
m_TestObject["pre"] = json_spirit::mValue(preState);
return oState;
}
u256 toInt(json_spirit::mValue const& _v)

8
test/TestHelper.h

@ -117,6 +117,13 @@ private:
json_spirit::mObject& m_TestObject;
};
class ZeroGasPricer: public eth::GasPricer
{
protected:
u256 ask(eth::State const&) const override { return 0; }
u256 bid(eth::TransactionPriority = eth::TransactionPriority::Medium) const override { return 0; }
};
// helping functions
u256 toInt(json_spirit::mValue const& _v);
byte toByte(json_spirit::mValue const& _v);
@ -136,6 +143,7 @@ void userDefinedTest(std::string testTypeFlag, std::function<void(json_spirit::m
RLPStream createRLPStreamFromTransactionFields(json_spirit::mObject& _tObj);
void processCommandLineOptions();
eth::LastHashes lastHashes(u256 _currentBlockNumber);
json_spirit::mObject fillJsonWithState(eth::State _state);
template<typename mapType>
void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs)

18
test/bcUncleTestFiller.json

@ -265,23 +265,7 @@
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
{
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0000000000000000000000000000000000000000",
"difficulty" : "131072",
"extraData" : "0x",
"gasLimit" : "99902343",
"gasUsed" : "0",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"seedHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "0x54c98c82",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
"sameAsPreviousSibling" : "1"
}
]
}

14
test/blockchain.cpp

@ -52,6 +52,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
ImportTest importer(o["pre"].get_obj());
State state(Address(), OverlayDB(), BaseState::Empty);
importer.importState(o["pre"].get_obj(), state);
o["pre"] = fillJsonWithState(state);
state.commit();
if (_fillin)
@ -87,7 +88,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
// get txs
TransactionQueue txs;
TrivialGasPricer gp;
ZeroGasPricer gp;
BOOST_REQUIRE(blObj.count("transactions"));
for (auto const& txObj: blObj["transactions"].get_array())
{
@ -101,10 +102,18 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
BlockQueue uncleBlockQueue;
mArray aUncleList;
vector<BlockInfo> vBiUncles;
mObject uncleHeaderObj_pre;
for (auto const& uHObj: blObj["uncleHeaders"].get_array())
{
mObject uncleHeaderObj = uHObj.get_obj();
if (uncleHeaderObj.count("sameAsPreviousSibling"))
{
writeBlockHeaderToJson(uncleHeaderObj_pre, vBiUncles[vBiUncles.size()-1]);
aUncleList.push_back(uncleHeaderObj_pre);
vBiUncles.push_back(vBiUncles[vBiUncles.size()-1]);
continue;
}
BlockInfo uncleBlockFromFields = constructBlock(uncleHeaderObj);
// make uncle header valid
@ -123,6 +132,8 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
cnote << "import uncle in blockQueue";
RLPStream uncle = createFullBlockFromHeader(uncleBlockFromFields);
uncleBlockQueue.import(&uncle.out(), bc);
uncleHeaderObj_pre = uncleHeaderObj;
}
blObj["uncleHeaders"] = aUncleList;
@ -579,6 +590,7 @@ void updatePoW(BlockInfo& _bi)
ret = pow.mine(_bi, 10000, true, true); // tie(ret, blockFromFields.nonce)
Ethash::assignResult(ret.second, _bi);
}
_bi.hash = _bi.headerHash(WithNonce);
}
void writeBlockHeaderToJson(mObject& _o, const BlockInfo& _bi)

19
test/natspec.cpp

@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(natspec_eval_function_exists)
// given
NatspecExpressionEvaluator e;
// when
string result = e.evalExpression("`typeof evaluateExpression`").toStdString();
string result = e.evalExpression("`typeof natspec.evaluateExpression`").toStdString();
// then
BOOST_CHECK_EQUAL(result, "function");
}
@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(natspec_js_eval_input_params)
// given
char const* abi = R"([
{
"name": "f",
"name": "multiply",
"constant": false,
"type": "function",
"inputs": [
@ -94,7 +94,18 @@ BOOST_AUTO_TEST_CASE(natspec_js_eval_input_params)
]
}
])";
NatspecExpressionEvaluator e(abi, "'f'", "[4]");
char const* transaction = R"({
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": "0x8521742d3f456bd237e312d6e30724960f72517a",
"data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000004"
}],
"id": 6
})";
NatspecExpressionEvaluator e(abi, transaction , "multiply");
// when
string result = e.evalExpression("Will multiply `a` by 7 and return `a * 7`.").toStdString();
// then
@ -108,7 +119,7 @@ BOOST_AUTO_TEST_CASE(natspec_js_eval_error)
// when
string result = e.evalExpression("`test(`").toStdString();
// then
BOOST_CHECK_EQUAL(result, "`test(`");
BOOST_CHECK_EQUAL(result, "Natspec evaluation failed, wrong input params");
}
BOOST_AUTO_TEST_SUITE_END()

64
test/vm.cpp

@ -533,33 +533,43 @@ BOOST_AUTO_TEST_CASE(vmPerformanceTest)
}
}
//BOOST_AUTO_TEST_CASE(vmInputLimitsTest1)
//{
// for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
// {
// string arg = boost::unit_test::framework::master_test_suite().argv[i];
// if (arg == "--inputlimits" || arg == "--all")
// {
// auto start = chrono::steady_clock::now();
// dev::test::executeTests("vmInputLimitsTest1", "/VMTests", dev::test::doVMTests);
// auto end = chrono::steady_clock::now();
// auto duration(chrono::duration_cast<chrono::milliseconds>(end - start));
// cnote << "test duration: " << duration.count() << " milliseconds.\n";
// }
// }
//}
//BOOST_AUTO_TEST_CASE(vmInputLimitsTest2)
//{
// for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
// {
// string arg = boost::unit_test::framework::master_test_suite().argv[i];
// if (arg == "--inputlimits" || arg == "--all")
// dev::test::executeTests("vmInputLimitsTest2", "/VMTests", dev::test::doVMTests);
// }
//}
BOOST_AUTO_TEST_CASE(vmInputLimitsTest1)
{
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
{
string arg = boost::unit_test::framework::master_test_suite().argv[i];
if (arg == "--inputlimits" || arg == "--all")
{
auto start = chrono::steady_clock::now();
dev::test::executeTests("vmInputLimits1", "/VMTests", dev::test::doVMTests);
auto end = chrono::steady_clock::now();
auto duration(chrono::duration_cast<chrono::milliseconds>(end - start));
cnote << "test duration: " << duration.count() << " milliseconds.\n";
}
}
}
BOOST_AUTO_TEST_CASE(vmInputLimitsTest2)
{
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
{
string arg = boost::unit_test::framework::master_test_suite().argv[i];
if (arg == "--inputlimits" || arg == "--all")
dev::test::executeTests("vmInputLimits2", "/VMTests", dev::test::doVMTests);
}
}
BOOST_AUTO_TEST_CASE(vmInputLimitsLightTest)
{
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
{
string arg = boost::unit_test::framework::master_test_suite().argv[i];
if (arg == "--inputlimits" || arg == "--all")
dev::test::executeTests("vmInputLimitsLight", "/VMTests", dev::test::doVMTests);
}
}
BOOST_AUTO_TEST_CASE(vmRandom)
{

Loading…
Cancel
Save