From 6706c26471bb6cb97b03eafab8b4adf4ae72e65a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 19 Oct 2014 15:44:08 +0300 Subject: [PATCH] Move misc to dev.* JS API. Potential crash fix. UPnP in thread. --- alethzero/MainWin.cpp | 4 ++- alethzero/MainWin.h | 1 + libdevcore/Worker.cpp | 1 + libdevcore/Worker.h | 1 + libp2p/Host.cpp | 51 +++++++++++++++------------- libp2p/Host.h | 5 +-- libp2p/UPnP.h | 2 ++ libqethereum/QEthereum.cpp | 8 ++--- libqethereum/QEthereum.h | 69 +++++++++++++++++++++----------------- third/MainWin.cpp | 4 ++- third/MainWin.h | 1 + 11 files changed, 85 insertions(+), 62 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index ac2ed7358..04a4e6bd6 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -135,15 +135,17 @@ Main::Main(QWidget *parent) : connect(ui->webView, &QWebView::loadStarted, [this]() { // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. + m_dev = new QDev(this); m_ethereum = new QEthereum(this, ethereum(), owned()); m_whisper = new QWhisper(this, whisper()); QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); QWebFrame* f = ui->webView->page()->mainFrame(); f->disconnect(SIGNAL(javaScriptWindowObjectCleared())); + auto qdev = m_dev; auto qeth = m_ethereum; auto qshh = m_whisper; - connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, qeth, qshh, this)); + connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qdev, qeth, qshh)); }); connect(ui->webView, &QWebView::loadFinished, [=]() diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 55b8b4e0a..aa5fcf572 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -247,6 +247,7 @@ private: QString m_logHistory; bool m_logChanged = true; + QDev* m_dev = nullptr; QEthereum* m_ethereum = nullptr; QWhisper* m_whisper = nullptr; }; diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp index 29ff766d7..b2660305a 100644 --- a/libdevcore/Worker.cpp +++ b/libdevcore/Worker.cpp @@ -38,6 +38,7 @@ void Worker::startWorking() m_work.reset(new thread([&]() { setThreadName(m_name.c_str()); + startedWorking(); while (!m_stop) { this_thread::sleep_for(chrono::milliseconds(30)); diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h index 8f0baaf60..a4a998dd7 100644 --- a/libdevcore/Worker.h +++ b/libdevcore/Worker.h @@ -46,6 +46,7 @@ protected: void startWorking(); void stopWorking(); bool isWorking() const { Guard l(x_work); return !!m_work; } + virtual void startedWorking() {} virtual void doWork() = 0; virtual void doneWorking() {} diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 354a9f84d..3a2eac84d 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -110,14 +110,6 @@ void Host::start() } } - determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); - ensureAccepting(); - - if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id())) - noteNode(id(), m_public, Origin::Perfect, false); - - clog(NetNote) << "Id:" << id().abridged(); - for (auto const& h: m_capabilities) h.second->onStarting(); @@ -549,31 +541,31 @@ void Host::connect(bi::tcp::endpoint const& _ep) }); } -void Node::connect(Host* _h) +void Host::connect(std::shared_ptr const& _n) { // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!_h->m_ioService) + if (!m_ioService) return; - clog(NetConnect) << "Attempting connection to node" << id.abridged() << "@" << address << "from" << _h->id().abridged(); - lastAttempted = std::chrono::system_clock::now(); - failedAttempts++; - _h->m_ready -= index; - bi::tcp::socket* s = new bi::tcp::socket(*_h->m_ioService); - s->async_connect(address, [=](boost::system::error_code const& ec) + clog(NetConnect) << "Attempting connection to node" << _n->id.abridged() << "@" << _n->address << "from" << id().abridged(); + _n->lastAttempted = std::chrono::system_clock::now(); + _n->failedAttempts++; + m_ready -= _n->index; + bi::tcp::socket* s = new bi::tcp::socket(*m_ioService); + s->async_connect(_n->address, [=](boost::system::error_code const& ec) { if (ec) { - clog(NetConnect) << "Connection refused to node" << id.abridged() << "@" << address << "(" << ec.message() << ")"; - lastDisconnect = TCPError; - lastAttempted = std::chrono::system_clock::now(); - _h->m_ready += index; + clog(NetConnect) << "Connection refused to node" << _n->id.abridged() << "@" << _n->address << "(" << ec.message() << ")"; + _n->lastDisconnect = TCPError; + _n->lastAttempted = std::chrono::system_clock::now(); + m_ready += _n->index; } else { - clog(NetConnect) << "Connected to" << id.abridged() << "@" << address; - lastConnected = std::chrono::system_clock::now(); - auto p = make_shared(_h, std::move(*s), _h->node(id), true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism. + clog(NetConnect) << "Connected to" << _n->id.abridged() << "@" << _n->address; + _n->lastConnected = std::chrono::system_clock::now(); + auto p = make_shared(this, std::move(*s), node(_n->id), true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism. p->start(); } delete s; @@ -638,7 +630,7 @@ void Host::growPeers() if (ns.size()) for (Node const& i: ns) { - m_nodes[i.id]->connect(this); + connect(m_nodes[i.id]); if (!--morePeers) return; } @@ -704,6 +696,17 @@ PeerInfos Host::peers(bool _updatePing) const return ret; } +void Host::startedWorking() +{ + determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); + ensureAccepting(); + + if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id())) + noteNode(id(), m_public, Origin::Perfect, false); + + clog(NetNote) << "Id:" << id().abridged(); +} + void Host::doWork() { // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. diff --git a/libp2p/Host.h b/libp2p/Host.h index 75e335ff1..37fd8a4eb 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -97,8 +97,6 @@ struct Node else return score < _n.score; } - - void connect(Host* _h); }; using Nodes = std::vector; @@ -147,6 +145,7 @@ public: static std::string pocHost(); void connect(std::string const& _addr, unsigned short _port = 30303) noexcept; void connect(bi::tcp::endpoint const& _ep); + void connect(std::shared_ptr const& _n); /// @returns true iff we have the a peer of the given id. bool havePeer(NodeId _id) const; @@ -197,6 +196,8 @@ private: void growPeers(); void prunePeers(); + virtual void startedWorking(); + /// Conduct I/O, polling, syncing, whatever. /// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway. /// This won't touch alter the blockchain. diff --git a/libp2p/UPnP.h b/libp2p/UPnP.h index 0031298a6..4d53a998b 100644 --- a/libp2p/UPnP.h +++ b/libp2p/UPnP.h @@ -25,6 +25,7 @@ #include #include #include +#include struct UPNPUrls; struct IGDdatas; @@ -46,6 +47,7 @@ public: bool isValid() const { return m_ok; } +private: std::set m_reg; bool m_ok; std::shared_ptr m_urls; diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index fbea844b3..2d8eef341 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -96,22 +96,22 @@ QString QEthereum::lll(QString _s) const return toQJS(dev::eth::compileLLL(_s.toStdString())); } -QString QEthereum::sha3(QString _s) const +QString QDev::sha3(QString _s) const { return toQJS(dev::sha3(toBytes(_s))); } -QString QEthereum::sha3(QString _s1, QString _s2) const +QString QDev::sha3(QString _s1, QString _s2) const { return toQJS(dev::sha3(asBytes(padded(_s1, 32)) + asBytes(padded(_s2, 32)))); } -QString QEthereum::sha3(QString _s1, QString _s2, QString _s3) const +QString QDev::sha3(QString _s1, QString _s2, QString _s3) const { return toQJS(dev::sha3(asBytes(padded(_s1, 32)) + asBytes(padded(_s2, 32)) + asBytes(padded(_s3, 32)))); } -QString QEthereum::offset(QString _s, int _i) const +QString QDev::offset(QString _s, int _i) const { return toQJS(toU256(_s) + _i); } diff --git a/libqethereum/QEthereum.h b/libqethereum/QEthereum.h index 6ab2c5013..3ef57d232 100644 --- a/libqethereum/QEthereum.h +++ b/libqethereum/QEthereum.h @@ -98,6 +98,27 @@ inline QString fromBinary(QString const& _s, unsigned _padding = 32) return fromBinary(asBytes(_s), _padding); } +class QDev: public QObject +{ + Q_OBJECT + +public: + QDev(QObject* _p): QObject(_p) {} + virtual ~QDev() {} + + Q_INVOKABLE QString sha3(QString _s) const; + Q_INVOKABLE QString sha3(QString _s1, QString _s2) const; + Q_INVOKABLE QString sha3(QString _s1, QString _s2, QString _s3) const; + Q_INVOKABLE QString offset(QString _s, int _offset) const; + + Q_INVOKABLE QString toAscii(QString _s) const { return ::toBinary(_s); } + Q_INVOKABLE QString fromAscii(QString _s) const { return ::fromBinary(_s, 32); } + Q_INVOKABLE QString fromAscii(QString _s, unsigned _padding) const { return ::fromBinary(_s, _padding); } + Q_INVOKABLE QString toDecimal(QString _s) const { return ::toDecimal(_s); } + Q_INVOKABLE double fromFixed(QString _s) const { return ::fromFixed(_s); } + Q_INVOKABLE QString toFixed(double _d) const { return ::toFixed(_d); } +}; + class QEthereum: public QObject { Q_OBJECT @@ -114,24 +135,11 @@ public: void setAccounts(QList _l) { m_accounts = _l; keysChanged(); } - Q_INVOKABLE QString ethTest() const { return "Hello world!"; } Q_INVOKABLE QEthereum* self() { return this; } Q_INVOKABLE QString secretToAddress(QString _s) const; Q_INVOKABLE QString lll(QString _s) const; - Q_INVOKABLE QString sha3(QString _s) const; - Q_INVOKABLE QString sha3(QString _s1, QString _s2) const; - Q_INVOKABLE QString sha3(QString _s1, QString _s2, QString _s3) const; - Q_INVOKABLE QString offset(QString _s, int _offset) const; - - Q_INVOKABLE QString toAscii(QString _s) const { return ::toBinary(_s); } - Q_INVOKABLE QString fromAscii(QString _s) const { return ::fromBinary(_s, 32); } - Q_INVOKABLE QString fromAscii(QString _s, unsigned _padding) const { return ::fromBinary(_s, _padding); } - Q_INVOKABLE QString toDecimal(QString _s) const { return ::toDecimal(_s); } - Q_INVOKABLE double fromFixed(QString _s) const { return ::fromFixed(_s); } - Q_INVOKABLE QString toFixed(double _d) const { return ::toFixed(_d); } - // [NEW API] - Use this instead. Q_INVOKABLE QString/*dev::u256*/ balanceAt(QString/*dev::Address*/ _a, int _block) const; Q_INVOKABLE double countAt(QString/*dev::Address*/ _a, int _block) const; @@ -247,27 +255,28 @@ private: }; // TODO: add p2p object -#define QETH_INSTALL_JS_NAMESPACE(frame, eth, shh, env) [frame, eth, shh, env]() \ +#define QETH_INSTALL_JS_NAMESPACE(_frame, _env, _dev, _eth, _shh) [_frame, _env, _dev, _eth, _shh]() \ { \ - frame->disconnect(); \ - frame->addToJavaScriptWindowObject("env", env, QWebFrame::QtOwnership); \ - if (eth) \ + _frame->disconnect(); \ + _frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \ + _frame->addToJavaScriptWindowObject("dev", _dev, QWebFrame::ScriptOwnership); \ + if (_eth) \ { \ - frame->addToJavaScriptWindowObject("eth", eth, QWebFrame::ScriptOwnership); \ - frame->evaluateJavaScript("eth.makeWatch = function(a) { var ww = eth.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { eth.killWatch(w); }; ret.changed = function(f) { eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(eth.watchMessages(this.w)) }; return ret; }"); \ - frame->evaluateJavaScript("eth.watch = function(a) { return eth.makeWatch(JSON.stringify(a)) }"); \ - frame->evaluateJavaScript("eth.transact = function(a, f) { var r = eth.doTransact(JSON.stringify(a)); if (f) f(r); }"); \ - frame->evaluateJavaScript("eth.call = function(a, f) { var ret = eth.doCallJson(JSON.stringify(a)); if (f) f(ret); return ret; }"); \ - frame->evaluateJavaScript("eth.messages = function(a) { return JSON.parse(eth.getMessages(JSON.stringify(a))); }"); \ - frame->evaluateJavaScript("eth.block = function(a) { return JSON.parse(eth.getBlock(a)); }"); \ - frame->evaluateJavaScript("eth.transaction = function(a) { return JSON.parse(eth.getTransaction(a)); }"); \ - frame->evaluateJavaScript("eth.uncle = function(a) { return JSON.parse(eth.getUncle(a)); }"); \ + _frame->addToJavaScriptWindowObject("eth", _eth, QWebFrame::ScriptOwnership); \ + _frame->evaluateJavaScript("eth.makeWatch = function(a) { var ww = eth.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { eth.killWatch(w); }; ret.changed = function(f) { eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(eth.watchMessages(this.w)) }; return ret; }"); \ + _frame->evaluateJavaScript("eth.watch = function(a) { return eth.makeWatch(JSON.stringify(a)) }"); \ + _frame->evaluateJavaScript("eth.transact = function(a, f) { var r = eth.doTransact(JSON.stringify(a)); if (f) f(r); }"); \ + _frame->evaluateJavaScript("eth.call = function(a, f) { var ret = eth.doCallJson(JSON.stringify(a)); if (f) f(ret); return ret; }"); \ + _frame->evaluateJavaScript("eth.messages = function(a) { return JSON.parse(eth.getMessages(JSON.stringify(a))); }"); \ + _frame->evaluateJavaScript("eth.block = function(a) { return JSON.parse(eth.getBlock(a)); }"); \ + _frame->evaluateJavaScript("eth.transaction = function(a) { return JSON.parse(eth.getTransaction(a)); }"); \ + _frame->evaluateJavaScript("eth.uncle = function(a) { return JSON.parse(eth.getUncle(a)); }"); \ } \ - if (shh) \ + if (_shh) \ { \ - frame->addToJavaScriptWindowObject("shh", shh, QWebFrame::ScriptOwnership); \ - frame->evaluateJavaScript("shh.makeWatch = function(a) { var ww = shh.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { shh.killWatch(w); }; ret.changed = function(f) { shh.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(shh.watchMessages(this.w)) }; return ret; }"); \ - frame->evaluateJavaScript("shh.watch = function(a) { return shh.makeWatch(JSON.stringify(a)) }"); \ + _frame->addToJavaScriptWindowObject("shh", _shh, QWebFrame::ScriptOwnership); \ + _frame->evaluateJavaScript("shh.makeWatch = function(a) { var ww = shh.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { shh.killWatch(w); }; ret.changed = function(f) { shh.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(shh.watchMessages(this.w)) }; return ret; }"); \ + _frame->evaluateJavaScript("shh.watch = function(a) { return shh.makeWatch(JSON.stringify(a)) }"); \ } \ } diff --git a/third/MainWin.cpp b/third/MainWin.cpp index d21077f86..d1f5938aa 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -105,14 +105,16 @@ Main::Main(QWidget *parent) : connect(ui->webView, &QWebView::loadStarted, [this]() { // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. + m_dev = new QDev(this); m_ethereum = new QEthereum(this, ethereum(), owned()); m_whisper = new QWhisper(this, whisper()); QWebFrame* f = ui->webView->page()->mainFrame(); f->disconnect(SIGNAL(javaScriptWindowObjectCleared())); + auto qdev = m_dev; auto qeth = m_ethereum; auto qshh = m_whisper; - connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, qeth, qshh, this)); + connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qdev, qeth, qshh)); }); connect(ui->webView, &QWebView::loadFinished, [=]() diff --git a/third/MainWin.h b/third/MainWin.h index fcb7ab304..0122cc257 100644 --- a/third/MainWin.h +++ b/third/MainWin.h @@ -132,6 +132,7 @@ private: QNetworkAccessManager m_webCtrl; + QDev* m_dev = nullptr; QEthereum* m_ethereum = nullptr; QWhisper* m_whisper = nullptr; };