diff --git a/alethzero/Main.ui b/alethzero/Main.ui
index 53466c510..282447b79 100644
--- a/alethzero/Main.ui
+++ b/alethzero/Main.ui
@@ -485,6 +485,7 @@
+
@@ -1637,6 +1638,11 @@ font-size: 14pt
&Pretty...
+
+
+ &Refresh
+
+
diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp
index a892bc125..c0e59cf7c 100644
--- a/alethzero/MainWin.cpp
+++ b/alethzero/MainWin.cpp
@@ -90,6 +90,32 @@ static void initUnits(QComboBox* _b)
_b->addItem(QString::fromStdString(units()[n].second), n);
}
+static QString fromRaw(eth::h256 _n, unsigned* _inc = nullptr)
+{
+ if (_n)
+ {
+ std::string s((char const*)_n.data(), 32);
+ auto l = s.find_first_of('\0');
+ if (!l)
+ return QString();
+ if (l != string::npos)
+ {
+ auto p = s.find_first_not_of('\0', l);
+ if (!(p == string::npos || (_inc && p == 31)))
+ return QString();
+ if (_inc)
+ *_inc = (byte)s[31];
+ s.resize(l);
+ }
+ for (auto i: s)
+ if (i < 32)
+ return QString();
+ return QString::fromStdString(s);
+ }
+ return QString();
+}
+
+
Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f");
Main::Main(QWidget *parent) :
@@ -145,24 +171,22 @@ Main::Main(QWidget *parent) :
connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved()));
m_client.reset(new Client("AlethZero"));
- m_client->start();
connect(ui->webView, &QWebView::loadStarted, [this]()
{
- QEthereum *eth = new QEthereum(this, this->m_client.get(), this->owned());
- this->m_ethereum = eth;
- connect(this, SIGNAL(changed()), this->m_ethereum, SIGNAL(changed()));
+ // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it.
+ m_ethereum = new QEthereum(this, m_client.get(), owned());
- QWebFrame* f = this->ui->webView->page()->mainFrame();
+ QWebFrame* f = ui->webView->page()->mainFrame();
f->disconnect(SIGNAL(javaScriptWindowObjectCleared()));
- eth->setup(f);
- f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership);
- connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE);
+ m_ethereum->setup(f);
+ auto qeth = m_ethereum;
+ connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, qeth, this));
});
connect(ui->webView, &QWebView::loadFinished, [=]()
{
- this->changed();
+ m_ethereum->poll();
});
connect(ui->webView, &QWebView::titleChanged, [=]()
@@ -171,11 +195,10 @@ Main::Main(QWidget *parent) :
});
readSettings();
- refresh();
- m_refresh = new QTimer(this);
- connect(m_refresh, SIGNAL(timeout()), SLOT(refresh()));
- m_refresh->start(100);
+ installWatches();
+
+ startTimer(100);
{
QSettings s("ethereum", "alethzero");
@@ -185,7 +208,6 @@ Main::Main(QWidget *parent) :
s.setValue("splashMessage", false);
}
}
- m_pcWarp.clear();
}
Main::~Main()
@@ -194,6 +216,106 @@ Main::~Main()
writeSettings();
}
+void Main::onKeysChanged()
+{
+ installBalancesWatch();
+}
+
+unsigned Main::installWatch(eth::TransactionFilter const& _tf, std::function const& _f)
+{
+ auto ret = m_client->installWatch(_tf);
+ m_handlers[ret] = _f;
+ return ret;
+}
+
+unsigned Main::installWatch(eth::h256 _tf, std::function const& _f)
+{
+ auto ret = m_client->installWatch(_tf);
+ m_handlers[ret] = _f;
+ return ret;
+}
+
+void Main::installWatches()
+{
+ installWatch(eth::TransactionFilter().altered(c_config, 0), [=](){ installNameRegWatch(); });
+ installWatch(eth::TransactionFilter().altered(c_config, 1), [=](){ installCurrenciesWatch(); });
+ installWatch(eth::NewPendingFilter, [=](){ onNewPending(); });
+ installWatch(eth::NewBlockFilter, [=](){ onNewBlock(); });
+}
+
+void Main::installNameRegWatch()
+{
+ m_client->uninstallWatch(m_nameRegFilter);
+ m_nameRegFilter = installWatch(eth::TransactionFilter().altered((u160)state().storage(c_config, 0)), [=](){ onNameRegChange(); });
+}
+
+void Main::installCurrenciesWatch()
+{
+ m_client->uninstallWatch(m_currenciesFilter);
+ m_currenciesFilter = installWatch(eth::TransactionFilter().altered((u160)m_client->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); });
+}
+
+void Main::installBalancesWatch()
+{
+ eth::TransactionFilter tf;
+
+ vector altCoins;
+ Address coinsAddr = right160(m_client->stateAt(c_config, 1));
+ for (unsigned i = 0; i < m_client->stateAt(coinsAddr, 0); ++i)
+ altCoins.push_back(right160(m_client->stateAt(coinsAddr, i + 1)));
+ for (auto i: m_myKeys)
+ {
+ tf.altered(i.address());
+ for (auto c: altCoins)
+ tf.altered(c, (u160)i.address());
+ }
+
+ m_client->uninstallWatch(m_balancesFilter);
+ m_balancesFilter = installWatch(tf, [=](){ onBalancesChange(); });
+}
+
+void Main::onNameRegChange()
+{
+ cdebug << "NameReg changed!";
+
+ // update any namereg-dependent stuff - for now force a full update.
+ refreshAll();
+}
+
+void Main::onCurrenciesChange()
+{
+ cdebug << "Currencies changed!";
+ installBalancesWatch();
+
+ // TODO: update any currency-dependent stuff?
+}
+
+void Main::onBalancesChange()
+{
+ cdebug << "Our balances changed!";
+
+ refreshBalances();
+}
+
+void Main::onNewBlock()
+{
+ cdebug << "Blockchain changed!";
+
+ // update blockchain dependent views.
+ refreshBlockCount();
+ refreshBlockChain();
+ refreshAccounts();
+}
+
+void Main::onNewPending()
+{
+ cdebug << "Pending transactions changed!";
+
+ // update any pending-transaction dependent views.
+ refreshPending();
+ refreshAccounts();
+}
+
void Main::on_clearPending_triggered()
{
m_client->clearPending();
@@ -276,40 +398,15 @@ void Main::eval(QString const& _js)
ui->jsConsole->setHtml(s);
}
-QString fromRaw(eth::h256 _n, unsigned* _inc = nullptr)
-{
- if (_n)
- {
- std::string s((char const*)_n.data(), 32);
- auto l = s.find_first_of('\0');
- if (!l)
- return QString();
- if (l != string::npos)
- {
- auto p = s.find_first_not_of('\0', l);
- if (!(p == string::npos || (_inc && p == 31)))
- return QString();
- if (_inc)
- *_inc = (byte)s[31];
- s.resize(l);
- }
- for (auto i: s)
- if (i < 32)
- return QString();
- return QString::fromStdString(s);
- }
- return QString();
-}
-
QString Main::pretty(eth::Address _a) const
{
h256 n;
- if (h160 nameReg = (u160)state().storage(c_config, 0))
- n = state().storage(nameReg, (u160)(_a));
+ if (h160 nameReg = (u160)m_client->stateAt(c_config, 0))
+ n = m_client->stateAt(nameReg, (u160)(_a));
if (!n)
- n = state().storage(m_nameReg, (u160)(_a));
+ n = m_client->stateAt(m_nameReg, (u160)(_a));
return fromRaw(n);
}
@@ -470,12 +567,18 @@ void Main::on_nameReg_textChanged()
if (s.size() == 40)
{
m_nameReg = Address(fromHex(s));
- refresh(true);
+ refreshAll();
}
else
m_nameReg = Address();
}
+void Main::on_preview_triggered()
+{
+ m_client->setDefault(ui->preview->isChecked() ? 0 : -1);
+ refreshAll();
+}
+
void Main::refreshMining()
{
eth::ClientGuard g(m_client.get());
@@ -495,6 +598,33 @@ void Main::refreshMining()
*/
}
+void Main::refreshBalances()
+{
+ // update all the balance-dependent stuff.
+ ui->ourAccounts->clear();
+ u256 totalBalance = 0;
+ map> altCoins;
+ Address coinsAddr = right160(m_client->stateAt(c_config, 1));
+ for (unsigned i = 0; i < m_client->stateAt(coinsAddr, 0); ++i)
+ altCoins[right160(m_client->stateAt(coinsAddr, m_client->stateAt(coinsAddr, i + 1)))] = make_pair(fromRaw(m_client->stateAt(coinsAddr, i + 1)), 0);
+ for (auto i: m_myKeys)
+ {
+ u256 b = m_client->balanceAt(i.address());
+ (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)m_client->countAt(i.address())), ui->ourAccounts))
+ ->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size));
+ totalBalance += b;
+
+ for (auto& c: altCoins)
+ c.second.second += (u256)m_client->stateAt(c.first, (u160)i.address());
+ }
+
+ QString b;
+ for (auto const& c: altCoins)
+ if (c.second.second)
+ b += QString::fromStdString(toString(c.second.second)) + " " + c.second.first.toUpper() + " | ";
+ ui->balance->setText(b + QString::fromStdString(formatBalance(totalBalance)));
+}
+
void Main::refreshNetwork()
{
auto ps = m_client->peers();
@@ -510,24 +640,78 @@ eth::State const& Main::state() const
return ui->preview->isChecked() ? m_client->postState() : m_client->state();
}
-void Main::updateBlockCount()
+void Main::refreshAll()
{
- auto d = m_client->blockChain().details();
- auto diff = BlockInfo(m_client->blockChain().block()).difficulty;
- ui->blockCount->setText(QString("#%1 @%3 T%2 N%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(eth::c_protocolVersion).arg(eth::c_databaseVersion));
+ refreshDestination();
+ refreshBlockChain();
+ refreshBlockCount();
+ refreshPending();
+ refreshAccounts();
}
-void Main::on_blockChainFilter_textChanged()
+void Main::refreshPending()
{
- static QTimer* s_delayed = nullptr;
- if (!s_delayed)
+ cdebug << "refreshPending()";
+ ui->transactionQueue->clear();
+ for (Transaction const& t: m_client->pending())
{
- s_delayed = new QTimer(this);
- s_delayed->setSingleShot(true);
- connect(s_delayed, SIGNAL(timeout()), SLOT(refreshBlockChain()));
+ QString s = t.receiveAddress ?
+ QString("%2 %5> %3: %1 [%4]")
+ .arg(formatBalance(t.value).c_str())
+ .arg(render(t.safeSender()))
+ .arg(render(t.receiveAddress))
+ .arg((unsigned)t.nonce)
+ .arg(m_client->codeAt(t.receiveAddress).size() ? '*' : '-') :
+ QString("%2 +> %3: %1 [%4]")
+ .arg(formatBalance(t.value).c_str())
+ .arg(render(t.safeSender()))
+ .arg(render(right160(sha3(rlpList(t.safeSender(), t.nonce)))))
+ .arg((unsigned)t.nonce);
+ ui->transactionQueue->addItem(s);
}
- s_delayed->stop();
- s_delayed->start(200);
+}
+
+void Main::refreshAccounts()
+{
+ cdebug << "refreshAccounts()";
+ ui->accounts->clear();
+ ui->contracts->clear();
+ for (auto n = 0; n < 2; ++n)
+ for (auto i: m_client->addresses())
+ {
+ auto r = render(i);
+ if (r.contains('(') == !n)
+ {
+ if (n == 0 || ui->showAllAccounts->isChecked())
+ (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(m_client->balanceAt(i)).c_str()).arg(r).arg((unsigned)m_client->countAt(i)), ui->accounts))
+ ->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size));
+ if (m_client->codeAt(i).size())
+ (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(m_client->balanceAt(i)).c_str()).arg(r).arg((unsigned)m_client->countAt(i)), ui->contracts))
+ ->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size));
+ }
+ }
+}
+
+void Main::refreshDestination()
+{
+ cdebug << "refreshDestination()";
+ QString s;
+ for (auto i: m_client->addresses())
+ if ((s = pretty(i)).size())
+ // A namereg address
+ if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1)
+ ui->destination->addItem(s);
+ for (int i = 0; i < ui->destination->count(); ++i)
+ if (ui->destination->itemText(i) != "(Create Contract)" && !fromString(ui->destination->itemText(i)))
+ ui->destination->removeItem(i--);
+}
+
+void Main::refreshBlockCount()
+{
+ cdebug << "refreshBlockCount()";
+ auto d = m_client->blockChain().details();
+ auto diff = BlockInfo(m_client->blockChain().block()).difficulty;
+ ui->blockCount->setText(QString("#%1 @%3 T%2 N%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(eth::c_protocolVersion).arg(eth::c_databaseVersion));
}
static bool blockMatch(string const& _f, eth::BlockDetails const& _b, h256 _h, BlockChain const& _bc)
@@ -557,6 +741,7 @@ static bool transactionMatch(string const& _f, Transaction const& _t)
void Main::refreshBlockChain()
{
+ cdebug << "refreshBlockChain()";
eth::ClientGuard g(m_client.get());
auto const& st = state();
@@ -612,113 +797,49 @@ void Main::refreshBlockChain()
ui->blocks->setCurrentRow(0);
}
-void Main::refresh(bool _override)
+void Main::on_blockChainFilter_textChanged()
+{
+ static QTimer* s_delayed = nullptr;
+ if (!s_delayed)
+ {
+ s_delayed = new QTimer(this);
+ s_delayed->setSingleShot(true);
+ connect(s_delayed, SIGNAL(timeout()), SLOT(refreshBlockChain()));
+ }
+ s_delayed->stop();
+ s_delayed->start(200);
+}
+
+void Main::on_refresh_triggered()
+{
+ refreshAll();
+}
+
+void Main::timerEvent(QTimerEvent*)
{
// 7/18, Alex: aggregating timers, prelude to better threading?
// Runs much faster on slower dual-core processors
static int interval = 100;
// refresh mining every 200ms
- if(interval / 100 % 2 == 0)
+ if (interval / 100 % 2 == 0)
refreshMining();
// refresh peer list every 1000ms, reset counter
- if(interval == 1000)
+ if (interval == 1000)
{
interval = 0;
refreshNetwork();
- } else
+ }
+ else
interval += 100;
-
- eth::ClientGuard g(m_client.get());
- auto const& st = state();
-
- bool c = m_client->changed();
- if (c || _override)
- {
- changed();
-
- updateBlockCount();
-
- auto acs = st.addresses();
- ui->accounts->clear();
- ui->contracts->clear();
- for (auto n = 0; n < 2; ++n)
- for (auto i: acs)
- {
- auto r = render(i.first);
- if (r.contains('(') == !n)
- {
- if (n == 0 || ui->showAllAccounts->isChecked())
- (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(i.second).c_str()).arg(r).arg((unsigned)state().transactionsFrom(i.first)), ui->accounts))
- ->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size));
- if (st.addressHasCode(i.first))
- (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(i.second).c_str()).arg(r).arg((unsigned)st.transactionsFrom(i.first)), ui->contracts))
- ->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size));
-
- if (r.contains('('))
- {
- // A namereg address
- QString s = pretty(i.first);
- if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1)
- ui->destination->addItem(s);
- }
- }
- }
-
- for (int i = 0; i < ui->destination->count(); ++i)
- if (ui->destination->itemText(i) != "(Create Contract)" && !fromString(ui->destination->itemText(i)))
- ui->destination->removeItem(i--);
-
- ui->transactionQueue->clear();
- for (Transaction const& t: m_client->pending())
- {
- QString s = t.receiveAddress ?
- QString("%2 %5> %3: %1 [%4]")
- .arg(formatBalance(t.value).c_str())
- .arg(render(t.safeSender()))
- .arg(render(t.receiveAddress))
- .arg((unsigned)t.nonce)
- .arg(st.addressHasCode(t.receiveAddress) ? '*' : '-') :
- QString("%2 +> %3: %1 [%4]")
- .arg(formatBalance(t.value).c_str())
- .arg(render(t.safeSender()))
- .arg(render(right160(sha3(rlpList(t.safeSender(), t.nonce)))))
- .arg((unsigned)t.nonce);
- ui->transactionQueue->addItem(s);
- }
-
- refreshBlockChain();
- }
-
- if (c || m_keysChanged || _override)
- {
- m_keysChanged = false;
- ui->ourAccounts->clear();
- u256 totalBalance = 0;
- map> altCoins;
- Address coinsAddr = right160(st.storage(c_config, 1));
- for (unsigned i = 0; i < st.storage(coinsAddr, 0); ++i)
- altCoins[right160(st.storage(coinsAddr, st.storage(coinsAddr, i + 1)))] = make_pair(fromRaw(st.storage(coinsAddr, i + 1)), 0);
-// u256 totalGavCoinBalance = 0;
- for (auto i: m_myKeys)
- {
- u256 b = st.balance(i.address());
- (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)st.transactionsFrom(i.address())), ui->ourAccounts))
- ->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size));
- totalBalance += b;
-
- for (auto& c: altCoins)
- c.second.second += (u256)st.storage(c.first, (u160)i.address());
- }
+ if (m_ethereum)
+ m_ethereum->poll();
- QString b;
- for (auto const& c: altCoins)
- if (c.second.second)
- b += QString::fromStdString(toString(c.second.second)) + " " + c.second.first.toUpper() + " | ";
- ui->balance->setText(b + QString::fromStdString(formatBalance(totalBalance)));
- }
+ for (auto const& i: m_handlers)
+ if (m_client->checkWatch(i.first))
+ i.second();
}
string Main::renderDiff(eth::StateDiff const& _d) const
@@ -846,7 +967,6 @@ void Main::on_inject_triggered()
QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex");
bytes b = fromHex(s.toStdString());
m_client->inject(&b);
- refresh();
}
void Main::on_blocks_currentItemChanged()
@@ -1203,7 +1323,6 @@ void Main::on_killBlockchain_triggered()
ui->net->setChecked(false);
m_client.reset();
m_client.reset(new Client("AlethZero", Address(), string(), true));
- m_client->start();
readSettings();
}
@@ -1321,7 +1440,6 @@ void Main::on_send_clicked()
m_client->transact(s, value(), m_data, ui->gas->value(), gasPrice());
else
m_client->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice());
- refresh();
return;
}
statusBar()->showMessage("Couldn't make transaction: no single account contains at least the required amount.");
diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h
index 99b337a20..7117618ea 100644
--- a/alethzero/MainWin.h
+++ b/alethzero/MainWin.h
@@ -39,6 +39,7 @@ class Main;
namespace eth {
class Client;
class State;
+class TransactionFilter;
}
class QQuickView;
@@ -78,6 +79,8 @@ public slots:
void debug(QString _entry);
void warn(QString _entry);
+ void onKeysChanged();
+
private slots:
void eval(QString const& _js);
@@ -106,7 +109,7 @@ private slots:
void on_about_triggered();
void on_paranoia_triggered();
void on_nameReg_textChanged();
- void on_preview_triggered() { refresh(true); }
+ void on_preview_triggered();
void on_quit_triggered() { close(); }
void on_urlEdit_returnPressed();
void on_debugStep_triggered();
@@ -118,8 +121,8 @@ private slots:
void on_importKey_triggered();
void on_exportKey_triggered();
void on_inject_triggered();
- void on_showAll_triggered() { refresh(true); }
- void on_showAllAccounts_triggered() { refresh(true); }
+ void on_showAll_triggered() { refreshBlockChain(); }
+ void on_showAllAccounts_triggered() { refreshAccounts(); }
void on_loadJS_triggered();
void on_blockChainFilter_textChanged();
void on_clearPending_triggered();
@@ -134,18 +137,12 @@ private slots:
void on_debugCurrent_triggered();
void on_debugDumpState_triggered(int _add = 1);
void on_debugDumpStatePre_triggered();
-
- void refresh(bool _override = false);
- void refreshNetwork();
- void refreshMining();
- void refreshBlockChain();
+ void on_refresh_triggered();
signals:
- void changed(); // TODO: manifest
+ void poll();
private:
- void updateBlockCount();
-
QString pretty(eth::Address _a) const;
QString prettyU256(eth::u256 _n) const;
@@ -171,13 +168,44 @@ private:
eth::u256 value() const;
eth::u256 gasPrice() const;
+ unsigned installWatch(eth::TransactionFilter const& _tf, std::function const& _f);
+ unsigned installWatch(eth::h256 _tf, std::function const& _f);
+
+ void onNewPending();
+ void onNewBlock();
+ void onNameRegChange();
+ void onCurrenciesChange();
+ void onBalancesChange();
+
+ void installWatches();
+ void installCurrenciesWatch();
+ void installNameRegWatch();
+ void installBalancesWatch();
+
+ virtual void timerEvent(QTimerEvent*);
+
+ void refreshNetwork();
+ void refreshMining();
+
+ void refreshAll();
+ void refreshPending();
+ void refreshAccounts();
+ void refreshDestination();
+ void refreshBlockChain();
+ void refreshBlockCount();
+ void refreshBalances();
+
std::unique_ptr ui;
std::unique_ptr m_client;
+ std::map> m_handlers;
+ unsigned m_nameRegFilter = (unsigned)-1;
+ unsigned m_currenciesFilter = (unsigned)-1;
+ unsigned m_balancesFilter = (unsigned)-1;
QByteArray m_peers;
QMutex m_guiLock;
- QTimer* m_refresh;
+ QTimer* m_ticker;
QTimer* m_refreshNetwork;
QTimer* m_refreshMining;
QStringList m_servers;
@@ -202,5 +230,5 @@ private:
QList> m_consoleHistory;
- QEthereum* m_ethereum;
+ QEthereum* m_ethereum = nullptr;
};
diff --git a/eth/EthStubServer.cpp b/eth/EthStubServer.cpp
index 0d5182eb8..fc281a706 100644
--- a/eth/EthStubServer.cpp
+++ b/eth/EthStubServer.cpp
@@ -72,14 +72,15 @@ std::string EthStubServer::balanceAt(std::string const& _a)
Json::Value EthStubServer::check(Json::Value const& _as)
{
- if (m_client.changed())
+ // TODO
+// if (m_client.changed())
return _as;
- else
+/* else
{
Json::Value ret;
ret.resize(0);
return ret;
- }
+ }*/
}
std::string EthStubServer::create(const std::string& _bCode, const std::string& _sec, const std::string& _xEndowment, const std::string& _xGas, const std::string& _xGasPrice)
diff --git a/eth/eth.js b/eth/eth.js
index 02c6f4b8a..733097723 100644
--- a/eth/eth.js
+++ b/eth/eth.js
@@ -75,19 +75,19 @@ window.eth = (function ethScope() {
p[s.order[j]] = (s.order[j][0] === "b") ? a[j].unbin() : a[j]
return p
};
- if (m == "create" || m == "transact")
- ret[m] = function() { return reqAsync(m, getParams(arguments), arguments[s.order.length]) }
+ if (m == "create" || m == "transact")
+ ret[m] = function() { return reqAsync(m, getParams(arguments), arguments[s.order.length]) }
else
{
ret[am] = function() { return reqAsync(m, getParams(arguments), arguments[s.order.length]) }
if (s.params)
ret[m] = function() { return reqSync(m, getParams(arguments)) }
- else
+ else
Object.defineProperty(ret, m, {
get: function() { return reqSync(m, {}); },
set: function(v) {}
- })
- }
+ })
+ }
})(spec[si]);
ret.check = function(force) {
diff --git a/eth/main.cpp b/eth/main.cpp
index 3ab4c5837..34361a7d3 100644
--- a/eth/main.cpp
+++ b/eth/main.cpp
@@ -292,7 +292,6 @@ int main(int argc, char** argv)
if (!clientName.empty())
clientName += "/";
Client c("Ethereum(++)/" + clientName + "v" + eth::EthVersion + "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM), coinbase, dbPath);
- c.start();
cout << credits();
cout << "Address: " << endl << toHex(us.address().asArray()) << endl;
diff --git a/libethential/FixedHash.h b/libethential/FixedHash.h
index f792e378d..07e257534 100644
--- a/libethential/FixedHash.h
+++ b/libethential/FixedHash.h
@@ -163,7 +163,7 @@ private:
/// Fast equality operator for h256.
template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) const
{
- const uint64_t* hash1 = (const uint64_t*)this->data();
+ const uint64_t* hash1 = (const uint64_t*)data();
const uint64_t* hash2 = (const uint64_t*)_other.data();
return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && (hash1[3] == hash2[3]);
}
diff --git a/libethential/RLP.h b/libethential/RLP.h
index a3f42a783..6d388763f 100644
--- a/libethential/RLP.h
+++ b/libethential/RLP.h
@@ -164,6 +164,10 @@ public:
explicit operator u256() const { return toInt(); }
explicit operator bigint() const { return toInt(); }
template explicit operator FixedHash<_N>() const { return toHash>(); }
+ template explicit operator std::pair() const { return toPair(); }
+ template explicit operator std::vector() const { return toVector(); }
+ template explicit operator std::set() const { return toSet(); }
+ template explicit operator std::array() const { return toArray(); }
/// Converts to bytearray. @returns the empty byte array if not a string.
bytes toBytes() const { if (!isData()) return bytes(); return bytes(payload().data(), payload().data() + length()); }
@@ -175,6 +179,8 @@ public:
std::string toStringStrict() const { if (!isData()) throw BadCast(); return payload().cropped(0, length()).toString(); }
template std::vector toVector() const { std::vector ret; if (isList()) { ret.reserve(itemCount()); for (auto const& i: *this) ret.push_back((T)i); } return ret; }
+ template std::set toSet() const { std::set ret; if (isList()) { for (auto const& i: *this) ret.insert((T)i); } return ret; }
+ template std::pair toPair() const { std::pair ret; if (isList()) { ret.first = (T)((*this)[0]); ret.second = (U)((*this)[1]); } return ret; }
template std::array toArray() const { if (itemCount() != N || !isList()) throw BadCast(); std::array ret; for (uint i = 0; i < N; ++i) ret[i] = (T)operator[](i); return ret; }
/// Int conversion flags
@@ -285,8 +291,10 @@ public:
/// Appends a sequence of data to the stream as a list.
template RLPStream& append(std::vector<_T> const& _s) { return appendVector(_s); }
- template RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template RLPStream& appendVector(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
+ template RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
+ template RLPStream& append(std::set<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
+ template RLPStream& append(std::pair const& _s) { appendList(2); append(_s.first); append(_s.second); return *this; }
/// Appends a list.
RLPStream& appendList(uint _items);
diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp
index 660bf3498..f2dd89b4c 100644
--- a/libethereum/BlockChain.cpp
+++ b/libethereum/BlockChain.cpp
@@ -196,7 +196,7 @@ inline ldb::Slice toSlice(h256 _h, unsigned _sub = 0)
#endif
}
-void BlockChain::import(bytes const& _block, OverlayDB const& _db)
+h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
{
// VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi;
@@ -295,9 +295,11 @@ void BlockChain::import(bytes const& _block, OverlayDB const& _db)
// cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children.";
+ h256s ret;
// This might be the new best block...
if (td > details(m_lastBlockHash).totalDifficulty)
{
+ ret = treeRoute(m_lastBlockHash, newHash);
m_lastBlockHash = newHash;
m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32));
clog(BlockChainNote) << " Imported and best. Has" << (details(bi.parentHash).children.size() - 1) << "siblings.";
@@ -306,6 +308,40 @@ void BlockChain::import(bytes const& _block, OverlayDB const& _db)
{
clog(BlockChainNote) << " Imported but not best (oTD:" << details(m_lastBlockHash).totalDifficulty << ", TD:" << td << ")";
}
+ return ret;
+}
+
+h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common) const
+{
+ h256s ret;
+ h256s back;
+ unsigned fn = details(_from).number;
+ unsigned tn = details(_to).number;
+ while (fn > tn)
+ {
+ ret.push_back(_from);
+ _from = details(_from).parent;
+ fn--;
+ }
+ while (fn < tn)
+ {
+ back.push_back(_to);
+ _to = details(_to).parent;
+ tn--;
+ }
+ while (_from != _to)
+ {
+ _from = details(_from).parent;
+ _to = details(_to).parent;
+ ret.push_back(_from);
+ back.push_back(_to);
+ }
+ if (o_common)
+ *o_common = _from;
+ ret.reserve(ret.size() + back.size());
+ for (auto it = back.cbegin(); it != back.cend(); ++it)
+ ret.push_back(*it);
+ return ret;
}
void BlockChain::checkConsistency()
diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h
index 2eeda53c0..959ec1bdf 100644
--- a/libethereum/BlockChain.h
+++ b/libethereum/BlockChain.h
@@ -114,7 +114,8 @@ public:
bool attemptImport(bytes const& _block, OverlayDB const& _stateDB);
/// Import block into disk-backed DB
- void import(bytes const& _block, OverlayDB const& _stateDB);
+ /// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
+ h256s import(bytes const& _block, OverlayDB const& _stateDB);
/// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe.
BlockDetails details(h256 _hash) const;
@@ -144,13 +145,29 @@ public:
/// Get the hash of a block of a given number.
h256 numberHash(unsigned _n) const;
- std::vector> interestQueue() { std::vector> ret; swap(ret, m_interestQueue); return ret; }
- void pushInterest(Address _a) { m_interest[_a]++; }
- void popInterest(Address _a) { if (m_interest[_a] > 1) m_interest[_a]--; else if (m_interest[_a]) m_interest.erase(_a); }
-
+ /// @returns the genesis block header.
static BlockInfo const& genesis() { if (!s_genesis) { auto gb = createGenesisBlock(); (s_genesis = new BlockInfo)->populate(&gb); } return *s_genesis; }
+
+ /// @returns the genesis block as its RLP-encoded byte array.
+ /// @note This is slow as it's constructed anew each call. Consider genesis() instead.
static bytes createGenesisBlock();
+ /** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of
+ * blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent.
+ *
+ * If non-null, the h256 at @a o_common is set to the latest common ancestor of both blocks.
+ *
+ * e.g. if the block tree is 3a -> 2a -> 1a -> g and 2b -> 1b -> g (g is genesis, *a, *b are competing chains),
+ * then:
+ * @code
+ * treeRoute(3a, 2b) == { 3a, 2a, 1a, 1b, 2b }; // *o_common == g
+ * treeRoute(2a, 1a) == { 2a, 1a }; // *o_common == 1a
+ * treeRoute(1a, 2a) == { 1a, 2a }; // *o_common == 1a
+ * treeRoute(1b, 2a) == { 1b, 1a, 2a }; // *o_common == g
+ * @endcode
+ */
+ h256s treeRoute(h256 _from, h256 _to, h256* o_common = nullptr) const;
+
private:
void checkConsistency();
@@ -162,10 +179,6 @@ private:
mutable std::map m_cache;
mutable std::recursive_mutex m_lock;
- /// The queue of transactions that have happened that we're interested in.
- std::map m_interest;
- std::vector> m_interestQueue;
-
ldb::DB* m_db;
ldb::DB* m_extrasDB;
diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp
index 3fc03b459..715c3a3cc 100644
--- a/libethereum/Client.cpp
+++ b/libethereum/Client.cpp
@@ -30,6 +30,18 @@
using namespace std;
using namespace eth;
+void TransactionFilter::fillStream(RLPStream& _s) const
+{
+ _s.appendList(8) << m_from << m_to << m_stateAltered << m_altered << m_earliest << m_latest << m_max << m_skip;
+}
+
+h256 TransactionFilter::sha3() const
+{
+ RLPStream s;
+ fillStream(s);
+ return eth::sha3(s.out());
+}
+
VersionChecker::VersionChecker(string const& _dbPath):
m_path(_dbPath.size() ? _dbPath : Defaults::dbPath())
{
@@ -62,23 +74,25 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
m_vc.setOk();
- m_changed = true;
}
-void Client::start() {
+void Client::ensureWorking()
+{
static const char* c_threadName = "eth";
- m_work.reset(new thread([&](){
- setThreadName(c_threadName);
- while (m_workState.load(std::memory_order_acquire) != Deleting)
- work();
- m_workState.store(Deleted, std::memory_order_release);
-
- // Synchronise the state according to the head of the block chain.
- // TODO: currently it contains keys for *all* blocks. Make it remove old ones.
- m_preMine.sync(m_bc);
- m_postMine = m_preMine;
- }));
+ if (!m_work)
+ m_work.reset(new thread([&]()
+ {
+ setThreadName(c_threadName);
+ while (m_workState.load(std::memory_order_acquire) != Deleting)
+ work();
+ m_workState.store(Deleted, std::memory_order_release);
+
+ // Synchronise the state according to the head of the block chain.
+ // TODO: currently it contains keys for *all* blocks. Make it remove old ones.
+ m_preMine.sync(m_bc);
+ m_postMine = m_preMine;
+ }));
}
Client::~Client()
@@ -90,8 +104,89 @@ Client::~Client()
m_work->join();
}
+void Client::flushTransactions()
+{
+ work(true);
+}
+
+void Client::clearPending()
+{
+ ClientGuard l(this);
+ if (!m_postMine.pending().size())
+ return;
+ h256Set changeds;
+ for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
+ appendFromNewPending(m_postMine.bloom(i), changeds);
+ changeds.insert(NewPendingFilter);
+ m_postMine = m_preMine;
+ noteChanged(changeds);
+}
+
+unsigned Client::installWatch(h256 _h)
+{
+ auto ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0;
+ m_watches[ret] = Watch(_h);
+ return ret;
+}
+
+unsigned Client::installWatch(TransactionFilter const& _f)
+{
+ lock_guard l(m_filterLock);
+
+ h256 h = _f.sha3();
+
+ if (!m_filters.count(h))
+ m_filters.insert(make_pair(h, _f));
+
+ return installWatch(h);
+}
+
+void Client::uninstallWatch(unsigned _i)
+{
+ lock_guard l(m_filterLock);
+
+ auto it = m_watches.find(_i);
+ if (it == m_watches.end())
+ return;
+
+ auto fit = m_filters.find(it->second.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.earliest()) == m_postMine.info().number && i.second.filter.matches(_bloom))
+ o_changed.insert(i.first);
+}
+
+void Client::appendFromNewBlock(h256 _block, h256Set& o_changed) const
+{
+ auto d = m_bc.details(_block);
+
+ lock_guard l(m_filterLock);
+ for (pair const& i: m_filters)
+ if (numberOf(i.second.filter.earliest()) >= d.number && i.second.filter.matches(d.bloom))
+ o_changed.insert(i.first);
+}
+
+void Client::noteChanged(h256Set const& _filters)
+{
+ lock_guard l(m_filterLock);
+ for (auto& i: m_watches)
+ if (_filters.count(i.second.id))
+ i.second.changes++;
+}
+
void Client::startNetwork(unsigned short _listenPort, std::string const& _seedHost, unsigned short _port, NodeMode _mode, unsigned _peers, string const& _publicIP, bool _upnp)
{
+ ensureWorking();
+
ClientGuard l(this);
if (m_net.get())
return;
@@ -139,6 +234,8 @@ void Client::stopNetwork()
void Client::startMining()
{
+ ensureWorking();
+
m_doMine = true;
m_restartMining = true;
}
@@ -184,23 +281,28 @@ void Client::inject(bytesConstRef _rlp)
{
ClientGuard l(this);
m_tq.attemptImport(_rlp);
- m_changed = true;
}
-void Client::work()
+void Client::work(bool _justQueue)
{
- bool changed = false;
+ h256Set changeds;
// Process network events.
// Synchronise block chain with network.
// Will broadcast any of our (new) transactions and blocks, and collect & add any of their (new) transactions and blocks.
- if (m_net)
+ if (m_net && !_justQueue)
{
ClientGuard l(this);
m_net->process(); // must be in guard for now since it uses the blockchain. TODO: make BlockChain thread-safe.
- if (m_net->sync(m_bc, m_tq, m_stateDB))
- changed = true;
+ // TODO: return h256Set as block hashes, once for each block that has come in/gone out.
+ h256Set newBlocks = m_net->sync(m_bc, m_tq, m_stateDB);
+ if (newBlocks.size())
+ {
+ for (auto i: newBlocks)
+ appendFromNewBlock(i, changeds);
+ changeds.insert(NewBlockFilter);
+ }
}
// Synchronise state to block chain.
@@ -216,74 +318,82 @@ void Client::work()
{
if (m_doMine)
cnote << "New block on chain: Restarting mining operation.";
- changed = true;
m_restartMining = true; // need to re-commit to mine.
m_postMine = m_preMine;
}
- if (m_postMine.sync(m_tq, &changed))
+
+ // returns h256s as blooms, once for each transaction.
+ h256s newPendingBlooms = m_postMine.sync(m_tq);
+ if (newPendingBlooms.size())
{
+ for (auto i: newPendingBlooms)
+ appendFromNewPending(i, changeds);
+ changeds.insert(NewPendingFilter);
+
if (m_doMine)
cnote << "Additional transaction ready: Restarting mining operation.";
m_restartMining = true;
}
}
- if (m_doMine)
+ noteChanged(changeds);
+
+ if (!_justQueue)
{
- if (m_restartMining)
+ if (m_doMine)
{
- m_mineProgress.best = (double)-1;
- m_mineProgress.hashes = 0;
- m_mineProgress.ms = 0;
- ClientGuard l(this);
- if (m_paranoia)
+ if (m_restartMining)
{
- if (m_postMine.amIJustParanoid(m_bc))
+ m_mineProgress.best = (double)-1;
+ m_mineProgress.hashes = 0;
+ m_mineProgress.ms = 0;
+ ClientGuard l(this);
+ if (m_paranoia)
{
- cnote << "I'm just paranoid. Block is fine.";
- m_postMine.commitToMine(m_bc);
+ if (m_postMine.amIJustParanoid(m_bc))
+ {
+ cnote << "I'm just paranoid. Block is fine.";
+ m_postMine.commitToMine(m_bc);
+ }
+ else
+ {
+ cwarn << "I'm not just paranoid. Cannot mine. Please file a bug report.";
+ m_doMine = false;
+ }
}
else
- {
- cwarn << "I'm not just paranoid. Cannot mine. Please file a bug report.";
- m_doMine = false;
- }
+ m_postMine.commitToMine(m_bc);
}
- else
- m_postMine.commitToMine(m_bc);
}
- }
- if (m_doMine)
- {
- m_restartMining = false;
+ if (m_doMine)
+ {
+ m_restartMining = false;
- // Mine for a while.
- MineInfo mineInfo = m_postMine.mine(100);
+ // Mine for a while.
+ MineInfo mineInfo = m_postMine.mine(100);
- m_mineProgress.best = min(m_mineProgress.best, mineInfo.best);
- m_mineProgress.current = mineInfo.best;
- m_mineProgress.requirement = mineInfo.requirement;
- m_mineProgress.ms += 100;
- m_mineProgress.hashes += mineInfo.hashes;
- {
- ClientGuard l(this);
- m_mineHistory.push_back(mineInfo);
- }
+ m_mineProgress.best = min(m_mineProgress.best, mineInfo.best);
+ m_mineProgress.current = mineInfo.best;
+ m_mineProgress.requirement = mineInfo.requirement;
+ m_mineProgress.ms += 100;
+ m_mineProgress.hashes += mineInfo.hashes;
+ {
+ ClientGuard l(this);
+ m_mineHistory.push_back(mineInfo);
+ }
- if (mineInfo.completed)
- {
- // Import block.
- ClientGuard l(this);
- m_postMine.completeMine();
- m_bc.attemptImport(m_postMine.blockData(), m_stateDB);
- m_changed = true;
+ if (mineInfo.completed)
+ {
+ // Import block.
+ ClientGuard l(this);
+ m_postMine.completeMine();
+ m_bc.attemptImport(m_postMine.blockData(), m_stateDB);
+ }
}
+ else
+ this_thread::sleep_for(chrono::milliseconds(100));
}
- else
- this_thread::sleep_for(chrono::milliseconds(100));
-
- m_changed = m_changed || changed;
}
void Client::lock() const
@@ -316,6 +426,15 @@ State Client::asOf(int _h) const
return State(m_stateDB, m_bc, m_bc.numberHash(numberOf(_h)));
}
+std::vector Client::addresses(int _block) const
+{
+ ClientGuard l(this);
+ vector ret;
+ for (auto const& i: asOf(_block).addresses())
+ ret.push_back(i.first);
+ return ret;
+}
+
u256 Client::balanceAt(Address _a, int _block) const
{
ClientGuard l(this);
@@ -480,28 +599,24 @@ PastMessages Client::transactions(TransactionFilter const& _f) const
ret.insert(ret.begin(), pm[j].polish(h256(), ts, 0));
}
}
-/* for (unsigned i = m_postMine.pending().size(); i-- && ret.size() != m;)
- if (_f.matches(m_postMine, i))
- {
- if (s)
- s--;
- else
- ret.insert(ret.begin(), PastMessage(m_postMine.pending()[i], h256(), i, time(0), 0));
- }*/
// 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)
{
auto d = m_bc.details(h);
+#if ETH_DEBUG
int total = 0;
+#endif
if (_f.matches(d.bloom))
{
// Might have a block that contains a transaction that contains a matching message.
@@ -517,7 +632,9 @@ PastMessages Client::transactions(TransactionFilter const& _f) const
PastMessages pm = _f.matches(changes, i);
if (pm.size())
{
+#if ETH_DEBUG
total += pm.size();
+#endif
auto ts = BlockInfo(m_bc.block(h)).timestamp;
for (unsigned j = 0; j < pm.size() && ret.size() != m; ++j)
if (s)
@@ -527,35 +644,20 @@ PastMessages Client::transactions(TransactionFilter const& _f) const
ret.insert(ret.begin(), pm[j].polish(h, ts, cn - n + 2));
}
}
+#if ETH_DEBUG
if (!total)
falsePos++;
-/* try
- {
- State st(m_stateDB, m_bc, h);
- unsigned os = s;
- for (unsigned i = st.pending().size(); i-- && ret.size() != m;)
- if (_f.matches(st, i))
- {
- if (s)
- s--;
- else
- ret.insert(ret.begin(), PastMessage(st.pending()[i], h, i, BlockInfo(m_bc.block(h)).timestamp, cn - n + 2));
- }
- if (os - s == st.pending().size())
- falsePos++;
- }
- catch (...)
- {
- // Gaa. bad state. not good at all. bury head in sand for now.
- }
-*/
}
else
skipped++;
-
+#else
+ }
+#endif
if (n == end)
break;
}
+#if ETH_DEBUG
// cdebug << (begin - n) << "searched; " << skipped << "skipped; " << falsePos << "false +ves";
+#endif
return ret;
}
diff --git a/libethereum/Client.h b/libethereum/Client.h
index 387282935..180d2d07b 100644
--- a/libethereum/Client.h
+++ b/libethereum/Client.h
@@ -82,7 +82,7 @@ struct PastMessage
{
PastMessage(Manifest const& _m, std::vector _path, Address _o): to(_m.to), from(_m.from), value(_m.value), input(_m.input), output(_m.output), path(_path), origin(_o) {}
- PastMessage& polish(h256 _b, u256 _ts, int _a) { block = _b; timestamp = _ts; age = _a; return *this; }
+ PastMessage& polish(h256 _b, u256 _ts, unsigned _n) { block = _b; timestamp = _ts; number = _n; return *this; }
Address to; ///< The receiving address of the transaction. Address() in the case of a creation.
Address from; ///< The receiving address of the transaction. Address() in the case of a creation.
@@ -93,8 +93,8 @@ struct PastMessage
std::vector path; ///< Call path into the block transaction. size() is always > 0. First item is the transaction index in the block.
Address origin; ///< Originating sender of the transaction.
h256 block; ///< Block hash.
- u256 timestamp; ///< Block timestamp.
- int age; ///< Transaction age.
+ u256 timestamp; ///< Block timestamp.
+ unsigned number; ///< Block number.
};
typedef std::vector PastMessages;
@@ -104,6 +104,9 @@ 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) {}
+ void fillStream(RLPStream& _s) const;
+ h256 sha3() const;
+
int earliest() const { return m_earliest; }
int latest() const { return m_latest; }
unsigned max() const { return m_max; }
@@ -134,6 +137,26 @@ private:
unsigned m_skip;
};
+struct InstalledFilter
+{
+ InstalledFilter(TransactionFilter const& _f): filter(_f) {}
+
+ TransactionFilter filter;
+ unsigned refCount = 1;
+};
+
+static const h256 NewPendingFilter = u256(0);
+static const h256 NewBlockFilter = u256(1);
+
+struct Watch
+{
+ Watch() {}
+ explicit Watch(h256 _id): id(_id) {}
+
+ h256 id;
+ unsigned changes = 1;
+};
+
/**
* @brief Main API hub for interfacing with Ethereum.
*/
@@ -143,9 +166,6 @@ public:
/// Constructor.
explicit Client(std::string const& _clientVersion, Address _us = Address(), std::string const& _dbPath = std::string(), bool _forceClean = false);
- // Start client. Boost require threads are started outside constructor.
- void start();
-
/// Destructor.
~Client();
@@ -156,36 +176,17 @@ public:
/// @returns the new contract's address (assuming it all goes through).
Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo);
+ /// Blocks until all pending transactions have been processed.
+ void flushTransactions();
+
+ /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly.
void inject(bytesConstRef _rlp);
/// Makes the given call. Nothing is recorded into the state. TODO
// bytes call(Secret _secret, u256 _amount, u256 _gasPrice, Address _dest, u256 _gas, bytes _data = bytes());
- /// Requires transactions involving this address be queued for inspection.
- void setInterest(Address _dest);
-
- /// @returns incoming minable transactions that we wanted to be notified of. Clears the queue.
- Transactions pendingQueue() { ClientGuard g(this); return m_tq.interestQueue(); }
-
- /// @returns alterations in state of a mined block that we wanted to be notified of. Clears the queue.
- std::vector> minedQueue() { ClientGuard g(this); return m_bc.interestQueue(); }
-
- // Not yet - probably best as using some sort of signals implementation.
- /// Calls @a _f when a valid transaction is received that involves @a _dest and once per such transaction.
-// void onPending(Address _dest, function const& _f);
-
- /// Calls @a _f when a transaction is mined that involves @a _dest and once per change.
-// void onConfirmed(Address _dest, function const& _f);
-
// Informational stuff
- /// Determines whether at least one of the state/blockChain/transactionQueue has changed since the last call to changed().
- bool changed() const { auto ret = m_changed; m_changed = false; return ret; }
- bool peekChanged() const { return m_changed; }
-
- /// Get a map containing each of the pending transactions.
- Transactions pending() const { return m_postMine.pending(); }
-
// [OLD API]:
/// Locks/unlocks the state/blockChain/transactionQueue for access.
@@ -201,11 +202,34 @@ public:
// [NEW API]
- u256 balanceAt(Address _a, int _block = -1) const;
- u256 countAt(Address _a, int _block = -1) const;
- u256 stateAt(Address _a, u256 _l, int _block = -1) const;
- bytes codeAt(Address _a, int _block = -1) const;
- PastMessages transactions(TransactionFilter const& _f) const;
+ void setDefault(int _block) { m_default = _block; }
+
+ u256 balanceAt(Address _a) const { return balanceAt(_a, m_default); }
+ u256 countAt(Address _a) const { return countAt(_a, m_default); }
+ u256 stateAt(Address _a, u256 _l) const { return stateAt(_a, _l, m_default); }
+ bytes codeAt(Address _a) const { return codeAt(_a, m_default); }
+
+ u256 balanceAt(Address _a, int _block) const;
+ u256 countAt(Address _a, int _block) const;
+ u256 stateAt(Address _a, u256 _l, int _block) const;
+ bytes codeAt(Address _a, int _block) const;
+ PastMessages transactions(TransactionFilter const& _filter) const;
+ PastMessages transactions(unsigned _watchId) const { try { std::lock_guard l(m_filterLock); return transactions(m_filters.at(m_watches.at(_watchId).id).filter); } catch (...) { return PastMessages(); } }
+ unsigned installWatch(TransactionFilter const& _filter);
+ unsigned installWatch(h256 _filterId);
+ void uninstallWatch(unsigned _watchId);
+ bool peekWatch(unsigned _watchId) const { std::lock_guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return 0; } }
+ bool checkWatch(unsigned _watchId) { std::lock_guard l(m_filterLock); bool ret = false; try { ret = m_watches.at(_watchId).changes; m_watches.at(_watchId).changes = 0; } catch (...) {} return ret; }
+
+ // [EXTRA API]:
+
+ /// Get a map containing each of the pending transactions.
+ /// @TODO: Remove in favour of transactions().
+ Transactions pending() const { return m_postMine.pending(); }
+
+ /// Get a list of all active addresses.
+ std::vector addresses() const { return addresses(m_default); }
+ std::vector addresses(int _block) const;
// Misc stuff:
@@ -253,10 +277,27 @@ public:
std::list miningHistory() { auto ret = m_mineHistory; m_mineHistory.clear(); return ret; }
/// Clears pending transactions. Just for debug use.
- void clearPending() { ClientGuard l(this); m_postMine = m_preMine; changed(); }
+ void clearPending();
private:
- void work();
+ /// Ensure the worker thread is running. Needed for networking & mining.
+ void ensureWorking();
+
+ /// Do some work. Handles networking and mining.
+ /// @param _justQueue If true will only processing the transaction queues.
+ void work(bool _justQueue = false);
+
+ /// Collate the changed filters for the bloom filter of the given pending transaction.
+ /// Insert any filters that are activated into @a o_changed.
+ void appendFromNewPending(h256 _pendingTransactionBloom, h256Set& o_changed) const;
+
+ /// Collate the changed filters for the hash of the given block.
+ /// Insert any filters that are activated into @a o_changed.
+ void appendFromNewBlock(h256 _blockHash, h256Set& o_changed) const;
+
+ /// Record that the set of filters @a _filters have changed.
+ /// This doesn't actually make any callbacks, but incrememnts some counters in m_watches.
+ void noteChanged(h256Set const& _filters);
/// Return the actual block number of the block with the given int-number (positive is the same, INT_MIN is genesis block, < 0 is negative age, thus -1 is most recently mined, 0 is pending.
unsigned numberOf(int _b) const;
@@ -268,9 +309,10 @@ private:
VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
BlockChain m_bc; ///< Maintains block database.
TransactionQueue m_tq; ///< Maintains list of incoming transactions not yet on the block chain.
- OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
+ OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
State m_preMine; ///< The present state of the client.
State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
+
std::unique_ptr m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required.
std::unique_ptr m_work;///< The work thread.
@@ -283,7 +325,11 @@ private:
std::list m_mineHistory;
mutable bool m_restartMining = false;
- mutable bool m_changed;
+ mutable std::mutex m_filterLock;
+ std::map m_filters;
+ std::map m_watches;
+
+ int m_default = -1;
};
inline ClientGuard::ClientGuard(Client const* _c): m_client(_c)
diff --git a/libethereum/PeerServer.cpp b/libethereum/PeerServer.cpp
index 275d9ace7..2de031ac2 100644
--- a/libethereum/PeerServer.cpp
+++ b/libethereum/PeerServer.cpp
@@ -350,9 +350,11 @@ bool PeerServer::noteBlock(h256 _hash, bytesConstRef _data)
return false;
}
-bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o)
+h256Set PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o)
{
- bool ret = ensureInitialised(_bc, _tq);
+ h256Set ret;
+
+ bool netChange = ensureInitialised(_bc, _tq);
if (m_mode == NodeMode::Full)
{
@@ -421,10 +423,11 @@ bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o)
{
try
{
- _bc.import(*it, _o);
+ for (auto h: _bc.import(*it, _o))
+ ret.insert(h);
it = m_incomingBlocks.erase(it);
++accepted;
- ret = true;
+ netChange = true;
}
catch (UnknownParent)
{
@@ -506,6 +509,7 @@ bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o)
worst->disconnect(TooManyPeers);
}
+ (void)netChange;
return ret;
}
diff --git a/libethereum/PeerServer.h b/libethereum/PeerServer.h
index 6e44d2ea4..0eae35a1f 100644
--- a/libethereum/PeerServer.h
+++ b/libethereum/PeerServer.h
@@ -58,7 +58,7 @@ public:
void connect(bi::tcp::endpoint const& _ep);
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
- bool sync(BlockChain& _bc, TransactionQueue&, OverlayDB& _o);
+ h256Set sync(BlockChain& _bc, TransactionQueue&, OverlayDB& _o);
/// 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.
diff --git a/libethereum/PeerSession.cpp b/libethereum/PeerSession.cpp
index a80cc6b27..373673f71 100644
--- a/libethereum/PeerSession.cpp
+++ b/libethereum/PeerSession.cpp
@@ -463,7 +463,7 @@ void PeerSession::writeImpl(bytes& _buffer)
if (m_writeq.size() > 1)
return;
- this->write();
+ write();
}
void PeerSession::write()
@@ -477,12 +477,12 @@ void PeerSession::write()
{
// must check que, as write callback can occur following dropped()
if (!m_writeq.empty())
- this->m_writeq.pop_front();
+ m_writeq.pop_front();
if (ec)
{
cwarn << "Error sending: " << ec.message();
- this->dropped();
+ dropped();
} else
m_strand.post(boost::bind(&PeerSession::write, this));
}));
diff --git a/libethereum/State.cpp b/libethereum/State.cpp
index 66dec31e7..f7fd5049c 100644
--- a/libethereum/State.cpp
+++ b/libethereum/State.cpp
@@ -454,28 +454,24 @@ bool State::cull(TransactionQueue& _tq) const
return ret;
}
-bool State::sync(TransactionQueue& _tq, bool* _changed)
+h256s State::sync(TransactionQueue& _tq, bool* o_transactionQueueChanged)
{
// TRANSACTIONS
- bool ret = false;
+ h256s ret;
auto ts = _tq.transactions();
- vector> futures;
for (int goodTxs = 1; goodTxs;)
{
goodTxs = 0;
for (auto const& i: ts)
- {
if (!m_transactionSet.count(i.first))
{
// don't have it yet! Execute it now.
try
{
- ret = true;
uncommitToMine();
execute(i.second);
- if (_changed)
- *_changed = true;
+ ret.push_back(m_transactions.back().changes.bloom());
_tq.noteGood(i);
++goodTxs;
}
@@ -485,8 +481,8 @@ bool State::sync(TransactionQueue& _tq, bool* _changed)
{
// too old
_tq.drop(i.first);
- if (_changed)
- *_changed = true;
+ if (o_transactionQueueChanged)
+ *o_transactionQueueChanged = true;
}
else
_tq.setFuture(i);
@@ -495,11 +491,10 @@ bool State::sync(TransactionQueue& _tq, bool* _changed)
{
// Something else went wrong - drop it.
_tq.drop(i.first);
- if (_changed)
- *_changed = true;
+ if (o_transactionQueueChanged)
+ *o_transactionQueueChanged = true;
}
}
- }
}
return ret;
}
diff --git a/libethereum/State.h b/libethereum/State.h
index 3618b9a9a..ccf6bfdd0 100644
--- a/libethereum/State.h
+++ b/libethereum/State.h
@@ -138,6 +138,8 @@ public:
/// @returns the set containing all addresses currently in use in Ethereum.
std::map addresses() const;
+ BlockInfo const& info() const { return m_currentBlock; }
+
/// @brief Checks that mining the current object will result in a valid block.
/// Effectively attempts to import the serialised block.
/// @returns true if all is ok. If it's false, worry.
@@ -178,10 +180,10 @@ public:
// TODO: Cleaner interface.
/// Sync our transactions, killing those from the queue that we have and assimilating those that we don't.
- /// @returns true if we uncommitted from mining during the operation.
- /// @a o_changed boolean pointer, the value of which will be set to true if the state changed and the pointer
- /// is non-null
- bool sync(TransactionQueue& _tq, bool* o_changed = nullptr);
+ /// @returns a list of bloom filters one for each transaction placed from the queue into the state.
+ /// @a o_transactionQueueChanged boolean pointer, the value of which will be set to true if the transaction queue
+ /// changed and the pointer is non-null
+ h256s sync(TransactionQueue& _tq, bool* o_transactionQueueChanged = nullptr);
/// Like sync but only operate on _tq, killing the invalid/old ones.
bool cull(TransactionQueue& _tq) const;
diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp
index 520547f64..9c9f5ce5f 100644
--- a/libqethereum/QEthereum.cpp
+++ b/libqethereum/QEthereum.cpp
@@ -1,6 +1,3 @@
-#if ETH_QTQML
-#include
-#endif
#include
#include
#include
@@ -47,143 +44,6 @@ using eth::g_logPost;
using eth::g_logVerbosity;
using eth::c_instructionInfo;
-// Horrible global for the mainwindow. Needed for the QmlEthereums 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.
-eth::Client* g_qmlClient;
-QObject* g_qmlMain;
-
-QmlAccount::QmlAccount(QObject*)
-{
-}
-
-QmlAccount::~QmlAccount()
-{
-}
-
-void QmlAccount::setEthereum(QmlEthereum* _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();
-}
-
-eth::u256 QmlAccount::balance() const
-{
- if (m_eth)
- return m_eth->balanceAt(m_address);
- return u256(0);
-}
-
-double QmlAccount::txCount() const
-{
- if (m_eth)
- return m_eth->txCountAt(m_address);
- return 0;
-}
-
-bool QmlAccount::isContract() const
-{
- if (m_eth)
- return m_eth->isContractAt(m_address);
- return 0;
-}
-
-QmlEthereum::QmlEthereum(QObject* _p): QObject(_p)
-{
- connect(g_qmlMain, SIGNAL(changed()), SIGNAL(changed()));
-}
-
-QmlEthereum::~QmlEthereum()
-{
-}
-
-Client* QmlEthereum::client() const
-{
- return g_qmlClient;
-}
-
-Address QmlEthereum::coinbase() const
-{
- return client()->address();
-}
-
-void QmlEthereum::setCoinbase(Address _a)
-{
- if (client()->address() != _a)
- {
- client()->setAddress(_a);
- changed();
- }
-}
-
-u256 QmlEthereum::balanceAt(Address _a) const
-{
- return client()->postState().balance(_a);
-}
-
-bool QmlEthereum::isContractAt(Address _a) const
-{
- return client()->postState().addressHasCode(_a);
-}
-
-bool QmlEthereum::isMining() const
-{
- return client()->isMining();
-}
-
-bool QmlEthereum::isListening() const
-{
- return client()->haveNetwork();
-}
-
-void QmlEthereum::setMining(bool _l)
-{
- if (_l)
- client()->startMining();
- else
- client()->stopMining();
-}
-
-void QmlEthereum::setListening(bool _l)
-{
- if (_l)
- client()->startNetwork();
- else
- client()->stopNetwork();
-}
-
-double QmlEthereum::txCountAt(Address _a) const
-{
- return (double)client()->postState().transactionsFrom(_a);
-}
-
-unsigned QmlEthereum::peerCount() const
-{
- return (unsigned)client()->peerCount();
-}
-
-void QmlEthereum::transact(Secret _secret, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _init)
-{
- client()->transact(_secret, _amount, bytes(_init.data(), _init.data() + _init.size()), _gas, _gasPrice);
-}
-
-void QmlEthereum::transact(Secret _secret, Address _dest, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _data)
-{
- client()->transact(_secret, _amount, _dest, bytes(_data.data(), _data.data() + _data.size()), _gas, _gasPrice);
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
eth::bytes toBytes(QString const& _s)
{
if (_s.startsWith("0x"))
@@ -209,11 +69,6 @@ QString padded(QString const& _s, unsigned _l, unsigned _r)
//"0xff".bin().unbin()
-QString QEthereum::secretToAddress(QString _s) const
-{
- return toQJS(KeyPair(toSecret(_s)).address());
-}
-
QString padded(QString const& _s, unsigned _l)
{
if (_s.startsWith("0x") || !_s.contains(QRegExp("[^0-9]")))
@@ -234,11 +89,24 @@ QString unpadded(QString _s)
QEthereum::QEthereum(QObject* _p, Client* _c, QList _accounts): QObject(_p), m_client(_c), m_accounts(_accounts)
{
// required to prevent crash on osx when performing addto/evaluatejavascript calls
- this->moveToThread(_p->thread());
+ moveToThread(_p->thread());
}
QEthereum::~QEthereum()
{
+ clearWatches();
+}
+
+void QEthereum::clearWatches()
+{
+ for (auto i: m_watches)
+ m_client->uninstallWatch(i);
+ m_watches.clear();
+}
+
+QString QEthereum::secretToAddress(QString _s) const
+{
+ return toQJS(KeyPair(toSecret(_s)).address());
}
void QEthereum::setup(QWebFrame*)
@@ -310,7 +178,7 @@ void QEthereum::setCoinbase(QString _a)
if (client()->address() != toAddress(_a))
{
client()->setAddress(toAddress(_a));
- changed();
+ coinbaseChanged();
}
}
@@ -370,11 +238,11 @@ double QEthereum::countAt(QString _a, int _block) const
return (double)(uint64_t)client()->countAt(toAddress(_a), _block);
}
-QString QEthereum::getTransactions(QString _a) const
+static eth::TransactionFilter toTransactionFilter(QString _json)
{
eth::TransactionFilter filter;
- QJsonObject f = QJsonDocument::fromJson(_a.toUtf8()).object();
+ QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
if (f.contains("earliest"))
filter.withEarliest(f["earliest"].toInt());
if (f.contains("latest"))
@@ -413,12 +281,15 @@ QString QEthereum::getTransactions(QString _a) const
else
filter.altered(toAddress(f["altered"].toString()));
}
+ return filter;
+}
- QJsonArray ret;
- for (eth::PastMessage const& t: m_client->transactions(filter))
+static QString toJson(eth::PastMessages const& _pms)
+{
+ QJsonArray jsonArray;
+ for (eth::PastMessage const& t: _pms)
{
QJsonObject v;
- v["data"] = ::fromBinary(t.input);
v["input"] = ::fromBinary(t.input);
v["output"] = ::fromBinary(t.output);
v["to"] = toQJS(t.to);
@@ -430,10 +301,15 @@ QString QEthereum::getTransactions(QString _a) const
for (int i: t.path)
path.append(i);
v["path"] = path;
- v["age"] = (int)t.age;
- ret.append(v);
+ v["number"] = (int)t.number;
+ jsonArray.append(v);
}
- return QString::fromUtf8(QJsonDocument(ret).toJson());
+ return QString::fromUtf8(QJsonDocument(jsonArray).toJson());
+}
+
+QString QEthereum::getTransactions(QString _json) const
+{
+ return toJson(m_client->transactions(toTransactionFilter(_json)));
}
bool QEthereum::isMining() const
@@ -469,19 +345,48 @@ unsigned QEthereum::peerCount() const
QString QEthereum::doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice)
{
- client()->changed();
auto ret = toQJS(client()->transact(toSecret(_secret), toU256(_amount), toBytes(_init), toU256(_gas), toU256(_gasPrice)));
- while (!client()->peekChanged())
- this_thread::sleep_for(chrono::milliseconds(10));
+ client()->flushTransactions();
return ret;
}
void QEthereum::doTransact(QString _secret, QString _amount, QString _dest, QString _data, QString _gas, QString _gasPrice)
{
- client()->changed();
client()->transact(toSecret(_secret), toU256(_amount), toAddress(_dest), toBytes(_data), toU256(_gas), toU256(_gasPrice));
- while (!client()->peekChanged())
- this_thread::sleep_for(chrono::milliseconds(10));
+ client()->flushTransactions();
+}
+
+unsigned QEthereum::newWatch(QString _json)
+{
+ unsigned ret;
+ if (_json == "chainChanged")
+ ret = m_client->installWatch(eth::NewBlockFilter);
+ else if (_json == "pendingChanged")
+ ret = m_client->installWatch(eth::NewPendingFilter);
+ else
+ ret = m_client->installWatch(toTransactionFilter(_json));
+ m_watches.push_back(ret);
+ return ret;
+}
+
+QString QEthereum::watchTransactions(unsigned _w)
+{
+ return toJson(m_client->transactions(_w));
+}
+
+void QEthereum::killWatch(unsigned _w)
+{
+ m_client->uninstallWatch(_w);
+ std::remove(m_watches.begin(), m_watches.end(), _w);
+}
+
+void QEthereum::poll()
+{
+ if (!m_client)
+ return;
+ for (auto w: m_watches)
+ if (m_client->checkWatch(w))
+ emit watchChanged(w);
}
// extra bits needed to link on VS
diff --git a/libqethereum/QEthereum.h b/libqethereum/QEthereum.h
index 3b0eb34de..bd5832304 100644
--- a/libqethereum/QEthereum.h
+++ b/libqethereum/QEthereum.h
@@ -1,9 +1,8 @@
#pragma once
-#include
-#if ETH_QTQML
-#include
-#endif
+#include
+#include
+#include
#include
#include
@@ -12,280 +11,10 @@ class Client;
class State;
}
-class QQmlEngine;
class QJSEngine;
class QWebFrame;
class QEthereum;
-class QmlAccount;
-class QmlEthereum;
-
-extern eth::Client* g_qmlClient;
-extern QObject* g_qmlMain;
-
-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(QmlAccount*)
-Q_DECLARE_METATYPE(QmlEthereum*)
-
-class QmlU256Helper: public QObject
-{
- Q_OBJECT
-
-public:
- QmlU256Helper(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 QmlKeyHelper: public QObject
-{
- Q_OBJECT
-
-public:
- QmlKeyHelper(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 QmlAccount: public QObject
-{
- Q_OBJECT
-
-public:
- QmlAccount(QObject* _p = nullptr);
- virtual ~QmlAccount();
-
- Q_INVOKABLE QmlEthereum* ethereum() const { return m_eth; }
- Q_INVOKABLE eth::u256 balance() const;
- Q_INVOKABLE double txCount() const;
- Q_INVOKABLE bool isContract() const;
- Q_INVOKABLE eth::Address address() const { return m_address; }
-
- // TODO: past transactions models.
-
-public slots:
- void setEthereum(QmlEthereum* _eth);
- void setAddress(eth::Address _a) { m_address = _a; }
-
-signals:
- void changed();
- void ethChanged();
-
-private:
- QmlEthereum* 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 READ address WRITE setAddress NOTIFY changed)
- Q_PROPERTY(QmlEthereum* ethereum READ ethereum WRITE setEthereum NOTIFY ethChanged)
-};
-
-class QmlEthereum: public QObject
-{
- Q_OBJECT
-
-public:
- QmlEthereum(QObject* _p = nullptr);
- virtual ~QmlEthereum();
-
- eth::Client* client() const;
-
- static QObject* constructU256Helper(QQmlEngine*, QJSEngine*) { return new QmlU256Helper; }
- static QObject* constructKeyHelper(QQmlEngine*, QJSEngine*) { return new QmlKeyHelper; }
-
- 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 QmlEthereum* self() { return this; }
-
-public slots:
- void transact(eth::Secret _secret, eth::Address _dest, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _data);
- void transact(eth::Secret _secret, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _init);
- 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)
-};
-
-#if 0
-template T to(QVariant const& _s) { if (_s.type() != QVariant::String) return T(); auto s = _s.toString().toLatin1(); assert(s.size() == sizeof(T)); return *(T*)s.data(); }
-template QVariant toQJS(T const& _s) { QLatin1String ret((char*)&_s, sizeof(T)); assert(QVariant(QString(ret)).toString().toLatin1().size() == sizeof(T)); assert(*(T*)(QVariant(QString(ret)).toString().toLatin1().data()) == _s); return QVariant(QString(ret)); }
-
-class U256Helper: public QObject
-{
- Q_OBJECT
-
-public:
- U256Helper(QObject* _p = nullptr): QObject(_p) {}
-
- static eth::u256 in(QVariant const& _s) { return to(_s); }
- static QVariant out(eth::u256 const& _s) { return toQJS(_s); }
-
- Q_INVOKABLE QVariant add(QVariant _a, QVariant _b) const { return out(in(_a) + in(_b)); }
- Q_INVOKABLE QVariant sub(QVariant _a, QVariant _b) const { return out(in(_a) - in(_b)); }
- Q_INVOKABLE QVariant mul(QVariant _a, int _b) const { return out(in(_a) * in(_b)); }
- Q_INVOKABLE QVariant mul(int _a, QVariant _b) const { return out(in(_a) * in(_b)); }
- Q_INVOKABLE QVariant div(QVariant _a, int _b) const { return out(in(_a) / in(_b)); }
-
- Q_INVOKABLE QVariant wei(double _s) const { return out(eth::u256(_s)); }
- Q_INVOKABLE QVariant szabo(double _s) const { return out(eth::u256(_s * (double)eth::szabo)); }
- Q_INVOKABLE QVariant finney(double _s) const { return out(eth::u256(_s * (double)eth::finney)); }
- Q_INVOKABLE QVariant ether(double _s) const { return out(eth::u256(_s * (double)eth::ether)); }
- Q_INVOKABLE QVariant wei(unsigned _s) const { return value(_s); }
- Q_INVOKABLE QVariant szabo(unsigned _s) const { return out(eth::u256(_s) * eth::szabo); }
- Q_INVOKABLE QVariant finney(unsigned _s) const { return out(eth::u256(_s) * eth::finney); }
- Q_INVOKABLE QVariant ether(unsigned _s) const { return out(eth::u256(_s) * eth::ether); }
- Q_INVOKABLE double toWei(QVariant _t) const { return toValue(_t); }
- Q_INVOKABLE double toSzabo(QVariant _t) const { return toWei(_t) / (double)eth::szabo; }
- Q_INVOKABLE double toFinney(QVariant _t) const { return toWei(_t) / (double)eth::finney; }
- Q_INVOKABLE double toEther(QVariant _t) const { return toWei(_t) / (double)eth::ether; }
-
- Q_INVOKABLE QVariant value(unsigned _s) const { return out(eth::u256(_s)); }
- Q_INVOKABLE double toValue(QVariant _t) const { return (double)in(_t); }
-
- Q_INVOKABLE QString ethOf(QVariant _t) const { return QString::fromStdString(eth::formatBalance(in(_t))); }
- Q_INVOKABLE QString stringOf(QVariant _t) const { return QString::fromStdString(eth::toString(in(_t))); }
-
- Q_INVOKABLE QByteArray bytesOf(QVariant _t) const { eth::h256 b = in(_t); return QByteArray((char const*)&b, sizeof(eth::h256)); }
- Q_INVOKABLE QVariant fromHex(QString _s) const { return out((eth::u256)eth::h256(_s.toStdString())); }
-
- Q_INVOKABLE QVariant fromAddress(QVariant/*eth::Address*/ _a) const { return out((eth::u160)to(_a)); }
- Q_INVOKABLE QVariant toAddress(QVariant/*eth::Address*/ _a) const { return toQJS((eth::u160)in(_a)); }
-
- Q_INVOKABLE bool isNull(QVariant/*eth::Address*/ _a) const { return !in(_a); }
-};
-
-class KeyHelper: public QObject
-{
- Q_OBJECT
-
-public:
- KeyHelper(QObject* _p = nullptr): QObject(_p) {}
-
- static eth::Address in(QVariant const& _s) { return to(_s); }
- static QVariant out(eth::Address const& _s) { return toQJS(_s); }
-
- Q_INVOKABLE QVariant/*eth::KeyPair*/ create() const { return toQJS(eth::KeyPair::create()); }
- Q_INVOKABLE QVariant/*eth::Address*/ address(QVariant/*eth::KeyPair*/ _p) const { return out(to(_p).address()); }
- Q_INVOKABLE QVariant/*eth::Secret*/ secret(QVariant/*eth::KeyPair*/ _p) const { return toQJS(to(_p).secret()); }
- Q_INVOKABLE QVariant/*eth::KeyPair*/ keypair(QVariant/*eth::Secret*/ _k) const { return toQJS(eth::KeyPair(to(_k))); }
-
- Q_INVOKABLE bool isNull(QVariant/*eth::Address*/ _a) const { return !in(_a); }
-
- Q_INVOKABLE QVariant/*eth::Address*/ addressOf(QString _s) const { return out(eth::Address(_s.toStdString())); }
- Q_INVOKABLE QString stringOf(QVariant/*eth::Address*/ _a) const { return QString::fromStdString(eth::toHex(in(_a).asArray())); }
- Q_INVOKABLE QString toAbridged(QVariant/*eth::Address*/ _a) const { return QString::fromStdString(in(_a).abridged()); }
-
-};
-
-class BytesHelper: public QObject
-{
- Q_OBJECT
-
-public:
- BytesHelper(QObject* _p = nullptr): QObject(_p) {}
-
- Q_INVOKABLE QByteArray concat(QVariant _v, QVariant _w) const
- {
- QByteArray ba;
- if (_v.type() == QVariant::ByteArray)
- ba = _v.toByteArray();
- else
- ba = _v.toString().toLatin1();
- QByteArray ba2;
- if (_w.type() == QVariant::ByteArray)
- ba2 = _w.toByteArray();
- else
- ba2 = _w.toString().toLatin1();
- ba.append(ba2);
- return QByteArray(ba);
- }
- Q_INVOKABLE QByteArray concat(QByteArray _v, QByteArray _w) const
- {
- _v.append(_w);
- return _v;
- }
- Q_INVOKABLE QByteArray fromString(QString _s) const
- {
- return _s.toLatin1();
- }
- Q_INVOKABLE QByteArray fromString(QString _s, unsigned _padding) const
- {
- QByteArray b = _s.toLatin1();
- for (unsigned i = b.size(); i < _padding; ++i)
- b.append((char)0);
- b.resize(_padding);
- return b;
- }
- Q_INVOKABLE QString toString(QByteArray _b) const
- {
- while (_b.size() && !_b[_b.size() - 1])
- _b.resize(_b.size() - 1);
- return QString::fromLatin1(_b);
- }
- Q_INVOKABLE QVariant u256of(QByteArray _s) const
- {
- while (_s.size() < 32)
- _s.append((char)0);
- eth::h256 ret((uint8_t const*)_s.data(), eth::h256::ConstructFromPointer);
- return toQJS(ret);
- }
-};
-#endif
inline eth::bytes asBytes(QString const& _s)
{
@@ -324,17 +53,7 @@ template eth::FixedHash toFixed(QString const& _s)
return eth::FixedHash(asBytes(padded(_s, N)));
}
-template boost::multiprecision::number> toInt(QString const& _s)
-{
- if (_s.startsWith("0x"))
- return eth::fromBigEndian>>(eth::fromHex(_s.toStdString().substr(2)));
- else if (!_s.contains(QRegExp("[^0-9]")))
- // Hex or Decimal
- return boost::multiprecision::number>(_s.toStdString());
- else
- // Binary
- return eth::fromBigEndian>>(asBytes(padded(_s, N)));
-}
+template inline boost::multiprecision::number> toInt(QString const& _s);
inline eth::Address toAddress(QString const& _s) { return toFixed<20>(_s); }
inline eth::Secret toSecret(QString const& _s) { return toFixed<32>(_s); }
@@ -376,7 +95,7 @@ public:
void setup(QWebFrame* _e);
void teardown(QWebFrame* _e);
- void setAccounts(QList _l) { m_accounts = _l; this->changed(); }
+ void setAccounts(QList _l) { m_accounts = _l; keysChanged(); }
Q_INVOKABLE QString ethTest() const { return "Hello world!"; }
Q_INVOKABLE QEthereum* self() { return this; }
@@ -403,11 +122,16 @@ public:
Q_INVOKABLE double countAt(QString/*eth::Address*/ _a, int _block) const;
Q_INVOKABLE QString/*eth::u256*/ stateAt(QString/*eth::Address*/ _a, QString/*eth::u256*/ _p, int _block) const;
Q_INVOKABLE QString/*eth::u256*/ codeAt(QString/*eth::Address*/ _a, int _block) const;
- Q_INVOKABLE QString getTransactions(QString _attribs) const;
+ Q_INVOKABLE QString/*json*/ getTransactions(QString _attribs/*json*/) const;
Q_INVOKABLE QString doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice);
Q_INVOKABLE void doTransact(QString _secret, QString _amount, QString _dest, QString _data, QString _gas, QString _gasPrice);
+ Q_INVOKABLE unsigned newWatch(QString _json);
+ Q_INVOKABLE QString watchTransactions(unsigned _w);
+ Q_INVOKABLE void killWatch(unsigned _w);
+ void clearWatches();
+
bool isListening() const;
bool isMining() const;
@@ -431,41 +155,61 @@ public slots:
void setMining(bool _l);
void setListening(bool _l);
+ /// Check to see if anything has changed, fire off signals if so.
+ /// @note Must be called in the QObject's thread.
+ void poll();
+
signals:
- void changed();
-// void netChanged();
-// void miningChanged();
+ void watchChanged(unsigned _w);
+ void coinbaseChanged();
+ void keysChanged();
+ void netChanged();
+ void miningChanged();
private:
- Q_PROPERTY(QString number READ number NOTIFY changed)
- Q_PROPERTY(QString coinbase READ coinbase WRITE setCoinbase NOTIFY changed)
- Q_PROPERTY(bool listening READ isListening WRITE setListening)
- Q_PROPERTY(bool mining READ isMining WRITE setMining)
- Q_PROPERTY(QString gasPrice READ gasPrice NOTIFY changed)
- Q_PROPERTY(QString key READ key NOTIFY changed)
- Q_PROPERTY(QStringList keys READ keys NOTIFY changed)
- Q_PROPERTY(unsigned peerCount READ peerCount)
+ Q_PROPERTY(QString number READ number NOTIFY watchChanged)
+ Q_PROPERTY(QString coinbase READ coinbase WRITE setCoinbase NOTIFY coinbaseChanged)
+ Q_PROPERTY(QString gasPrice READ gasPrice)
+ Q_PROPERTY(QString key READ key NOTIFY keysChanged)
+ Q_PROPERTY(QStringList keys READ keys NOTIFY keysChanged)
+ Q_PROPERTY(bool mining READ isMining WRITE setMining NOTIFY netChanged)
+ Q_PROPERTY(bool listening READ isListening WRITE setListening NOTIFY netChanged)
+ Q_PROPERTY(unsigned peerCount READ peerCount NOTIFY miningChanged)
eth::Client* m_client;
+ std::vector m_watches;
QList m_accounts;
};
-#define QETH_INSTALL_JS_NAMESPACE [f, eth, this]() \
+#define QETH_INSTALL_JS_NAMESPACE(frame, eth, env) [frame, eth, env]() \
{ \
- f->disconnect(); \
- f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership); \
- f->addToJavaScriptWindowObject("eth", eth, QWebFrame::ScriptOwnership); \
- f->evaluateJavaScript("eth.watch = function(a, s, f) { eth.changed.connect(f ? f : s) }"); \
- f->evaluateJavaScript("eth.newBlock = function(f) { eth.changed.connect(f) }"); \
- \
- f->evaluateJavaScript("eth.create = function(s, v, c, g, p, f) { var v = eth.doCreate(s, v, c, g, p); if (f) f(v) }"); \
- f->evaluateJavaScript("eth.transact = function(s, v, t, d, g, p, f) { eth.doTransact(s, v, t, d, g, p); if (f) f() }"); \
- f->evaluateJavaScript("eth.transactions = function(a) { return JSON.parse(eth.getTransactions(JSON.stringify(a))); }"); \
- f->evaluateJavaScript("String.prototype.pad = function(l, r) { return eth.pad(this, l, r) }"); \
- f->evaluateJavaScript("String.prototype.bin = function() { return eth.toBinary(this) }"); \
- f->evaluateJavaScript("String.prototype.unbin = function(l) { return eth.fromBinary(this) }"); \
- f->evaluateJavaScript("String.prototype.unpad = function(l) { return eth.unpad(this) }"); \
- f->evaluateJavaScript("String.prototype.dec = function() { return eth.toDecimal(this) }"); \
- f->evaluateJavaScript("String.prototype.sha3 = function() { return eth.sha3(this) }"); \
+ 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.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) }"); \
+ frame->evaluateJavaScript("eth.transact = function(s, v, t, d, g, p, f) { eth.doTransact(s, v, t, d, g, p); if (f) f() }"); \
+ frame->evaluateJavaScript("eth.transactions = function(a) { return JSON.parse(eth.getTransactions(JSON.stringify(a))); }"); \
+ frame->evaluateJavaScript("String.prototype.pad = function(l, r) { return eth.pad(this, l, r) }"); \
+ frame->evaluateJavaScript("String.prototype.bin = function() { return eth.toBinary(this) }"); \
+ frame->evaluateJavaScript("String.prototype.unbin = function(l) { return eth.fromBinary(this) }"); \
+ frame->evaluateJavaScript("String.prototype.unpad = function(l) { return eth.unpad(this) }"); \
+ frame->evaluateJavaScript("String.prototype.dec = function() { return eth.toDecimal(this) }"); \
+ frame->evaluateJavaScript("String.prototype.sha3 = function() { return eth.sha3(this) }"); \
+}
+
+template inline boost::multiprecision::number> toInt(QString const& _s)
+{
+ if (_s.startsWith("0x"))
+ return eth::fromBigEndian>>(eth::fromHex(_s.toStdString().substr(2)));
+ else if (!_s.contains(QRegExp("[^0-9]")))
+ // Hex or Decimal
+ return boost::multiprecision::number>(_s.toStdString());
+ else
+ // Binary
+ return eth::fromBigEndian>>(asBytes(padded(_s, N)));
}
diff --git a/libqethereum/QmlEthereum.cpp b/libqethereum/QmlEthereum.cpp
new file mode 100644
index 000000000..560d75ddc
--- /dev/null
+++ b/libqethereum/QmlEthereum.cpp
@@ -0,0 +1,188 @@
+#if ETH_QTQML
+#include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "QmlEthereum.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::u256s;
+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::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 QmlEthereums 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.
+eth::Client* g_qmlClient;
+QObject* g_qmlMain;
+
+QmlAccount::QmlAccount(QObject*)
+{
+}
+
+QmlAccount::~QmlAccount()
+{
+}
+
+void QmlAccount::setEthereum(QmlEthereum* _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();
+}
+
+eth::u256 QmlAccount::balance() const
+{
+ if (m_eth)
+ return m_eth->balanceAt(m_address);
+ return u256(0);
+}
+
+double QmlAccount::txCount() const
+{
+ if (m_eth)
+ return m_eth->txCountAt(m_address);
+ return 0;
+}
+
+bool QmlAccount::isContract() const
+{
+ if (m_eth)
+ return m_eth->isContractAt(m_address);
+ return 0;
+}
+
+QmlEthereum::QmlEthereum(QObject* _p): QObject(_p)
+{
+// connect(g_qmlMain, SIGNAL(changed()), SIGNAL(changed()));
+}
+
+QmlEthereum::~QmlEthereum()
+{
+}
+
+Client* QmlEthereum::client() const
+{
+ return g_qmlClient;
+}
+
+Address QmlEthereum::coinbase() const
+{
+ return client()->address();
+}
+
+void QmlEthereum::setCoinbase(Address _a)
+{
+ if (client()->address() != _a)
+ {
+ client()->setAddress(_a);
+// changed();
+ }
+}
+
+u256 QmlEthereum::balanceAt(Address _a) const
+{
+ return client()->postState().balance(_a);
+}
+
+bool QmlEthereum::isContractAt(Address _a) const
+{
+ return client()->postState().addressHasCode(_a);
+}
+
+bool QmlEthereum::isMining() const
+{
+ return client()->isMining();
+}
+
+bool QmlEthereum::isListening() const
+{
+ return client()->haveNetwork();
+}
+
+void QmlEthereum::setMining(bool _l)
+{
+ if (_l)
+ client()->startMining();
+ else
+ client()->stopMining();
+}
+
+void QmlEthereum::setListening(bool _l)
+{
+ if (_l)
+ client()->startNetwork();
+ else
+ client()->stopNetwork();
+}
+
+double QmlEthereum::txCountAt(Address _a) const
+{
+ return (double)client()->postState().transactionsFrom(_a);
+}
+
+unsigned QmlEthereum::peerCount() const
+{
+ return (unsigned)client()->peerCount();
+}
+
+void QmlEthereum::transact(Secret _secret, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _init)
+{
+ client()->transact(_secret, _amount, bytes(_init.data(), _init.data() + _init.size()), _gas, _gasPrice);
+}
+
+void QmlEthereum::transact(Secret _secret, Address _dest, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _data)
+{
+ client()->transact(_secret, _amount, _dest, bytes(_data.data(), _data.data() + _data.size()), _gas, _gasPrice);
+}
+
+// extra bits needed to link on VS
+#ifdef _MSC_VER
+
+// include moc file, ofuscated to hide from automoc
+#include\
+"moc_QmlEthereum.cpp"
+
+#endif
diff --git a/libqethereum/QmlEthereum.h b/libqethereum/QmlEthereum.h
new file mode 100644
index 000000000..2554dd02f
--- /dev/null
+++ b/libqethereum/QmlEthereum.h
@@ -0,0 +1,283 @@
+#pragma once
+
+#include
+#if ETH_QTQML
+#include
+#endif
+#include
+#include
+
+namespace eth {
+class Client;
+class State;
+}
+
+class QQmlEngine;
+class QmlAccount;
+class QmlEthereum;
+
+extern eth::Client* g_qmlClient;
+extern QObject* g_qmlMain;
+
+Q_DECLARE_METATYPE(eth::u256)
+Q_DECLARE_METATYPE(eth::Address)
+Q_DECLARE_METATYPE(eth::Secret)
+Q_DECLARE_METATYPE(eth::KeyPair)
+Q_DECLARE_METATYPE(QmlAccount*)
+Q_DECLARE_METATYPE(QmlEthereum*)
+
+class QmlU256Helper: public QObject
+{
+ Q_OBJECT
+
+public:
+ QmlU256Helper(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 QmlKeyHelper: public QObject
+{
+ Q_OBJECT
+
+public:
+ QmlKeyHelper(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 QmlAccount: public QObject
+{
+ Q_OBJECT
+
+public:
+ QmlAccount(QObject* _p = nullptr);
+ virtual ~QmlAccount();
+
+ Q_INVOKABLE QmlEthereum* ethereum() const { return m_eth; }
+ Q_INVOKABLE eth::u256 balance() const;
+ Q_INVOKABLE double txCount() const;
+ Q_INVOKABLE bool isContract() const;
+ Q_INVOKABLE eth::Address address() const { return m_address; }
+
+ // TODO: past transactions models.
+
+public slots:
+ void setEthereum(QmlEthereum* _eth);
+ void setAddress(eth::Address _a) { m_address = _a; }
+
+signals:
+ void changed();
+ void ethChanged();
+
+private:
+ QmlEthereum* 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 READ address WRITE setAddress NOTIFY changed)
+ Q_PROPERTY(QmlEthereum* ethereum READ ethereum WRITE setEthereum NOTIFY ethChanged)
+};
+
+class QmlEthereum: public QObject
+{
+ Q_OBJECT
+
+public:
+ QmlEthereum(QObject* _p = nullptr);
+ virtual ~QmlEthereum();
+
+ eth::Client* client() const;
+
+ static QObject* constructU256Helper(QQmlEngine*, QJSEngine*) { return new QmlU256Helper; }
+ static QObject* constructKeyHelper(QQmlEngine*, QJSEngine*) { return new QmlKeyHelper; }
+
+ 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 QmlEthereum* self() { return this; }
+
+public slots:
+ void transact(eth::Secret _secret, eth::Address _dest, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _data);
+ void transact(eth::Secret _secret, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _init);
+ void setCoinbase(eth::Address);
+ void setMining(bool _l);
+
+ void setListening(bool _l);
+
+signals:
+ void coinbaseChanged();
+// void netChanged();
+// void miningChanged();
+
+private:
+ Q_PROPERTY(eth::Address coinbase READ coinbase WRITE setCoinbase NOTIFY coinbaseChanged)
+ Q_PROPERTY(bool listening READ isListening WRITE setListening)
+ Q_PROPERTY(bool mining READ isMining WRITE setMining)
+};
+
+#if 0
+template T to(QVariant const& _s) { if (_s.type() != QVariant::String) return T(); auto s = _s.toString().toLatin1(); assert(s.size() == sizeof(T)); return *(T*)s.data(); }
+template QVariant toQJS(T const& _s) { QLatin1String ret((char*)&_s, sizeof(T)); assert(QVariant(QString(ret)).toString().toLatin1().size() == sizeof(T)); assert(*(T*)(QVariant(QString(ret)).toString().toLatin1().data()) == _s); return QVariant(QString(ret)); }
+
+class U256Helper: public QObject
+{
+ Q_OBJECT
+
+public:
+ U256Helper(QObject* _p = nullptr): QObject(_p) {}
+
+ static eth::u256 in(QVariant const& _s) { return to(_s); }
+ static QVariant out(eth::u256 const& _s) { return toQJS(_s); }
+
+ Q_INVOKABLE QVariant add(QVariant _a, QVariant _b) const { return out(in(_a) + in(_b)); }
+ Q_INVOKABLE QVariant sub(QVariant _a, QVariant _b) const { return out(in(_a) - in(_b)); }
+ Q_INVOKABLE QVariant mul(QVariant _a, int _b) const { return out(in(_a) * in(_b)); }
+ Q_INVOKABLE QVariant mul(int _a, QVariant _b) const { return out(in(_a) * in(_b)); }
+ Q_INVOKABLE QVariant div(QVariant _a, int _b) const { return out(in(_a) / in(_b)); }
+
+ Q_INVOKABLE QVariant wei(double _s) const { return out(eth::u256(_s)); }
+ Q_INVOKABLE QVariant szabo(double _s) const { return out(eth::u256(_s * (double)eth::szabo)); }
+ Q_INVOKABLE QVariant finney(double _s) const { return out(eth::u256(_s * (double)eth::finney)); }
+ Q_INVOKABLE QVariant ether(double _s) const { return out(eth::u256(_s * (double)eth::ether)); }
+ Q_INVOKABLE QVariant wei(unsigned _s) const { return value(_s); }
+ Q_INVOKABLE QVariant szabo(unsigned _s) const { return out(eth::u256(_s) * eth::szabo); }
+ Q_INVOKABLE QVariant finney(unsigned _s) const { return out(eth::u256(_s) * eth::finney); }
+ Q_INVOKABLE QVariant ether(unsigned _s) const { return out(eth::u256(_s) * eth::ether); }
+ Q_INVOKABLE double toWei(QVariant _t) const { return toValue(_t); }
+ Q_INVOKABLE double toSzabo(QVariant _t) const { return toWei(_t) / (double)eth::szabo; }
+ Q_INVOKABLE double toFinney(QVariant _t) const { return toWei(_t) / (double)eth::finney; }
+ Q_INVOKABLE double toEther(QVariant _t) const { return toWei(_t) / (double)eth::ether; }
+
+ Q_INVOKABLE QVariant value(unsigned _s) const { return out(eth::u256(_s)); }
+ Q_INVOKABLE double toValue(QVariant _t) const { return (double)in(_t); }
+
+ Q_INVOKABLE QString ethOf(QVariant _t) const { return QString::fromStdString(eth::formatBalance(in(_t))); }
+ Q_INVOKABLE QString stringOf(QVariant _t) const { return QString::fromStdString(eth::toString(in(_t))); }
+
+ Q_INVOKABLE QByteArray bytesOf(QVariant _t) const { eth::h256 b = in(_t); return QByteArray((char const*)&b, sizeof(eth::h256)); }
+ Q_INVOKABLE QVariant fromHex(QString _s) const { return out((eth::u256)eth::h256(_s.toStdString())); }
+
+ Q_INVOKABLE QVariant fromAddress(QVariant/*eth::Address*/ _a) const { return out((eth::u160)to(_a)); }
+ Q_INVOKABLE QVariant toAddress(QVariant/*eth::Address*/ _a) const { return toQJS((eth::u160)in(_a)); }
+
+ Q_INVOKABLE bool isNull(QVariant/*eth::Address*/ _a) const { return !in(_a); }
+};
+
+class KeyHelper: public QObject
+{
+ Q_OBJECT
+
+public:
+ KeyHelper(QObject* _p = nullptr): QObject(_p) {}
+
+ static eth::Address in(QVariant const& _s) { return to(_s); }
+ static QVariant out(eth::Address const& _s) { return toQJS(_s); }
+
+ Q_INVOKABLE QVariant/*eth::KeyPair*/ create() const { return toQJS(eth::KeyPair::create()); }
+ Q_INVOKABLE QVariant/*eth::Address*/ address(QVariant/*eth::KeyPair*/ _p) const { return out(to(_p).address()); }
+ Q_INVOKABLE QVariant/*eth::Secret*/ secret(QVariant/*eth::KeyPair*/ _p) const { return toQJS(to(_p).secret()); }
+ Q_INVOKABLE QVariant/*eth::KeyPair*/ keypair(QVariant/*eth::Secret*/ _k) const { return toQJS(eth::KeyPair(to(_k))); }
+
+ Q_INVOKABLE bool isNull(QVariant/*eth::Address*/ _a) const { return !in(_a); }
+
+ Q_INVOKABLE QVariant/*eth::Address*/ addressOf(QString _s) const { return out(eth::Address(_s.toStdString())); }
+ Q_INVOKABLE QString stringOf(QVariant/*eth::Address*/ _a) const { return QString::fromStdString(eth::toHex(in(_a).asArray())); }
+ Q_INVOKABLE QString toAbridged(QVariant/*eth::Address*/ _a) const { return QString::fromStdString(in(_a).abridged()); }
+
+};
+
+class BytesHelper: public QObject
+{
+ Q_OBJECT
+
+public:
+ BytesHelper(QObject* _p = nullptr): QObject(_p) {}
+
+ Q_INVOKABLE QByteArray concat(QVariant _v, QVariant _w) const
+ {
+ QByteArray ba;
+ if (_v.type() == QVariant::ByteArray)
+ ba = _v.toByteArray();
+ else
+ ba = _v.toString().toLatin1();
+ QByteArray ba2;
+ if (_w.type() == QVariant::ByteArray)
+ ba2 = _w.toByteArray();
+ else
+ ba2 = _w.toString().toLatin1();
+ ba.append(ba2);
+ return QByteArray(ba);
+ }
+ Q_INVOKABLE QByteArray concat(QByteArray _v, QByteArray _w) const
+ {
+ _v.append(_w);
+ return _v;
+ }
+ Q_INVOKABLE QByteArray fromString(QString _s) const
+ {
+ return _s.toLatin1();
+ }
+ Q_INVOKABLE QByteArray fromString(QString _s, unsigned _padding) const
+ {
+ QByteArray b = _s.toLatin1();
+ for (unsigned i = b.size(); i < _padding; ++i)
+ b.append((char)0);
+ b.resize(_padding);
+ return b;
+ }
+ Q_INVOKABLE QString toString(QByteArray _b) const
+ {
+ while (_b.size() && !_b[_b.size() - 1])
+ _b.resize(_b.size() - 1);
+ return QString::fromLatin1(_b);
+ }
+ Q_INVOKABLE QVariant u256of(QByteArray _s) const
+ {
+ while (_s.size() < 32)
+ _s.append((char)0);
+ eth::h256 ret((uint8_t const*)_s.data(), eth::h256::ConstructFromPointer);
+ return toQJS(ret);
+ }
+};
+#endif
diff --git a/neth/main.cpp b/neth/main.cpp
index 01199b7fc..f8947fd7c 100644
--- a/neth/main.cpp
+++ b/neth/main.cpp
@@ -417,7 +417,6 @@ int main(int argc, char** argv)
if (!clientName.empty())
clientName += "/";
Client c("NEthereum(++)/" + clientName + "v" + eth::EthVersion + "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM), coinbase, dbPath);
- c.start();
cout << credits();
std::ostringstream ccout;
diff --git a/walleth/MainWin.cpp b/walleth/MainWin.cpp
index 2c36d94fc..7047e05b4 100644
--- a/walleth/MainWin.cpp
+++ b/walleth/MainWin.cpp
@@ -63,7 +63,6 @@ Main::Main(QWidget *parent) :
g_qmlMain = this;
m_client.reset(new Client("Walleth", Address(), eth::getDataDir() + "/Walleth"));
- m_client->start();
g_qmlClient = m_client.get();
@@ -105,8 +104,6 @@ Main::Main(QWidget *parent) :
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);
@@ -135,8 +132,7 @@ Main::~Main()
void Main::timerEvent(QTimerEvent *)
{
- if (m_client->changed())
- changed();
+
}
void Main::on_about_triggered()
diff --git a/walleth/MainWin.h b/walleth/MainWin.h
index 61891e0a3..b4c192d01 100644
--- a/walleth/MainWin.h
+++ b/walleth/MainWin.h
@@ -6,7 +6,7 @@
#include
#include
#include
-#include
+#include
namespace Ui {
class Main;
@@ -43,9 +43,6 @@ private slots:
void refresh();
void refreshNetwork();
-signals:
- void changed();
-
protected:
virtual void timerEvent(QTimerEvent *);