Browse Source

Merge branch 'changetracking' into develop

cl-refactor
Gav Wood 11 years ago
parent
commit
281e4d5a26
  1. 20
      alethzero/Main.ui
  2. 455
      alethzero/MainWin.cpp
  3. 57
      alethzero/MainWin.h
  4. 7
      eth/EthStubServer.cpp
  5. 10
      eth/eth.js
  6. 1
      eth/main.cpp
  7. 2
      libethential/FixedHash.h
  8. 10
      libethential/RLP.h
  9. 235
      libethereum/BlockChain.cpp
  10. 147
      libethereum/BlockChain.h
  11. 40
      libethereum/BlockDetails.cpp
  12. 76
      libethereum/BlockDetails.h
  13. 102
      libethereum/BlockQueue.cpp
  14. 66
      libethereum/BlockQueue.h
  15. 427
      libethereum/Client.cpp
  16. 141
      libethereum/Client.h
  17. 29
      libethereum/Guards.cpp
  18. 37
      libethereum/Guards.h
  19. 13
      libethereum/PeerNetwork.h
  20. 283
      libethereum/PeerServer.cpp
  21. 34
      libethereum/PeerServer.h
  22. 100
      libethereum/PeerSession.cpp
  23. 2
      libethereum/PeerSession.h
  24. 19
      libethereum/State.cpp
  25. 10
      libethereum/State.h
  26. 31
      libethereum/TransactionQueue.cpp
  27. 30
      libethereum/TransactionQueue.h
  28. 8
      liblll/Assembly.cpp
  29. 293
      libqethereum/QEthereum.cpp
  30. 377
      libqethereum/QEthereum.h
  31. 188
      libqethereum/QmlEthereum.cpp
  32. 283
      libqethereum/QmlEthereum.h
  33. 1
      neth/main.cpp
  34. 6
      walleth/MainWin.cpp
  35. 5
      walleth/MainWin.h

20
alethzero/Main.ui

@ -377,19 +377,21 @@
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="log">
<widget class="QPlainTextEdit" name="log">
<property name="font">
<font>
<family>Monospace</family>
<pointsize>12</pointsize>
<family>Ubuntu Mono</family>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
</item>
<item>
@ -498,6 +500,7 @@
<addaction name="connect"/>
<addaction name="preview"/>
<addaction name="mine"/>
<addaction name="refresh"/>
</widget>
<widget class="QDockWidget" name="dockWidget_5">
<property name="sizePolicy">
@ -1650,6 +1653,11 @@ font-size: 14pt</string>
<string>&amp;Pretty...</string>
</property>
</action>
<action name="refresh">
<property name="text">
<string>&amp;Refresh</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

455
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) :
@ -98,7 +124,15 @@ Main::Main(QWidget *parent) :
{
setWindowFlags(Qt::Window);
ui->setupUi(this);
g_logPost = [=](std::string const& s, char const* c) { simpleDebugOut(s, c); ui->log->addItem(QString::fromStdString(s)); };
g_logPost = [=](std::string const& s, char const* c)
{
simpleDebugOut(s, c);
m_logLock.lock();
m_logHistory.append(QString::fromStdString(s) + "\n");
m_logChanged = true;
m_logLock.unlock();
// ui->log->addItem(QString::fromStdString(s));
};
#if 0&&ETH_DEBUG
m_servers.append("192.168.0.10:30301");
@ -145,24 +179,21 @@ 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);
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 +202,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,15 +215,118 @@ Main::Main(QWidget *parent) :
s.setValue("splashMessage", false);
}
}
m_pcWarp.clear();
}
Main::~Main()
{
// Must do this here since otherwise m_ethereum'll be deleted (and therefore clearWatches() called by the destructor)
// *after* the client is dead.
m_ethereum->clientDieing();
g_logPost = simpleDebugOut;
writeSettings();
}
void Main::onKeysChanged()
{
installBalancesWatch();
}
unsigned Main::installWatch(eth::TransactionFilter const& _tf, std::function<void()> const& _f)
{
auto ret = m_client->installWatch(_tf);
m_handlers[ret] = _f;
return ret;
}
unsigned Main::installWatch(eth::h256 _tf, std::function<void()> 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<Address> 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()
{
cwatch << "NameReg changed!";
// update any namereg-dependent stuff - for now force a full update.
refreshAll();
}
void Main::onCurrenciesChange()
{
cwatch << "Currencies changed!";
installBalancesWatch();
// TODO: update any currency-dependent stuff?
}
void Main::onBalancesChange()
{
cwatch << "Our balances changed!";
refreshBalances();
}
void Main::onNewBlock()
{
cwatch << "Blockchain changed!";
// update blockchain dependent views.
refreshBlockCount();
refreshBlockChain();
refreshAccounts();
}
void Main::onNewPending()
{
cwatch << "Pending transactions changed!";
// update any pending-transaction dependent views.
refreshPending();
refreshAccounts();
}
void Main::on_clearPending_triggered()
{
m_client->clearPending();
@ -257,7 +390,8 @@ void Main::eval(QString const& _js)
{
if (_js.trimmed().isEmpty())
return;
QVariant ev = ui->webView->page()->currentFrame()->evaluateJavaScript(_js);
QVariant ev = ui->webView->page()->currentFrame()->evaluateJavaScript("___RET=(" + _js + ")");
QVariant jsonEv = ui->webView->page()->currentFrame()->evaluateJavaScript("JSON.stringify(___RET)");
QString s;
if (ev.isNull())
s = "<span style=\"color: #888\">null</span>";
@ -265,6 +399,8 @@ void Main::eval(QString const& _js)
s = "<span style=\"color: #444\">\"</span><span style=\"color: #c00\">" + ev.toString().toHtmlEscaped() + "</span><span style=\"color: #444\">\"</span>";
else if (ev.type() == QVariant::Int || ev.type() == QVariant::Double)
s = "<span style=\"color: #00c\">" + ev.toString().toHtmlEscaped() + "</span>";
else if (jsonEv.type() == QVariant::String)
s = "<span style=\"color: #840\">" + jsonEv.toString().toHtmlEscaped() + "</span>";
else
s = "<span style=\"color: #888\">unknown type</span>";
m_consoleHistory.push_back(qMakePair(_js, s));
@ -276,40 +412,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);
}
@ -472,12 +583,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());
@ -497,6 +614,34 @@ void Main::refreshMining()
*/
}
void Main::refreshBalances()
{
cwatch << "refreshBalances()";
// update all the balance-dependent stuff.
ui->ourAccounts->clear();
u256 totalBalance = 0;
map<Address, pair<QString, u256>> 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();
@ -512,24 +657,79 @@ 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();
refreshBalances();
}
void Main::on_blockChainFilter_textChanged()
void Main::refreshPending()
{
static QTimer* s_delayed = nullptr;
if (!s_delayed)
cwatch << "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()
{
cwatch << "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()
{
cwatch << "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()
{
cwatch << "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)
@ -559,6 +759,7 @@ static bool transactionMatch(string const& _f, Transaction const& _t)
void Main::refreshBlockChain()
{
cwatch << "refreshBlockChain()";
eth::ClientGuard g(m_client.get());
auto const& st = state();
@ -614,113 +815,58 @@ 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();
if (m_logChanged)
{
m_logLock.lock();
m_logChanged = false;
ui->log->appendPlainText(m_logHistory);
m_logHistory.clear();
m_logLock.unlock();
}
// 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<Address, pair<QString, u256>> 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
@ -791,7 +937,7 @@ void Main::on_transactionQueue_currentItemChanged()
stringstream s;
int i = ui->transactionQueue->currentRow();
if (i >= 0)
if (i >= 0 && i < (int)m_client->postState().pending().size())
{
Transaction tx(m_client->postState().pending()[i]);
auto ss = tx.safeSender();
@ -848,7 +994,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()
@ -1071,7 +1216,8 @@ void Main::on_ourAccounts_doubleClicked()
void Main::on_log_doubleClicked()
{
qApp->clipboard()->setText(ui->log->currentItem()->text());
ui->log->setPlainText("");
m_logHistory.clear();
}
void Main::on_accounts_doubleClicked()
@ -1205,8 +1351,10 @@ void Main::on_killBlockchain_triggered()
ui->net->setChecked(false);
m_client.reset();
m_client.reset(new Client("AlethZero", Address(), string(), true));
m_client->start();
m_ethereum->setClient(m_client.get());
readSettings();
installWatches();
refreshAll();
}
bool Main::isCreation() const
@ -1323,7 +1471,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.");

57
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<void()> const& _f);
unsigned installWatch(eth::h256 _tf, std::function<void()> 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::Main> ui;
std::unique_ptr<eth::Client> m_client;
std::map<unsigned, std::function<void()>> 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;
@ -201,6 +229,9 @@ private:
QNetworkAccessManager m_webCtrl;
QList<QPair<QString, QString>> m_consoleHistory;
QMutex m_logLock;
QString m_logHistory;
bool m_logChanged = true;
QEthereum* m_ethereum;
QEthereum* m_ethereum = nullptr;
};

7
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)

10
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) {

1
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;

2
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]);
}

10
libethential/RLP.h

@ -164,6 +164,10 @@ public:
explicit operator u256() const { return toInt<u256>(); }
explicit operator bigint() const { return toInt<bigint>(); }
template <unsigned _N> explicit operator FixedHash<_N>() const { return toHash<FixedHash<_N>>(); }
template <class T, class U> explicit operator std::pair<T, U>() const { return toPair<T, U>(); }
template <class T> explicit operator std::vector<T>() const { return toVector<T>(); }
template <class T> explicit operator std::set<T>() const { return toSet<T>(); }
template <class T, size_t N> explicit operator std::array<T, N>() const { return toArray<T, N>(); }
/// 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 <class T> std::vector<T> toVector() const { std::vector<T> ret; if (isList()) { ret.reserve(itemCount()); for (auto const& i: *this) ret.push_back((T)i); } return ret; }
template <class T> std::set<T> toSet() const { std::set<T> ret; if (isList()) { for (auto const& i: *this) ret.insert((T)i); } return ret; }
template <class T, class U> std::pair<T, U> toPair() const { std::pair<T, U> ret; if (isList()) { ret.first = (T)((*this)[0]); ret.second = (U)((*this)[1]); } return ret; }
template <class T, size_t N> std::array<T, N> toArray() const { if (itemCount() != N || !isList()) throw BadCast(); std::array<T, N> 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 <class _T> RLPStream& append(std::vector<_T> const& _s) { return appendVector(_s); }
template <class _T, size_t S> RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T> RLPStream& appendVector(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T, size_t S> RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T> RLPStream& append(std::set<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class T, class U> RLPStream& append(std::pair<T, U> const& _s) { appendList(2); append(_s.first); append(_s.second); return *this; }
/// Appends a list.
RLPStream& appendList(uint _items);

235
libethereum/BlockChain.cpp

@ -35,12 +35,9 @@ using namespace eth;
#define ETH_CATCH 1
namespace eth
std::ostream& eth::operator<<(std::ostream& _out, BlockChain const& _bc)
{
std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc)
{
string cmp = toBigEndianString(_bc.m_lastBlockHash);
string cmp = toBigEndianString(_bc.currentHash());
auto it = _bc.m_extrasDB->NewIterator(_bc.m_readOptions);
for (it->SeekToFirst(); it->Valid(); it->Next())
if (it->key().ToString() != "best")
@ -51,21 +48,6 @@ std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc)
delete it;
return _out;
}
}
BlockDetails::BlockDetails(RLP const& _r)
{
number = _r[0].toInt<uint>();
totalDifficulty = _r[1].toInt<u256>();
parent = _r[2].toHash<h256>();
children = _r[3].toVector<h256>();
bloom = _r[4].toHash<h256>();
}
bytes BlockDetails::rlp() const
{
return rlpList(number, totalDifficulty, parent, children, bloom);
}
std::map<Address, AddressState> const& eth::genesisState()
{
@ -87,6 +69,21 @@ std::map<Address, AddressState> const& eth::genesisState()
}
BlockInfo* BlockChain::s_genesis = nullptr;
boost::shared_mutex BlockChain::x_genesis;
ldb::Slice eth::toSlice(h256 _h, unsigned _sub)
{
#if ALL_COMPILERS_ARE_CPP11_COMPLIANT
static thread_local h256 h = _h ^ h256(u256(_sub));
return ldb::Slice((char const*)&h, 32);
#else
static boost::thread_specific_ptr<h256> t_h;
if (!t_h.get())
t_h.reset(new h256);
*t_h = _h ^ h256(u256(_sub));
return ldb::Slice((char const*)t_h.get(), 32);
#endif
}
bytes BlockChain::createGenesisBlock()
{
@ -144,9 +141,10 @@ BlockChain::BlockChain(std::string _path, bool _killExisting)
// TODO: Implement ability to rebuild details map from DB.
std::string l;
m_extrasDB->Get(m_readOptions, ldb::Slice("best"), &l);
m_lastBlockHash = l.empty() ? m_genesisHash : *(h256*)l.data();
cnote << "Opened blockchain DB. Latest: " << m_lastBlockHash;
cnote << "Opened blockchain DB. Latest: " << currentHash();
}
BlockChain::~BlockChain()
@ -165,38 +163,43 @@ bool contains(T const& _t, V const& _v)
return false;
}
bool BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB)
h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max)
{
vector<bytes> blocks;
_bq.drain(blocks);
h256s ret;
for (auto const& block: blocks)
try
{
for (auto h: import(block, _stateDB))
if (!_max--)
break;
else
ret.push_back(h);
}
catch (UnknownParent)
{
cwarn << "Unknown parent of block!!!" << eth::sha3(block).abridged();
_bq.import(&block, *this);
}
catch (...){}
return ret;
}
h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept
{
#if ETH_CATCH
try
#endif
{
import(_block, _stateDB);
return true;
return import(_block, _stateDB);
}
#if ETH_CATCH
catch (...)
{
return false;
return h256s();
}
#endif
}
inline ldb::Slice toSlice(h256 _h, unsigned _sub = 0)
{
#if ALL_COMPILERS_ARE_CPP11_COMPLIANT
static thread_local h256 h = _h ^ h256(u256(_sub));
return ldb::Slice((char const*)&h, 32);
#else
static boost::thread_specific_ptr<h256> t_h;
if (!t_h.get())
t_h.reset(new h256);
*t_h = _h ^ h256(u256(_sub));
return ldb::Slice((char const*)t_h.get(), 32);
#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;
@ -237,7 +240,7 @@ void BlockChain::import(bytes const& _block, OverlayDB const& _db)
if (bi.timestamp > (u256)time(0))
{
clog(BlockChainNote) << newHash << ": Future time " << bi.timestamp << " (now at " << time(0) << ")";
// We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on.
// Block has a timestamp in the future. This is no good.
throw FutureTime();
}
@ -268,10 +271,16 @@ void BlockChain::import(bytes const& _block, OverlayDB const& _db)
#endif
// All ok - insert into DB
{
lock_guard<recursive_mutex> l(m_lock);
WriteGuard l(x_details);
m_details[newHash] = BlockDetails((uint)pd.number + 1, td, bi.parentHash, {}, b);
m_details[bi.parentHash].children.push_back(newHash);
}
{
WriteGuard l(x_blooms);
m_blooms[newHash] = bb;
}
{
WriteGuard l(x_traces);
m_traces[newHash] = bt;
}
@ -295,22 +304,63 @@ 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)
h256 last = currentHash();
if (td > details(last).totalDifficulty)
{
m_lastBlockHash = newHash;
ret = treeRoute(last, newHash);
{
WriteGuard l(x_lastBlockHash);
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.";
clog(BlockChainNote) << " Imported and best. Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:";
for (auto r: ret)
clog(BlockChainNote) << r;
}
else
{
clog(BlockChainNote) << " Imported but not best (oTD:" << details(m_lastBlockHash).totalDifficulty << ", TD:" << td << ")";
clog(BlockChainNote) << " Imported but not best (oTD:" << details(last).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()
{
lock_guard<recursive_mutex> l(m_lock);
m_details.clear();
ldb::Iterator* it = m_db->NewIterator(m_readOptions);
for (it->SeekToFirst(); it->Valid(); it->Next())
@ -334,15 +384,17 @@ bytes BlockChain::block(h256 _hash) const
if (_hash == m_genesisHash)
return m_genesisBlock;
lock_guard<recursive_mutex> l(m_lock);
auto it = m_cache.find(_hash);
if (it != m_cache.end())
return it->second;
{
ReadGuard l(x_cache);
auto it = m_cache.find(_hash);
if (it != m_cache.end())
return it->second;
}
string d;
m_db->Get(m_readOptions, ldb::Slice((char const*)&_hash, 32), &d);
WriteGuard l(x_cache);
m_cache[_hash].resize(d.size());
memcpy(m_cache[_hash].data(), d.data(), d.size());
@ -352,11 +404,6 @@ bytes BlockChain::block(h256 _hash) const
return m_cache[_hash];
}
eth::uint BlockChain::number(h256 _hash) const
{
return details(_hash).number;
}
h256 BlockChain::numberHash(unsigned _n) const
{
if (!_n)
@ -365,69 +412,3 @@ h256 BlockChain::numberHash(unsigned _n) const
for (; _n < details().number; ++_n, ret = details(ret).parent) {}
return ret;
}
BlockDetails BlockChain::details(h256 _h) const
{
lock_guard<recursive_mutex> l(m_lock);
BlockDetailsHash::const_iterator it = m_details.find(_h);
if (it != m_details.end())
return it->second;
std::string s;
m_extrasDB->Get(m_readOptions, toSlice(_h), &s);
if (s.empty())
{
// cout << "Not found in DB: " << _h << endl;
return NullBlockDetails;
}
{
bool ok;
tie(it, ok) = m_details.insert(std::make_pair(_h, BlockDetails(RLP(s))));
}
return it->second;
}
BlockBlooms BlockChain::blooms(h256 _h) const
{
lock_guard<recursive_mutex> l(m_lock);
BlockBloomsHash::const_iterator it = m_blooms.find(_h);
if (it != m_blooms.end())
return it->second;
std::string s;
m_extrasDB->Get(m_readOptions, toSlice(_h, 1), &s);
if (s.empty())
{
// cout << "Not found in DB: " << _h << endl;
return NullBlockBlooms;
}
{
bool ok;
tie(it, ok) = m_blooms.insert(std::make_pair(_h, BlockBlooms(RLP(s))));
}
return it->second;
}
BlockTraces BlockChain::traces(h256 _h) const
{
lock_guard<recursive_mutex> l(m_lock);
BlockTracesHash::const_iterator it = m_traces.find(_h);
if (it != m_traces.end())
return it->second;
std::string s;
m_extrasDB->Get(m_readOptions, toSlice(_h, 2), &s);
if (s.empty())
{
// cout << "Not found in DB: " << _h << endl;
return NullBlockTraces;
}
{
bool ok;
tie(it, ok) = m_traces.insert(std::make_pair(_h, BlockTraces(RLP(s))));
}
return it->second;
}

147
libethereum/BlockChain.h

@ -25,60 +25,15 @@
#include <libethential/Log.h>
#include <libethcore/CommonEth.h>
#include <libethcore/BlockInfo.h>
#include "Manifest.h"
#include "Guards.h"
#include "BlockDetails.h"
#include "AddressState.h"
#include "BlockQueue.h"
namespace ldb = leveldb;
namespace eth
{
class RLP;
class RLPStream;
struct BlockDetails
{
BlockDetails(): number(0), totalDifficulty(0) {}
BlockDetails(uint _n, u256 _tD, h256 _p, h256s _c, h256 _bloom): number(_n), totalDifficulty(_tD), parent(_p), children(_c), bloom(_bloom) {}
BlockDetails(RLP const& _r);
bytes rlp() const;
bool isNull() const { return !totalDifficulty; }
explicit operator bool() const { return !isNull(); }
uint number; // TODO: remove?
u256 totalDifficulty;
h256 parent;
h256s children;
h256 bloom;
};
struct BlockBlooms
{
BlockBlooms() {}
BlockBlooms(RLP const& _r) { blooms = _r.toVector<h256>(); }
bytes rlp() const { RLPStream s; s << blooms; return s.out(); }
h256s blooms;
};
struct BlockTraces
{
BlockTraces() {}
BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); }
bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamOut(s); return s.out(); }
Manifests traces;
};
typedef std::map<h256, BlockDetails> BlockDetailsHash;
typedef std::map<h256, BlockBlooms> BlockBloomsHash;
typedef std::map<h256, BlockTraces> BlockTracesHash;
static const BlockDetails NullBlockDetails;
static const BlockBlooms NullBlockBlooms;
static const BlockTraces NullBlockTraces;
static const h256s NullH256s;
class State;
@ -94,9 +49,11 @@ struct BlockChainNote: public LogChannel { static const char* name() { return "=
// TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::map<Address, AddressState> const& genesisState();
ldb::Slice toSlice(h256 _h, unsigned _sub = 0);
/**
* @brief Implements the blockchain database. All data this gives is disk-backed.
* @todo Make thread-safe.
* @threadsafe
* @todo Make not memory hog (should actually act as a cache and deallocate old entries).
*/
class BlockChain
@ -110,67 +67,113 @@ public:
/// To be called from main loop every 100ms or so.
void process();
/// Attempt to import the given block.
bool attemptImport(bytes const& _block, OverlayDB const& _stateDB);
/// Sync the chain with any incoming blocks. All blocks should, if processed in order
h256s sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max);
/// Attempt to import the given block directly into the BlockChain and sync with the state DB.
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept;
/// 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;
BlockDetails details(h256 _hash) const { return queryExtras<BlockDetails, 0>(_hash, m_details, x_details, NullBlockDetails); }
BlockDetails details() const { return details(currentHash()); }
/// Get the transactions' bloom filters of a block (or the most recent mined if none given). Thread-safe.
BlockBlooms blooms(h256 _hash) const;
BlockBlooms blooms(h256 _hash) const { return queryExtras<BlockBlooms, 1>(_hash, m_blooms, x_blooms, NullBlockBlooms); }
BlockBlooms blooms() const { return blooms(currentHash()); }
/// Get the transactions' trace manifests of a block (or the most recent mined if none given). Thread-safe.
BlockTraces traces(h256 _hash) const;
BlockTraces traces(h256 _hash) const { return queryExtras<BlockTraces, 2>(_hash, m_traces, x_traces, NullBlockTraces); }
BlockTraces traces() const { return traces(currentHash()); }
/// Get a given block (RLP format). Thread-safe.
/// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe.
bytes block(h256 _hash) const;
bytes block() const { return block(currentHash()); }
uint number(h256 _hash) const;
/// Get a number for the given hash (or the most recent mined if none given). Thread-safe.
uint number(h256 _hash) const { return details(_hash).number; }
uint number() const { return number(currentHash()); }
/// Get a given block (RLP format). Thread-safe.
h256 currentHash() const { return m_lastBlockHash; }
h256 currentHash() const { ReadGuard l(x_lastBlockHash); return m_lastBlockHash; }
/// Get the hash of the genesis block.
/// Get the hash of the genesis block. Thread-safe.
h256 genesisHash() const { return m_genesisHash; }
/// Get the hash of a block of a given number.
/// Get the hash of a block of a given number. Slow; try not to use it too much.
h256 numberHash(unsigned _n) const;
std::vector<std::pair<Address, AddressState>> interestQueue() { std::vector<std::pair<Address, AddressState>> 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() { UpgradableGuard l(x_genesis); if (!s_genesis) { auto gb = createGenesisBlock(); UpgradeGuard ul(l); (s_genesis = new BlockInfo)->populate(&gb); } return *s_genesis; }
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:
template<class T, unsigned N> T queryExtras(h256 _h, std::map<h256, T>& _m, boost::shared_mutex& _x, T const& _n) const
{
{
ReadGuard l(_x);
auto it = _m.find(_h);
if (it != _m.end())
return it->second;
}
std::string s;
m_extrasDB->Get(m_readOptions, toSlice(_h, N), &s);
if (s.empty())
{
// cout << "Not found in DB: " << _h << endl;
return _n;
}
WriteGuard l(_x);
auto ret = _m.insert(std::make_pair(_h, T(RLP(s))));
return ret.first->second;
}
void checkConsistency();
/// Get fully populated from disk DB.
/// The caches of the disk DB and their locks.
mutable boost::shared_mutex x_details;
mutable BlockDetailsHash m_details;
mutable boost::shared_mutex x_blooms;
mutable BlockBloomsHash m_blooms;
mutable boost::shared_mutex x_traces;
mutable BlockTracesHash m_traces;
mutable boost::shared_mutex x_cache;
mutable std::map<h256, bytes> m_cache;
mutable std::recursive_mutex m_lock;
/// The queue of transactions that have happened that we're interested in.
std::map<Address, int> m_interest;
std::vector<std::pair<Address, AddressState>> m_interestQueue;
/// The disk DBs. Thread-safe, so no need for locks.
ldb::DB* m_db;
ldb::DB* m_extrasDB;
/// Hash of the last (valid) block on the longest chain.
mutable boost::shared_mutex x_lastBlockHash;
h256 m_lastBlockHash;
/// Genesis block info.
h256 m_genesisHash;
bytes m_genesisBlock;
@ -179,6 +182,8 @@ private:
friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
/// Static genesis info and its lock.
static boost::shared_mutex x_genesis;
static BlockInfo* s_genesis;
};

40
libethereum/BlockDetails.cpp

@ -0,0 +1,40 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BlockDetails.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "BlockDetails.h"
#include <libethential/Common.h>
using namespace std;
using namespace eth;
BlockDetails::BlockDetails(RLP const& _r)
{
number = _r[0].toInt<uint>();
totalDifficulty = _r[1].toInt<u256>();
parent = _r[2].toHash<h256>();
children = _r[3].toVector<h256>();
bloom = _r[4].toHash<h256>();
}
bytes BlockDetails::rlp() const
{
return rlpList(number, totalDifficulty, parent, children, bloom);
}

76
libethereum/BlockDetails.h

@ -0,0 +1,76 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BlockDetails.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libethential/Log.h>
#include <libethential/RLP.h>
#include "Manifest.h"
namespace ldb = leveldb;
namespace eth
{
struct BlockDetails
{
BlockDetails(): number(0), totalDifficulty(0) {}
BlockDetails(uint _n, u256 _tD, h256 _p, h256s _c, h256 _bloom): number(_n), totalDifficulty(_tD), parent(_p), children(_c), bloom(_bloom) {}
BlockDetails(RLP const& _r);
bytes rlp() const;
bool isNull() const { return !totalDifficulty; }
explicit operator bool() const { return !isNull(); }
uint number; // TODO: remove?
u256 totalDifficulty;
h256 parent;
h256s children;
h256 bloom;
};
struct BlockBlooms
{
BlockBlooms() {}
BlockBlooms(RLP const& _r) { blooms = _r.toVector<h256>(); }
bytes rlp() const { RLPStream s; s << blooms; return s.out(); }
h256s blooms;
};
struct BlockTraces
{
BlockTraces() {}
BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); }
bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamOut(s); return s.out(); }
Manifests traces;
};
typedef std::map<h256, BlockDetails> BlockDetailsHash;
typedef std::map<h256, BlockBlooms> BlockBloomsHash;
typedef std::map<h256, BlockTraces> BlockTracesHash;
static const BlockDetails NullBlockDetails;
static const BlockBlooms NullBlockBlooms;
static const BlockTraces NullBlockTraces;
}

102
libethereum/BlockQueue.cpp

@ -0,0 +1,102 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BlockQueue.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "BlockQueue.h"
#include <libethential/Log.h>
#include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h>
#include "BlockChain.h"
using namespace std;
using namespace eth;
bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
{
// Check if we already know this block.
h256 h = sha3(_block);
UpgradableGuard l(m_lock);
if (m_readySet.count(h) || m_futureSet.count(h))
// Already know about this one.
return false;
// VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi;
#if ETH_CATCH
try
#endif
{
bi.populate(_block);
bi.verifyInternals(_block);
}
#if ETH_CATCH
catch (Exception const& _e)
{
cwarn << "Ignoring malformed block: " << _e.description();
return false;
}
#endif
auto newHash = eth::sha3(_block);
// Check block doesn't already exist first!
if (_bc.details(newHash))
return false;
// Check it's not crazy
if (bi.timestamp > (u256)time(0))
return false;
UpgradeGuard ul(l);
// We now know it.
if (!m_readySet.count(bi.parentHash) && !_bc.details(bi.parentHash))
{
// We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on.
m_future.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes())));
m_futureSet.insert(h);
return true;
}
// If valid, append to blocks.
m_ready.push_back(_block.toBytes());
m_readySet.insert(h);
noteReadyWithoutWriteGuard(h);
return true;
}
void BlockQueue::noteReadyWithoutWriteGuard(h256 _b)
{
auto r = m_future.equal_range(_b);
h256s good;
for (auto it = r.first; it != r.second; ++it)
{
m_futureSet.erase(it->second.first);
m_ready.push_back(it->second.second);
m_readySet.erase(it->second.first);
good.push_back(it->second.first);
}
m_future.erase(r.first, r.second);
for (auto g: good)
noteReadyWithoutWriteGuard(g);
}

66
libethereum/BlockQueue.h

@ -0,0 +1,66 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BlockQueue.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <boost/thread.hpp>
#include <libethential/Common.h>
#include "libethcore/CommonEth.h"
#include "Guards.h"
namespace eth
{
class BlockChain;
/**
* @brief A queue of blocks. Sits between network or other I/O and the BlockChain.
* Sorts them ready for blockchain insertion (with the BlockChain::sync() method).
* @threadsafe
*/
class BlockQueue
{
public:
/// Import a block into the queue.
bool import(bytesConstRef _tx, BlockChain const& _bc);
/// Grabs the blocks that are ready, giving them in the correct order for insertion into the chain.
void drain(std::vector<bytes>& o_out) { WriteGuard l(m_lock); swap(o_out, m_ready); m_readySet.clear(); }
/// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain).
void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); }
/// Get information on the items queued.
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_future.size()); }
private:
void noteReadyWithoutWriteGuard(h256 _b);
mutable boost::shared_mutex m_lock; ///< General lock.
std::set<h256> m_readySet; ///< All blocks ready for chain-import.
std::vector<bytes> m_ready; ///< List of blocks, in correct order, ready for chain-import.
std::set<h256> m_futureSet; ///< Set of all blocks whose parents are not ready/in-chain.
std::multimap<h256, std::pair<h256, bytes>> m_future; ///< For transactions that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears.
};
}

427
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())
{
@ -57,75 +69,172 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const
m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)),
m_preMine(_us, m_stateDB),
m_postMine(_us, m_stateDB),
m_workState(Active)
m_workState(Deleted)
{
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
m_vc.setOk();
m_changed = true;
work(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);
m_workState.store(Active, std::memory_order_release);
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()
{
if (m_workState.load(std::memory_order_acquire) == Active)
m_workState.store(Deleting, std::memory_order_release);
while (m_workState.load(std::memory_order_acquire) != Deleted)
this_thread::sleep_for(chrono::milliseconds(10));
m_work->join();
if (m_work)
{
if (m_workState.load(std::memory_order_acquire) == Active)
m_workState.store(Deleting, std::memory_order_release);
while (m_workState.load(std::memory_order_acquire) != Deleted)
this_thread::sleep_for(chrono::milliseconds(10));
m_work->join();
}
}
void Client::startNetwork(unsigned short _listenPort, std::string const& _seedHost, unsigned short _port, NodeMode _mode, unsigned _peers, string const& _publicIP, bool _upnp)
void Client::flushTransactions()
{
work(true);
}
void Client::clearPending()
{
ClientGuard l(this);
if (m_net.get())
if (!m_postMine.pending().size())
return;
try
{
m_net.reset(new PeerServer(m_clientVersion, m_bc, 0, _listenPort, _mode, _publicIP, _upnp));
}
catch (std::exception const&)
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);
cwatch << "+++" << ret << _h;
return ret;
}
unsigned Client::installWatch(TransactionFilter const& _f)
{
lock_guard<mutex> 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)
{
cwatch << "XXX" << _i;
lock_guard<mutex> l(m_filterLock);
auto it = m_watches.find(_i);
if (it == m_watches.end())
return;
auto id = it->second.id;
m_watches.erase(it);
auto fit = m_filters.find(id);
if (fit != m_filters.end())
if (!--fit->second.refCount)
m_filters.erase(fit);
}
void Client::appendFromNewPending(h256 _bloom, h256Set& o_changed) const
{
lock_guard<mutex> l(m_filterLock);
for (pair<h256, InstalledFilter> const& i: m_filters)
if ((unsigned)i.second.filter.latest() >= 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<mutex> l(m_filterLock);
for (pair<h256, InstalledFilter> const& i: m_filters)
if ((unsigned)i.second.filter.latest() >= d.number && (unsigned)i.second.filter.earliest() <= d.number && i.second.filter.matches(d.bloom))
o_changed.insert(i.first);
}
void Client::noteChanged(h256Set const& _filters)
{
lock_guard<mutex> l(m_filterLock);
for (auto& i: m_watches)
if (_filters.count(i.second.id))
{
cwatch << "!!!" << i.first << 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();
{
// Probably already have the port open.
cwarn << "Could not initialize with specified/default port. Trying system-assigned port";
m_net.reset(new PeerServer(m_clientVersion, m_bc, 0, _mode, _publicIP, _upnp));
Guard l(x_net);
if (m_net.get())
return;
try
{
m_net.reset(new PeerServer(m_clientVersion, m_bc, 0, _listenPort, _mode, _publicIP, _upnp));
}
catch (std::exception const&)
{
// Probably already have the port open.
cwarn << "Could not initialize with specified/default port. Trying system-assigned port";
m_net.reset(new PeerServer(m_clientVersion, m_bc, 0, _mode, _publicIP, _upnp));
}
m_net->setIdealPeerCount(_peers);
}
m_net->setIdealPeerCount(_peers);
if (_seedHost.size())
connect(_seedHost, _port);
}
std::vector<PeerInfo> Client::peers()
{
ClientGuard l(this);
Guard l(x_net);
return m_net ? m_net->peers() : std::vector<PeerInfo>();
}
size_t Client::peerCount() const
{
ClientGuard l(this);
Guard l(x_net);
return m_net ? m_net->peerCount() : 0;
}
void Client::connect(std::string const& _seedHost, unsigned short _port)
{
ClientGuard l(this);
Guard l(x_net);
if (!m_net.get())
return;
m_net->connect(_seedHost, _port);
@ -133,12 +242,14 @@ void Client::connect(std::string const& _seedHost, unsigned short _port)
void Client::stopNetwork()
{
ClientGuard l(this);
Guard l(x_net);
m_net.reset(nullptr);
}
void Client::startMining()
{
ensureWorking();
m_doMine = true;
m_restartMining = true;
}
@ -150,9 +261,11 @@ void Client::stopMining()
void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
ensureWorking();
ClientGuard l(this);
Transaction t;
cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
t.nonce = m_postMine.transactionsFrom(toAddress(_secret));
t.value = _value;
t.gasPrice = _gasPrice;
@ -166,6 +279,8 @@ void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _
Address Client::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
{
ensureWorking();
ClientGuard l(this);
Transaction t;
t.nonce = m_postMine.transactionsFrom(toAddress(_secret));
@ -182,108 +297,156 @@ Address Client::transact(Secret _secret, u256 _endowment, bytes const& _init, u2
void Client::inject(bytesConstRef _rlp)
{
ensureWorking();
ClientGuard l(this);
m_tq.attemptImport(_rlp);
m_changed = true;
}
void Client::work()
void Client::work(bool _justQueue)
{
bool changed = false;
cdebug << ">>> WORK";
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)
{
ClientGuard l(this);
m_net->process(); // must be in guard for now since it uses the blockchain. TODO: make BlockChain thread-safe.
Guard l(x_net);
if (m_net && !_justQueue)
{
cdebug << "--- WORK: NETWORK";
m_net->process(); // must be in guard for now since it uses the blockchain.
if (m_net->sync(m_bc, m_tq, m_stateDB))
changed = true;
}
// returns h256Set as block hashes, once for each block that has come in/gone out.
cdebug << "--- WORK: NET <==> TQ ; CHAIN ==> NET ==> BQ";
m_net->sync(m_tq, m_bq);
// Synchronise state to block chain.
// This should remove any transactions on our queue that are included within our state.
// It also guarantees that the state reflects the longest (valid!) chain on the block chain.
// This might mean reverting to an earlier state and replaying some blocks, or, (worst-case:
// if there are no checkpoints before our fork) reverting to the genesis block and replaying
// all blocks.
// Resynchronise state with block chain & trans
{
ClientGuard l(this);
if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address())
{
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))
{
if (m_doMine)
cnote << "Additional transaction ready: Restarting mining operation.";
m_restartMining = true;
cdebug << "--- TQ:" << m_tq.items() << "; BQ:" << m_bq.items();
}
}
if (m_doMine)
// Do some mining.
if (!_justQueue)
{
if (m_restartMining)
// TODO: Separate "Miner" object.
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
m_postMine.commitToMine(m_bc);
}
}
if (m_doMine)
{
cdebug << "--- WORK: MINE";
m_restartMining = false;
// 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);
if (mineInfo.completed)
{
// Import block.
cdebug << "--- WORK: COMPLETE MINE%";
m_postMine.completeMine();
cdebug << "--- WORK: CHAIN <== postSTATE";
h256s hs = m_bc.attemptImport(m_postMine.blockData(), m_stateDB);
if (hs.size())
{
cwarn << "I'm not just paranoid. Cannot mine. Please file a bug report.";
m_doMine = false;
for (auto h: hs)
appendFromNewBlock(h, changeds);
changeds.insert(NewBlockFilter);
//changeds.insert(NewPendingFilter); // if we mined the new block, then we've probably reset the pending transactions.
}
}
else
m_postMine.commitToMine(m_bc);
}
else
{
cdebug << "--- WORK: SLEEP";
this_thread::sleep_for(chrono::milliseconds(100));
}
}
if (m_doMine)
// Synchronise state to block chain.
// This should remove any transactions on our queue that are included within our state.
// It also guarantees that the state reflects the longest (valid!) chain on the block chain.
// This might mean reverting to an earlier state and replaying some blocks, or, (worst-case:
// if there are no checkpoints before our fork) reverting to the genesis block and replaying
// all blocks.
// Resynchronise state with block chain & trans
{
m_restartMining = false;
ClientGuard l(this);
// Mine for a while.
MineInfo mineInfo = m_postMine.mine(100);
cdebug << "--- WORK: BQ ==> CHAIN ==> STATE";
OverlayDB db = m_stateDB;
m_lock.unlock();
h256s newBlocks = m_bc.sync(m_bq, db, 100);
if (newBlocks.size())
{
for (auto i: newBlocks)
appendFromNewBlock(i, changeds);
changeds.insert(NewBlockFilter);
}
m_lock.lock();
if (newBlocks.size())
m_stateDB = db;
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;
cdebug << "--- WORK: preSTATE <== CHAIN";
if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address())
{
ClientGuard l(this);
m_mineHistory.push_back(mineInfo);
if (m_doMine)
cnote << "New block on chain: Restarting mining operation.";
m_restartMining = true; // need to re-commit to mine.
m_postMine = m_preMine;
changeds.insert(NewPendingFilter);
}
if (mineInfo.completed)
// returns h256s as blooms, once for each transaction.
cdebug << "--- WORK: postSTATE <== TQ";
h256s newPendingBlooms = m_postMine.sync(m_tq);
if (newPendingBlooms.size())
{
// Import block.
ClientGuard l(this);
m_postMine.completeMine();
m_bc.attemptImport(m_postMine.blockData(), m_stateDB);
m_changed = true;
for (auto i: newPendingBlooms)
appendFromNewPending(i, changeds);
changeds.insert(NewPendingFilter);
if (m_doMine)
cnote << "Additional transaction ready: Restarting mining operation.";
m_restartMining = true;
}
}
else
this_thread::sleep_for(chrono::milliseconds(100));
m_changed = m_changed || changed;
cdebug << "--- WORK: noteChanged" << changeds.size() << "items";
noteChanged(changeds);
cdebug << "<<< WORK";
}
void Client::lock() const
@ -316,6 +479,15 @@ State Client::asOf(int _h) const
return State(m_stateDB, m_bc, m_bc.numberHash(numberOf(_h)));
}
std::vector<Address> Client::addresses(int _block) const
{
ClientGuard l(this);
vector<Address> 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);
@ -456,13 +628,13 @@ PastMessages Client::transactions(TransactionFilter const& _f) const
ClientGuard l(this);
PastMessages ret;
unsigned begin = numberOf(_f.latest());
unsigned end = min(begin, numberOf(_f.earliest()));
unsigned begin = min<unsigned>(m_bc.number(), (unsigned)_f.latest());
unsigned end = min(begin, (unsigned)_f.earliest());
unsigned m = _f.max();
unsigned s = _f.skip();
// Handle pending transactions differently as they're not on the block chain.
if (_f.latest() == 0)
if (begin == m_bc.number())
{
for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
{
@ -477,31 +649,23 @@ PastMessages Client::transactions(TransactionFilter const& _f) const
s--;
else
// Have a transaction that contains a matching message.
ret.insert(ret.begin(), pm[j].polish(h256(), ts, 0));
ret.insert(ret.begin(), pm[j].polish(h256(), ts, m_bc.number() + 1));
}
}
/* 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;
auto cn = m_bc.number();
#endif
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,45 +681,32 @@ 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)
s--;
else
// Have a transaction that contains a matching message.
ret.insert(ret.begin(), pm[j].polish(h, ts, cn - n + 2));
ret.insert(ret.begin(), pm[j].polish(h, ts, n));
}
}
#if ETH_DEBUG
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;
}

141
libethereum/Client.h

@ -82,7 +82,7 @@ struct PastMessage
{
PastMessage(Manifest const& _m, std::vector<unsigned> _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<unsigned> 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<PastMessage> PastMessages;
@ -102,7 +102,10 @@ typedef std::vector<PastMessage> PastMessages;
class TransactionFilter
{
public:
TransactionFilter(int _earliest = GenesisBlock, int _latest = 0, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {}
TransactionFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {}
void fillStream(RLPStream& _s) const;
h256 sha3() const;
int earliest() const { return m_earliest; }
int latest() const { return m_latest; }
@ -128,12 +131,35 @@ private:
std::set<Address> m_to;
std::set<std::pair<Address, u256>> m_stateAltered;
std::set<Address> m_altered;
int m_earliest;
int m_latest;
int m_earliest = 0;
int m_latest = -1;
unsigned m_max;
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;
};
struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 6; };
#define cwatch eth::LogOutputStream<eth::WatchChannel, true>()
/**
* @brief Main API hub for interfacing with Ethereum.
*/
@ -143,9 +169,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 +179,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<std::pair<Address, AddressState>> 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<void(Transaction)> const& _f);
/// Calls @a _f when a transaction is mined that involves @a _dest and once per change.
// void onConfirmed(Address _dest, function<void(Transaction, AddressState)> 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 +205,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<std::mutex> 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<std::mutex> l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return 0; } }
bool checkWatch(unsigned _watchId) { std::lock_guard<std::mutex> 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<Address> addresses() const { return addresses(m_default); }
std::vector<Address> addresses(int _block) const;
// Misc stuff:
@ -225,9 +252,9 @@ public:
/// Stop the network subsystem.
void stopNetwork();
/// Is the network subsystem up?
bool haveNetwork() { return !!m_net; }
/// Get access to the peer server object. This will be null if the network isn't online.
PeerServer* peerServer() const { return m_net.get(); }
bool haveNetwork() { Guard l(x_net); return !!m_net; }
/// Get access to the peer server object. This will be null if the network isn't online. DANGEROUS! DO NOT USE!
PeerServer* peerServer() const { Guard l(x_net); return m_net.get(); }
// Mining stuff:
@ -253,10 +280,27 @@ public:
std::list<MineInfo> 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;
@ -267,10 +311,13 @@ private:
std::string m_clientVersion; ///< Our end-application client's name/version.
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.
TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
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).
mutable std::mutex x_net; ///< Lock for the network.
std::unique_ptr<PeerServer> m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required.
std::unique_ptr<std::thread> m_work;///< The work thread.
@ -283,7 +330,11 @@ private:
std::list<MineInfo> m_mineHistory;
mutable bool m_restartMining = false;
mutable bool m_changed;
mutable std::mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters;
std::map<unsigned, Watch> m_watches;
int m_default = -1;
};
inline ClientGuard::ClientGuard(Client const* _c): m_client(_c)

29
libethereum/Guards.cpp

@ -0,0 +1,29 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Guards.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Guards.h"
using namespace std;
using namespace eth;
namespace eth
{
}

37
libethereum/Guards.h

@ -0,0 +1,37 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Guards.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <mutex>
#include <boost/thread.hpp>
namespace eth
{
using Guard = std::lock_guard<std::mutex>;
using RecursiveGuard = std::lock_guard<std::recursive_mutex>;
using ReadGuard = boost::shared_lock<boost::shared_mutex>;
using UpgradableGuard = boost::upgrade_lock<boost::shared_mutex>;
using UpgradeGuard = boost::upgrade_to_unique_lock<boost::shared_mutex>;
using WriteGuard = boost::unique_lock<boost::shared_mutex>;
}

13
libethereum/PeerNetwork.h

@ -46,12 +46,13 @@ class PeerSession;
struct NetWarn: public LogChannel { static const char* name() { return "!N!"; } static const int verbosity = 0; };
struct NetNote: public LogChannel { static const char* name() { return "*N*"; } static const int verbosity = 1; };
struct NetMessageSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 2; };
struct NetMessageDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 3; };
struct NetTriviaSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 4; };
struct NetTriviaDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 5; };
struct NetAllDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 6; };
struct NetRight: public LogChannel { static const char* name() { return ">N>"; } static const int verbosity = 8; };
struct NetLeft: public LogChannel { static const char* name() { return "<N<"; } static const int verbosity = 9; };
struct NetConnect: public LogChannel { static const char* name() { return "+N+"; } static const int verbosity = 4; };
struct NetMessageDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 5; };
struct NetTriviaSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 10; };
struct NetTriviaDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 11; };
struct NetAllDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 15; };
struct NetRight: public LogChannel { static const char* name() { return ">N>"; } static const int verbosity = 18; };
struct NetLeft: public LogChannel { static const char* name() { return "<N<"; } static const int verbosity = 19; };
enum PacketType
{

283
libethereum/PeerServer.cpp

@ -39,6 +39,7 @@
#include <libethcore/Exceptions.h>
#include "BlockChain.h"
#include "TransactionQueue.h"
#include "BlockQueue.h"
#include "PeerSession.h"
using namespace std;
using namespace eth;
@ -106,9 +107,34 @@ PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch,
PeerServer::~PeerServer()
{
for (auto const& i: m_peers)
if (auto p = i.second.lock())
p->disconnect(ClientQuit);
disconnectPeers();
}
void PeerServer::registerPeer(std::shared_ptr<PeerSession> _s)
{
Guard l(x_peers);
m_peers[_s->m_id] = _s;
}
void PeerServer::disconnectPeers()
{
for (unsigned n = 0;; n = 0)
{
{
Guard l(x_peers);
for (auto i: m_peers)
if (auto p = i.second.lock())
{
p->disconnect(ClientQuit);
n++;
}
}
if (!n)
break;
m_ioService.poll();
usleep(100000);
}
delete m_upnp;
}
@ -252,6 +278,7 @@ std::map<Public, bi::tcp::endpoint> PeerServer::potentialPeers()
std::map<Public, bi::tcp::endpoint> ret;
if (!m_public.address().is_unspecified())
ret.insert(make_pair(m_key.pub(), m_public));
Guard l(x_peers);
for (auto i: m_peers)
if (auto j = i.second.lock())
{
@ -268,7 +295,7 @@ void PeerServer::ensureAccepting()
{
if (m_accepting == false)
{
clog(NetNote) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")";
clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")";
m_accepting = true;
m_acceptor.async_accept(m_socket, [=](boost::system::error_code ec)
{
@ -276,7 +303,7 @@ void PeerServer::ensureAccepting()
try
{
try {
clog(NetNote) << "Accepted connection from " << m_socket.remote_endpoint();
clog(NetConnect) << "Accepted connection from " << m_socket.remote_endpoint();
} catch (...){}
bi::address remoteAddress = m_socket.remote_endpoint().address();
// Port defaults to 0 - we let the hello tell us which port the peer listens to
@ -288,7 +315,7 @@ void PeerServer::ensureAccepting()
clog(NetWarn) << "ERROR: " << _e.what();
}
m_accepting = false;
if (ec.value() != 1 && (m_mode == NodeMode::PeerServer || m_peers.size() < m_idealPeerCount * 2))
if (ec.value() != 1 && (m_mode == NodeMode::PeerServer || peerCount() < m_idealPeerCount * 2))
ensureAccepting();
});
}
@ -303,19 +330,19 @@ void PeerServer::connect(std::string const& _addr, unsigned short _port) noexcep
catch (exception const& e)
{
// Couldn't connect
clog(NetNote) << "Bad host " << _addr << " (" << e.what() << ")";
clog(NetConnect) << "Bad host " << _addr << " (" << e.what() << ")";
}
}
void PeerServer::connect(bi::tcp::endpoint const& _ep)
{
clog(NetNote) << "Attempting connection to " << _ep;
clog(NetConnect) << "Attempting connection to " << _ep;
bi::tcp::socket* s = new bi::tcp::socket(m_ioService);
s->async_connect(_ep, [=](boost::system::error_code const& ec)
{
if (ec)
{
clog(NetNote) << "Connection refused to " << _ep << " (" << ec.message() << ")";
clog(NetConnect) << "Connection refused to " << _ep << " (" << ec.message() << ")";
for (auto i = m_incomingPeers.begin(); i != m_incomingPeers.end(); ++i)
if (i->second.first == _ep && i->second.second < 3)
{
@ -323,25 +350,25 @@ void PeerServer::connect(bi::tcp::endpoint const& _ep)
goto OK;
}
// for-else
clog(NetNote) << "Giving up.";
clog(NetConnect) << "Giving up.";
OK:;
}
else
{
auto p = make_shared<PeerSession>(this, std::move(*s), m_networkId, _ep.address(), _ep.port());
clog(NetNote) << "Connected to " << _ep;
clog(NetConnect) << "Connected to " << _ep;
p->start();
}
delete s;
});
}
bool PeerServer::ensureInitialised(BlockChain& _bc, TransactionQueue& _tq)
bool PeerServer::ensureInitialised(TransactionQueue& _tq)
{
if (m_latestBlockSent == h256())
{
// First time - just initialise.
m_latestBlockSent = _bc.currentHash();
m_latestBlockSent = m_chain->currentHash();
clog(NetNote) << "Initialising: latest=" << m_latestBlockSent;
for (auto const& i: _tq.transactions())
@ -363,141 +390,141 @@ bool PeerServer::noteBlock(h256 _hash, bytesConstRef _data)
return false;
}
bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o)
bool PeerServer::sync(TransactionQueue& _tq, BlockQueue& _bq)
{
bool ret = ensureInitialised(_bc, _tq);
bool netChange = ensureInitialised(_tq);
if (m_mode == NodeMode::Full)
{
for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it)
if (_tq.import(&*it))
{}//ret = true; // just putting a transaction in the queue isn't enough to change the state - it might have an invalid nonce...
else
m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on.
m_incomingTransactions.clear();
auto h = m_chain->currentHash();
auto h = _bc.currentHash();
bool resendAll = (h != m_latestBlockSent);
maintainTransactions(_tq, h);
maintainBlocks(_bq, h);
// Send any new transactions.
for (auto j: m_peers)
if (auto p = j.second.lock())
{
bytes b;
uint n = 0;
for (auto const& i: _tq.transactions())
if ((!m_transactionsSent.count(i.first) && !p->m_knownTransactions.count(i.first)) || p->m_requireTransactions || resendAll)
{
b += i.second;
++n;
m_transactionsSent.insert(i.first);
}
if (n)
{
RLPStream ts;
PeerSession::prep(ts);
ts.appendList(n + 1) << TransactionsPacket;
ts.appendRaw(b, n).swapOut(b);
seal(b);
p->send(&b);
}
p->m_knownTransactions.clear();
p->m_requireTransactions = false;
}
// Connect to additional peers
growPeers();
}
// platform for consensus of social contract.
// restricts your freedom but does so fairly. and that's the value proposition.
// guarantees that everyone else respect the rules of the system. (i.e. obeys laws).
prunePeers();
return netChange;
}
void PeerServer::maintainTransactions(TransactionQueue& _tq, h256 _currentHash)
{
bool resendAll = (_currentHash != m_latestBlockSent);
for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it)
if (_tq.import(&*it))
{}//ret = true; // just putting a transaction in the queue isn't enough to change the state - it might have an invalid nonce...
else
m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on.
m_incomingTransactions.clear();
// Send any new blocks.
if (h != m_latestBlockSent)
// Send any new transactions.
Guard l(x_peers);
for (auto j: m_peers)
if (auto p = j.second.lock())
{
// TODO: find where they diverge and send complete new branch.
RLPStream ts;
PeerSession::prep(ts);
ts.appendList(2) << BlocksPacket;
bytes b;
ts.appendRaw(_bc.block(_bc.currentHash())).swapOut(b);
seal(b);
for (auto j: m_peers)
if (auto p = j.second.lock())
uint n = 0;
for (auto const& i: _tq.transactions())
if ((!m_transactionsSent.count(i.first) && !p->m_knownTransactions.count(i.first)) || p->m_requireTransactions || resendAll)
{
if (!p->m_knownBlocks.count(_bc.currentHash()))
p->send(&b);
p->m_knownBlocks.clear();
b += i.second;
++n;
m_transactionsSent.insert(i.first);
}
if (n)
{
RLPStream ts;
PeerSession::prep(ts);
ts.appendList(n + 1) << TransactionsPacket;
ts.appendRaw(b, n).swapOut(b);
seal(b);
p->send(&b);
}
p->m_knownTransactions.clear();
p->m_requireTransactions = false;
}
m_latestBlockSent = h;
}
for (int accepted = 1, n = 0; accepted; ++n)
{
accepted = 0;
lock_guard<recursive_mutex> l(m_incomingLock);
if (m_incomingBlocks.size())
for (auto it = prev(m_incomingBlocks.end());; --it)
{
try
{
_bc.import(*it, _o);
it = m_incomingBlocks.erase(it);
++accepted;
ret = true;
}
catch (UnknownParent)
{
// Don't (yet) know its parent. Leave it for later.
m_unknownParentBlocks.push_back(*it);
it = m_incomingBlocks.erase(it);
}
catch (...)
{
// Some other error - erase it.
it = m_incomingBlocks.erase(it);
}
void PeerServer::maintainBlocks(BlockQueue& _bq, h256 _currentHash)
{
// Import new blocks
{
lock_guard<recursive_mutex> l(m_incomingLock);
for (auto it = m_incomingBlocks.rbegin(); it != m_incomingBlocks.rend(); ++it)
if (_bq.import(&*it, *m_chain))
{}
else{} // TODO: don't forward it.
m_incomingBlocks.clear();
}
if (it == m_incomingBlocks.begin())
break;
}
if (!n && accepted)
// Send any new blocks.
if (_currentHash != m_latestBlockSent)
{
// TODO: find where they diverge and send complete new branch.
RLPStream ts;
PeerSession::prep(ts);
ts.appendList(2) << BlocksPacket;
bytes b;
ts.appendRaw(m_chain->block()).swapOut(b);
seal(b);
Guard l(x_peers);
for (auto j: m_peers)
if (auto p = j.second.lock())
{
for (auto i: m_unknownParentBlocks)
m_incomingBlocks.push_back(i);
m_unknownParentBlocks.clear();
if (!p->m_knownBlocks.count(_currentHash))
p->send(&b);
p->m_knownBlocks.clear();
}
}
}
m_latestBlockSent = _currentHash;
}
// Connect to additional peers
while (m_peers.size() < m_idealPeerCount)
void PeerServer::growPeers()
{
Guard l(x_peers);
while (m_peers.size() < m_idealPeerCount)
{
if (m_freePeers.empty())
{
if (m_freePeers.empty())
if (chrono::steady_clock::now() > m_lastPeersRequest + chrono::seconds(10))
{
if (chrono::steady_clock::now() > m_lastPeersRequest + chrono::seconds(10))
{
RLPStream s;
bytes b;
(PeerSession::prep(s).appendList(1) << GetPeersPacket).swapOut(b);
seal(b);
for (auto const& i: m_peers)
if (auto p = i.second.lock())
if (p->isOpen())
p->send(&b);
m_lastPeersRequest = chrono::steady_clock::now();
}
RLPStream s;
bytes b;
(PeerSession::prep(s).appendList(1) << GetPeersPacket).swapOut(b);
seal(b);
for (auto const& i: m_peers)
if (auto p = i.second.lock())
if (p->isOpen())
p->send(&b);
m_lastPeersRequest = chrono::steady_clock::now();
}
if (!m_accepting)
ensureAccepting();
break;
}
if (!m_accepting)
ensureAccepting();
auto x = time(0) % m_freePeers.size();
m_incomingPeers[m_freePeers[x]].second++;
connect(m_incomingPeers[m_freePeers[x]].first);
m_freePeers.erase(m_freePeers.begin() + x);
break;
}
}
// platform for consensus of social contract.
// restricts your freedom but does so fairly. and that's the value proposition.
// guarantees that everyone else respect the rules of the system. (i.e. obeys laws).
auto x = time(0) % m_freePeers.size();
m_incomingPeers[m_freePeers[x]].second++;
connect(m_incomingPeers[m_freePeers[x]].first);
m_freePeers.erase(m_freePeers.begin() + x);
}
}
void PeerServer::prunePeers()
{
Guard l(x_peers);
// We'll keep at most twice as many as is ideal, halfing what counts as "too young to kill" until we get there.
for (uint old = 15000; m_peers.size() > m_idealPeerCount * 2 && old > 100; old /= 2)
while (m_peers.size() > m_idealPeerCount)
@ -519,11 +546,17 @@ bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o)
worst->disconnect(TooManyPeers);
}
return ret;
// Remove dead peers from list.
for (auto i = m_peers.begin(); i != m_peers.end();)
if (i->second.lock().get())
++i;
else
i = m_peers.erase(i);
}
std::vector<PeerInfo> PeerServer::peers(bool _updatePing) const
{
Guard l(x_peers);
if (_updatePing)
const_cast<PeerServer*>(this)->pingAll();
this_thread::sleep_for(chrono::milliseconds(200));
@ -537,6 +570,7 @@ std::vector<PeerInfo> PeerServer::peers(bool _updatePing) const
void PeerServer::pingAll()
{
Guard l(x_peers);
for (auto& i: m_peers)
if (auto j = i.second.lock())
j->ping();
@ -544,6 +578,7 @@ void PeerServer::pingAll()
bytes PeerServer::savePeers() const
{
Guard l(x_peers);
RLPStream ret;
int n = 0;
for (auto& i: m_peers)

34
libethereum/PeerServer.h

@ -30,12 +30,20 @@
#include <thread>
#include <libethcore/CommonEth.h>
#include "PeerNetwork.h"
#include "Guards.h"
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
namespace eth
{
class TransactionQueue;
class BlockQueue;
/**
* @brief The PeerServer class
* @warning None of this is thread-safe. You have been warned.
*/
class PeerServer
{
friend class PeerSession;
@ -48,8 +56,12 @@ public:
/// Start server, but don't listen.
PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, NodeMode _m = NodeMode::Full);
/// Will block on network process events.
~PeerServer();
/// Closes all peers.
void disconnectPeers();
static unsigned protocolVersion();
unsigned networkId() { return m_networkId; }
@ -58,13 +70,15 @@ 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);
bool sync(TransactionQueue&, BlockQueue& _bc);
/// Conduct I/O, polling, syncing, whatever.
/// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway.
/// This won't touch alter the blockchain.
void process() { if (isInitialised()) m_ioService.poll(); }
bool havePeer(Public _id) const { Guard l(x_peers); return m_peers.count(_id); }
/// Set ideal number of peers.
void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; }
@ -74,7 +88,7 @@ public:
std::vector<PeerInfo> peers(bool _updatePing = false) const;
/// Get number of peers connected; equivalent to, but faster than, peers().size().
size_t peerCount() const { return m_peers.size(); }
size_t peerCount() const { Guard l(x_peers); return m_peers.size(); }
/// Ping the peers, to update the latency information.
void pingAll();
@ -85,6 +99,8 @@ public:
bytes savePeers() const;
void restorePeers(bytesConstRef _b);
void registerPeer(std::shared_ptr<PeerSession> _s);
private:
/// Session wants to pass us a block that we might not have.
/// @returns true if we didn't have it.
@ -95,10 +111,15 @@ private:
void determinePublic(std::string const& _publicAddress, bool _upnp);
void ensureAccepting();
void growPeers();
void prunePeers();
void maintainTransactions(TransactionQueue& _tq, h256 _currentBlock);
void maintainBlocks(BlockQueue& _bq, h256 _currentBlock);
/// Check to see if the network peer-state initialisation has happened.
bool isInitialised() const { return m_latestBlockSent; }
/// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first.
bool ensureInitialised(BlockChain& _bc, TransactionQueue& _tq);
bool ensureInitialised(TransactionQueue& _tq);
std::map<Public, bi::tcp::endpoint> potentialPeers();
@ -117,14 +138,15 @@ private:
KeyPair m_key;
unsigned m_networkId;
mutable std::mutex x_peers;
std::map<Public, std::weak_ptr<PeerSession>> m_peers;
mutable std::recursive_mutex m_incomingLock;
std::vector<bytes> m_incomingTransactions;
std::vector<bytes> m_incomingBlocks;
mutable std::recursive_mutex m_incomingLock;
std::vector<bytes> m_unknownParentBlocks;
std::vector<Public> m_freePeers;
std::map<Public, std::pair<bi::tcp::endpoint, unsigned>> m_incomingPeers;
std::vector<Public> m_freePeers;
h256 m_latestBlockSent;
std::set<h256> m_transactionsSent;

100
libethereum/PeerSession.cpp

@ -61,9 +61,11 @@ PeerSession::~PeerSession()
bi::tcp::endpoint PeerSession::endpoint() const
{
if (m_socket.is_open())
try {
try
{
return bi::tcp::endpoint(m_socket.remote_endpoint().address(), m_listenPort);
} catch (...){}
}
catch (...){}
return bi::tcp::endpoint();
}
@ -84,15 +86,13 @@ bool PeerSession::interpret(RLP const& _r)
clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << m_id.abridged() << showbase << hex << m_caps << dec << m_listenPort;
if (m_server->m_peers.count(m_id))
if (auto l = m_server->m_peers[m_id].lock())
if (l.get() != this && l->isOpen())
{
// Already connected.
cwarn << "Already have peer id" << m_id.abridged() << "at" << l->endpoint() << "rather than" << endpoint();
disconnect(DuplicatePeer);
return false;
}
if (m_server->havePeer(m_id))
{
// Already connected.
cwarn << "Already have peer id" << m_id.abridged();// << "at" << l->endpoint() << "rather than" << endpoint();
disconnect(DuplicatePeer);
return false;
}
if (m_protocolVersion != PeerServer::protocolVersion() || m_networkId != m_server->networkId() || !m_id)
{
@ -107,26 +107,12 @@ bool PeerSession::interpret(RLP const& _r)
return false;
}
m_server->m_peers[m_id] = shared_from_this();
m_server->registerPeer(shared_from_this());
startInitialSync();
// Grab their block chain off them.
// Grab trsansactions off them.
{
uint n = m_server->m_chain->number(m_server->m_latestBlockSent);
clogS(NetAllDetail) << "Want chain. Latest:" << m_server->m_latestBlockSent << ", number:" << n;
uint count = std::min(c_maxHashes, n + 1);
RLPStream s;
prep(s).appendList(2 + count);
s << GetChainPacket;
auto h = m_server->m_latestBlockSent;
for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent)
{
clogS(NetAllDetail) << " " << i << ":" << h;
s << h;
}
s << c_maxBlocksAsk;
sealAndSend(s);
s.clear();
prep(s).appendList(1);
s << GetTransactionsPacket;
sealAndSend(s);
@ -186,7 +172,7 @@ bool PeerSession::interpret(RLP const& _r)
clogS(NetAllDetail) << "Checking: " << ep << "(" << toHex(id.ref().cropped(0, 4)) << ")";
// check that it's not us or one we already know:
if (id && (m_server->m_key.pub() == id || m_server->m_peers.count(id) || m_server->m_incomingPeers.count(id)))
if (id && (m_server->m_key.pub() == id || m_server->havePeer(id) || m_server->m_incomingPeers.count(id)))
goto CONTINUE;
// check that we're not already connected to addr:
@ -195,13 +181,6 @@ bool PeerSession::interpret(RLP const& _r)
for (auto i: m_server->m_addresses)
if (ep.address() == i && ep.port() == m_server->listenPort())
goto CONTINUE;
for (auto i: m_server->m_peers)
if (shared_ptr<PeerSession> p = i.second.lock())
{
clogS(NetAllDetail) << " ...against " << p->endpoint();
if (p->m_socket.is_open() && p->endpoint() == ep)
goto CONTINUE;
}
for (auto i: m_server->m_incomingPeers)
if (i.second.first == ep)
goto CONTINUE;
@ -238,16 +217,27 @@ bool PeerSession::interpret(RLP const& _r)
}
}
m_rating += used;
if (g_logVerbosity >= 3)
unsigned knownParents = 0;
unsigned unknownParents = 0;
if (g_logVerbosity >= 2)
{
for (unsigned i = 1; i < _r.itemCount(); ++i)
{
auto h = sha3(_r[i].data());
BlockInfo bi(_r[i].data());
if (!m_server->m_chain->details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash))
{
unknownParents++;
clogS(NetMessageDetail) << "Unknown parent " << bi.parentHash << " of block " << h;
}
else
{
knownParents++;
clogS(NetMessageDetail) << "Known parent " << bi.parentHash << " of block " << h;
}
}
}
clogS(NetMessageSummary) << dec << knownParents << " known parents, " << unknownParents << "unknown, " << used << "used.";
if (used) // we received some - check if there's any more
{
RLPStream s;
@ -257,6 +247,8 @@ bool PeerSession::interpret(RLP const& _r)
s << c_maxBlocksAsk;
sealAndSend(s);
}
else
clogS(NetMessageSummary) << "Peer sent all blocks in chain.";
break;
}
case GetChainPacket:
@ -314,6 +306,9 @@ bool PeerSession::interpret(RLP const& _r)
clogS(NetAllDetail) << " " << dec << i << " " << h;
s.appendRaw(m_server->m_chain->block(h));
}
if (!count)
clogS(NetMessageSummary) << "Sent peer all we have.";
clogS(NetAllDetail) << "Parent: " << h;
}
else if (parent != parents.back())
@ -485,23 +480,15 @@ void PeerSession::dropped()
if (m_socket.is_open())
try
{
clogS(NetNote) << "Closing " << m_socket.remote_endpoint();
clogS(NetConnect) << "Closing " << m_socket.remote_endpoint();
m_socket.close();
}
catch (...) {}
// Remove from peer server
for (auto i = m_server->m_peers.begin(); i != m_server->m_peers.end(); ++i)
if (i->second.lock().get() == this)
{
m_server->m_peers.erase(i);
break;
}
}
void PeerSession::disconnect(int _reason)
{
clogS(NetNote) << "Disconnecting (reason:" << reasonOf((DisconnectReason)_reason) << ")";
clogS(NetConnect) << "Disconnecting (reason:" << reasonOf((DisconnectReason)_reason) << ")";
if (m_socket.is_open())
{
if (m_disconnect == chrono::steady_clock::time_point::max())
@ -529,6 +516,25 @@ void PeerSession::start()
doRead();
}
void PeerSession::startInitialSync()
{
uint n = m_server->m_chain->number(m_server->m_latestBlockSent);
clogS(NetAllDetail) << "Want chain. Latest:" << m_server->m_latestBlockSent << ", number:" << n;
uint count = std::min(c_maxHashes, n + 1);
RLPStream s;
prep(s).appendList(2 + count);
s << GetChainPacket;
auto h = m_server->m_latestBlockSent;
for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent)
{
clogS(NetAllDetail) << " " << i << ":" << h;
s << h;
}
s << c_maxBlocksAsk;
sealAndSend(s);
}
void PeerSession::doRead()
{
// ignore packets received while waiting to disconnect

2
libethereum/PeerSession.h

@ -51,6 +51,8 @@ public:
bi::tcp::endpoint endpoint() const; ///< for other peers to connect to.
private:
void startInitialSync();
void dropped();
void doRead();
void doWrite(std::size_t length);

19
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<pair<h256, bytes>> 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;
}

10
libethereum/State.h

@ -138,6 +138,8 @@ public:
/// @returns the set containing all addresses currently in use in Ethereum.
std::map<Address, u256> 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;

31
libethereum/TransactionQueue.cpp

@ -31,7 +31,7 @@ bool TransactionQueue::import(bytesConstRef _block)
{
// Check if we already know this transaction.
h256 h = sha3(_block);
if (m_data.count(h))
if (m_known.count(h))
return false;
try
@ -40,11 +40,9 @@ bool TransactionQueue::import(bytesConstRef _block)
// The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction).
Transaction t(_block);
auto s = t.sender();
if (m_interest.count(s))
m_interestQueue.push_back(t);
// If valid, append to blocks.
m_data[h] = _block.toBytes();
m_current[h] = _block.toBytes();
}
catch (InvalidTransactionFormat const& _e)
{
@ -62,9 +60,9 @@ bool TransactionQueue::import(bytesConstRef _block)
void TransactionQueue::setFuture(std::pair<h256, bytes> const& _t)
{
if (m_data.count(_t.first))
if (m_current.count(_t.first))
{
m_data.erase(_t.first);
m_current.erase(_t.first);
m_future.insert(make_pair(Transaction(_t.second).sender(), _t));
}
}
@ -73,6 +71,25 @@ void TransactionQueue::noteGood(std::pair<h256, bytes> const& _t)
{
auto r = m_future.equal_range(Transaction(_t.second).sender());
for (auto it = r.first; it != r.second; ++it)
m_data.insert(_t);
m_current.insert(it->second);
m_future.erase(r.first, r.second);
}
void TransactionQueue::drop(h256 _txHash)
{
WriteGuard l(m_lock);
if (!m_known.erase(_txHash))
return;
if (m_current.count(_txHash))
m_current.erase(_txHash);
else
{
for (auto i = m_future.begin(); i != m_future.end(); ++i)
if (i->second.first == _txHash)
{
m_future.erase(i);
break;
}
}
}

30
libethereum/TransactionQueue.h

@ -21,8 +21,10 @@
#pragma once
#include <boost/thread.hpp>
#include <libethential/Common.h>
#include "Transaction.h"
#include "libethcore/CommonEth.h"
#include "Guards.h"
namespace eth
{
@ -31,28 +33,28 @@ class BlockChain;
/**
* @brief A queue of Transactions, each stored as RLP.
* @threadsafe
*/
class TransactionQueue
{
public:
bool attemptImport(bytesConstRef _block) { try { import(_block); return true; } catch (...) { return false; } }
bool attemptImport(bytes const& _block) { try { import(&_block); return true; } catch (...) { return false; } }
bool import(bytesConstRef _block);
void drop(h256 _txHash) { m_data.erase(_txHash); }
std::map<h256, bytes> const& transactions() const { return m_data; }
bool attemptImport(bytesConstRef _tx) { try { import(_tx); return true; } catch (...) { return false; } }
bool attemptImport(bytes const& _tx) { return attemptImport(&_tx); }
bool import(bytesConstRef _tx);
void drop(h256 _txHash);
std::map<h256, bytes> transactions() const { ReadGuard l(m_lock); return m_current; }
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_future.size()); }
void setFuture(std::pair<h256, bytes> const& _t);
void noteGood(std::pair<h256, bytes> const& _t);
Transactions interestQueue() { Transactions 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); }
private:
std::map<h256, bytes> m_data; ///< Map of SHA3(tx) to tx.
Transactions m_interestQueue;
std::map<Address, int> m_interest;
std::multimap<Address, std::pair<h256, bytes>> m_future; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX.
mutable boost::shared_mutex m_lock; ///< General lock.
std::set<h256> m_known; ///< Hashes of transactions in both sets.
std::map<h256, bytes> m_current; ///< Map of SHA3(tx) to tx.
std::multimap<Address, std::pair<h256, bytes>> m_future; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX.
};
}

8
liblll/Assembly.cpp

@ -116,7 +116,7 @@ ostream& eth::operator<<(ostream& _out, AssemblyItemsConstRef _i)
_out << " PUSH" << i.data();
break;
case PushString:
_out << " PUSH'[" << h256(i.data()).abridged() << "]";
_out << " PUSH'[" << hex << (unsigned)i.data() << "]";
break;
case PushTag:
_out << " PUSH[tag" << i.data() << "]";
@ -125,7 +125,7 @@ ostream& eth::operator<<(ostream& _out, AssemblyItemsConstRef _i)
_out << " tag" << i.data() << ":";
break;
case PushData:
_out << " PUSH*[" << h256(i.data()).abridged() << "]";
_out << " PUSH*[" << hex << (unsigned)i.data() << "]";
break;
case UndefinedItem:
_out << " ???";
@ -156,7 +156,7 @@ ostream& Assembly::streamOut(ostream& _out) const
_out << "tag" << i.m_data << ": " << endl;
break;
case PushData:
_out << " PUSH [" << h256(i.m_data).abridged() << "]" << endl;
_out << " PUSH [" << hex << (unsigned)i.m_data << "]" << endl;
break;
default:;
}
@ -165,7 +165,7 @@ ostream& Assembly::streamOut(ostream& _out) const
{
_out << ".data:" << endl;
for (auto const& i: m_data)
_out << " " << i.first.abridged() << ": " << toHex(i.second) << endl;
_out << " " << hex << (unsigned)(u256)i.first << ": " << toHex(i.second) << endl;
}
return _out;
}

293
libqethereum/QEthereum.cpp

@ -1,6 +1,3 @@
#if ETH_QTQML
#include <QtQml/QtQml>
#endif
#include <QtCore/QtCore>
#include <QtWebKitWidgets/QWebFrame>
#include <libethcore/FileSystem.h>
@ -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,20 +89,31 @@ QString unpadded(QString _s)
QEthereum::QEthereum(QObject* _p, Client* _c, QList<eth::KeyPair> _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::setup(QWebFrame*)
void QEthereum::clientDieing()
{
// Alex: JS codes moved to mainwin until qtwebkit bugs are resolved (#245)
clearWatches();
m_client = nullptr;
}
void QEthereum::teardown(QWebFrame*)
void QEthereum::clearWatches()
{
if (m_client)
for (auto i: m_watches)
m_client->uninstallWatch(i);
m_watches.clear();
}
QString QEthereum::secretToAddress(QString _s) const
{
return toQJS(KeyPair(toSecret(_s)).address());
}
Client* QEthereum::client() const
@ -267,12 +133,12 @@ QString QEthereum::sha3(QString _s) const
QString QEthereum::coinbase() const
{
return toQJS(client()->address());
return m_client ? toQJS(client()->address()) : "";
}
QString QEthereum::number() const
{
return QString::number(client()->blockChain().number() + 1);
return m_client ? QString::number(client()->blockChain().number() + 1) : "";
}
QString QEthereum::account() const
@ -307,74 +173,91 @@ QStringList QEthereum::keys() const
void QEthereum::setCoinbase(QString _a)
{
if (client()->address() != toAddress(_a))
if (m_client && client()->address() != toAddress(_a))
{
client()->setAddress(toAddress(_a));
changed();
coinbaseChanged();
}
}
QString QEthereum::balanceAt(QString _a) const
{
return toQJS(client()->postState().balance(toAddress(_a)));
return m_client ? toQJS(client()->postState().balance(toAddress(_a))) : "";
}
QString QEthereum::storageAt(QString _a, QString _p) const
{
if (!m_client)
return "";
eth::ClientGuard l(const_cast<Client*>(m_client));
return toQJS(client()->postState().storage(toAddress(_a), toU256(_p)));
}
double QEthereum::txCountAt(QString _a) const
{
if (!m_client)
return 0.0;
eth::ClientGuard l(const_cast<Client*>(m_client));
return (double)client()->postState().transactionsFrom(toAddress(_a));
}
bool QEthereum::isContractAt(QString _a) const
{
if (!m_client)
return false;
eth::ClientGuard l(const_cast<Client*>(m_client));
return client()->postState().addressHasCode(toAddress(_a));
}
u256 QEthereum::balanceAt(Address _a) const
{
if (!m_client)
return 0;
eth::ClientGuard l(const_cast<Client*>(m_client));
return client()->postState().balance(_a);
}
double QEthereum::txCountAt(Address _a) const
{
if (!m_client)
return 0.0;
eth::ClientGuard l(const_cast<Client*>(m_client));
return (double)client()->postState().transactionsFrom(_a);
}
bool QEthereum::isContractAt(Address _a) const
{
if (!m_client)
return false;
eth::ClientGuard l(const_cast<Client*>(m_client));
return client()->postState().addressHasCode(_a);
}
QString QEthereum::balanceAt(QString _a, int _block) const
{
return toQJS(client()->balanceAt(toAddress(_a), _block));
return m_client ? toQJS(client()->balanceAt(toAddress(_a), _block)) : "";
}
QString QEthereum::stateAt(QString _a, QString _p, int _block) const
{
return toQJS(client()->stateAt(toAddress(_a), toU256(_p), _block));
return m_client ? toQJS(client()->stateAt(toAddress(_a), toU256(_p), _block)) : "";
}
QString QEthereum::codeAt(QString _a, int _block) const
{
return ::fromBinary(client()->codeAt(toAddress(_a), _block));
return m_client ? ::fromBinary(client()->codeAt(toAddress(_a), _block)) : "";
}
double QEthereum::countAt(QString _a, int _block) const
{
return (double)(uint64_t)client()->countAt(toAddress(_a), _block);
return m_client ? (double)(uint64_t)client()->countAt(toAddress(_a), _block) : 0;
}
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 +296,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,32 +316,42 @@ 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 m_client ? toJson(m_client->transactions(toTransactionFilter(_json))) : "";
}
bool QEthereum::isMining() const
{
return client()->isMining();
return m_client ? client()->isMining() : false;
}
bool QEthereum::isListening() const
{
return client()->haveNetwork();
return m_client ? client()->haveNetwork() : false;
}
void QEthereum::setMining(bool _l)
{
if (_l)
client()->startMining();
else
client()->stopMining();
if (m_client)
{
if (_l)
client()->startMining();
else
client()->stopMining();
}
}
void QEthereum::setListening(bool _l)
{
if (!m_client)
return;
if (_l)
client()->startNetwork();
else
@ -464,24 +360,63 @@ void QEthereum::setListening(bool _l)
unsigned QEthereum::peerCount() const
{
return (unsigned)client()->peerCount();
return m_client ? (unsigned)client()->peerCount() : 0;
}
QString QEthereum::doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice)
{
client()->changed();
if (!m_client)
return "";
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();
if (!m_client)
return;
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)
{
if (!m_client)
return (unsigned)-1;
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)
{
if (!m_client)
return "";
return toJson(m_client->transactions(_w));
}
void QEthereum::killWatch(unsigned _w)
{
if (!m_client)
return;
m_client->uninstallWatch(_w);
std::remove(m_watches.begin(), m_watches.end(), _w);
}
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

377
libqethereum/QEthereum.h

@ -1,9 +1,8 @@
#pragma once
#include <QtCore/QAbstractListModel>
#if ETH_QTQML
#include <QtQml/QtQml>
#endif
#include <QtCore/QObject>
#include <QtCore/QStringList>
#include <QtCore/QList>
#include <libethential/CommonIO.h>
#include <libethcore/CommonEth.h>
@ -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 <class T> 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 <class T> 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<eth::u256>(_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<eth::Address>(_a)); }
Q_INVOKABLE QVariant toAddress(QVariant/*eth::Address*/ _a) const { return toQJS<eth::Address>((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<eth::Address>(_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<eth::KeyPair>(_p).address()); }
Q_INVOKABLE QVariant/*eth::Secret*/ secret(QVariant/*eth::KeyPair*/ _p) const { return toQJS(to<eth::KeyPair>(_p).secret()); }
Q_INVOKABLE QVariant/*eth::KeyPair*/ keypair(QVariant/*eth::Secret*/ _k) const { return toQJS(eth::KeyPair(to<eth::Secret>(_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<eth::u256>(ret);
}
};
#endif
inline eth::bytes asBytes(QString const& _s)
{
@ -324,17 +53,7 @@ template <unsigned N> eth::FixedHash<N> toFixed(QString const& _s)
return eth::FixedHash<N>(asBytes(padded(_s, N)));
}
template <unsigned N> boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> toInt(QString const& _s)
{
if (_s.startsWith("0x"))
return eth::fromBigEndian<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>>(eth::fromHex(_s.toStdString().substr(2)));
else if (!_s.contains(QRegExp("[^0-9]")))
// Hex or Decimal
return boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>(_s.toStdString());
else
// Binary
return eth::fromBigEndian<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>>(asBytes(padded(_s, N)));
}
template <unsigned N> inline boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> 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); }
@ -372,11 +91,12 @@ public:
virtual ~QEthereum();
eth::Client* client() const;
void setClient(eth::Client* _c) { m_client = _c; }
void setup(QWebFrame* _e);
void teardown(QWebFrame* _e);
/// Call when the client() is going to be deleted to make this object useless but safe.
void clientDieing();
void setAccounts(QList<eth::KeyPair> _l) { m_accounts = _l; this->changed(); }
void setAccounts(QList<eth::KeyPair> _l) { m_accounts = _l; keysChanged(); }
Q_INVOKABLE QString ethTest() const { return "Hello world!"; }
Q_INVOKABLE QEthereum* self() { return this; }
@ -403,11 +123,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 +156,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<unsigned> m_watches;
QList<eth::KeyPair> 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); var ret = { w: ww }; ret.uninstall = function() { eth.killWatch(w); }; ret.changed = function(f) { eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.transactions = function() { return JSON.parse(eth.watchTransactions(this.w)) }; return ret; }"); \
frame->evaluateJavaScript("eth.watch = function(a) { return eth.makeWatch(JSON.stringify(a)) }"); \
frame->evaluateJavaScript("eth.watchChain = function() { return eth.makeWatch('chainChanged') }"); \
frame->evaluateJavaScript("eth.watchPending = function() { return eth.makeWatch('pendingChanged') }"); \
frame->evaluateJavaScript("eth.create = function(s, v, c, g, p, f) { var v = eth.doCreate(s, v, c, g, p); if (f) f(v) }"); \
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 <unsigned N> inline boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> toInt(QString const& _s)
{
if (_s.startsWith("0x"))
return eth::fromBigEndian<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>>(eth::fromHex(_s.toStdString().substr(2)));
else if (!_s.contains(QRegExp("[^0-9]")))
// Hex or Decimal
return boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>(_s.toStdString());
else
// Binary
return eth::fromBigEndian<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>>(asBytes(padded(_s, N)));
}

188
libqethereum/QmlEthereum.cpp

@ -0,0 +1,188 @@
#if ETH_QTQML
#include <QtQml/QtQml>
#endif
#include <QtCore/QtCore>
#include <QtWebKitWidgets/QWebFrame>
#include <libethcore/FileSystem.h>
#include <libethcore/Dagger.h>
#include <libevmface/Instruction.h>
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
#include <libethereum/PeerServer.h>
#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

283
libqethereum/QmlEthereum.h

@ -0,0 +1,283 @@
#pragma once
#include <QtCore/QAbstractListModel>
#if ETH_QTQML
#include <QtQml/QtQml>
#endif
#include <libethential/CommonIO.h>
#include <libethcore/CommonEth.h>
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 <class T> 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 <class T> 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<eth::u256>(_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<eth::Address>(_a)); }
Q_INVOKABLE QVariant toAddress(QVariant/*eth::Address*/ _a) const { return toQJS<eth::Address>((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<eth::Address>(_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<eth::KeyPair>(_p).address()); }
Q_INVOKABLE QVariant/*eth::Secret*/ secret(QVariant/*eth::KeyPair*/ _p) const { return toQJS(to<eth::KeyPair>(_p).secret()); }
Q_INVOKABLE QVariant/*eth::KeyPair*/ keypair(QVariant/*eth::Secret*/ _k) const { return toQJS(eth::KeyPair(to<eth::Secret>(_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<eth::u256>(ret);
}
};
#endif

1
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;

6
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()

5
walleth/MainWin.h

@ -6,7 +6,7 @@
#include <QtCore/QMutex>
#include <QtWidgets/QMainWindow>
#include <libethcore/CommonEth.h>
#include <libqethereum/QEthereum.h>
#include <libqethereum/QmlEthereum.h>
namespace Ui {
class Main;
@ -43,9 +43,6 @@ private slots:
void refresh();
void refreshNetwork();
signals:
void changed();
protected:
virtual void timerEvent(QTimerEvent *);

Loading…
Cancel
Save