From 0d2f5439ce9e5edec95bc985ad40c82f6c4b1258 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 24 Jul 2014 18:16:36 +0200 Subject: [PATCH] Nearly complete differential changelog system. --- alethzero/MainWin.cpp | 9 ++++- libethereum/Client.cpp | 24 +++++------- libethereum/Client.h | 6 +-- libqethereum/QEthereum.cpp | 77 ++++++++++++++++++++++++++++---------- libqethereum/QEthereum.h | 7 +++- 5 files changed, 84 insertions(+), 39 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index bb4ab9889..3a3982d30 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -212,6 +212,10 @@ Main::Main(QWidget *parent) : Main::~Main() { + // Must do this here since otherwise m_ethereum'll be deleted (and therefore clearWatches() called by the destructor) + // *after* the client is dead. + m_ethereum->clientDieing(); + g_logPost = simpleDebugOut; writeSettings(); } @@ -379,7 +383,8 @@ void Main::eval(QString const& _js) { if (_js.trimmed().isEmpty()) return; - QVariant ev = ui->webView->page()->currentFrame()->evaluateJavaScript(_js); + QVariant ev = ui->webView->page()->currentFrame()->evaluateJavaScript("___RET=(" + _js + ")"); + QVariant jsonEv = ui->webView->page()->currentFrame()->evaluateJavaScript("JSON.stringify(__RET)"); QString s; if (ev.isNull()) s = "null"; @@ -387,6 +392,8 @@ void Main::eval(QString const& _js) s = "\"" + ev.toString().toHtmlEscaped() + "\""; else if (ev.type() == QVariant::Int || ev.type() == QVariant::Double) s = "" + ev.toString().toHtmlEscaped() + ""; + else if (jsonEv.type() == QVariant::String) + s = "" + jsonEv.toString().toHtmlEscaped() + ""; else s = "unknown type"; m_consoleHistory.push_back(qMakePair(_js, s)); diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 94a801df8..e059c9c7c 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -153,20 +153,20 @@ void Client::uninstallWatch(unsigned _i) auto it = m_watches.find(_i); if (it == m_watches.end()) return; + auto id = it->second.id; + m_watches.erase(it); - auto fit = m_filters.find(it->second.id); + auto fit = m_filters.find(id); if (fit != m_filters.end()) if (!--fit->second.refCount) m_filters.erase(fit); - - m_watches.erase(it); } void Client::appendFromNewPending(h256 _bloom, h256Set& o_changed) const { lock_guard l(m_filterLock); for (pair const& i: m_filters) - if (numberOf(i.second.filter.latest()) == m_postMine.info().number && i.second.filter.matches(_bloom)) + if ((unsigned)i.second.filter.latest() >= m_postMine.info().number && i.second.filter.matches(_bloom)) o_changed.insert(i.first); } @@ -176,7 +176,7 @@ void Client::appendFromNewBlock(h256 _block, h256Set& o_changed) const lock_guard l(m_filterLock); for (pair const& i: m_filters) - if (numberOf(i.second.filter.latest()) >= d.number && i.second.filter.matches(d.bloom)) + if ((unsigned)i.second.filter.latest() >= d.number && (unsigned)i.second.filter.earliest() <= d.number && i.second.filter.matches(d.bloom)) o_changed.insert(i.first); } @@ -587,13 +587,13 @@ PastMessages Client::transactions(TransactionFilter const& _f) const ClientGuard l(this); PastMessages ret; - unsigned begin = numberOf(_f.latest()); - unsigned end = min(begin, numberOf(_f.earliest())); + unsigned begin = min(m_bc.number(), (unsigned)_f.latest()); + unsigned end = min(begin, (unsigned)_f.earliest()); unsigned m = _f.max(); unsigned s = _f.skip(); // Handle pending transactions differently as they're not on the block chain. - if (_f.latest() == 0) + if (begin == m_bc.number()) { for (unsigned i = 0; i < m_postMine.pending().size(); ++i) { @@ -608,19 +608,15 @@ PastMessages Client::transactions(TransactionFilter const& _f) const s--; else // Have a transaction that contains a matching message. - ret.insert(ret.begin(), pm[j].polish(h256(), ts, 0)); + ret.insert(ret.begin(), pm[j].polish(h256(), ts, m_bc.number() + 1)); } } - // Early exit here since we can't rely on begin/end, being out of the blockchain as we are. - if (_f.earliest() == 0) - return ret; } #if ETH_DEBUG unsigned skipped = 0; unsigned falsePos = 0; #endif - auto cn = m_bc.number(); auto h = m_bc.numberHash(begin); unsigned n = begin; for (; ret.size() != m && n != end; n--, h = m_bc.details(h).parent) @@ -653,7 +649,7 @@ PastMessages Client::transactions(TransactionFilter const& _f) const s--; else // Have a transaction that contains a matching message. - ret.insert(ret.begin(), pm[j].polish(h, ts, cn - n + 2)); + ret.insert(ret.begin(), pm[j].polish(h, ts, n)); } } #if ETH_DEBUG diff --git a/libethereum/Client.h b/libethereum/Client.h index 180d2d07b..b1089741c 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -102,7 +102,7 @@ typedef std::vector PastMessages; class TransactionFilter { public: - TransactionFilter(int _earliest = GenesisBlock, int _latest = 0, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {} + TransactionFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {} void fillStream(RLPStream& _s) const; h256 sha3() const; @@ -131,8 +131,8 @@ private: std::set
m_to; std::set> m_stateAltered; std::set
m_altered; - int m_earliest; - int m_latest; + int m_earliest = 0; + int m_latest = -1; unsigned m_max; unsigned m_skip; }; diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index 9c9f5ce5f..38d7ca9da 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -97,11 +97,18 @@ QEthereum::~QEthereum() clearWatches(); } +void QEthereum::clientDieing() +{ + clearWatches(); + m_client = nullptr; +} + void QEthereum::clearWatches() { - for (auto i: m_watches) - m_client->uninstallWatch(i); - m_watches.clear(); + if (m_client) + for (auto i: m_watches) + m_client->uninstallWatch(i); + m_watches.clear(); } QString QEthereum::secretToAddress(QString _s) const @@ -135,12 +142,12 @@ QString QEthereum::sha3(QString _s) const QString QEthereum::coinbase() const { - return toQJS(client()->address()); + return m_client ? toQJS(client()->address()) : ""; } QString QEthereum::number() const { - return QString::number(client()->blockChain().number() + 1); + return m_client ? QString::number(client()->blockChain().number() + 1) : ""; } QString QEthereum::account() const @@ -175,7 +182,7 @@ QStringList QEthereum::keys() const void QEthereum::setCoinbase(QString _a) { - if (client()->address() != toAddress(_a)) + if (m_client && client()->address() != toAddress(_a)) { client()->setAddress(toAddress(_a)); coinbaseChanged(); @@ -184,58 +191,75 @@ void QEthereum::setCoinbase(QString _a) QString QEthereum::balanceAt(QString _a) const { - return toQJS(client()->postState().balance(toAddress(_a))); + return m_client ? toQJS(client()->postState().balance(toAddress(_a))) : ""; } QString QEthereum::storageAt(QString _a, QString _p) const { + if (!m_client) + return ""; eth::ClientGuard l(const_cast(m_client)); return toQJS(client()->postState().storage(toAddress(_a), toU256(_p))); } double QEthereum::txCountAt(QString _a) const { + if (!m_client) + return 0.0; + eth::ClientGuard l(const_cast(m_client)); return (double)client()->postState().transactionsFrom(toAddress(_a)); } bool QEthereum::isContractAt(QString _a) const { + if (!m_client) + return false; + eth::ClientGuard l(const_cast(m_client)); return client()->postState().addressHasCode(toAddress(_a)); } u256 QEthereum::balanceAt(Address _a) const { + if (!m_client) + return 0; + eth::ClientGuard l(const_cast(m_client)); return client()->postState().balance(_a); } double QEthereum::txCountAt(Address _a) const { + if (!m_client) + return 0.0; + eth::ClientGuard l(const_cast(m_client)); return (double)client()->postState().transactionsFrom(_a); } bool QEthereum::isContractAt(Address _a) const { + if (!m_client) + return false; + eth::ClientGuard l(const_cast(m_client)); return client()->postState().addressHasCode(_a); } QString QEthereum::balanceAt(QString _a, int _block) const { - return toQJS(client()->balanceAt(toAddress(_a), _block)); + return m_client ? toQJS(client()->balanceAt(toAddress(_a), _block)) : ""; } QString QEthereum::stateAt(QString _a, QString _p, int _block) const { - return toQJS(client()->stateAt(toAddress(_a), toU256(_p), _block)); + return m_client ? toQJS(client()->stateAt(toAddress(_a), toU256(_p), _block)) : ""; } QString QEthereum::codeAt(QString _a, int _block) const { - return ::fromBinary(client()->codeAt(toAddress(_a), _block)); + return m_client ? ::fromBinary(client()->codeAt(toAddress(_a), _block)) : ""; } double QEthereum::countAt(QString _a, int _block) const { - return (double)(uint64_t)client()->countAt(toAddress(_a), _block); + return m_client ? (double)(uint64_t)client()->countAt(toAddress(_a), _block) : 0; } static eth::TransactionFilter toTransactionFilter(QString _json) @@ -309,29 +333,34 @@ static QString toJson(eth::PastMessages const& _pms) QString QEthereum::getTransactions(QString _json) const { - return toJson(m_client->transactions(toTransactionFilter(_json))); + return m_client ? toJson(m_client->transactions(toTransactionFilter(_json))) : ""; } bool QEthereum::isMining() const { - return client()->isMining(); + return m_client ? client()->isMining() : false; } bool QEthereum::isListening() const { - return client()->haveNetwork(); + return m_client ? client()->haveNetwork() : false; } void QEthereum::setMining(bool _l) { - if (_l) - client()->startMining(); - else - client()->stopMining(); + if (m_client) + { + if (_l) + client()->startMining(); + else + client()->stopMining(); + } } void QEthereum::setListening(bool _l) { + if (!m_client) + return; if (_l) client()->startNetwork(); else @@ -340,11 +369,13 @@ void QEthereum::setListening(bool _l) unsigned QEthereum::peerCount() const { - return (unsigned)client()->peerCount(); + return m_client ? (unsigned)client()->peerCount() : 0; } QString QEthereum::doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice) { + if (!m_client) + return ""; auto ret = toQJS(client()->transact(toSecret(_secret), toU256(_amount), toBytes(_init), toU256(_gas), toU256(_gasPrice))); client()->flushTransactions(); return ret; @@ -352,12 +383,16 @@ QString QEthereum::doCreate(QString _secret, QString _amount, QString _init, QSt void QEthereum::doTransact(QString _secret, QString _amount, QString _dest, QString _data, QString _gas, QString _gasPrice) { + if (!m_client) + return; client()->transact(toSecret(_secret), toU256(_amount), toAddress(_dest), toBytes(_data), toU256(_gas), toU256(_gasPrice)); client()->flushTransactions(); } unsigned QEthereum::newWatch(QString _json) { + if (!m_client) + return (unsigned)-1; unsigned ret; if (_json == "chainChanged") ret = m_client->installWatch(eth::NewBlockFilter); @@ -371,11 +406,15 @@ unsigned QEthereum::newWatch(QString _json) QString QEthereum::watchTransactions(unsigned _w) { + if (!m_client) + return ""; return toJson(m_client->transactions(_w)); } void QEthereum::killWatch(unsigned _w) { + if (!m_client) + return; m_client->uninstallWatch(_w); std::remove(m_watches.begin(), m_watches.end(), _w); } diff --git a/libqethereum/QEthereum.h b/libqethereum/QEthereum.h index bd5832304..eba10f5ed 100644 --- a/libqethereum/QEthereum.h +++ b/libqethereum/QEthereum.h @@ -92,6 +92,9 @@ public: eth::Client* client() const; + /// Call when the client() is going to be deleted to make this object useless but safe. + void clientDieing(); + void setup(QWebFrame* _e); void teardown(QWebFrame* _e); @@ -186,8 +189,8 @@ private: frame->disconnect(); \ frame->addToJavaScriptWindowObject("env", env, QWebFrame::QtOwnership); \ frame->addToJavaScriptWindowObject("eth", eth, QWebFrame::ScriptOwnership); \ - frame->evaluateJavaScript("eth.makeWatch = function(a) { var ww = eth.newWatch(a); return { w: ww, uninstall: function() { eth.killWatch(w) }, changed: function(f) { eth.watchChanged.connect(function(nw) { if (nw == this.w) f() }) }, transactions: function() { return JSON.parse(eth.watchTransactions(w)) } }; }"); \ - frame->evaluateJavaScript("eth.watch = function(a) { return makeWatch(JSON.stringify(a)); }"); \ + 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) { env.note('check:' + nw + ' vs ' + ww); if (nw == ww) f() }); }; ret.transactions = function() { return JSON.parse(eth.watchTransactions(this.w)) }; return ret; }"); \ + frame->evaluateJavaScript("eth.watch = function(a) { return eth.makeWatch(JSON.stringify(a)) }"); \ frame->evaluateJavaScript("eth.watchChain = function() { return eth.makeWatch('chainChanged') }"); \ frame->evaluateJavaScript("eth.watchPending = function() { return eth.makeWatch('pendingChanged') }"); \ frame->evaluateJavaScript("eth.create = function(s, v, c, g, p, f) { var v = eth.doCreate(s, v, c, g, p); if (f) f(v) }"); \