diff --git a/libethereum/Client.h b/libethereum/Client.h index bf1964fe0..8c6a1d740 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -121,6 +121,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(); } @@ -134,6 +136,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/walleth/MainWin.cpp b/walleth/MainWin.cpp index 88a341d4b..58465972c 100644 --- a/walleth/MainWin.cpp +++ b/walleth/MainWin.cpp @@ -51,57 +51,132 @@ 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; + #define ADD_QUOTES_HELPER(s) #s #define ADD_QUOTES(s) ADD_QUOTES_HELPER(s) QEthereum::QEthereum(QObject* _p): QObject(_p) { - m_client.reset(new Client("Walleth", Address(), eth::getDataDir() + "/Walleth")); - startTimer(200); + connect(g_main, SIGNAL(changed()), SIGNAL(changed())); } QEthereum::~QEthereum() { } -Address QEthereum::address() const +Client* QEthereum::client() const +{ + return g_main->client(); +} + +Address QEthereum::coinbase() const { - return m_client->address(); + return client()->address(); } -void QEthereum::setAddress(Address _a) +void QEthereum::setCoinbase(Address _a) { - if (m_client->address() != _a) + if (client()->address() != _a) { - m_client->setAddress(_a); + client()->setAddress(_a); changed(); } } -u256 QEthereum::balance() const +QAccount::QAccount(QObject* _p) +{ +} + +QAccount::~QAccount() +{ +} + +void QAccount::setEthereum(QEthereum* _eth) { - return m_client->postState().balance(address()); + 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; +} + +uint64_t 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 m_client->postState().balance(_a); + return client()->postState().balance(_a); } -unsigned QEthereum::peerCount() const +bool QEthereum::isContractAt(Address _a) const { - return m_client->peerCount(); + return client()->postState().isContractAddress(_a); } -void QEthereum::transact(Secret _secret, Address _dest, u256 _amount) +bool QEthereum::isMining() const { - m_client->transact(_secret, _dest, _amount); + return client()->isMining(); } -void QEthereum::timerEvent(QTimerEvent *) +bool QEthereum::isListening() const { - if (m_client->changed()) - changed(); + 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(); +} + +uint64_t QEthereum::txCountAt(Address _a) const +{ + return (uint64_t)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) : @@ -110,56 +185,42 @@ Main::Main(QWidget *parent) : { 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); - setWindowIcon(QIcon(":/Noted.png")); -*/ - // TODO: Figure out why not working. -// qmlRegisterSingletonType("org.ethereum", 1, 0, "balance", QEthereum::constructU256Helper); -// qmlRegisterSingletonType("org.ethereum", 1, 0, "key", QEthereum::constructKeyHelper); -/* 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(); + */ - m_eth = new QEthereum(); - context->setContextProperty("eth", m_eth); - // TODO: should be singletons. - context->setContextProperty("u256", new U256Helper(this)); - context->setContextProperty("key", new KeyHelper(this)); + m_view = new QQuickView(); -/* 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:/Simple.qml")); +// 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"); - qDebug() << m_view->rootObject(); - */ + +// m_timelinesItem = m_view->rootObject()->findChild("timelines"); readSettings(); refresh(); @@ -167,6 +228,7 @@ Main::Main(QWidget *parent) : 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) @@ -183,6 +245,8 @@ Main::Main(QWidget *parent) : m_webCtrl.get(r); srand(time(0)); + startTimer(200); + statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->peerCount); statusBar()->addPermanentWidget(ui->blockCount); @@ -193,9 +257,15 @@ Main::~Main() writeSettings(); } +void Main::timerEvent(QTimerEvent *) +{ + if (m_client->changed()) + changed(); +} + void Main::on_about_triggered() { - QMessageBox::about(this, "About Walleth PoC-" + QString(ADD_QUOTES(ETH_VERSION)).section('.', 1, 1), "AlethZero/v" ADD_QUOTES(ETH_VERSION) "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM) "\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."); + QMessageBox::about(this, "About Walleth PoC-" + QString(ADD_QUOTES(ETH_VERSION)).section('.', 1, 1), "Walleth/v" ADD_QUOTES(ETH_VERSION) "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM) "\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() @@ -216,9 +286,9 @@ void Main::writeSettings() s.setValue("idealPeers", m_idealPeers); s.setValue("port", m_port); - if (m_eth->client()->peerServer()) + if (client()->peerServer()) { - bytes d = m_eth->client()->peerServer()->savePeers(); + bytes d = client()->peerServer()->savePeers(); m_peers = QByteArray((char*)d.data(), (int)d.size()); } @@ -248,7 +318,7 @@ void Main::readSettings() m_myKeys.append(KeyPair(k)); } } - m_eth->setAddress(m_myKeys.last().address()); + //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(); @@ -258,22 +328,22 @@ void Main::readSettings() void Main::refreshNetwork() { - auto ps = m_eth->client()->peers(); + auto ps = client()->peers(); ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); } eth::State const& Main::state() const { - return ui->preview->isChecked() ? m_eth->client()->postState() : m_eth->client()->state(); + return ui->preview->isChecked() ? client()->postState() : client()->state(); } void Main::refresh() { - eth::ClientGuard l(m_eth->client()); + eth::ClientGuard l(client()); auto const& st = state(); - auto d = m_eth->client()->blockChain().details(); - auto diff = BlockInfo(m_eth->client()->blockChain().block()).difficulty; + 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; @@ -292,21 +362,21 @@ void Main::on_net_triggered(bool _auto) if (m_clientName.size()) n += "/" + m_clientName.toStdString(); n += "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM); - m_eth->client()->setClientVersion(n); + client()->setClientVersion(n); if (ui->net->isChecked()) { if (_auto) { QString s = m_servers[rand() % m_servers.size()]; - m_eth->client()->startNetwork(m_port, s.section(':', 0, 0).toStdString(), s.section(':', 1).toInt(), NodeMode::Full, m_idealPeers, std::string(), ui->upnp->isChecked()); + client()->startNetwork(m_port, s.section(':', 0, 0).toStdString(), s.section(':', 1).toInt(), NodeMode::Full, m_idealPeers, std::string(), ui->upnp->isChecked()); } else - m_eth->client()->startNetwork(m_port, string(), 0, NodeMode::Full, m_idealPeers, std::string(), ui->upnp->isChecked()); + client()->startNetwork(m_port, string(), 0, NodeMode::Full, m_idealPeers, std::string(), ui->upnp->isChecked()); if (m_peers.size()) - m_eth->client()->peerServer()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); + client()->peerServer()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); } else - m_eth->client()->stopNetwork(); + client()->stopNetwork(); } void Main::on_connect_triggered() @@ -322,7 +392,7 @@ void Main::on_connect_triggered() { string host = s.section(":", 0, 0).toStdString(); unsigned short port = s.section(":", 1).toInt(); - m_eth->client()->connect(host, port); + client()->connect(host, port); } } @@ -330,11 +400,11 @@ void Main::on_mine_triggered() { if (ui->mine->isChecked()) { - m_eth->client()->setAddress(m_myKeys.last().address()); - m_eth->client()->startMining(); + client()->setAddress(m_myKeys.last().address()); + client()->startMining(); } else - m_eth->client()->stopMining(); + client()->stopMining(); } void Main::on_create_triggered() diff --git a/walleth/MainWin.h b/walleth/MainWin.h index 01cdadaae..4df12e3a2 100644 --- a/walleth/MainWin.h +++ b/walleth/MainWin.h @@ -17,14 +17,18 @@ class State; } class QQuickView; -class QEthereum; 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 { @@ -74,6 +78,39 @@ public: 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 uint64_t 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(uint64_t 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 @@ -82,33 +119,41 @@ public: QEthereum(QObject* _p = nullptr); virtual ~QEthereum(); - eth::Client* client() const { return m_client.get(); } + eth::Client* client() const; static QObject* constructU256Helper(QQmlEngine*, QJSEngine*) { return new U256Helper; } static QObject* constructKeyHelper(QQmlEngine*, QJSEngine*) { return new KeyHelper; } - eth::u256 balance() const; + Q_INVOKABLE eth::Address coinbase() const; + + Q_INVOKABLE bool isListening() const; + Q_INVOKABLE bool isMining() const; - Q_INVOKABLE eth::Address address() const; Q_INVOKABLE eth::u256 balanceAt(eth::Address _a) const; + Q_INVOKABLE uint64_t 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 setAddress(eth::Address); + + void setCoinbase(eth::Address); + void setMining(bool _l); + + void setListening(bool _l); signals: void changed(); - -protected: - virtual void timerEvent(QTimerEvent *); +// void netChanged(); +// void miningChanged(); private: - Q_PROPERTY(eth::u256 balance READ balance NOTIFY changed) - Q_PROPERTY(eth::Address address READ address WRITE setAddress NOTIFY changed) - - std::unique_ptr m_client; + 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 @@ -118,6 +163,8 @@ class Main : public QMainWindow public: explicit Main(QWidget *parent = 0); ~Main(); + + eth::Client* client() const { return m_client.get(); } private slots: void on_connect_triggered(); @@ -131,6 +178,12 @@ private slots: 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; @@ -163,7 +216,7 @@ private: QNetworkAccessManager m_webCtrl; - QEthereum* m_eth; + std::unique_ptr m_client; }; #endif // MAIN_H diff --git a/walleth/Simple.qml b/walleth/Simple.qml index b39dffc18..c56a7e870 100644 --- a/walleth/Simple.qml +++ b/walleth/Simple.qml @@ -1,14 +1,36 @@ +import QtQml 2.2 import QtQuick 2.1 import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 -//import org.ethereum 1.0 +import Qt.labs.settings 1.0 +import org.ethereum 1.0 -Label { +Item { id: main anchors.fill: parent anchors.margins: 9 - Label { - text: "Balance: " + u256.stringOf(eth.balance) + "\nAccount: " + key.stringOf(eth.address) + + 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