diff --git a/CMakeLists.txt b/CMakeLists.txt index c715f1344..ec5ae1db9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,7 @@ add_subdirectory(test) add_subdirectory(eth) if (NOT HEADLESS) add_subdirectory(alethzero) + add_subdirectory(walleth) endif () unset(HEADLESS CACHE) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index e0415ca8b..2e313fb28 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -64,44 +64,6 @@ Main::Main(QWidget *parent) : g_logPost = [=](std::string const& s, char const* c) { simpleDebugOut(s, c); ui->log->addItem(QString::fromStdString(s)); }; m_client.reset(new Client("AlethZero")); - /* - ui->librariesView->setModel(m_libraryMan); - ui->graphsView->setModel(m_graphMan); - setWindowIcon(QIcon(":/Noted.png")); - - qmlRegisterSingletonType("com.llr", 1, 0, "Time", TimelineItem::constructTimeHelper); - qmlRegisterType("com.llr", 1, 0, "Graph"); - qmlRegisterType("com.llr", 1, 0, "CursorGraph"); - qmlRegisterType("com.llr", 1, 0, "Interval"); - qmlRegisterType("com.llr", 1, 0, "Cursor"); - qmlRegisterType("com.llr", 1, 0, "Timelines"); - qmlRegisterType("com.llr", 1, 0, "TimeLabels"); - qmlRegisterType("com.llr", 1, 0, "XLabels"); - qmlRegisterType("com.llr", 1, 0, "XScale"); - qmlRegisterType("com.llr", 1, 0, "YLabels"); - qmlRegisterType("com.llr", 1, 0, "YScale"); - - m_view = new QQuickView(); - QQmlContext* context = m_view->rootContext(); - context->setContextProperty("libs", libs()); - context->setContextProperty("compute", compute()); - context->setContextProperty("data", data()); - context->setContextProperty("graphs", graphs()); - context->setContextProperty("audio", audio()); - context->setContextProperty("view", view()); - m_view->setSource(QUrl("qrc:/Noted.qml")); - - QWidget* w = QWidget::createWindowContainer(m_view); - w->setAcceptDrops(true); - m_view->setResizeMode(QQuickView::SizeRootObjectToView); - ui->fullDisplay->insertWidget(0, w); - m_view->create(); - - m_timelinesItem = m_view->rootObject()->findChild("timelines"); - qDebug() << m_view->rootObject(); - */ - - readSettings(); refresh(); diff --git a/libethereum/Client.h b/libethereum/Client.h index 0844fc207..a334109a2 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -135,6 +135,8 @@ public: void connect(std::string const& _seedHost, unsigned short _port = 30303); /// Stop the network subsystem. void stopNetwork(); + /// Is the network subsystem up? + bool haveNetwork() { return !!m_net; } /// Get access to the peer server object. This will be null if the network isn't online. PeerServer* peerServer() const { return m_net.get(); } @@ -148,6 +150,8 @@ public: void startMining(); /// Stop mining. void stopMining(); + /// Are we mining now? + bool isMining() { return m_doMine; } /// Check the progress of the mining. MineProgress miningProgress() const { return m_mineProgress; } diff --git a/libethereum/PeerSession.cpp b/libethereum/PeerSession.cpp index 0bd7f53a9..eaedefdf0 100644 --- a/libethereum/PeerSession.cpp +++ b/libethereum/PeerSession.cpp @@ -430,16 +430,17 @@ void PeerSession::sendDestroy(bytes& _msg) auto self(shared_from_this()); bytes* buffer = new bytes(std::move(_msg)); - ba::async_write(m_socket, ba::buffer(*buffer), [self, buffer](boost::system::error_code ec, std::size_t length) - { - delete buffer; - if (ec) + if (m_socket.is_open()) + ba::async_write(m_socket, ba::buffer(*buffer), [self, buffer](boost::system::error_code ec, std::size_t length) { - cwarn << "Error sending: " << ec.message(); - self->dropped(); - } -// cbug << length << " bytes written (EC: " << ec << ")"; - }); + delete buffer; + if (ec) + { + cwarn << "Error sending: " << ec.message(); + self->dropped(); + } + // cbug << length << " bytes written (EC: " << ec << ")"; + }); } void PeerSession::send(bytesConstRef _msg) @@ -453,16 +454,17 @@ void PeerSession::send(bytesConstRef _msg) auto self(shared_from_this()); bytes* buffer = new bytes(_msg.toBytes()); - ba::async_write(m_socket, ba::buffer(*buffer), [self, buffer](boost::system::error_code ec, std::size_t length) - { - delete buffer; - if (ec) + if (m_socket.is_open()) + ba::async_write(m_socket, ba::buffer(*buffer), [self, buffer](boost::system::error_code ec, std::size_t length) { - cwarn << "Error sending: " << ec.message(); - self->dropped(); - } -// cbug << length << " bytes written (EC: " << ec << ")"; - }); + delete buffer; + if (ec) + { + cwarn << "Error sending: " << ec.message(); + self->dropped(); + } + // cbug << length << " bytes written (EC: " << ec << ")"; + }); } void PeerSession::dropped() diff --git a/walleth/CMakeLists.txt b/walleth/CMakeLists.txt new file mode 100644 index 000000000..24d9d34cf --- /dev/null +++ b/walleth/CMakeLists.txt @@ -0,0 +1,108 @@ +cmake_policy(SET CMP0015 NEW) + +if ("${TARGET_PLATFORM}" STREQUAL "w64") + cmake_policy(SET CMP0020 NEW) +endif () + + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +aux_source_directory(. SRC_LIST) + +include_directories(..) +link_directories(../libethereum) + +# Find Qt5 for Apple and update src_list for windows +if (APPLE) + # homebrew defaults to qt4 and installs qt5 as 'keg-only' + # which places it into /usr/local/opt insteadof /usr/local. + + set(CMAKE_PREFIX_PATH /usr/local/opt/qt5) + include_directories(/usr/local/opt/qt5/include /usr/local/include) +elseif (${TARGET_PLATFORM} STREQUAL "w64") + set(SRC_LIST ${SRC_LIST} ../windows/qt_plugin_import.cpp) +endif () + + +find_package(Qt5Widgets REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Quick REQUIRED) +find_package(Qt5Qml REQUIRED) +find_package(Qt5Network REQUIRED) +qt5_wrap_ui(ui_Main.h Main.ui) +qt5_add_resources(RESOURCE_ADDED Resources.qrc) + +# Set name of binary and add_executable() +if (APPLE) + set(EXECUTEABLE Walleth) + set(CMAKE_INSTALL_PREFIX ./) + set(BIN_INSTALL_DIR ".") + set(DOC_INSTALL_DIR ".") + + set(PROJECT_VERSION "${ETH_VERSION}") + set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") + set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}") + set(MACOSX_BUNDLE_BUNDLE_NAME ${EXECUTEABLE}) + include(BundleUtilities) + + add_executable(${EXECUTEABLE} MACOSX_BUNDLE Main.ui ${RESOURCE_ADDED} ${SRC_LIST}) +else () + set(EXECUTEABLE walleth) + add_executable(${EXECUTEABLE} Main.ui ${RESOURCE_ADDED} ${SRC_LIST}) +endif () + +qt5_use_modules(${EXECUTEABLE} Core Gui Widgets Network Quick Qml) +target_link_libraries(${EXECUTEABLE} ethereum secp256k1 ${CRYPTOPP_LIBRARIES}) + +if (APPLE) + set_target_properties(${EXECUTEABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/EthereumMacOSXBundleInfo.plist.in") + + SET_SOURCE_FILES_PROPERTIES(${EXECUTEABLE} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) + + # This is a workaround for when the build-type defaults to Debug, and when a multi-config generator like xcode is used, where the type + # will not be set but defaults to release. + set(generator_lowercase "${CMAKE_GENERATOR}") + string(TOLOWER "${CMAKE_GENERATOR}" generator_lowercase) + if (generator_lowercase STREQUAL "xcode") + # TODO: Not sure how to resolve this. Possibly \${TARGET_BUILD_DIR} + set(binary_build_dir "${CMAKE_CURRENT_BINARY_DIR}/Debug") + else () + set(binary_build_dir "${CMAKE_CURRENT_BINARY_DIR}") + endif () + + set(APPS ${binary_build_dir}/${EXECUTEABLE}.app) + + # This tool and the next will automatically looked at the linked libraries in order to determine what dependencies are required. Thus, target_link_libaries only needs to add ethereum and secp256k1 (above) + install(CODE " + include(BundleUtilities) + set(BU_CHMOD_BUNDLE_ITEMS 1) + fixup_bundle(\"${APPS}\" \"${BUNDLELIBS}\" \"../libethereum ../secp256k1\") + " COMPONENT RUNTIME ) + + add_custom_target(addframeworks ALL + COMMAND /usr/local/opt/qt5/bin/macdeployqt ${binary_build_dir}/${EXECUTEABLE}.app + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + DEPENDS ${PROJECT_NAME} + ) + +elseif (${TARGET_PLATFORM} STREQUAL "w64") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-keep-inline-dllexport -static-libgcc -static-libstdc++ -static") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-s -Wl,-subsystem,windows -mthreads -L/usr/x86_64-w64-mingw32/plugins/platforms") + target_link_libraries(${EXECUTEABLE} gcc) + target_link_libraries(${EXECUTEABLE} mingw32 qtmain mswsock iphlpapi qwindows shlwapi Qt5PlatformSupport gdi32 comdlg32 oleaut32 imm32 winmm ole32 uuid ws2_32) + target_link_libraries(${EXECUTEABLE} boost_system-mt-s) + target_link_libraries(${EXECUTEABLE} boost_filesystem-mt-s) + target_link_libraries(${EXECUTEABLE} boost_thread_win32-mt-s) + target_link_libraries(${EXECUTEABLE} Qt5PlatformSupport) + set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) +else () + target_link_libraries(${EXECUTEABLE} boost_system) + target_link_libraries(${EXECUTEABLE} boost_filesystem) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTEABLE} ${CMAKE_THREAD_LIBS_INIT}) + install( TARGETS ${EXECUTEABLE} RUNTIME DESTINATION bin ) +endif () + diff --git a/walleth/Main.ui b/walleth/Main.ui new file mode 100644 index 000000000..6904590a8 --- /dev/null +++ b/walleth/Main.ui @@ -0,0 +1,168 @@ + + + Main + + + + 0 + 0 + 562 + 488 + + + + Walleth + + + true + + + QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs + + + true + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 0 wei + + + + + + + 0 peers + + + + + + + 1 block + + + + + + + + + + + 0 + 0 + 562 + 20 + + + + + &File + + + + + + &Network + + + + + + + + T&ools + + + + + + + + &Help + + + + + + + + + + + + &Quit + + + + + true + + + true + + + Use &UPnP + + + + + &Connect to Peer... + + + + + true + + + Enable &Network + + + + + true + + + &Mine + + + + + &New Address + + + + + &About... + + + + + true + + + &Preview + + + + + + + diff --git a/walleth/MainWin.cpp b/walleth/MainWin.cpp new file mode 100644 index 000000000..95a4b2a4d --- /dev/null +++ b/walleth/MainWin.cpp @@ -0,0 +1,443 @@ +#include +#include +//#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BuildInfo.h" +#include "MainWin.h" +#include "ui_Main.h" +using namespace std; + +// types +using eth::bytes; +using eth::bytesConstRef; +using eth::h160; +using eth::h256; +using eth::u160; +using eth::u256; +using eth::Address; +using eth::BlockInfo; +using eth::Client; +using eth::Instruction; +using eth::KeyPair; +using eth::NodeMode; +using eth::PeerInfo; +using eth::RLP; +using eth::Secret; +using eth::Transaction; + +// functions +using eth::toHex; +using eth::assemble; +using eth::compileLisp; +using eth::disassemble; +using eth::formatBalance; +using eth::fromHex; +using eth::right160; +using eth::simpleDebugOut; +using eth::toLog2; +using eth::toString; +using eth::units; + +// vars +using eth::g_logPost; +using eth::g_logVerbosity; +using eth::c_instructionInfo; + +// Horrible global for the mainwindow. Needed for the QEthereums to find the Main window which acts as multiplexer for now. +// Can get rid of this once we've sorted out ITC for signalling & multiplexed querying. +Main* g_main = nullptr; + +QEthereum::QEthereum(QObject* _p): QObject(_p) +{ + connect(g_main, SIGNAL(changed()), SIGNAL(changed())); +} + +QEthereum::~QEthereum() +{ +} + +Client* QEthereum::client() const +{ + return g_main->client(); +} + +Address QEthereum::coinbase() const +{ + return client()->address(); +} + +void QEthereum::setCoinbase(Address _a) +{ + if (client()->address() != _a) + { + client()->setAddress(_a); + changed(); + } +} + +QAccount::QAccount(QObject* _p) +{ +} + +QAccount::~QAccount() +{ +} + +void QAccount::setEthereum(QEthereum* _eth) +{ + if (m_eth == _eth) + return; + if (m_eth) + disconnect(m_eth, SIGNAL(changed()), this, SIGNAL(changed())); + m_eth = _eth; + if (m_eth) + connect(m_eth, SIGNAL(changed()), this, SIGNAL(changed())); + ethChanged(); + changed(); +} + +u256 QAccount::balance() const +{ + if (m_eth) + return m_eth->balanceAt(m_address); + return 0; +} + +double QAccount::txCount() const +{ + if (m_eth) + return m_eth->txCountAt(m_address); + return 0; +} + +bool QAccount::isContract() const +{ + if (m_eth) + return m_eth->isContractAt(m_address); + return 0; +} + +u256 QEthereum::balanceAt(Address _a) const +{ + return client()->postState().balance(_a); +} + +bool QEthereum::isContractAt(Address _a) const +{ + return client()->postState().isContractAddress(_a); +} + +bool QEthereum::isMining() const +{ + return client()->isMining(); +} + +bool QEthereum::isListening() const +{ + return client()->haveNetwork(); +} + +void QEthereum::setMining(bool _l) +{ + if (_l) + client()->startMining(); + else + client()->stopMining(); +} + +void QEthereum::setListening(bool _l) +{ + if (_l) + client()->startNetwork(); + else + client()->stopNetwork(); +} + +double QEthereum::txCountAt(Address _a) const +{ + return (double)client()->postState().transactionsFrom(_a); +} + +unsigned QEthereum::peerCount() const +{ + return client()->peerCount(); +} + +void QEthereum::transact(Secret _secret, Address _dest, u256 _amount) +{ + client()->transact(_secret, _dest, _amount); +} + +Main::Main(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::Main) +{ + setWindowFlags(Qt::Window); + ui->setupUi(this); + setWindowIcon(QIcon(":/Ethereum.png")); + + g_main = this; + + m_client.reset(new Client("Walleth", Address(), eth::getDataDir() + "/Walleth")); + + qRegisterMetaType("eth::u256"); + qRegisterMetaType("eth::KeyPair"); + qRegisterMetaType("eth::Secret"); + qRegisterMetaType("eth::Address"); + qRegisterMetaType("QAccount*"); + qRegisterMetaType("QEthereum*"); + + qmlRegisterType("org.ethereum", 1, 0, "Ethereum"); + qmlRegisterType("org.ethereum", 1, 0, "Account"); + qmlRegisterSingletonType("org.ethereum", 1, 0, "Balance", QEthereum::constructU256Helper); + qmlRegisterSingletonType("org.ethereum", 1, 0, "Key", QEthereum::constructKeyHelper); + + /* + ui->librariesView->setModel(m_libraryMan); + ui->graphsView->setModel(m_graphMan); + */ + + m_view = new QQuickView(); + +// QQmlContext* context = m_view->rootContext(); +// context->setContextProperty("u256", new U256Helper(this)); + + m_view->setSource(QUrl("qrc:/Simple.qml")); + + QWidget* w = QWidget::createWindowContainer(m_view); + m_view->setResizeMode(QQuickView::SizeRootObjectToView); + ui->fullDisplay->insertWidget(0, w); + m_view->create(); + +// m_timelinesItem = m_view->rootObject()->findChild("timelines"); + + readSettings(); + refresh(); + + m_refreshNetwork = new QTimer(this); + connect(m_refreshNetwork, SIGNAL(timeout()), SLOT(refreshNetwork())); + m_refreshNetwork->start(1000); + + connect(this, SIGNAL(changed()), SLOT(refresh())); + + connect(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r) + { + m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts); + if (m_servers.size()) + { + ui->net->setChecked(true); + on_net_triggered(true); + } + }); + QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc" + QString(ETH_QUOTED(ETH_VERSION)).section('.', 1, 1) + ".txt")); + r.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1712.0 Safari/537.36"); + m_webCtrl.get(r); + srand(time(0)); + + startTimer(200); + + statusBar()->addPermanentWidget(ui->balance); + statusBar()->addPermanentWidget(ui->peerCount); + statusBar()->addPermanentWidget(ui->blockCount); +} + +Main::~Main() +{ + writeSettings(); +} + +void Main::timerEvent(QTimerEvent *) +{ + if (m_client->changed()) + changed(); +} + +void Main::on_about_triggered() +{ + QMessageBox::about(this, "About Walleth PoC-" + QString(ETH_QUOTED(ETH_VERSION)).section('.', 1, 1), "Walleth/v" ETH_QUOTED(ETH_VERSION) "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM) " - " ETH_QUOTED(ETH_COMMIT_HASH) "\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Tim Hughes, Eric Lombrozo, Marko Simovic, Alex Leverington and several others."); +} + +void Main::writeSettings() +{ + QSettings s("ethereum", "walleth"); + QByteArray b; + b.resize(sizeof(Secret) * m_myKeys.size()); + auto p = b.data(); + for (auto i: m_myKeys) + { + memcpy(p, &(i.secret()), sizeof(Secret)); + p += sizeof(Secret); + } + s.setValue("address", b); + + s.setValue("upnp", ui->upnp->isChecked()); + s.setValue("clientName", m_clientName); + s.setValue("idealPeers", m_idealPeers); + s.setValue("port", m_port); + + if (client()->peerServer()) + { + bytes d = client()->peerServer()->savePeers(); + m_peers = QByteArray((char*)d.data(), (int)d.size()); + + } + s.setValue("peers", m_peers); + + s.setValue("geometry", saveGeometry()); + s.setValue("windowState", saveState()); +} + +void Main::readSettings() +{ + QSettings s("ethereum", "walleth"); + + restoreGeometry(s.value("geometry").toByteArray()); + restoreState(s.value("windowState").toByteArray()); + + + QByteArray b = s.value("address").toByteArray(); + if (b.isEmpty()) + m_myKeys.append(KeyPair::create()); + else + { + h256 k; + for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) + { + memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); + m_myKeys.append(KeyPair(k)); + } + } + //m_eth->setAddress(m_myKeys.last().address()); + m_peers = s.value("peers").toByteArray(); + ui->upnp->setChecked(s.value("upnp", true).toBool()); + m_clientName = s.value("clientName", "").toString(); + m_idealPeers = s.value("idealPeers", 5).toInt(); + m_port = s.value("port", 30303).toInt(); +} + +void Main::refreshNetwork() +{ + auto ps = client()->peers(); + ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); +} + +eth::State const& Main::state() const +{ + return ui->preview->isChecked() ? client()->postState() : client()->state(); +} + +void Main::refresh() +{ + eth::ClientGuard l(client()); + auto const& st = state(); + + auto d = client()->blockChain().details(); + auto diff = BlockInfo(client()->blockChain().block()).difficulty; + ui->blockCount->setText(QString("#%1 @%3 T%2").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff))); + + m_keysChanged = false; + u256 totalBalance = 0; + for (auto i: m_myKeys) + { + u256 b = st.balance(i.address()); + totalBalance += b; + } + ui->balance->setText(QString::fromStdString(formatBalance(totalBalance))); +} + +void Main::on_net_triggered(bool _auto) +{ + string n = "Walleth/v" ETH_QUOTED(ETH_VERSION); + if (m_clientName.size()) + n += "/" + m_clientName.toStdString(); + n += "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM); + client()->setClientVersion(n); + if (ui->net->isChecked()) + { + if (_auto) + { + QString s = m_servers[rand() % m_servers.size()]; + client()->startNetwork(m_port, s.section(':', 0, 0).toStdString(), s.section(':', 1).toInt(), NodeMode::Full, m_idealPeers, std::string(), ui->upnp->isChecked()); + } + else + client()->startNetwork(m_port, string(), 0, NodeMode::Full, m_idealPeers, std::string(), ui->upnp->isChecked()); + if (m_peers.size()) + client()->peerServer()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); + } + else + client()->stopNetwork(); +} + +void Main::on_connect_triggered() +{ + if (!ui->net->isChecked()) + { + ui->net->setChecked(true); + on_net_triggered(); + } + bool ok = false; + QString s = QInputDialog::getItem(this, "Connect to a Network Peer", "Enter a peer to which a connection may be made:", m_servers, m_servers.count() ? rand() % m_servers.count() : 0, true, &ok); + if (ok && s.contains(":")) + { + string host = s.section(":", 0, 0).toStdString(); + unsigned short port = s.section(":", 1).toInt(); + client()->connect(host, port); + } +} + +void Main::on_mine_triggered() +{ + if (ui->mine->isChecked()) + { + client()->setAddress(m_myKeys.last().address()); + client()->startMining(); + } + else + client()->stopMining(); +} + +void Main::on_create_triggered() +{ + m_myKeys.append(KeyPair::create()); + m_keysChanged = true; +} + +// extra bits needed to link on VS +#ifdef _MSC_VER + +// include moc file, ofuscated to hide from automoc +#include\ +"moc_MainWin.cpp" + +// specify library dependencies, it's easier to do here than in the project since we can control the "d" debug suffix +#ifdef _DEBUG +#define QTLIB(x) x"d.lib" +#else +#define QTLIB(x) x".lib" +#endif + +#pragma comment(lib, QTLIB("Qt5PlatformSupport")) +#pragma comment(lib, QTLIB("Qt5Core")) +#pragma comment(lib, QTLIB("Qt5GUI")) +#pragma comment(lib, QTLIB("Qt5Widgets")) +#pragma comment(lib, QTLIB("Qt5Network")) +#pragma comment(lib, QTLIB("Qt5Quick")) +#pragma comment(lib, QTLIB("Qt5Declarative")) +#pragma comment(lib, QTLIB("Qt5Qml")) +#pragma comment(lib, QTLIB("qwindows")) +#pragma comment(lib, "Imm32.lib") +#pragma comment(lib, "opengl32.lib") +#pragma comment(lib, "winmm.lib") + + +#endif diff --git a/walleth/MainWin.h b/walleth/MainWin.h new file mode 100644 index 000000000..fcfc4028c --- /dev/null +++ b/walleth/MainWin.h @@ -0,0 +1,224 @@ +#ifndef MAIN_H +#define MAIN_H + +#include +#include +#include +#include +#include + +namespace Ui { +class Main; +} + +namespace eth { +class Client; +class State; +} + +class QQuickView; +class QQmlEngine; +class QJSEngine; + +class QEthereum; +class QAccount; + +Q_DECLARE_METATYPE(eth::u256) +Q_DECLARE_METATYPE(eth::Address) +Q_DECLARE_METATYPE(eth::Secret) +Q_DECLARE_METATYPE(eth::KeyPair) +Q_DECLARE_METATYPE(QEthereum*) +Q_DECLARE_METATYPE(QAccount*) + +class U256Helper: public QObject +{ + Q_OBJECT + +public: + U256Helper(QObject* _p = nullptr): QObject(_p) {} + + Q_INVOKABLE eth::u256 add(eth::u256 _a, eth::u256 _b) const { return _a + _b; } + Q_INVOKABLE eth::u256 sub(eth::u256 _a, eth::u256 _b) const { return _a - _b; } + Q_INVOKABLE eth::u256 mul(eth::u256 _a, int _b) const { return _a * _b; } + Q_INVOKABLE eth::u256 mul(int _a, eth::u256 _b) const { return _a * _b; } + Q_INVOKABLE eth::u256 div(eth::u256 _a, int _b) const { return _a / _b; } + + Q_INVOKABLE eth::u256 wei(double _s) const { return (eth::u256)_s; } + Q_INVOKABLE eth::u256 szabo(double _s) const { return (eth::u256)(_s * (double)eth::szabo); } + Q_INVOKABLE eth::u256 finney(double _s) const { return (eth::u256)(_s * (double)eth::finney); } + Q_INVOKABLE eth::u256 ether(double _s) const { return (eth::u256)(_s * (double)eth::ether); } + Q_INVOKABLE eth::u256 wei(unsigned _s) const { return (eth::u256)_s; } + Q_INVOKABLE eth::u256 szabo(unsigned _s) const { return (eth::u256)(_s * eth::szabo); } + Q_INVOKABLE eth::u256 finney(unsigned _s) const { return (eth::u256)(_s * eth::finney); } + Q_INVOKABLE eth::u256 ether(unsigned _s) const { return (eth::u256)(_s * eth::ether); } + Q_INVOKABLE double toWei(eth::u256 _t) const { return (double)_t; } + Q_INVOKABLE double toSzabo(eth::u256 _t) const { return toWei(_t) / (double)eth::szabo; } + Q_INVOKABLE double toFinney(eth::u256 _t) const { return toWei(_t) / (double)eth::finney; } + Q_INVOKABLE double toEther(eth::u256 _t) const { return toWei(_t) / (double)eth::ether; } + + Q_INVOKABLE double value(eth::u256 _t) const { return (double)_t; } + + Q_INVOKABLE QString stringOf(eth::u256 _t) const { return QString::fromStdString(eth::formatBalance(_t)); } +}; + +class KeyHelper: public QObject +{ + Q_OBJECT + +public: + KeyHelper(QObject* _p = nullptr): QObject(_p) {} + + Q_INVOKABLE eth::KeyPair create() const { return eth::KeyPair::create(); } + Q_INVOKABLE eth::Address address(eth::KeyPair _p) const { return _p.address(); } + Q_INVOKABLE eth::Secret secret(eth::KeyPair _p) const { return _p.secret(); } + Q_INVOKABLE eth::KeyPair keypair(eth::Secret _k) const { return eth::KeyPair(_k); } + + Q_INVOKABLE bool isNull(eth::Address _a) const { return !_a; } + + Q_INVOKABLE eth::Address addressOf(QString _s) const { return eth::Address(_s.toStdString()); } + Q_INVOKABLE QString stringOf(eth::Address _a) const { return QString::fromStdString(eth::toHex(_a.asArray())); } + Q_INVOKABLE QString toAbridged(eth::Address _a) const { return QString::fromStdString(_a.abridged()); } +}; + +class QAccount: public QObject +{ + Q_OBJECT + +public: + QAccount(QObject* _p = nullptr); + virtual ~QAccount(); + + Q_INVOKABLE QEthereum* ethereum() const { return m_eth; } + Q_INVOKABLE eth::u256 balance() const; + Q_INVOKABLE double txCount() const; + Q_INVOKABLE bool isContract() const; + + // TODO: past transactions models. + +public slots: + void setEthereum(QEthereum* _eth); + +signals: + void changed(); + void ethChanged(); + +private: + QEthereum* m_eth = nullptr; + eth::Address m_address; + + Q_PROPERTY(eth::u256 balance READ balance NOTIFY changed STORED false) + Q_PROPERTY(double txCount READ txCount NOTIFY changed STORED false) + Q_PROPERTY(bool isContract READ isContract NOTIFY changed STORED false) + Q_PROPERTY(eth::Address address MEMBER m_address NOTIFY changed) + Q_PROPERTY(QEthereum* ethereum READ ethereum WRITE setEthereum NOTIFY ethChanged) +}; + +class QEthereum: public QObject +{ + Q_OBJECT + +public: + QEthereum(QObject* _p = nullptr); + virtual ~QEthereum(); + + eth::Client* client() const; + + static QObject* constructU256Helper(QQmlEngine*, QJSEngine*) { return new U256Helper; } + static QObject* constructKeyHelper(QQmlEngine*, QJSEngine*) { return new KeyHelper; } + + Q_INVOKABLE eth::Address coinbase() const; + + Q_INVOKABLE bool isListening() const; + Q_INVOKABLE bool isMining() const; + + Q_INVOKABLE eth::u256 balanceAt(eth::Address _a) const; + Q_INVOKABLE double txCountAt(eth::Address _a) const; + Q_INVOKABLE bool isContractAt(eth::Address _a) const; + + Q_INVOKABLE unsigned peerCount() const; + + Q_INVOKABLE QEthereum* self() { return this; } + +public slots: + void transact(eth::Secret _secret, eth::Address _dest, eth::u256 _amount); + + void setCoinbase(eth::Address); + void setMining(bool _l); + + void setListening(bool _l); + +signals: + void changed(); +// void netChanged(); +// void miningChanged(); + +private: + Q_PROPERTY(eth::Address coinbase READ coinbase WRITE setCoinbase NOTIFY changed) + Q_PROPERTY(bool listening READ isListening WRITE setListening) + Q_PROPERTY(bool mining READ isMining WRITE setMining) +}; + +class Main : public QMainWindow +{ + Q_OBJECT + +public: + explicit Main(QWidget *parent = 0); + ~Main(); + + eth::Client* client() const { return m_client.get(); } + +private slots: + void on_connect_triggered(); + void on_mine_triggered(); + void on_create_triggered(); + void on_net_triggered(bool _auto = false); + void on_about_triggered(); + void on_preview_triggered() { refresh(); } + void on_quit_triggered() { close(); } + + void refresh(); + void refreshNetwork(); + +signals: + void changed(); + +protected: + virtual void timerEvent(QTimerEvent *); + +private: +/* QString pretty(eth::Address _a) const; + QString render(eth::Address _a) const; + eth::Address fromString(QString const& _a) const; +*/ + eth::State const& state() const; + + void updateFee(); + void readSettings(); + void writeSettings(); + + eth::u256 fee() const; + eth::u256 total() const; + eth::u256 value() const; + + std::unique_ptr ui; + + QByteArray m_peers; + QMutex m_guiLock; + QTimer* m_refresh; + QTimer* m_refreshNetwork; + QVector m_myKeys; + bool m_keysChanged = false; + int m_port; + int m_idealPeers; + QString m_clientName; + QStringList m_servers; + + QQuickView* m_view; + + QNetworkAccessManager m_webCtrl; + + std::unique_ptr m_client; +}; + +#endif // MAIN_H diff --git a/walleth/Resources.qrc b/walleth/Resources.qrc new file mode 100644 index 000000000..1789216ed --- /dev/null +++ b/walleth/Resources.qrc @@ -0,0 +1,5 @@ + + + Simple.qml + + diff --git a/walleth/Simple.qml b/walleth/Simple.qml new file mode 100644 index 000000000..53cb7def4 --- /dev/null +++ b/walleth/Simple.qml @@ -0,0 +1,38 @@ +import QtQml 2.2 +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import Qt.labs.settings 1.0 +import org.ethereum 1.0 + +Item { + id: main + anchors.fill: parent + anchors.margins: 9 + +// Qt.application.name: "Walleth" +// Qt.application.organization: "Ethereum" +// Qt.application.domain: "org.ethereum" + + Ethereum { + id: eth + } + + Account { + id: myAccount + address: Key.addressOf("84fc4ba9373c30bfe32d8c5a502854e7f1175878") + ethereum: eth + // TODO: state: eth.latest // could be eth.pending + // will provide balance, txCount, isContract, incomingTransactions (list model), outgoingTransactions (list model). + // transaction lists' items will provide value, from, to, final balance. + } + + // KeyPair provides makeTransaction(recvAddress, value, data (array)) + + Text { + text: "Balance: " + Balance.stringOf(myAccount.balance) + " [" + myAccount.txCount + "]" + "\nAccount: " + Key.stringOf(myAccount.address) + Layout.minimumHeight: 30 + Layout.fillHeight: true + Layout.fillWidth: true + } +} diff --git a/walleth/main.cpp b/walleth/main.cpp new file mode 100644 index 000000000..42afd5e66 --- /dev/null +++ b/walleth/main.cpp @@ -0,0 +1,11 @@ +#include "MainWin.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + Main w; + w.show(); + + return a.exec(); +} diff --git a/windows/Ethereum.sln b/windows/Ethereum.sln index 1be0ab848..f2d148a47 100644 --- a/windows/Ethereum.sln +++ b/windows/Ethereum.sln @@ -23,7 +23,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibCryptoPP", "LibCryptoPP. EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibMiniUPnPc", "LibMiniUPnPc.vcxproj", "{1B1CA20E-39C3-4D9B-AC37-3783048E6672}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Alethzero", "Alethzero.vcxproj", "{BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AlethZero", "Alethzero.vcxproj", "{BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Walleth", "Walleth.vcxproj", "{326EF470-463F-4751-A22A-48BBAAD8B143}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -105,6 +107,14 @@ Global {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Release|Win32.Build.0 = Release|Win32 {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Release|x64.ActiveCfg = Release|x64 {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Release|x64.Build.0 = Release|x64 + {326EF470-463F-4751-A22A-48BBAAD8B143}.Debug|Win32.ActiveCfg = Debug|Win32 + {326EF470-463F-4751-A22A-48BBAAD8B143}.Debug|Win32.Build.0 = Debug|Win32 + {326EF470-463F-4751-A22A-48BBAAD8B143}.Debug|x64.ActiveCfg = Debug|x64 + {326EF470-463F-4751-A22A-48BBAAD8B143}.Debug|x64.Build.0 = Debug|x64 + {326EF470-463F-4751-A22A-48BBAAD8B143}.Release|Win32.ActiveCfg = Release|Win32 + {326EF470-463F-4751-A22A-48BBAAD8B143}.Release|Win32.Build.0 = Release|Win32 + {326EF470-463F-4751-A22A-48BBAAD8B143}.Release|x64.ActiveCfg = Release|x64 + {326EF470-463F-4751-A22A-48BBAAD8B143}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/windows/UseQt.props b/windows/UseQt.props index b3a177623..2e6efc488 100644 --- a/windows/UseQt.props +++ b/windows/UseQt.props @@ -4,7 +4,7 @@ ../../Qt/$(Platform) $(QtDir)/qtbase/bin - $(QtDir)/qtbase/include;../../Qt/Src/qtbase/include + $(QtDir)/qtbase/include;../../Qt/Src/qtbase/include;../../Qt/Src/qtdeclarative/include $(QtDir)/qtbase/lib;$(QtDir)/qtbase/plugins/platforms diff --git a/windows/Walleth.vcxproj b/windows/Walleth.vcxproj new file mode 100644 index 000000000..81076d0ef --- /dev/null +++ b/windows/Walleth.vcxproj @@ -0,0 +1,212 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + Win32Proj + Walleth + Walleth + {326EF470-463F-4751-A22A-48BBAAD8B143} + + + + Application + true + v120 + + + Application + true + v120 + + + Application + false + true + v120 + + + Application + false + true + v120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + false + + + false + + + + Use + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + true + stdafx.h + + + Windows + true + true + + + + + Use + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + true + stdafx.h + + + Windows + true + + + + + Use + MaxSpeed + true + true + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreaded + true + AnySuitable + stdafx.h + + + Windows + true + true + true + true + + + + + Use + MaxSpeed + true + true + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreaded + true + AnySuitable + stdafx.h + + + Windows + true + true + true + + + + + {826e68cb-d3ee-4a8a-b540-59a8c3f38d4f} + + + + + + + + Create + Create + Create + Create + + + + + + "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + $(IntDir)moc_%(FileName).cpp + + + "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + $(IntDir)moc_%(FileName).cpp + $(IntDir)moc_%(FileName).cpp + $(IntDir)moc_%(FileName).cpp + + + + + + Document + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + $(IntDir)ui_%(FileName).h + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + $(IntDir)ui_%(FileName).h + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + $(IntDir)ui_%(FileName).h + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + $(IntDir)ui_%(FileName).h + + + + + + \ No newline at end of file diff --git a/windows/Walleth.vcxproj.filters b/windows/Walleth.vcxproj.filters new file mode 100644 index 000000000..404370754 --- /dev/null +++ b/windows/Walleth.vcxproj.filters @@ -0,0 +1,28 @@ + + + + + + + + Windows + + + Windows + + + + + + + + + {37f5b48c-b602-474b-8683-041898e328b5} + + + + + Windows + + + \ No newline at end of file diff --git a/windows/compile_qt.bat b/windows/compile_qt.bat index c9f0d4ef5..8f813c081 100644 --- a/windows/compile_qt.bat +++ b/windows/compile_qt.bat @@ -20,22 +20,29 @@ rem : assume our root Qt dir is the current dir set QT=%CD% rem : create the build folder and add the qtbase/bin folder to the PATH -if not exist %QT%\%PLATFORM% mkdir %QT%\%PLATFORM% +if not exist %QT%\%PLATFORM% ( + set DO_CONFIGURE=1 + mkdir %QT%\%PLATFORM% +) else ( + set DO_CONFIGURE=0 +) if %USE_PREFIX%==1 ( if not exist %QT%\%PLATFORM%-Build mkdir %QT%\%PLATFORM%-Build if not exist %QT%\%PLATFORM%\qtbase mkdir %QT%\%PLATFORM%\qtbase cd %QT%\%PLATFORM%-Build set QT_PREFIX=-prefix %Qt%\%PLATFORM%\qtbase - set QT_TARGETS=module-qtbase-install_subtargets + set QT_TARGETS=module-qtbase-install_subtargets module-qtquick1-install_subtargets ) else ( cd %QT%\%PLATFORM% set QT_PREFIX= - set QT_TARGETS=module-qtbase + set QT_TARGETS=module-qtbase module-qtquick1 ) set PATH=%CD%\qtbase\bin;%PATH% rem : run Qt configure with desired settings -call %QT%\Src\configure.bat -opensource -confirm-license %QT_PREFIX% -mp -opengl desktop -static -debug-and-release -platform win32-msvc2013 -nomake examples -nomake tests +if %DO_CONFIGURE%==1 ( + call %QT%\Src\configure.bat -opensource -confirm-license %QT_PREFIX% -mp -opengl desktop -static -debug-and-release -platform win32-msvc2013 -nomake examples -nomake tests +) rem : compile and install module-qtbase %QT%\jom\jom %QT_TARGETS% diff --git a/windows/stdafx.h b/windows/stdafx.h index 1ad98d97b..04ef305af 100644 --- a/windows/stdafx.h +++ b/windows/stdafx.h @@ -40,3 +40,5 @@ #include #include #include +#include +