diff --git a/CMakeLists.txt b/CMakeLists.txt index 46cc0753f..6c6ec18cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -353,7 +353,7 @@ if (NOT LANGUAGES) add_subdirectory(exp) endif () if(NOT ("${TARGET_PLATFORM}" STREQUAL "w64")) - add_subdirectory(neth) + #add_subdirectory(neth) // resurect once moved over to WebThree API. endif () if(QTQML) add_definitions(-DETH_QTQML) @@ -369,7 +369,7 @@ if (NOT LANGUAGES) add_subdirectory(third) if(QTQML) #add_subdirectory(iethxi) - add_subdirectory(walleth) + #add_subdirectory(walleth) // resurect once we want to submit ourselves to QML. endif() endif() endif() diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 3eac0a0b6..c5d1aa0be 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -52,7 +52,7 @@ else () endif () qt5_use_modules(${EXECUTEABLE} Core)# Gui Widgets Network WebKit WebKitWidgets) -target_link_libraries(${EXECUTEABLE} qethereum ethereum evm ethcore devcrypto secp256k1 gmp ${CRYPTOPP_LS} serpent lll evmface devcore) +target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore devcrypto secp256k1 gmp ${CRYPTOPP_LS} serpent lll evmface devcore) if (APPLE) # First have qt5 install plugins and frameworks diff --git a/alethzero/DownloadView.cpp b/alethzero/DownloadView.cpp new file mode 100644 index 000000000..0fe5e1414 --- /dev/null +++ b/alethzero/DownloadView.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 . +*/ +/** @file DownloadView.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "DownloadView.h" + +#include +#include +#include +#include "Grapher.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +DownloadView::DownloadView(QWidget* _p): QWidget(_p) +{ +} + +void DownloadView::paintEvent(QPaintEvent*) +{ + QPainter p(this); +} diff --git a/alethzero/DownloadView.h b/alethzero/DownloadView.h new file mode 100644 index 000000000..ea3e05b8f --- /dev/null +++ b/alethzero/DownloadView.h @@ -0,0 +1,49 @@ +/* + 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 . +*/ +/** @file DownloadView.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#ifdef Q_MOC_RUN +#define BOOST_MPL_IF_HPP_INCLUDED +#endif + +#include +#include +#ifndef Q_MOC_RUN +#include +#endif + +namespace dev { namespace eth { +struct MineInfo; +}} + +class DownloadView: public QWidget +{ + Q_OBJECT + +public: + DownloadView(QWidget* _p = nullptr); + +protected: + virtual void paintEvent(QPaintEvent*); + +private: +}; diff --git a/alethzero/Main.ui b/alethzero/Main.ui index aa8a43f1c..dbc4bb144 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -132,6 +132,7 @@ + @@ -1432,6 +1433,39 @@ font-size: 14pt + + + QDockWidget::DockWidgetFeatureMask + + + Blockchain Download + + + 2 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + &Quit @@ -1698,6 +1732,14 @@ font-size: 14pt Reserved Debug 1 + + + true + + + Enable Local Addresses + + @@ -1712,6 +1754,12 @@ font-size: 14pt
MiningView.h
1 + + DownloadView + QWidget +
DownloadView.h
+ 1 +
destination diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index e9cdf90bc..5842664b3 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -43,45 +44,9 @@ #include "MainWin.h" #include "ui_Main.h" using namespace std; - -// types -using dev::bytes; -using dev::bytesConstRef; -using dev::h160; -using dev::h256; -using dev::u160; -using dev::u256; -using dev::Address; -using dev::eth::BlockInfo; -using dev::eth::Client; -using dev::eth::Instruction; -using dev::KeyPair; -using dev::eth::NodeMode; -using dev::eth::BlockChain; -using dev::p2p::PeerInfo; -using dev::RLP; -using dev::Secret; -using dev::eth::Transaction; -using dev::eth::Executive; - -// functions -using dev::toHex; -using dev::fromHex; -using dev::left160; -using dev::right160; -using dev::simpleDebugOut; -using dev::toLog2; -using dev::toString; -using dev::operator<<; -using dev::eth::units; -using dev::eth::sha3; -using dev::eth::compileLLL; -using dev::eth::disassemble; -using dev::eth::formatBalance; - -// vars -using dev::g_logPost; -using dev::g_logVerbosity; +using namespace dev; +using namespace dev::p2p; +using namespace dev::eth; static void initUnits(QComboBox* _b) { @@ -114,7 +79,6 @@ static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr) return QString(); } - Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f"); Main::Main(QWidget *parent) : @@ -177,12 +141,12 @@ 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_webThree.reset(new WebThreeDirect("AlethZero", getDataDir() + "/AlethZero", false, {"eth", "shh"})); connect(ui->webView, &QWebView::loadStarted, [this]() { // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. - m_ethereum = new QEthereum(this, client(), owned()); + m_ethereum = new QEthereum(this, ethereum(), owned()); QWebFrame* f = ui->webView->page()->mainFrame(); f->disconnect(SIGNAL(javaScriptWindowObjectCleared())); @@ -226,6 +190,11 @@ Main::~Main() writeSettings(); } +dev::p2p::NetworkPreferences Main::netPrefs() const +{ + return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked()); +} + void Main::onKeysChanged() { installBalancesWatch(); @@ -233,14 +202,14 @@ void Main::onKeysChanged() unsigned Main::installWatch(dev::eth::MessageFilter const& _tf, std::function const& _f) { - auto ret = client()->installWatch(_tf); + auto ret = ethereum()->installWatch(_tf); m_handlers[ret] = _f; return ret; } unsigned Main::installWatch(dev::h256 _tf, std::function const& _f) { - auto ret = client()->installWatch(_tf); + auto ret = ethereum()->installWatch(_tf); m_handlers[ret] = _f; return ret; } @@ -255,14 +224,14 @@ void Main::installWatches() void Main::installNameRegWatch() { - client()->uninstallWatch(m_nameRegFilter); - m_nameRegFilter = installWatch(dev::eth::MessageFilter().altered((u160)client()->stateAt(c_config, 0)), [=](){ onNameRegChange(); }); + ethereum()->uninstallWatch(m_nameRegFilter); + m_nameRegFilter = installWatch(dev::eth::MessageFilter().altered((u160)ethereum()->stateAt(c_config, 0)), [=](){ onNameRegChange(); }); } void Main::installCurrenciesWatch() { - client()->uninstallWatch(m_currenciesFilter); - m_currenciesFilter = installWatch(dev::eth::MessageFilter().altered((u160)client()->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); }); + ethereum()->uninstallWatch(m_currenciesFilter); + m_currenciesFilter = installWatch(dev::eth::MessageFilter().altered((u160)ethereum()->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); }); } void Main::installBalancesWatch() @@ -270,9 +239,9 @@ void Main::installBalancesWatch() dev::eth::MessageFilter tf; vector
altCoins; - Address coinsAddr = right160(client()->stateAt(c_config, 1)); - for (unsigned i = 0; i < client()->stateAt(coinsAddr, 0); ++i) - altCoins.push_back(right160(client()->stateAt(coinsAddr, i + 1))); + Address coinsAddr = right160(ethereum()->stateAt(c_config, 1)); + for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) + altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); for (auto i: m_myKeys) { tf.altered(i.address()); @@ -280,7 +249,7 @@ void Main::installBalancesWatch() tf.altered(c, (u160)i.address()); } - client()->uninstallWatch(m_balancesFilter); + ethereum()->uninstallWatch(m_balancesFilter); m_balancesFilter = installWatch(tf, [=](){ onBalancesChange(); }); } @@ -328,7 +297,7 @@ void Main::onNewPending() void Main::on_forceMining_triggered() { - client()->setForceMining(ui->forceMining->isChecked()); + ethereum()->setForceMining(ui->forceMining->isChecked()); } void Main::on_enableOptimizer_triggered() @@ -422,11 +391,11 @@ QString Main::pretty(dev::Address _a) const { h256 n; - if (h160 nameReg = (u160)client()->stateAt(c_config, 0)) - n = client()->stateAt(nameReg, (u160)(_a)); + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0)) + n = ethereum()->stateAt(nameReg, (u160)(_a)); if (!n) - n = client()->stateAt(m_nameReg, (u160)(_a)); + n = ethereum()->stateAt(m_nameReg, (u160)(_a)); return fromRaw(n); } @@ -452,11 +421,11 @@ Address Main::fromString(QString const& _a) const memset(n.data() + sn.size(), 0, 32 - sn.size()); if (_a.size()) { - if (h160 nameReg = (u160)client()->stateAt(c_config, 0)) - if (h256 a = client()->stateAt(nameReg, n)) + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0)) + if (h256 a = ethereum()->stateAt(nameReg, n)) return right160(a); - if (h256 a = client()->stateAt(m_nameReg, n)) + if (h256 a = ethereum()->stateAt(m_nameReg, n)) return right160(a); } if (_a.size() == 40) @@ -484,11 +453,11 @@ QString Main::lookup(QString const& _a) const */ h256 ret; - if (h160 dnsReg = (u160)client()->stateAt(c_config, 4, 0)) - ret = client()->stateAt(dnsReg, n); + if (h160 dnsReg = (u160)ethereum()->stateAt(c_config, 4, 0)) + ret = ethereum()->stateAt(dnsReg, n); /* if (!ret) - if (h160 nameReg = (u160)m_client->stateAt(c_config, 0, 0)) - ret = m_client->stateAt(nameReg, n2); + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0, 0)) + ret = ethereum()->stateAt(nameReg, n2); */ if (ret && !((u256)ret >> 32)) return QString("%1.%2.%3.%4").arg((int)ret[28]).arg((int)ret[29]).arg((int)ret[30]).arg((int)ret[31]); @@ -506,7 +475,7 @@ void Main::on_about_triggered() void Main::on_paranoia_triggered() { - client()->setParanoia(ui->paranoia->isChecked()); + ethereum()->setParanoia(ui->paranoia->isChecked()); } void Main::writeSettings() @@ -525,6 +494,7 @@ void Main::writeSettings() s.setValue("upnp", ui->upnp->isChecked()); s.setValue("forceAddress", ui->forceAddress->text()); s.setValue("usePast", ui->usePast->isChecked()); + s.setValue("localNetworking", ui->localNetworking->isChecked()); s.setValue("forceMining", ui->forceMining->isChecked()); s.setValue("paranoia", ui->paranoia->isChecked()); s.setValue("showAll", ui->showAll->isChecked()); @@ -537,7 +507,7 @@ void Main::writeSettings() s.setValue("privateChain", m_privateChain); s.setValue("verbosity", ui->verbosity->value()); - bytes d = client()->savePeers(); + bytes d = m_webThree->savePeers(); if (d.size()) m_peers = QByteArray((char*)d.data(), (int)d.size()); s.setValue("peers", m_peers); @@ -569,11 +539,12 @@ void Main::readSettings(bool _skipGeometry) m_myKeys.append(KeyPair(k)); } } - client()->setAddress(m_myKeys.back().address()); + ethereum()->setAddress(m_myKeys.back().address()); m_peers = s.value("peers").toByteArray(); ui->upnp->setChecked(s.value("upnp", true).toBool()); ui->forceAddress->setText(s.value("forceAddress", "").toString()); ui->usePast->setChecked(s.value("usePast", true).toBool()); + ui->localNetworking->setChecked(s.value("localNetworking", true).toBool()); ui->forceMining->setChecked(s.value("forceMining", false).toBool()); on_forceMining_triggered(); ui->paranoia->setChecked(s.value("paranoia", false).toBool()); @@ -665,17 +636,17 @@ void Main::on_nameReg_textChanged() void Main::on_preview_triggered() { - client()->setDefault(ui->preview->isChecked() ? 0 : -1); + ethereum()->setDefault(ui->preview->isChecked() ? 0 : -1); refreshAll(); } void Main::refreshMining() { - dev::eth::MineProgress p = client()->miningProgress(); - ui->mineStatus->setText(client()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); + dev::eth::MineProgress p = ethereum()->miningProgress(); + ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); if (!ui->miningView->isVisible()) return; - list l = client()->miningHistory(); + list l = ethereum()->miningHistory(); static unsigned lh = 0; if (p.hashes < lh) ui->miningView->resetStats(); @@ -694,12 +665,12 @@ void Main::refreshBalances() ui->ourAccounts->clear(); u256 totalBalance = 0; map> altCoins; - Address coinsAddr = right160(client()->stateAt(c_config, 1)); - for (unsigned i = 0; i < client()->stateAt(coinsAddr, 0); ++i) + Address coinsAddr = right160(ethereum()->stateAt(c_config, 1)); + for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) { - auto n = client()->stateAt(coinsAddr, i + 1); - auto addr = right160(client()->stateAt(coinsAddr, n)); - auto denom = client()->stateAt(coinsAddr, sha3(h256(n).asBytes())); + auto n = ethereum()->stateAt(coinsAddr, i + 1); + auto addr = right160(ethereum()->stateAt(coinsAddr, n)); + auto denom = ethereum()->stateAt(coinsAddr, sha3(h256(n).asBytes())); if (denom == 0) denom = 1; // cdebug << n << addr << denom << sha3(h256(n).asBytes()); @@ -707,13 +678,13 @@ void Main::refreshBalances() } for (auto i: m_myKeys) { - u256 b = client()->balanceAt(i.address()); - (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)client()->countAt(i.address())), ui->ourAccounts)) + u256 b = ethereum()->balanceAt(i.address()); + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)ethereum()->countAt(i.address())), ui->ourAccounts)) ->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size)); totalBalance += b; for (auto& c: altCoins) - get<1>(c.second) += (u256)client()->stateAt(c.first, (u160)i.address()); + get<1>(c.second) += (u256)ethereum()->stateAt(c.first, (u160)i.address()); } QString b; @@ -729,7 +700,7 @@ void Main::refreshBalances() void Main::refreshNetwork() { - auto ps = client()->peers(); + auto ps = web3()->peers(); ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); ui->peers->clear(); @@ -751,7 +722,7 @@ void Main::refreshPending() { cwatch << "refreshPending()"; ui->transactionQueue->clear(); - for (Transaction const& t: client()->pending()) + for (Transaction const& t: ethereum()->pending()) { QString s = t.receiveAddress ? QString("%2 %5> %3: %1 [%4]") @@ -759,7 +730,7 @@ void Main::refreshPending() .arg(render(t.safeSender())) .arg(render(t.receiveAddress)) .arg((unsigned)t.nonce) - .arg(client()->codeAt(t.receiveAddress).size() ? '*' : '-') : + .arg(ethereum()->codeAt(t.receiveAddress).size() ? '*' : '-') : QString("%2 +> %3: %1 [%4]") .arg(formatBalance(t.value).c_str()) .arg(render(t.safeSender())) @@ -775,16 +746,16 @@ void Main::refreshAccounts() ui->accounts->clear(); ui->contracts->clear(); for (auto n = 0; n < 2; ++n) - for (auto i: client()->addresses()) + for (auto i: ethereum()->addresses()) { auto r = render(i); if (r.contains('(') == !n) { if (n == 0 || ui->showAllAccounts->isChecked()) - (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(client()->balanceAt(i)).c_str()).arg(r).arg((unsigned)client()->countAt(i)), ui->accounts)) + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(ethereum()->balanceAt(i)).c_str()).arg(r).arg((unsigned)ethereum()->countAt(i)), ui->accounts)) ->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size)); - if (client()->codeAt(i).size()) - (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(client()->balanceAt(i)).c_str()).arg(r).arg((unsigned)client()->countAt(i)), ui->contracts)) + if (ethereum()->codeAt(i).size()) + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(ethereum()->balanceAt(i)).c_str()).arg(r).arg((unsigned)ethereum()->countAt(i)), ui->contracts)) ->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size)); } } @@ -794,7 +765,7 @@ void Main::refreshDestination() { cwatch << "refreshDestination()"; QString s; - for (auto i: client()->addresses()) + for (auto i: ethereum()->addresses()) if ((s = pretty(i)).size()) // A namereg address if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1) @@ -807,8 +778,8 @@ void Main::refreshDestination() void Main::refreshBlockCount() { cwatch << "refreshBlockCount()"; - auto d = client()->blockChain().details(); - auto diff = BlockInfo(client()->blockChain().block()).difficulty; + auto d = ethereum()->blockChain().details(); + auto diff = BlockInfo(ethereum()->blockChain().block()).difficulty; ui->blockCount->setText(QString("%6 #%1 @%3 T%2 N%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(dev::eth::c_protocolVersion).arg(dev::eth::c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet")); } @@ -839,7 +810,7 @@ static bool transactionMatch(string const& _f, Transaction const& _t) void Main::on_turboMining_triggered() { - client()->setTurboMining(ui->turboMining->isChecked()); + ethereum()->setTurboMining(ui->turboMining->isChecked()); } void Main::refreshBlockChain() @@ -850,7 +821,7 @@ void Main::refreshBlockChain() ui->blocks->clear(); string filter = ui->blockChainFilter->text().toLower().toStdString(); - auto const& bc = client()->blockChain(); + auto const& bc = ethereum()->blockChain(); unsigned i = (ui->showAll->isChecked() || !filter.empty()) ? (unsigned)-1 : 10; for (auto h = bc.currentHash(); h != bc.genesisHash() && bc.details(h) && i; h = bc.details(h).parent, --i) { @@ -877,7 +848,7 @@ void Main::refreshBlockChain() .arg(render(t.safeSender())) .arg(render(t.receiveAddress)) .arg((unsigned)t.nonce) - .arg(client()->codeAt(t.receiveAddress).size() ? '*' : '-') : + .arg(ethereum()->codeAt(t.receiveAddress).size() ? '*' : '-') : QString(" %2 +> %3: %1 [%4]") .arg(formatBalance(t.value).c_str()) .arg(render(t.safeSender())) @@ -948,7 +919,7 @@ void Main::timerEvent(QTimerEvent*) m_ethereum->poll(); for (auto const& i: m_handlers) - if (client()->checkWatch(i.first)) + if (ethereum()->checkWatch(i.first)) i.second(); } @@ -1019,9 +990,9 @@ void Main::on_transactionQueue_currentItemChanged() stringstream s; int i = ui->transactionQueue->currentRow(); - if (i >= 0 && i < (int)client()->pending().size()) + if (i >= 0 && i < (int)ethereum()->pending().size()) { - Transaction tx(client()->pending()[i]); + Transaction tx(ethereum()->pending()[i]); auto ss = tx.safeSender(); h256 th = sha3(rlpList(ss, tx.nonce)); s << "

" << th << "

"; @@ -1049,7 +1020,7 @@ void Main::on_transactionQueue_currentItemChanged() // s << "Pre: " << fs.rootHash() << "
"; // s << "Post: " << ts.rootHash() << ""; - s << renderDiff(client()->diff(i, 0)); + s << renderDiff(ethereum()->diff(i, 0)); } ui->pendingInfo->setHtml(QString::fromStdString(s.str())); @@ -1076,7 +1047,7 @@ void Main::on_inject_triggered() { QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex"); bytes b = fromHex(s.toStdString()); - client()->inject(&b); + ethereum()->inject(&b); } void Main::on_blocks_currentItemChanged() @@ -1090,8 +1061,8 @@ void Main::on_blocks_currentItemChanged() auto hba = item->data(Qt::UserRole).toByteArray(); assert(hba.size() == 32); auto h = h256((byte const*)hba.data(), h256::ConstructFromPointer); - auto details = client()->blockChain().details(h); - auto blockData = client()->blockChain().block(h); + auto details = ethereum()->blockChain().details(h); + auto blockData = ethereum()->blockChain().block(h); auto block = RLP(blockData); BlockInfo info(blockData); @@ -1115,7 +1086,7 @@ void Main::on_blocks_currentItemChanged() s << "
Bloom: " << details.bloom << ""; s << "
Transactions: " << block[1].itemCount() << " @" << info.transactionsRoot << ""; s << "
Uncles: " << block[2].itemCount() << " @" << info.sha3Uncles << ""; - s << "
Pre: " << BlockInfo(client()->blockChain().block(info.parentHash)).stateRoot << ""; + s << "
Pre: " << BlockInfo(ethereum()->blockChain().block(info.parentHash)).stateRoot << ""; for (auto const& i: block[1]) s << "
" << sha3(i[0].data()).abridged() << ": " << i[1].toHash() << " [" << i[2].toInt() << " used]"; s << "
Post: " << info.stateRoot << ""; @@ -1151,7 +1122,7 @@ void Main::on_blocks_currentItemChanged() if (tx.data.size()) s << dev::memDump(tx.data, 16, true); } - s << renderDiff(client()->diff(txi, h)); + s << renderDiff(ethereum()->diff(txi, h)); ui->debugCurrent->setEnabled(true); ui->debugDumpState->setEnabled(true); ui->debugDumpStatePre->setEnabled(true); @@ -1173,7 +1144,7 @@ void Main::on_debugCurrent_triggered() if (!item->data(Qt::UserRole + 1).isNull()) { unsigned txi = item->data(Qt::UserRole + 1).toInt(); - m_executiveState = client()->state(txi + 1, h); + m_executiveState = ethereum()->state(txi + 1, h); m_currentExecution = unique_ptr(new Executive(m_executiveState)); Transaction t = m_executiveState.pending()[txi]; m_executiveState = m_executiveState.fromPending(txi); @@ -1199,7 +1170,7 @@ void Main::on_debugDumpState_triggered(int _add) if (f.is_open()) { unsigned txi = item->data(Qt::UserRole + 1).toInt(); - f << client()->state(txi + _add, h) << endl; + f << ethereum()->state(txi + _add, h) << endl; } } } @@ -1265,10 +1236,10 @@ void Main::on_contracts_currentItemChanged() stringstream s; try { - auto storage = client()->storageAt(address); + auto storage = ethereum()->storageAt(address); for (auto const& i: storage) s << "@" << showbase << hex << prettyU256(i.first).toStdString() << "    " << showbase << hex << prettyU256(i.second).toStdString() << "
"; - s << "

Body Code

" << disassemble(client()->codeAt(address)); + s << "

Body Code

" << disassemble(ethereum()->codeAt(address)); ui->contractInfo->appendHtml(QString::fromStdString(s.str())); } catch (dev::eth::InvalidTrie) @@ -1281,7 +1252,7 @@ void Main::on_contracts_currentItemChanged() void Main::on_idealPeers_valueChanged() { - client()->setIdealPeerCount(ui->idealPeers->value()); + m_webThree->setIdealPeerCount(ui->idealPeers->value()); } void Main::on_ourAccounts_doubleClicked() @@ -1427,7 +1398,7 @@ void Main::on_data_textChanged() s = s.mid(1); } ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true))); - if (client()->codeAt(fromString(ui->destination->currentText()), 0).size()) + if (ethereum()->codeAt(fromString(ui->destination->currentText()), 0).size()) { ui->gas->setMinimum((qint64)Client::txGas(m_data.size(), 1)); if (!ui->gas->isEnabled()) @@ -1450,9 +1421,9 @@ void Main::on_killBlockchain_triggered() writeSettings(); ui->mine->setChecked(false); ui->net->setChecked(false); - m_client.reset(); - m_client.reset(new Client("AlethZero", Address(), string(), true)); - m_ethereum->setClient(client()); + web3()->stopNetwork(); + ethereum()->killChain(); + m_ethereum->setClient(ethereum()); readSettings(true); installWatches(); refreshAll(); @@ -1495,7 +1466,7 @@ void Main::updateFee() bool ok = false; for (auto i: m_myKeys) - if (client()->balanceAt(i.address()) >= totalReq) + if (ethereum()->balanceAt(i.address()) >= totalReq) { ok = true; break; @@ -1514,15 +1485,20 @@ void Main::on_net_triggered() if (ui->clientName->text().size()) n += "/" + ui->clientName->text().toStdString(); n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM); - client()->setClientVersion(n); + web3()->setClientVersion(n); if (ui->net->isChecked()) { - client()->startNetwork(ui->port->value(), string(), 0, NodeMode::Full, ui->idealPeers->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), m_privateChain.size() ? sha3(m_privateChain.toStdString()) : 0); + // TODO: alter network stuff? + //ui->port->value(), string(), 0, NodeMode::Full, ui->idealPeers->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), m_privateChain.size() ? sha3(m_privateChain.toStdString()) : 0 + web3()->setIdealPeerCount(ui->idealPeers->value()); + web3()->setNetworkPreferences(netPrefs()); + ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : 0); + web3()->startNetwork(); if (m_peers.size() && ui->usePast->isChecked()) - client()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); + web3()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); } else - client()->stopNetwork(); + web3()->stopNetwork(); } void Main::on_connect_triggered() @@ -1538,7 +1514,7 @@ void Main::on_connect_triggered() { string host = s.section(":", 0, 0).toStdString(); unsigned short port = s.section(":", 1).toInt(); - client()->connect(host, port); + web3()->connect(host, port); } } @@ -1552,25 +1528,25 @@ void Main::on_mine_triggered() { if (ui->mine->isChecked()) { - client()->setAddress(m_myKeys.last().address()); - client()->startMining(); + ethereum()->setAddress(m_myKeys.last().address()); + ethereum()->startMining(); } else - client()->stopMining(); + ethereum()->stopMining(); } void Main::on_send_clicked() { u256 totalReq = value() + fee(); for (auto i: m_myKeys) - if (client()->balanceAt(i.address(), 0) >= totalReq) + if (ethereum()->balanceAt(i.address(), 0) >= totalReq) { debugFinished(); Secret s = i.secret(); if (isCreation()) - client()->transact(s, value(), m_data, ui->gas->value(), gasPrice()); + ethereum()->transact(s, value(), m_data, ui->gas->value(), gasPrice()); else - client()->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); + ethereum()->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); return; } statusBar()->showMessage("Couldn't make transaction: no single account contains at least the required amount."); @@ -1583,10 +1559,10 @@ void Main::on_debug_clicked() { u256 totalReq = value() + fee(); for (auto i: m_myKeys) - if (client()->balanceAt(i.address()) >= totalReq) + if (ethereum()->balanceAt(i.address()) >= totalReq) { Secret s = i.secret(); - m_executiveState = client()->postState(); + m_executiveState = ethereum()->postState(); m_currentExecution = unique_ptr(new Executive(m_executiveState)); Transaction t; t.nonce = m_executiveState.transactionsFrom(dev::toAddress(s)); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index a3bfa718d..ce1a9b670 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -31,6 +31,7 @@ #include #include #include +#include namespace Ui { class Main; @@ -69,7 +70,9 @@ public: explicit Main(QWidget *parent = 0); ~Main(); - dev::eth::Client* client() const { return m_client.get(); } + dev::WebThreeDirect* web3() const { return m_webThree.get(); } + dev::eth::Client* ethereum() const { return m_webThree->ethereum(); } + dev::shh::WhisperHost* whisper() const { return m_webThree->whisper(); } QList const& owned() const { return m_myKeys; } @@ -146,6 +149,8 @@ signals: void poll(); private: + dev::p2p::NetworkPreferences netPrefs() const; + QString pretty(dev::Address _a) const; QString prettyU256(dev::u256 _n) const; @@ -200,7 +205,7 @@ private: std::unique_ptr ui; - std::unique_ptr m_client; + std::unique_ptr m_webThree; std::map> m_handlers; unsigned m_nameRegFilter = (unsigned)-1; diff --git a/eth/CMakeLists.txt b/eth/CMakeLists.txt index 93946aa8f..eee1d258b 100644 --- a/eth/CMakeLists.txt +++ b/eth/CMakeLists.txt @@ -4,13 +4,13 @@ aux_source_directory(. SRC_LIST) include_directories(..) link_directories(../libethcore) -link_directories(../libethereum) +link_directories(../libwebthree) set(EXECUTABLE eth) add_executable(${EXECUTABLE} ${SRC_LIST}) -target_link_libraries(${EXECUTABLE} ethereum) +target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} gmp) if(MINIUPNPC_LS) diff --git a/eth/EthStubServer.cpp b/eth/EthStubServer.cpp index c82d61103..e0baaa9c7 100644 --- a/eth/EthStubServer.cpp +++ b/eth/EthStubServer.cpp @@ -25,14 +25,15 @@ #include #include #include +#include #include "CommonJS.h" using namespace std; using namespace dev; using namespace dev::eth; -EthStubServer::EthStubServer(jsonrpc::AbstractServerConnector* _conn, Client& _client): +EthStubServer::EthStubServer(jsonrpc::AbstractServerConnector* _conn, WebThreeDirect& _web3): AbstractEthStubServer(_conn), - m_client(_client) + m_web3(_web3) { } @@ -59,20 +60,25 @@ Json::Value EthStubServer::procedures() return ret; } +dev::eth::Client& EthStubServer::ethereum() const +{ + return *m_web3.ethereum(); +} + std::string EthStubServer::coinbase() { - return toJS(m_client.address()); + return toJS(ethereum().address()); } std::string EthStubServer::balanceAt(std::string const& _a) { - return toJS(m_client.balanceAt(jsToAddress(_a), 0)); + return toJS(ethereum().balanceAt(jsToAddress(_a), 0)); } Json::Value EthStubServer::check(Json::Value const& _as) { // TODO -// if (m_client.changed()) +// if (ethereum().changed()) return _as; /* else { @@ -84,7 +90,7 @@ Json::Value EthStubServer::check(Json::Value const& _as) std::string EthStubServer::create(const std::string& _bCode, const std::string& _sec, const std::string& _xEndowment, const std::string& _xGas, const std::string& _xGasPrice) { - Address ret = m_client.transact(jsToSecret(_sec), jsToU256(_xEndowment), jsToBytes(_bCode), jsToU256(_xGas), jsToU256(_xGasPrice)); + Address ret = ethereum().transact(jsToSecret(_sec), jsToU256(_xEndowment), jsToBytes(_bCode), jsToU256(_xGas), jsToU256(_xGasPrice)); return toJS(ret); } @@ -100,17 +106,17 @@ std::string EthStubServer::gasPrice() bool EthStubServer::isContractAt(const std::string& _a) { - return m_client.codeAt(jsToAddress(_a), 0).size(); + return ethereum().codeAt(jsToAddress(_a), 0).size(); } bool EthStubServer::isListening() { - return m_client.haveNetwork(); + return m_web3.haveNetwork(); } bool EthStubServer::isMining() { - return m_client.isMining(); + return ethereum().isMining(); } std::string EthStubServer::key() @@ -130,12 +136,12 @@ Json::Value EthStubServer::keys() int EthStubServer::peerCount() { - return m_client.peerCount(); + return m_web3.peerCount(); } std::string EthStubServer::storageAt(const std::string& _a, const std::string& x) { - return toJS(m_client.stateAt(jsToAddress(_a), jsToU256(x), 0)); + return toJS(ethereum().stateAt(jsToAddress(_a), jsToU256(x), 0)); } std::string EthStubServer::stateAt(const std::string& _a, const std::string& x, const std::string& s) @@ -145,13 +151,13 @@ std::string EthStubServer::stateAt(const std::string& _a, const std::string& x, Json::Value EthStubServer::transact(const std::string& _aDest, const std::string& _bData, const std::string& _sec, const std::string& _xGas, const std::string& _xGasPrice, const std::string& _xValue) { - m_client.transact(jsToSecret(_sec), jsToU256(_xValue), jsToAddress(_aDest), jsToBytes(_bData), jsToU256(_xGas), jsToU256(_xGasPrice)); + ethereum().transact(jsToSecret(_sec), jsToU256(_xValue), jsToAddress(_aDest), jsToBytes(_bData), jsToU256(_xGas), jsToU256(_xGasPrice)); return Json::Value(); } std::string EthStubServer::txCountAt(const std::string& _a) { - return toJS(m_client.countAt(jsToAddress(_a), 0)); + return toJS(ethereum().countAt(jsToAddress(_a), 0)); } std::string EthStubServer::secretToAddress(const std::string& _a) @@ -172,7 +178,7 @@ Json::Value EthStubServer::block(const std::string& _hash) Json::Value EthStubServer::blockJson(const std::string& _hash) { Json::Value res; - auto const& bc = m_client.blockChain(); + auto const& bc = ethereum().blockChain(); auto b = _hash.length() ? bc.block(h256(_hash)) : bc.block(); diff --git a/eth/EthStubServer.h b/eth/EthStubServer.h index 1034dbc5e..469abed07 100644 --- a/eth/EthStubServer.h +++ b/eth/EthStubServer.h @@ -29,12 +29,12 @@ #include "abstractethstubserver.h" #pragma GCC diagnostic pop -namespace dev { namespace eth { class Client; } class KeyPair; } +namespace dev { class WebThreeDirect; namespace eth { class Client; } class KeyPair; } class EthStubServer: public AbstractEthStubServer { public: - EthStubServer(jsonrpc::AbstractServerConnector* _conn, dev::eth::Client& _client); + EthStubServer(jsonrpc::AbstractServerConnector* _conn, dev::WebThreeDirect& _web3); virtual Json::Value procedures(); virtual std::string balanceAt(std::string const& _a); @@ -58,7 +58,8 @@ public: virtual Json::Value block(const std::string&); void setKeys(std::vector _keys) { m_keys = _keys; } private: - dev::eth::Client& m_client; + dev::eth::Client& ethereum() const; + dev::WebThreeDirect& m_web3; std::vector m_keys; Json::Value jsontypeToValue(int); Json::Value blockJson(const std::string&); diff --git a/eth/main.cpp b/eth/main.cpp index bd71f6677..3ea02f744 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #if ETH_READLINE #include #include @@ -43,6 +44,7 @@ #include "BuildInfo.h" using namespace std; using namespace dev; +using namespace dev::p2p; using namespace dev::eth; using namespace boost::algorithm; using dev::eth::Instruction; @@ -110,7 +112,8 @@ void help() << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl << " -m,--mining Enable mining, optionally for a specified number of blocks (Default: off)" << endl << " -n,--upnp Use upnp for NAT (default: on)." << endl - << " -o,--mode Start a full node or a peer node (Default: full)." << endl + << " -L,--local-networking Use peers whose addresses are local." << endl + << " -o,--mode Start a full node or a peer node (Default: full)." << endl << " -p,--port Connect to remote port (default: 30303)." << endl << " -r,--remote Connect to remote host (default: none)." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl @@ -186,6 +189,7 @@ int main(int argc, char** argv) #endif string publicIP; bool upnp = true; + bool useLocal = false; bool forceMining = false; string clientName; @@ -231,10 +235,12 @@ int main(int argc, char** argv) upnp = false; else { - cerr << "Invalid UPnP option: " << m << endl; + cerr << "Invalid -n/--upnp option: " << m << endl; return -1; } } + else if (arg == "-L" || arg == "--local-networking") + useLocal = true; else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) clientName = argv[++i]; else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) @@ -254,7 +260,7 @@ int main(int argc, char** argv) mining = i; else { - cerr << "Unknown mining option: " << m << endl; + cerr << "Unknown -m/--mining option: " << m << endl; return -1; } } @@ -296,22 +302,25 @@ int main(int argc, char** argv) if (!clientName.empty()) clientName += "/"; - Client c("Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), coinbase, dbPath); - - c.setForceMining(true); - cout << credits(); + NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); + dev::WebThreeDirect web3("Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), dbPath, false, mode == NodeMode::Full ? set{"eth", "shh"} : set{}, netPrefs); + web3.setIdealPeerCount(peers); + eth::Client& c = *web3.ethereum(); + c.setForceMining(forceMining); + c.setAddress(coinbase); cout << "Address: " << endl << toHex(us.address().asArray()) << endl; - c.startNetwork(listenPort, remoteHost, remotePort, mode, peers, publicIP, upnp); + web3.startNetwork(); + web3.connect(remoteHost, remotePort); #if ETH_JSONRPC auto_ptr jsonrpcServer; if (jsonrpc > -1) { - jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), c)); + jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), web3)); jsonrpcServer->setKeys({us}); jsonrpcServer->StartListening(); } @@ -349,20 +358,20 @@ int main(int argc, char** argv) iss >> cmd; if (cmd == "netstart") { - unsigned port; - iss >> port; - c.startNetwork((short)port); + iss >> netPrefs.listenPort; + web3.setNetworkPreferences(netPrefs); + web3.startNetwork(); } else if (cmd == "connect") { string addr; unsigned port; iss >> addr >> port; - c.connect(addr, (short)port); + web3.connect(addr, (short)port); } else if (cmd == "netstop") { - c.stopNetwork(); + web3.stopNetwork(); } else if (cmd == "minestart") { @@ -395,7 +404,7 @@ int main(int argc, char** argv) { if (jsonrpc < 0) jsonrpc = 8080; - jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), c)); + jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), web3)); jsonrpcServer->setKeys({us}); jsonrpcServer->StartListening(); } @@ -422,7 +431,7 @@ int main(int argc, char** argv) } else if (cmd == "peers") { - for (auto it: c.peers()) + for (auto it: web3.peers()) cout << it.host << ":" << it.port << ", " << it.clientVersion << ", " << std::chrono::duration_cast(it.lastPing).count() << "ms" << endl; @@ -742,7 +751,7 @@ int main(int argc, char** argv) c.startMining(); while (true) { - if (c.blockChain().details().number - n == mining) + if (c.isMining() && c.blockChain().details().number - n == mining) c.stopMining(); this_thread::sleep_for(chrono::milliseconds(100)); } diff --git a/exp/main.cpp b/exp/main.cpp index c49b13e44..77d4aa00f 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -322,20 +322,21 @@ int main(int argc, char** argv) remoteHost = argv[i]; } - Host ph("Test", listenPort, "", false, true); + Host ph("Test", NetworkPreferences(listenPort, "", false, true)); ph.registerCapability(new WhisperHost()); auto wh = ph.cap(); + ph.start(); + if (!remoteHost.empty()) ph.connect(remoteHost, remotePort); /// Only interested in the packet if the lowest bit is 1 auto w = wh->installWatch(MessageFilter(std::vector >({{fromHex("0000000000000000000000000000000000000000000000000000000000000001"), fromHex("0000000000000000000000000000000000000000000000000000000000000001")}}))); + for (int i = 0; ; ++i) { - this_thread::sleep_for(chrono::milliseconds(1000)); - ph.process(); wh->sendRaw(h256(u256(i * i)).asBytes(), h256(u256(i)).asBytes(), 1000); for (auto i: wh->checkWatch(w)) cnote << "New message:" << (u256)h256(wh->message(i).payload); diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 325ba274e..ad779e35d 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.6.8b"; +char const* Version = "0.6.8c"; } diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp new file mode 100644 index 000000000..fe0a4fe92 --- /dev/null +++ b/libdevcore/Worker.cpp @@ -0,0 +1,63 @@ +/* + 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 . +*/ +/** @file Worker.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Worker.h" + +#include +#include +#include "Log.h" +using namespace std; +using namespace dev; + +void Worker::startWorking() +{ + cdebug << "startWorking for thread" << m_name; + Guard l(x_work); + if (m_work) + return; + cdebug << "Spawning" << m_name; + m_stop = false; + m_work.reset(new thread([&]() + { + setThreadName(m_name.c_str()); + while (!m_stop) + { + this_thread::sleep_for(chrono::milliseconds(1)); + doWork(); + } + cdebug << "Finishing up worker thread"; + doneWorking(); + })); +} + +void Worker::stopWorking() +{ + cdebug << "stopWorking for thread" << m_name; + Guard l(x_work); + if (!m_work) + return; + cdebug << "Stopping" << m_name; + m_stop = true; + m_work->join(); + m_work.reset(); + cdebug << "Stopped" << m_name; +} + diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h new file mode 100644 index 000000000..8f0baaf60 --- /dev/null +++ b/libdevcore/Worker.h @@ -0,0 +1,59 @@ +/* + 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 . +*/ +/** @file Worker.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include "Guards.h" + +namespace dev +{ + +class Worker +{ +protected: + Worker(std::string const& _name = "anon"): m_name(_name) {} + + /// Move-constructor. + Worker(Worker&& _m) { std::swap(m_name, _m.m_name); } + + /// Move-assignment. + Worker& operator=(Worker&& _m) { std::swap(m_name, _m.m_name); return *this; } + + virtual ~Worker() { stopWorking(); } + + void setName(std::string _n) { if (!isWorking()) m_name = _n; } + + void startWorking(); + void stopWorking(); + bool isWorking() const { Guard l(x_work); return !!m_work; } + virtual void doWork() = 0; + virtual void doneWorking() {} + +private: + mutable Mutex x_work; ///< Lock for the network existance. + std::unique_ptr m_work; ///< The network thread. + bool m_stop = false; + std::string m_name; +}; + +} diff --git a/libdevcrypto/FileSystem.cpp b/libdevcrypto/FileSystem.cpp index 1fb38b57f..e161c3234 100644 --- a/libdevcrypto/FileSystem.cpp +++ b/libdevcrypto/FileSystem.cpp @@ -31,9 +31,8 @@ #include using namespace std; using namespace dev; -using namespace dev::eth; -std::string dev::eth::getDataDir() +std::string dev::getDataDir() { #ifdef _WIN32 char path[1024] = ""; diff --git a/libdevcrypto/FileSystem.h b/libdevcrypto/FileSystem.h index 9c597c421..605545b0d 100644 --- a/libdevcrypto/FileSystem.h +++ b/libdevcrypto/FileSystem.h @@ -27,11 +27,8 @@ namespace dev { -namespace eth -{ /// @returns the path for user data. std::string getDataDir(); } -} diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index 8186acaee..283f11b88 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -56,7 +56,7 @@ extern u256 c_genesisDifficulty; struct BlockInfo { public: - h256 hash; ///< SHA3 hash of the entire block! Not serialised (the only member not contained in a block header). + h256 hash; ///< SHA3 hash of the block header! Not serialised (the only member not contained in a block header). h256 parentHash; h256 sha3Uncles; Address coinbaseAddress; diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index f96fb5480..eed8b4bb9 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -34,7 +34,7 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 32; +const unsigned c_protocolVersion = 33; const unsigned c_databaseVersion = 1; static const vector> g_units = diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 716add2ae..181f4fd64 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -108,6 +108,20 @@ bytes BlockChain::createGenesisBlock() } BlockChain::BlockChain(std::string _path, bool _killExisting) +{ + // Initialise with the genesis as the last block on the longest chain. + m_genesisHash = BlockChain::genesis().hash; + m_genesisBlock = BlockChain::createGenesisBlock(); + + open(_path, _killExisting); +} + +BlockChain::~BlockChain() +{ + close(); +} + +void BlockChain::open(std::string _path, bool _killExisting) { if (_path.empty()) _path = Defaults::get()->m_dbPath; @@ -127,10 +141,6 @@ BlockChain::BlockChain(std::string _path, bool _killExisting) if (!m_extrasDB) throw DatabaseAlreadyOpen(); - // Initialise with the genesis as the last block on the longest chain. - m_genesisHash = BlockChain::genesis().hash; - m_genesisBlock = BlockChain::createGenesisBlock(); - if (!details(m_genesisHash)) { // Insert details of genesis block. @@ -150,11 +160,16 @@ BlockChain::BlockChain(std::string _path, bool _killExisting) cnote << "Opened blockchain DB. Latest: " << currentHash(); } -BlockChain::~BlockChain() +void BlockChain::close() { cnote << "Closing blockchain DB"; delete m_extrasDB; delete m_db; + m_lastBlockHash = m_genesisHash; + m_details.clear(); + m_blooms.clear(); + m_traces.clear(); + m_cache.clear(); } template @@ -245,21 +260,23 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) auto newHash = BlockInfo::headerHash(_block); // Check block doesn't already exist first! - if (details(newHash)) + if (isKnown(newHash)) { clog(BlockChainNote) << newHash << ": Not new."; throw AlreadyHaveBlock(); } // Work out its number as the parent's number + 1 - auto pd = details(bi.parentHash); - if (!pd) + if (!isKnown(bi.parentHash)) { clog(BlockChainNote) << newHash << ": Unknown parent " << bi.parentHash; // 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. throw UnknownParent(); } + auto pd = details(bi.parentHash); + assert(pd); + // Check it's not crazy if (bi.timestamp > (u256)time(0)) { @@ -432,6 +449,20 @@ h256Set BlockChain::allUnclesFrom(h256 _parent) const return ret; } +bool BlockChain::isKnown(h256 _hash) const +{ + if (_hash == m_genesisHash) + return true; + { + ReadGuard l(x_cache); + if (m_cache.count(_hash)) + return true; + } + string d; + m_db->Get(m_readOptions, ldb::Slice((char const*)&_hash, 32), &d); + return d.size(); +} + bytes BlockChain::block(h256 _hash) const { if (_hash == m_genesisHash) diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index c0b3988a9..cf33a5104 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -70,6 +70,8 @@ public: BlockChain(std::string _path, bool _killExisting = false); ~BlockChain(); + void reopen(std::string _path, bool _killExisting = false) { close(); open(_path, _killExisting); } + /// (Potentially) renders invalid existing bytesConstRef returned by lastBlock. /// To be called from main loop every 100ms or so. void process(); @@ -85,6 +87,9 @@ public: /// @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); + /// Returns true if the given block is known (though not necessarily a part of the canon chain). + bool isKnown(h256 _hash) const; + /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. BlockDetails details(h256 _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); } BlockDetails details() const { return details(currentHash()); } @@ -143,6 +148,9 @@ public: h256s treeRoute(h256 _from, h256 _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; private: + void open(std::string _path, bool _killExisting = false); + void close(); + template T queryExtras(h256 _h, std::map& _m, boost::shared_mutex& _x, T const& _n) const { { diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index 21aeb5a72..a845965d5 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -74,21 +74,24 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) // Check it's not in the future if (bi.timestamp > (u256)time(0)) + { m_future.insert(make_pair((unsigned)bi.timestamp, _block.toBytes())); + cblockq << "OK - queued for future."; + } else { // We now know it. - if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.details(bi.parentHash)) + if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(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. -// cnote << "OK - queued for future."; + cblockq << "OK - queued as unknown parent:" << bi.parentHash.abridged(); m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknownSet.insert(h); } else { // If valid, append to blocks. -// cnote << "OK - ready for chain insertion."; + cblockq << "OK - ready for chain insertion."; m_ready.push_back(_block.toBytes()); m_readySet.insert(h); diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index 5a4a86ad6..20bc4ce59 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -34,7 +34,7 @@ namespace eth class BlockChain; -struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 7; }; +struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; }; #define cblockq dev::LogOutputStream() /** @@ -64,6 +64,9 @@ public: /// Get information on the items queued. std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_unknown.size()); } + /// Clear everything. + void clear() { WriteGuard l(m_lock); m_readySet.clear(); m_drainingSet.clear(); m_ready.clear(); m_unknownSet.clear(); m_unknown.clear(); m_future.clear(); } + private: void noteReadyWithoutWriteGuard(h256 _b); void notePresentWithoutWriteGuard(bytesConstRef _block); diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 3bc159f17..d64c9fafb 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -53,94 +53,102 @@ void VersionChecker::setOk() } } -Client::Client(std::string const& _clientVersion, Address _us, std::string const& _dbPath, bool _forceClean): - m_clientVersion(_clientVersion), - m_vc(_dbPath), - m_bc(_dbPath, !m_vc.ok() || _forceClean), - m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)), - m_preMine(_us, m_stateDB), - m_postMine(_us, m_stateDB) -{ - setMiningThreads(); - if (_dbPath.size()) - Defaults::setDBPath(_dbPath); - m_vc.setOk(); - work(); -} - Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean, u256 _networkId): + Worker("eth"), m_vc(_dbPath), m_bc(_dbPath, !m_vc.ok() || _forceClean), m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)), m_preMine(Address(), m_stateDB), m_postMine(Address(), m_stateDB) { - m_extHost = _extNet->registerCapability(new EthereumHost(m_bc, _networkId)); + m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); -// setMiningThreads(); + setMiningThreads(); if (_dbPath.size()) Defaults::setDBPath(_dbPath); m_vc.setOk(); - work(); + doWork(); + + startWorking(); } Client::~Client() { - 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(); - m_work.reset(nullptr); - } - stopNetwork(); + stopWorking(); } -void Client::ensureWorking() +void Client::setNetworkId(u256 _n) { - static const char* c_threadName = "eth"; + if (auto h = m_host.lock()) + h->setNetworkId(_n); +} - 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. - WriteGuard l(x_stateDB); - m_preMine.sync(m_bc); - m_postMine = m_preMine; - })); +void Client::doneWorking() +{ + // Synchronise the state according to the head of the block chain. + // TODO: currently it contains keys for *all* blocks. Make it remove old ones. + WriteGuard l(x_stateDB); + m_preMine.sync(m_bc); + m_postMine = m_preMine; } void Client::flushTransactions() { - work(); + doWork(); +} + +void Client::killChain() +{ + bool wasMining = isMining(); + if (wasMining) + stopMining(); + stopWorking(); + + m_tq.clear(); + m_bq.clear(); + m_miners.clear(); + m_preMine = State(); + m_postMine = State(); + + { + WriteGuard l(x_stateDB); + m_stateDB = OverlayDB(); + m_stateDB = State::openDB(Defaults::dbPath(), true); + } + m_bc.reopen(Defaults::dbPath(), true); + + m_preMine = State(Address(), m_stateDB); + m_postMine = State(Address(), m_stateDB); + + if (auto h = m_host.lock()) + h->reset(); + + doWork(); + + startWorking(); + if (wasMining) + startMining(); } void Client::clearPending() { - WriteGuard l(x_stateDB); - if (!m_postMine.pending().size()) - return; h256Set changeds; - for (unsigned i = 0; i < m_postMine.pending().size(); ++i) - appendFromNewPending(m_postMine.bloom(i), changeds); - changeds.insert(PendingChangedFilter); - m_postMine = m_preMine; + { + WriteGuard l(x_stateDB); + if (!m_postMine.pending().size()) + return; + for (unsigned i = 0; i < m_postMine.pending().size(); ++i) + appendFromNewPending(m_postMine.bloom(i), changeds); + changeds.insert(PendingChangedFilter); + m_postMine = m_preMine; + } - if (!m_extHost.lock()) { ReadGuard l(x_miners); for (auto& m: m_miners) m.noteStateChange(); } + noteChanged(changeds); } @@ -211,111 +219,10 @@ void Client::appendFromNewBlock(h256 _block, h256Set& o_changed) const o_changed.insert(i.first); } -void Client::startNetwork(unsigned short _listenPort, std::string const& _seedHost, unsigned short _port, NodeMode _mode, unsigned _peers, string const& _publicIP, bool _upnp, u256 _networkId) -{ - static const char* c_threadName = "net"; - - { - UpgradableGuard l(x_net); - if (m_net.get()) - return; - { - UpgradeGuard ul(l); - - if (!m_workNet) - m_workNet.reset(new thread([&]() - { - setThreadName(c_threadName); - m_workNetState.store(Active, std::memory_order_release); - while (m_workNetState.load(std::memory_order_acquire) != Deleting) - workNet(); - m_workNetState.store(Deleted, std::memory_order_release); - })); - - if (!m_extHost.lock()) - { - try - { - m_net.reset(new Host(m_clientVersion, _listenPort, _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 Host(m_clientVersion, 0, _publicIP, _upnp)); - } - if (_mode == NodeMode::Full) - m_net->registerCapability(new EthereumHost(m_bc, _networkId)); - } - } - m_net->setIdealPeerCount(_peers); - } - - if (!m_extHost.lock()) - if (_seedHost.size()) - connect(_seedHost, _port); - - ensureWorking(); -} - -void Client::stopNetwork() -{ - UpgradableGuard l(x_net); - - if (m_workNet) - { - if (m_workNetState.load(std::memory_order_acquire) == Active) - m_workNetState.store(Deleting, std::memory_order_release); - while (m_workNetState.load(std::memory_order_acquire) != Deleted) - this_thread::sleep_for(chrono::milliseconds(10)); - m_workNet->join(); - } - if (m_net) - { - UpgradeGuard ul(l); - m_net.reset(nullptr); - m_workNet.reset(nullptr); - } -} - -std::vector Client::peers() -{ - ReadGuard l(x_net); - return m_net ? m_net->peers() : std::vector(); -} - -size_t Client::peerCount() const -{ - ReadGuard l(x_net); - return m_net ? m_net->peerCount() : 0; -} - -void Client::setIdealPeerCount(size_t _n) const -{ - ReadGuard l(x_net); - if (m_net) - return m_net->setIdealPeerCount(_n); -} - -bytes Client::savePeers() -{ - ReadGuard l(x_net); - if (m_net) - return m_net->savePeers(); - return bytes(); -} - -void Client::restorePeers(bytesConstRef _saved) -{ - ReadGuard l(x_net); - if (m_net) - return m_net->restorePeers(_saved); -} - void Client::setForceMining(bool _enable) { m_forceMining = _enable; - if (!m_extHost.lock()) + if (!m_host.lock()) { ReadGuard l(x_miners); for (auto& m: m_miners) @@ -323,19 +230,8 @@ void Client::setForceMining(bool _enable) } } -void Client::connect(std::string const& _seedHost, unsigned short _port) -{ - ReadGuard l(x_net); - if (!m_net.get()) - return; - m_net->connect(_seedHost, _port); -} - void Client::setMiningThreads(unsigned _threads) { - if (m_extHost.lock()) - return; - stopMining(); auto t = _threads ? _threads : thread::hardware_concurrency(); @@ -350,8 +246,6 @@ void Client::setMiningThreads(unsigned _threads) MineProgress Client::miningProgress() const { MineProgress ret; - if (m_extHost.lock()) - return ret; ReadGuard l(x_miners); for (auto& m: m_miners) ret.combine(m.miningProgress()); @@ -361,8 +255,6 @@ MineProgress Client::miningProgress() const std::list Client::miningHistory() { std::list ret; - if (m_extHost.lock()) - return ret; ReadGuard l(x_miners); if (m_miners.empty()) @@ -404,7 +296,7 @@ void Client::setupState(State& _s) void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { - ensureWorking(); + startWorking(); Transaction t; // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); @@ -446,7 +338,7 @@ bytes Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _dat Address Client::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) { - ensureWorking(); + startWorking(); Transaction t; { @@ -466,47 +358,18 @@ Address Client::transact(Secret _secret, u256 _endowment, bytes const& _init, u2 void Client::inject(bytesConstRef _rlp) { - ensureWorking(); + startWorking(); m_tq.attemptImport(_rlp); } -void Client::workNet() -{ - // 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. - { - ReadGuard l(x_net); - if (m_net) - { - cwork << "NETWORK"; - m_net->process(); // must be in guard for now since it uses the blockchain. - - // returns h256Set as block hashes, once for each block that has come in/gone out. - if (m_net->cap()) - { - cwork << "NET <==> TQ ; CHAIN ==> NET ==> BQ"; - m_net->cap()->sync(m_tq, m_bq); - cwork << "TQ:" << m_tq.items() << "; BQ:" << m_bq.items(); - } - } - } - - if (auto h = m_extHost.lock()) - h->sync(m_tq, m_bq); - - this_thread::sleep_for(chrono::milliseconds(1)); -} - -void Client::work() +void Client::doWork() { // TODO: Use condition variable rather than polling. cworkin << "WORK"; h256Set changeds; - if (!m_extHost.lock()) { ReadGuard l(x_miners); for (auto& m: m_miners) @@ -557,7 +420,8 @@ void Client::work() cwork << "preSTATE <== CHAIN"; if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) { - cnote << "New block on chain: Restarting mining operation."; + if (isMining()) + cnote << "New block on chain: Restarting mining operation."; m_postMine = m_preMine; rsm = true; changeds.insert(PendingChangedFilter); @@ -573,11 +437,12 @@ void Client::work() appendFromNewPending(i, changeds); changeds.insert(PendingChangedFilter); - cnote << "Additional transaction ready: Restarting mining operation."; + if (isMining()) + cnote << "Additional transaction ready: Restarting mining operation."; rsm = true; } } - if (!m_extHost.lock() && rsm) + if (rsm) { ReadGuard l(x_miners); for (auto& m: m_miners) diff --git a/libethereum/Client.h b/libethereum/Client.h index 8bdb48051..6415defb0 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -108,14 +109,11 @@ struct WorkChannel: public LogChannel { static const char* name() { return "-W-" /** * @brief Main API hub for interfacing with Ethereum. */ -class Client: public MinerHost, public Interface +class Client: public MinerHost, public Interface, Worker { friend class Miner; public: - /// Original Constructor. - explicit Client(std::string const& _clientVersion, Address _us = Address(), std::string const& _dbPath = std::string(), bool _forceClean = false); - /// New-style Constructor. explicit Client(p2p::Host* _host, std::string const& _dbPath = std::string(), bool _forceClean = false, u256 _networkId = 0); @@ -165,6 +163,9 @@ public: // [EXTRA API]: + /// @returns the length of the chain. + virtual unsigned number() const { return m_bc.number(); } + /// Get a map containing each of the pending transactions. /// @TODO: Remove in favour of transactions(). Transactions pending() const { return m_postMine.pending(); } @@ -192,32 +193,6 @@ public: /// Get the object representing the current canonical blockchain. BlockChain const& blockChain() const { return m_bc; } - // Misc stuff: - - void setClientVersion(std::string const& _name) { m_clientVersion = _name; } - - // Network stuff: - - /// Get information on the current peer set. - std::vector peers(); - /// Same as peers().size(), but more efficient. - size_t peerCount() const; - /// Same as peers().size(), but more efficient. - void setIdealPeerCount(size_t _n) const; - - /// Start the network subsystem. - void startNetwork(unsigned short _listenPort = 30303, std::string const& _remoteHost = std::string(), unsigned short _remotePort = 30303, NodeMode _mode = NodeMode::Full, unsigned _peers = 5, std::string const& _publicIP = std::string(), bool _upnp = true, u256 _networkId = 0); - /// Connect to a particular peer. - void connect(std::string const& _seedHost, unsigned short _port = 30303); - /// Stop the network subsystem. - void stopNetwork(); - /// Is the network subsystem up? - bool haveNetwork() { ReadGuard l(x_net); return !!m_net; } - /// Save peers - bytes savePeers(); - /// Restore peers - void restorePeers(bytesConstRef _saved); - // Mining stuff: /// Check block validity prior to mining. @@ -243,7 +218,7 @@ public: unsigned miningThreads() const { ReadGuard l(x_miners); return m_miners.size(); } /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread - void startMining() { ensureWorking(); ReadGuard l(x_miners); for (auto& m: m_miners) m.start(); } + void startMining() { startWorking(); ReadGuard l(x_miners); for (auto& m: m_miners) m.start(); } /// Stop mining. /// NOT thread-safe void stopMining() { ReadGuard l(x_miners); for (auto& m: m_miners) m.stop(); } @@ -254,19 +229,20 @@ public: /// Get and clear the mining history. std::list miningHistory(); + // Debug stuff: + + /// Sets the network id. + void setNetworkId(u256 _n); /// Clears pending transactions. Just for debug use. void clearPending(); + /// Kills the blockchain. Just for debug use. + void killChain(); private: - /// Ensure the worker thread is running. Needed for blockchain maintenance & mining. - void ensureWorking(); - /// Do some work. Handles blockchain maintenance and mining. - /// @param _justQueue If true will only processing the transaction queues. - void work(); + virtual void doWork(); - /// Do some work on the network. - void workNet(); + virtual void doneWorking(); /// Overrides for being a mining host. virtual void setupState(State& _s); @@ -291,26 +267,17 @@ private: State asOf(int _h) const; State asOf(unsigned _h) const; - 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 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). - // TODO: remove in favour of copying m_stateDB as required and thread-safing/copying State. Have a good think about what state objects there should be. Probably want 4 (pre, post, mining, user-visible). + mutable boost::shared_mutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine. OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. State m_preMine; ///< The present state of the client. State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). - std::unique_ptr m_workNet; ///< The network thread. - std::atomic m_workNetState; - mutable boost::shared_mutex x_net; ///< Lock for the network existance. - std::unique_ptr m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. - - std::weak_ptr m_extHost; ///< Our Ethereum Host. Don't do anything if we can't lock. - - std::unique_ptr m_work; ///< The work thread. - std::atomic m_workState; + std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. std::vector m_miners; mutable boost::shared_mutex x_miners; diff --git a/libethereum/CommonNet.h b/libethereum/CommonNet.h index 3bb45b55d..6774c02ae 100644 --- a/libethereum/CommonNet.h +++ b/libethereum/CommonNet.h @@ -33,11 +33,17 @@ namespace dev namespace eth { +#if ETH_DEBUG +static const unsigned c_maxHashes = 4; ///< Maximum number of hashes BlockHashes will ever send. +static const unsigned c_maxHashesAsk = 4; ///< Maximum number of hashes GetBlockHashes will ever ask for. +static const unsigned c_maxBlocks = 2; ///< Maximum number of blocks Blocks will ever send. +static const unsigned c_maxBlocksAsk = 2; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). +#else static const unsigned c_maxHashes = 256; ///< Maximum number of hashes BlockHashes will ever send. static const unsigned c_maxHashesAsk = 256; ///< Maximum number of hashes GetBlockHashes will ever ask for. static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send. static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). - +#endif class OverlayDB; class BlockChain; class TransactionQueue; @@ -55,5 +61,13 @@ enum EthereumPacket BlocksPacket, }; +enum class Grabbing +{ + State, + Hashes, + Chain, + Nothing +}; + } } diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 1e623b605..2add39482 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -39,11 +39,15 @@ using namespace dev; using namespace dev::eth; using namespace p2p; -EthereumHost::EthereumHost(BlockChain const& _ch, u256 _networkId): +EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId): HostCapability(), - m_chain (&_ch), + Worker ("ethsync"), + m_chain (_ch), + m_tq (_tq), + m_bq (_bq), m_networkId (_networkId) { + m_latestBlockSent = _ch.currentHash(); } EthereumHost::~EthereumHost() @@ -68,20 +72,20 @@ h256Set EthereumHost::neededBlocks(h256Set const& _exclude) m_blocksNeeded.erase(it); } } - if (!ret.size()) + if (ret.empty()) for (auto i = m_blocksOnWay.begin(); ret.size() < c_maxBlocksAsk && i != m_blocksOnWay.end() && !_exclude.count(*i); ++i) ret.insert(*i); - - clog(NetMessageSummary) << "Asking for" << ret.size() << "blocks that we don't yet have." << m_blocksNeeded.size() << "blocks still needed," << m_blocksOnWay.size() << "blocks on way."; + if (ret.size()) + clog(NetMessageSummary) << "Asking for" << ret.size() << "blocks that we don't yet have." << m_blocksNeeded.size() << "blocks still needed," << m_blocksOnWay.size() << "total blocks on way."; return ret; } bool EthereumHost::ensureInitialised(TransactionQueue& _tq) { - if (m_latestBlockSent == h256()) + if (!m_latestBlockSent) { // First time - just initialise. - m_latestBlockSent = m_chain->currentHash(); + m_latestBlockSent = m_chain.currentHash(); clog(NetNote) << "Initialising: latest=" << m_latestBlockSent.abridged(); for (auto const& i: _tq.transactions()) @@ -91,9 +95,85 @@ bool EthereumHost::ensureInitialised(TransactionQueue& _tq) return false; } +void EthereumHost::noteHavePeerState(EthereumPeer* _who) +{ + clog(NetAllDetail) << "Have peer state."; + + // if already downloading hash-chain, ignore. + if (m_grabbing != Grabbing::Nothing) + { + clog(NetAllDetail) << "Already downloading chain. Just set to help out."; + _who->restartGettingChain(); + return; + } + + // otherwise check to see if we should be downloading... + _who->tryGrabbingHashChain(); +} + +void EthereumHost::updateGrabbing(Grabbing _g) +{ + m_grabbing = _g; + if (_g == Grabbing::Nothing) + readyForSync(); + else if (_g == Grabbing::Chain) + for (auto j: peers()) + j->cap()->restartGettingChain(); +} + +void EthereumHost::noteHaveChain(EthereumPeer* _from) +{ + auto td = _from->m_totalDifficulty; + + if (_from->m_neededBlocks.empty()) + { + _from->m_grabbing = Grabbing::Nothing; + updateGrabbing(Grabbing::Nothing); + return; + } + + clog(NetNote) << "Hash-chain COMPLETE:" << _from->m_totalDifficulty << "vs" << m_chain.details().totalDifficulty << "," << m_totalDifficultyOfNeeded << ";" << _from->m_neededBlocks.size() << " blocks, ends" << _from->m_neededBlocks.back().abridged(); + + if ((m_totalDifficultyOfNeeded && (td < m_totalDifficultyOfNeeded || (td == m_totalDifficultyOfNeeded && m_latestBlockSent == _from->m_latestHash))) || td < m_chain.details().totalDifficulty || (td == m_chain.details().totalDifficulty && m_chain.currentHash() == _from->m_latestHash)) + { + clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; + _from->m_grabbing = Grabbing::Nothing; + updateGrabbing(Grabbing::Nothing); + return; + } + + clog(NetNote) << "Difficulty of hashchain HIGHER. Replacing fetch queue [latest now" << _from->m_latestHash.abridged() << ", was" << m_latestBlockSent.abridged() << "]"; + + // Looks like it's the best yet for total difficulty. Set to download. + { + Guard l(x_blocksNeeded); + m_blocksNeeded = _from->m_neededBlocks; + m_blocksOnWay.clear(); + m_totalDifficultyOfNeeded = td; + m_latestBlockSent = _from->m_latestHash; + } + + _from->m_grabbing = Grabbing::Chain; + updateGrabbing(Grabbing::Chain); +} + +void EthereumHost::readyForSync() +{ + // start grabbing next hash chain if there is one. + for (auto j: peers()) + { + j->cap()->tryGrabbingHashChain(); + if (j->cap()->m_grabbing == Grabbing::Hashes) + { + m_grabbing = Grabbing::Hashes; + return; + } + } + clog(NetNote) << "No more peers to sync with."; +} + void EthereumHost::noteDoneBlocks() { - clog(NetNote) << "Peer given up on blocks fetch."; if (m_blocksOnWay.empty()) { // Done our chain-get. @@ -101,7 +181,7 @@ void EthereumHost::noteDoneBlocks() clog(NetNote) << "No more blocks coming. Missing" << m_blocksNeeded.size() << "blocks."; else clog(NetNote) << "No more blocks to get."; - m_latestBlockSent = m_chain->currentHash(); + updateGrabbing(Grabbing::Nothing); } } @@ -109,7 +189,7 @@ bool EthereumHost::noteBlock(h256 _hash, bytesConstRef _data) { Guard l(x_blocksNeeded); m_blocksOnWay.erase(_hash); - if (!m_chain->details(_hash)) + if (!m_chain.details(_hash)) { lock_guard l(m_incomingLock); m_incomingBlocks.push_back(_data.toBytes()); @@ -118,19 +198,20 @@ bool EthereumHost::noteBlock(h256 _hash, bytesConstRef _data) return false; } -bool EthereumHost::sync(TransactionQueue& _tq, BlockQueue& _bq) +void EthereumHost::doWork() { - bool netChange = ensureInitialised(_tq); - auto h = m_chain->currentHash(); - maintainTransactions(_tq, h); - maintainBlocks(_bq, h); - return netChange; + bool netChange = ensureInitialised(m_tq); + auto h = m_chain.currentHash(); + maintainTransactions(m_tq, h); + maintainBlocks(m_bq, h); +// return netChange; + // TODO: Figure out what to do with netChange. + (void)netChange; } void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash) { bool resendAll = (_currentHash != m_latestBlockSent); - { lock_guard l(m_incomingLock); for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it) @@ -172,34 +253,43 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash } } +void EthereumHost::reset() +{ + m_grabbing = Grabbing::Nothing; + + m_incomingTransactions.clear(); + m_incomingBlocks.clear(); + + m_totalDifficultyOfNeeded = 0; + m_blocksNeeded.clear(); + m_blocksOnWay.clear(); + + m_latestBlockSent = h256(); + m_transactionsSent.clear(); +} + void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) { // Import new blocks { lock_guard l(m_incomingLock); for (auto it = m_incomingBlocks.rbegin(); it != m_incomingBlocks.rend(); ++it) - if (_bq.import(&*it, *m_chain)) + if (_bq.import(&*it, m_chain)) {} else{} // TODO: don't forward it. m_incomingBlocks.clear(); } - // If we've finished our initial sync... - { - Guard l(x_blocksNeeded); - if (m_blocksOnWay.size()) - return; - } - // ...send any new blocks. - if (m_latestBlockSent != _currentHash) + // If we've finished our initial sync send any new blocks. + if (m_grabbing == Grabbing::Nothing && m_chain.details(m_latestBlockSent) && m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty) { RLPStream ts; EthereumPeer::prep(ts); bytes bs; unsigned c = 0; - for (auto h: m_chain->treeRoute(m_latestBlockSent, _currentHash, nullptr, false, true)) + for (auto h: m_chain.treeRoute(m_latestBlockSent, _currentHash, nullptr, false, true)) { - bs += m_chain->block(h); + bs += m_chain.block(h); ++c; } clog(NetMessageSummary) << "Sending" << c << "new blocks (current is" << _currentHash << ", was" << m_latestBlockSent << ")"; @@ -211,6 +301,7 @@ void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) for (auto j: peers()) { auto p = j->cap(); + Guard l(p->x_knownBlocks); if (!p->m_knownBlocks.count(_currentHash)) p->send(&b); p->m_knownBlocks.clear(); @@ -218,32 +309,3 @@ void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) m_latestBlockSent = _currentHash; } } - -void EthereumHost::noteHaveChain(EthereumPeer* _from) -{ - auto td = _from->m_totalDifficulty; - - if (_from->m_neededBlocks.empty()) - return; - - clog(NetNote) << "Hash-chain COMPLETE:" << _from->m_totalDifficulty << "vs" << m_chain->details().totalDifficulty << "," << m_totalDifficultyOfNeeded << ";" << _from->m_neededBlocks.size() << " blocks, ends" << _from->m_neededBlocks.back().abridged(); - - if ((m_totalDifficultyOfNeeded && td < m_totalDifficultyOfNeeded) || td < m_chain->details().totalDifficulty) - { - clog(NetNote) << "Difficulty of hashchain LOWER. Ignoring."; - return; - } - - clog(NetNote) << "Difficulty of hashchain HIGHER. Replacing fetch queue."; - - // Looks like it's the best yet for total difficulty. Set to download. - { - Guard l(x_blocksNeeded); - m_blocksNeeded = _from->m_neededBlocks; - m_blocksOnWay.clear(); - m_totalDifficultyOfNeeded = td; - } - - for (auto j: peers()) - j->cap()->restartGettingChain(); -} diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 6cd9332a2..c875d74fe 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "CommonNet.h" @@ -45,28 +46,149 @@ namespace eth class TransactionQueue; class BlockQueue; +using UnsignedRange = std::pair; +using UnsignedRanges = std::vector; + +class RangeMask +{ +public: + RangeMask() {} + RangeMask(unsigned _begin, unsigned _end): m_ranges({{_begin, _end}}) {} + + RangeMask& operator+=(RangeMask const& _m) + { + for (auto const& i: _m.m_ranges) + operator+=(i); + return *this; + } + RangeMask& operator+=(UnsignedRange const& _m) + { + for (auto i = _m.first; i < _m.second;) + { + // for each number, we find the element equal or next lower. this must contain the value. + auto it = m_ranges.lower_bound(i); + auto uit = m_ranges.upper_bound(i + 1); + if (it == m_ranges.end() || it->second < i) + // lower range is too low to merge. + // if the next higher range is too high. + if (uit == m_ranges.end() || uit->first > _m.second) + { + // just create a new range + m_ranges[i] = _m.second; + break; + } + else + { + if (uit->first == i) + // move i to end of range + i = uit->second; + else + { + // merge with the next higher range + // move i to end of range + i = m_ranges[i] = uit->second; + i = uit->second; + m_ranges.erase(uit); + } + } + else if (it->second == i) + { + // if the next higher range is too high. + if (uit == m_ranges.end() || uit->first > _m.second) + { + // merge with the next lower range + m_ranges[it->first] = _m.second; + break; + } + else + { + // merge with both next lower & next higher. + i = m_ranges[it->first] = uit->second; + m_ranges.erase(uit); + } + } + else + i = it->second; + } + return *this; + } + + RangeMask& operator+=(unsigned _i) + { + return operator+=(UnsignedRange(_i, _i + 1)); + } + + bool contains(unsigned _i) const + { + auto it = m_ranges.lower_bound(_i); + return it != m_ranges.end() && it->first <= _i && it->second > _i; + } + +private: + std::map m_ranges; +}; + +#if 0 +class DownloadSub +{ + friend class DownloadMan; + +public: + h256s nextFetch(); + void noteBlock(h256 _hash, bytesConstRef _data); + +private: + void resetFetch(); // Called by DownloadMan when we need to reset the download. + + DownloadMan* m_man; + + Mutex m_fetch; + h256s m_fetching; + h256s m_activeGet; + bool m_killFetch; + RangeMask m_attempted; +}; + +class DownloadMan +{ + friend class DownloadSub; + +public: + void resetToChain(h256s const& _chain); + +private: + void cancelFetch(DownloadSub* ); + void noteBlock(h256 _hash, bytesConstRef _data); + + h256s m_chain; + RangeMask m_complete; + std::map m_fetching; +}; +#endif + /** * @brief The EthereumHost class * @warning None of this is thread-safe. You have been warned. */ -class EthereumHost: public p2p::HostCapability +class EthereumHost: public p2p::HostCapability, Worker { friend class EthereumPeer; public: /// Start server, but don't listen. - EthereumHost(BlockChain const& _ch, u256 _networkId); + EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId); /// Will block on network process events. virtual ~EthereumHost(); unsigned protocolVersion() const { return c_protocolVersion; } u256 networkId() const { return m_networkId; } + void setNetworkId(u256 _n) { m_networkId = _n; } - /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. - bool sync(TransactionQueue&, BlockQueue& _bc); + void reset(); private: + void noteHavePeerState(EthereumPeer* _who); /// Session wants to pass us a block that we might not have. /// @returns true if we didn't have it. bool noteBlock(h256 _hash, bytesConstRef _data); @@ -75,9 +197,12 @@ private: /// Called when the peer can no longer provide us with any needed blocks. void noteDoneBlocks(); + /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. + void doWork(); + /// Called by peer to add incoming transactions. void addIncomingTransaction(bytes const& _bytes) { std::lock_guard l(m_incomingLock); m_incomingTransactions.push_back(_bytes); } - + void maintainTransactions(TransactionQueue& _tq, h256 _currentBlock); void maintainBlocks(BlockQueue& _bq, h256 _currentBlock); @@ -87,15 +212,25 @@ private: h256Set neededBlocks(h256Set const& _exclude); /// Check to see if the network peer-state initialisation has happened. - virtual bool isInitialised() const { return m_latestBlockSent; } + 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(TransactionQueue& _tq); - BlockChain const* m_chain = nullptr; + virtual void onStarting() { startWorking(); } + virtual void onStopping() { stopWorking(); } + + void readyForSync(); + void updateGrabbing(Grabbing _g); + + BlockChain const& m_chain; + 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). u256 m_networkId; + Grabbing m_grabbing = Grabbing::Nothing; + mutable std::recursive_mutex m_incomingLock; std::vector m_incomingTransactions; std::vector m_incomingBlocks; @@ -106,7 +241,7 @@ private: h256Set m_blocksOnWay; h256 m_latestBlockSent; - std::set m_transactionsSent; + h256Set m_transactionsSent; }; } diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index c961f6733..7ccf6939b 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -57,9 +57,9 @@ void EthereumPeer::sendStatus() s.appendList(6) << StatusPacket << host()->protocolVersion() << host()->networkId() - << host()->m_chain->details().totalDifficulty - << host()->m_chain->currentHash() - << host()->m_chain->genesisHash(); + << host()->m_chain.details().totalDifficulty + << host()->m_chain.currentHash() + << host()->m_chain.genesisHash(); sealAndSend(s); } @@ -73,16 +73,35 @@ void EthereumPeer::startInitialSync() sealAndSend(s); } - h256 c = host()->m_chain->currentHash(); - unsigned n = host()->m_chain->number(); - u256 td = max(host()->m_chain->details().totalDifficulty, host()->m_totalDifficultyOfNeeded); + host()->noteHavePeerState(this); +} + +void EthereumPeer::tryGrabbingHashChain() +{ + // if already done this, then ignore. + if (m_grabbing != Grabbing::State) + { + clogS(NetAllDetail) << "Already synced with this peer."; + return; + } - clogS(NetAllDetail) << "Initial sync. Latest:" << c.abridged() << ", number:" << n << ", TD: max(" << host()->m_chain->details().totalDifficulty << "," << host()->m_totalDifficultyOfNeeded << ") versus " << m_totalDifficulty; + h256 c = host()->m_chain.currentHash(); + unsigned n = host()->m_chain.number(); + u256 td = max(host()->m_chain.details().totalDifficulty, host()->m_totalDifficultyOfNeeded); + + clogS(NetAllDetail) << "Attempt chain-grab? Latest:" << c.abridged() << ", number:" << n << ", TD: max(" << host()->m_chain.details().totalDifficulty << "," << host()->m_totalDifficultyOfNeeded << ") versus " << m_totalDifficulty; if (td > m_totalDifficulty) + { + clogS(NetAllDetail) << "No. Our chain is better."; + m_grabbing = Grabbing::Nothing; return; // All good - we have the better chain. + } // Our chain isn't better - grab theirs. { + clogS(NetAllDetail) << "Yes. Their chain is better."; + + m_grabbing = Grabbing::Hashes; RLPStream s; prep(s).appendList(3); s << GetBlockHashesPacket << m_latestHash << c_maxHashesAsk; @@ -94,6 +113,16 @@ void EthereumPeer::startInitialSync() void EthereumPeer::giveUpOnFetch() { clogS(NetNote) << "GIVE UP FETCH; can't get" << toString(m_askedBlocks); + + // a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry. + if (m_grabbing == Grabbing::Chain) + { + m_grabbing = Grabbing::Nothing; + host()->updateGrabbing(Grabbing::Nothing); + } + + // NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary. + if (m_askedBlocks.size()) { Guard l (host()->x_blocksNeeded); @@ -122,7 +151,7 @@ bool EthereumPeer::interpret(RLP const& _r) clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged(); - if (genesisHash != host()->m_chain->genesisHash()) + if (genesisHash != host()->m_chain.genesisHash()) disable("Invalid genesis hash"); if (m_protocolVersion != host()->protocolVersion()) disable("Invalid protocol version."); @@ -138,8 +167,10 @@ bool EthereumPeer::interpret(RLP const& _r) break; } case TransactionsPacket: + { clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; addRating(_r.itemCount() - 1); + lock_guard l(host()->m_incomingLock); for (unsigned i = 1; i < _r.itemCount(); ++i) { host()->addIncomingTransaction(_r[i].data().toBytes()); @@ -148,25 +179,32 @@ bool EthereumPeer::interpret(RLP const& _r) m_knownTransactions.insert(sha3(_r[i].data())); } break; + } case GetBlockHashesPacket: { h256 later = _r[1].toHash(); unsigned limit = _r[2].toInt(); clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")"; - unsigned c = min(host()->m_chain->number(later), limit); + unsigned c = min(max(1, host()->m_chain.number(later)) - 1, limit); RLPStream s; prep(s).appendList(1 + c).append(BlockHashesPacket); - h256 p = host()->m_chain->details(later).parent; - for (unsigned i = 0; i < c; ++i, p = host()->m_chain->details(p).parent) + h256 p = host()->m_chain.details(later).parent; + for (unsigned i = 0; i < c; ++i, p = host()->m_chain.details(p).parent) s << p; sealAndSend(s); break; } case BlockHashesPacket: { - clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)"; + clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreHashes"); + + if (m_grabbing != Grabbing::Hashes) + { + cwarn << "Peer giving us hashes when we didn't ask for them."; + break; + } if (_r.itemCount() == 1) { host()->noteHaveChain(this); @@ -175,7 +213,7 @@ bool EthereumPeer::interpret(RLP const& _r) for (unsigned i = 1; i < _r.itemCount(); ++i) { auto h = _r[i].toHash(); - if (host()->m_chain->details(h)) + if (host()->m_chain.details(h)) { host()->noteHaveChain(this); return true; @@ -193,12 +231,12 @@ bool EthereumPeer::interpret(RLP const& _r) case GetBlocksPacket: { clogS(NetMessageSummary) << "GetBlocks (" << dec << (_r.itemCount() - 1) << "entries)"; - // TODO: return the requested blocks. + // return the requested blocks. bytes rlp; unsigned n = 0; for (unsigned i = 1; i < _r.itemCount() && i <= c_maxBlocks; ++i) { - auto b = host()->m_chain->block(_r[i].toHash()); + auto b = host()->m_chain.block(_r[i].toHash()); if (b.size()) { rlp += b; @@ -211,7 +249,7 @@ bool EthereumPeer::interpret(RLP const& _r) } case BlocksPacket: { - clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)"; + clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); if (_r.itemCount() == 1 && !m_askedBlocksChanged) { @@ -227,6 +265,7 @@ bool EthereumPeer::interpret(RLP const& _r) if (host()->noteBlock(h, _r[i].data())) used++; m_askedBlocks.erase(h); + Guard l(x_knownBlocks); m_knownBlocks.insert(h); } addRating(used); @@ -238,7 +277,8 @@ bool EthereumPeer::interpret(RLP const& _r) { auto h = BlockInfo::headerHash(_r[i].data()); BlockInfo bi(_r[i].data()); - if (!host()->m_chain->details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash)) + Guard l(x_knownBlocks); + if (!host()->m_chain.details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash)) { unknownParents++; clogS(NetAllDetail) << "Unknown parent" << bi.parentHash << "of block" << h; @@ -296,7 +336,8 @@ void EthereumPeer::continueGettingChain() } else { - clogS(NetMessageSummary) << "No blocks left to get. Peer doesn't seem to have" << m_failedBlocks.size() << "of our needed blocks."; + if (m_failedBlocks.size()) + clogS(NetMessageSummary) << "No blocks left to get. Peer doesn't seem to have" << m_failedBlocks.size() << "of our needed blocks."; host()->noteDoneBlocks(); } } diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index 92eb475ec..7248fabd0 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include "CommonNet.h" @@ -58,6 +59,8 @@ private: void sendStatus(); void startInitialSync(); + void tryGrabbingHashChain(); + /// Ensure that we are waiting for a bunch of blocks from our peer. void ensureGettingChain(); /// Ensure that we are waiting for a bunch of blocks from our peer. @@ -72,6 +75,8 @@ private: unsigned m_protocolVersion; u256 m_networkId; + Grabbing m_grabbing = Grabbing::State; + h256 m_latestHash; ///< Peer's latest block's hash. u256 m_totalDifficulty; ///< Peer's latest block's total difficulty. h256s m_neededBlocks; ///< The blocks that we should download from this peer. @@ -82,6 +87,7 @@ private: bool m_requireTransactions; + Mutex x_knownBlocks; std::set m_knownBlocks; std::set m_knownTransactions; std::mutex x_knownTransactions; diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index e8c1fd1cb..a6b57a05c 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -187,6 +187,7 @@ bool Executive::go(OnOpFunc const& _onOp) catch (VMException const& _e) { clog(StateChat) << "VM Exception: " << _e.description(); + m_endGas = m_vm->gas(); } catch (Exception const& _e) { diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 3fa93a286..a5df3e5b1 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -95,8 +95,13 @@ public: virtual bool peekWatch(unsigned _watchId) const = 0; virtual bool checkWatch(unsigned _watchId) = 0; + // TODO: Block query API. + // [EXTRA API]: + /// @returns The height of the chain. + virtual unsigned number() const = 0; + /// Get a map containing each of the pending transactions. /// @TODO: Remove in favour of transactions(). virtual Transactions pending() const = 0; diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp index c602cedc1..9b651e2c5 100644 --- a/libethereum/Miner.cpp +++ b/libethereum/Miner.cpp @@ -27,42 +27,12 @@ using namespace dev; using namespace dev::eth; Miner::Miner(MinerHost* _host, unsigned _id): - m_host(_host), - m_id(_id) + Worker("miner-" + toString(_id)), + m_host(_host) { } -void Miner::start() -{ - if (!m_host) - return; - - Guard l(x_work); - if (!m_work) - { - m_stop = false; - m_work.reset(new thread([&]() - { - setThreadName(("miner-" + toString(m_id)).c_str()); - m_miningStatus = Preparing; - while (!m_stop) - work(); - })); - } -} - -void Miner::stop() -{ - Guard l(x_work); - if (m_work) - { - m_stop = true; - m_work->join(); - m_work.reset(nullptr); - } -} - -void Miner::work() +void Miner::doWork() { // Do some mining. if (m_miningStatus != Waiting && m_miningStatus != Mined) diff --git a/libethereum/Miner.h b/libethereum/Miner.h index 8912b0e2a..763249b7d 100644 --- a/libethereum/Miner.h +++ b/libethereum/Miner.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "State.h" @@ -74,38 +75,38 @@ public: * @threadsafe * @todo Signal Miner to restart once with condition variables. */ -class Miner +class Miner: Worker { public: /// Null constructor. - Miner(): m_host(nullptr), m_id(0) {} + Miner(): m_host(nullptr) {} /// Constructor. Miner(MinerHost* _host, unsigned _id = 0); /// Move-constructor. - Miner(Miner&& _m) { std::swap(m_host, _m.m_host); std::swap(m_id, _m.m_id); } + Miner(Miner&& _m): Worker((Worker&&)_m) { std::swap(m_host, _m.m_host); } /// Move-assignment. - Miner& operator=(Miner&& _m) { std::swap(m_host, _m.m_host); std::swap(m_id, _m.m_id); return *this; } + Miner& operator=(Miner&& _m) { Worker::operator=((Worker&&)_m); std::swap(m_host, _m.m_host); return *this; } /// Destructor. Stops miner. ~Miner() { stop(); } /// Setup its basics. - void setup(MinerHost* _host, unsigned _id = 0) { m_host = _host; m_id = _id; } + void setup(MinerHost* _host, unsigned _id = 0) { m_host = _host; setName("miner-" + toString(_id)); } /// Start mining. - void start(); + void start() { startWorking(); } /// Stop mining. - void stop(); + void stop() { stopWorking(); } /// Call to notify Miner of a state change. void noteStateChange() { m_miningStatus = Preparing; } /// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force(). - bool isRunning() { return !!m_work; } + bool isRunning() { return isWorking(); } /// @returns true if mining is complete. bool isComplete() const { return m_miningStatus == Mined; } @@ -121,14 +122,9 @@ public: private: /// Do some work on the mining. - void work(); + virtual void doWork(); MinerHost* m_host = nullptr; ///< Our host. - unsigned m_id = 0; ///< Our identity. - - std::mutex x_work; ///< Mutex protecting the creation of the work thread. - std::unique_ptr m_work; ///< The work thread. - bool m_stop = false; ///< Stop working? enum MiningStatus { Waiting, Preparing, Mining, Mined, Stopping, Stopped }; MiningStatus m_miningStatus = Waiting; ///< TODO: consider mutex/atomic variable. diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 093db299f..a74b6ba18 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -152,10 +152,15 @@ State& State::operator=(State const& _s) m_currentBlock = _s.m_currentBlock; m_ourAddress = _s.m_ourAddress; m_blockReward = _s.m_blockReward; + m_lastTx = _s.m_lastTx; paranoia("after state cloning (assignment op)", true); return *this; } +State::~State() +{ +} + struct CachedAddressState { CachedAddressState(std::string const& _rlp, AddressState const* _s, OverlayDB const* _o): rS(_rlp), r(rS), s(_s), o(_o) {} diff --git a/libethereum/State.h b/libethereum/State.h index 335f27168..fea8e06c7 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -90,6 +90,8 @@ public: /// Copy state object. State& operator=(State const& _s); + ~State(); + /// Set the coinbase address for any transactions we do. /// This causes a complete reset of current block. void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); } diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index 154eed9d2..22a420602 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -52,6 +52,8 @@ public: void setFuture(std::pair const& _t); void noteGood(std::pair const& _t); + void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } + private: mutable boost::shared_mutex m_lock; ///< General lock. std::set m_known; ///< Hashes of transactions in both sets. diff --git a/libevm/VM.h b/libevm/VM.h index a07e225e6..cbecc3ee7 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -41,7 +41,6 @@ class BreakPointHit: public VMException {}; class BadInstruction: public VMException {}; class OutOfGas: public VMException {}; class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; -class OperandOutOfRange: public VMException { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; // Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. // Currently we just pull out the right (low-order in BE) 160-bits. diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index fdf1338b3..15727f9c1 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -17,7 +17,7 @@ /** @file Host.cpp * @authors: * Gav Wood - * Eric Lombrozo + * Eric Lombrozo (Windows version of populateAddresses()) * @date 2014 */ @@ -54,55 +54,83 @@ static const set c_rejectAddresses = { {bi::address_v6::from_string("::")} }; -Host::Host(std::string const& _clientVersion, unsigned short _port, string const& _publicAddress, bool _upnp, bool _localNetworking): +Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool _start): + Worker("p2p"), m_clientVersion(_clientVersion), - m_listenPort(_port), - m_localNetworking(_localNetworking), - m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), _port)), + m_netPrefs(_n), + m_acceptor(m_ioService), m_socket(m_ioService), m_id(h512::random()) { populateAddresses(); - determinePublic(_publicAddress, _upnp); - ensureAccepting(); m_lastPeersRequest = chrono::steady_clock::time_point::min(); clog(NetNote) << "Id:" << m_id.abridged(); + if (_start) + start(); } -Host::Host(std::string const& _clientVersion, string const& _publicAddress, bool _upnp, bool _localNetworking): - m_clientVersion(_clientVersion), - m_listenPort(0), - m_localNetworking(_localNetworking), - m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), - m_socket(m_ioService), - m_id(h512::random()) +Host::~Host() { - m_listenPort = m_acceptor.local_endpoint().port(); - - // populate addresses. - populateAddresses(); - determinePublic(_publicAddress, _upnp); - ensureAccepting(); - m_lastPeersRequest = chrono::steady_clock::time_point::min(); - clog(NetNote) << "Id:" << m_id.abridged(); + stop(); } -Host::Host(std::string const& _clientVersion): - m_clientVersion(_clientVersion), - m_listenPort(0), - m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), - m_socket(m_ioService), - m_id(h512::random()) +void Host::start() { - // populate addresses. - populateAddresses(); + stop(); + for (unsigned i = 0; i < 2; ++i) + { + bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : m_netPrefs.listenPort); + try + { + m_acceptor.open(endpoint.protocol()); + m_acceptor.set_option(ba::socket_base::reuse_address(true)); + m_acceptor.bind(endpoint); + m_acceptor.listen(); + m_listenPort = i ? m_acceptor.local_endpoint().port() : m_netPrefs.listenPort; + break; + } + catch (...) + { + if (i) + { + cwarn << "Couldn't start accepting connections on host. Something very wrong with network?"; + return; + } + m_acceptor.close(); + continue; + } + } + + determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); + ensureAccepting(); m_lastPeersRequest = chrono::steady_clock::time_point::min(); clog(NetNote) << "Id:" << m_id.abridged(); + + for (auto const& h: m_capabilities) + h.second->onStarting(); + + startWorking(); } -Host::~Host() +void Host::stop() { + for (auto const& h: m_capabilities) + h.second->onStopping(); + + stopWorking(); + + if (m_acceptor.is_open()) + { + if (m_accepting) + m_acceptor.cancel(); + m_acceptor.close(); + m_accepting = false; + } + if (m_socket.is_open()) + m_socket.close(); disconnectPeers(); + + m_ioService.reset(); } unsigned Host::protocolVersion() const @@ -284,7 +312,7 @@ std::map Host::potentialPeers() { auto ep = j->endpoint(); // Skip peers with a listen port of zero or are on a private network - bool peerOnNet = (j->m_listenPort != 0 && (!isPrivateAddress(ep.address()) || m_localNetworking)); + bool peerOnNet = (j->m_listenPort != 0 && (!isPrivateAddress(ep.address()) || m_netPrefs.localNetworking)); if (peerOnNet && ep.port() && j->m_id) ret.insert(make_pair(i.first, ep)); } @@ -293,7 +321,7 @@ std::map Host::potentialPeers() void Host::ensureAccepting() { - if (m_accepting == false) + if (!m_accepting) { clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")"; m_accepting = true; @@ -315,7 +343,7 @@ void Host::ensureAccepting() clog(NetWarn) << "ERROR: " << _e.what(); } m_accepting = false; - if (ec.value() != 1) + if (ec.value() < 1) ensureAccepting(); }); } @@ -325,6 +353,7 @@ void Host::connect(std::string const& _addr, unsigned short _port) noexcept { try { + // TODO: actual DNS lookup. connect(bi::tcp::endpoint(bi::address::from_string(_addr), _port)); } catch (exception const& e) @@ -457,11 +486,8 @@ std::vector Host::peers(bool _updatePing) const return ret; } -void Host::process() +void Host::doWork() { - for (auto const& i: m_capabilities) - if (!i.second->isInitialised()) - return; growPeers(); prunePeers(); m_ioService.poll(); diff --git a/libp2p/Host.h b/libp2p/Host.h index 54e8f967e..e8b95e2a8 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -29,6 +29,7 @@ #include #include #include +#include #include "HostCapability.h" namespace ba = boost::asio; namespace bi = boost::asio::ip; @@ -41,22 +42,28 @@ class RLPStream; namespace p2p { +struct NetworkPreferences +{ + NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), publicIP(i), upnp(u), localNetworking(l) {} + + unsigned short listenPort = 30303; + std::string publicIP; + bool upnp = true; + bool localNetworking = false; +}; + /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. */ -class Host +class Host: public Worker { friend class Session; friend class HostCapabilityFace; public: /// Start server, listening for connections on the given port. - Host(std::string const& _clientVersion, unsigned short _port, std::string const& _publicAddress = std::string(), bool _upnp = true, bool _localNetworking = false); - /// Start server, listening for connections on a system-assigned port. - Host(std::string const& _clientVersion, std::string const& _publicAddress = std::string(), bool _upnp = true, bool _localNetworking = false); - /// Start server, but don't listen. - Host(std::string const& _clientVersion); + Host(std::string const& _clientVersion, NetworkPreferences const& _n = NetworkPreferences(), bool _start = false); /// Will block on network process events. virtual ~Host(); @@ -78,11 +85,6 @@ public: void connect(std::string const& _addr, unsigned short _port = 30303) noexcept; void connect(bi::tcp::endpoint const& _ep); - /// 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(); - /// @returns true iff we have the a peer of the given id. bool havePeer(h512 _id) const; @@ -107,11 +109,17 @@ public: /// Deserialise the data and populate the set of known peers. void restorePeers(bytesConstRef _b); + void setNetworkPreferences(NetworkPreferences const& _p) { stop(); m_netPrefs = _p; start(); } + + void start(); + void stop(); + bool isStarted() const { return isWorking(); } + h512 id() const { return m_id; } void registerPeer(std::shared_ptr _s, std::vector const& _caps); -protected: +private: /// Called when the session has provided us with a new peer we can connect to. void noteNewPeers() {} @@ -123,12 +131,19 @@ protected: void growPeers(); void prunePeers(); + /// 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. + virtual void doWork(); + std::map potentialPeers(); std::string m_clientVersion; - unsigned short m_listenPort; - bool m_localNetworking = false; + NetworkPreferences m_netPrefs; + + static const int NetworkStopped = -1; + int m_listenPort = NetworkStopped; ba::io_service m_ioService; bi::tcp::acceptor m_acceptor; diff --git a/libp2p/HostCapability.h b/libp2p/HostCapability.h index 4b6d38f64..1c532788b 100644 --- a/libp2p/HostCapability.h +++ b/libp2p/HostCapability.h @@ -48,7 +48,9 @@ public: protected: virtual std::string name() const = 0; virtual Capability* newPeerCapability(Session* _s) = 0; - virtual bool isInitialised() const { return true; } + + virtual void onStarting() {} + virtual void onStopping() {} void seal(bytes& _b); diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 777717371..f117b426e 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -159,7 +159,7 @@ bool Session::interpret(RLP const& _r) bi::address_v4 peerAddress(_r[i][0].toHash>().asArray()); auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt()); h512 id = _r[i][2].toHash(); - if (isPrivateAddress(peerAddress) && !m_server->m_localNetworking) + if (isPrivateAddress(peerAddress) && !m_server->m_netPrefs.localNetworking) goto CONTINUE; clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")"; diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index 689daf8d7..714081dfe 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -8,41 +8,8 @@ #include #include "QEthereum.h" using namespace std; - -// types -using dev::bytes; -using dev::bytesConstRef; -using dev::h160; -using dev::h256; -using dev::u160; -using dev::u256; -using dev::u256s; -using dev::RLP; -using dev::Address; -using dev::eth::BlockInfo; -using dev::eth::Client; -using dev::eth::Instruction; -using dev::KeyPair; -using dev::eth::NodeMode; -using dev::p2p::PeerInfo; -using dev::Secret; -using dev::eth::Transaction; - -// functions -using dev::toHex; -using dev::fromHex; -using dev::right160; -using dev::simpleDebugOut; -using dev::toLog2; -using dev::toString; -using dev::operator+; -using dev::eth::disassemble; -using dev::eth::units; -using dev::eth::formatBalance; - -// vars -using dev::g_logPost; -using dev::g_logVerbosity; +using namespace dev; +using namespace dev::eth; dev::bytes toBytes(QString const& _s) { @@ -89,7 +56,7 @@ QString unpadded(QString _s) return _s; } -QEthereum::QEthereum(QObject* _p, Client* _c, QList _accounts): +QEthereum::QEthereum(QObject* _p, eth::Interface* _c, QList _accounts): QObject(_p), m_client(_c), m_accounts(_accounts) { // required to prevent crash on osx when performing addto/evaluatejavascript calls @@ -120,7 +87,7 @@ QString QEthereum::secretToAddress(QString _s) const return toQJS(KeyPair(toSecret(_s)).address()); } -Client* QEthereum::client() const +eth::Interface* QEthereum::client() const { return m_client; } @@ -162,7 +129,7 @@ QString QEthereum::coinbase() const QString QEthereum::number() const { - return m_client ? QString::number(client()->blockChain().number() + 1) : ""; + return m_client ? QString::number(client()->number() + 1) : ""; } QString QEthereum::account() const @@ -381,7 +348,7 @@ bool QEthereum::isMining() const bool QEthereum::isListening() const { - return m_client ? client()->haveNetwork() : false; + return /*m_client ? client()->haveNetwork() :*/ false; } void QEthereum::setMining(bool _l) @@ -395,19 +362,19 @@ void QEthereum::setMining(bool _l) } } -void QEthereum::setListening(bool _l) +void QEthereum::setListening(bool) { if (!m_client) return; - if (_l) +/* if (_l) client()->startNetwork(); else - client()->stopNetwork(); + client()->stopNetwork();*/ } unsigned QEthereum::peerCount() const { - return m_client ? (unsigned)client()->peerCount() : 0; + return /*m_client ? (unsigned)client()->peerCount() :*/ 0; } QString QEthereum::doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice) diff --git a/libqethereum/QEthereum.h b/libqethereum/QEthereum.h index cacf1cfc4..50f2d96a5 100644 --- a/libqethereum/QEthereum.h +++ b/libqethereum/QEthereum.h @@ -7,8 +7,7 @@ #include namespace dev { namespace eth { -class Client; -class State; +class Interface; }} class QJSEngine; @@ -99,11 +98,11 @@ class QEthereum: public QObject Q_OBJECT public: - QEthereum(QObject* _p, dev::eth::Client* _c, QList _accounts); + QEthereum(QObject* _p, dev::eth::Interface* _c, QList _accounts); virtual ~QEthereum(); - dev::eth::Client* client() const; - void setClient(dev::eth::Client* _c) { m_client = _c; } + dev::eth::Interface* client() const; + void setClient(dev::eth::Interface* _c) { m_client = _c; } /// Call when the client() is going to be deleted to make this object useless but safe. void clientDieing(); @@ -199,7 +198,7 @@ private: Q_PROPERTY(unsigned peerCount READ peerCount NOTIFY miningChanged) Q_PROPERTY(int defaultBlock READ getDefault WRITE setDefault) - dev::eth::Client* m_client; + dev::eth::Interface* m_client; std::vector m_watches; QList m_accounts; }; diff --git a/libqethereum/QmlEthereum.cpp b/libqethereum/QmlEthereum.cpp index 350427f3e..89578a29a 100644 --- a/libqethereum/QmlEthereum.cpp +++ b/libqethereum/QmlEthereum.cpp @@ -50,7 +50,7 @@ using dev::g_logVerbosity; // Can get rid of this once we've sorted out ITC for signalling & multiplexed querying. dev::eth::Client* g_qmlClient; QObject* g_qmlMain; - +#if 0 QmlAccount::QmlAccount(QObject*) { } @@ -177,6 +177,8 @@ void QmlEthereum::transact(Secret _secret, Address _dest, u256 _amount, u256 _ga client()->transact(_secret, _amount, _dest, bytes(_data.data(), _data.data() + _data.size()), _gas, _gasPrice); } +#endif + // extra bits needed to link on VS #ifdef _MSC_VER diff --git a/libqethereum/QmlEthereum.h b/libqethereum/QmlEthereum.h index dcb636301..d242540a1 100644 --- a/libqethereum/QmlEthereum.h +++ b/libqethereum/QmlEthereum.h @@ -23,8 +23,8 @@ Q_DECLARE_METATYPE(dev::u256) Q_DECLARE_METATYPE(dev::Address) Q_DECLARE_METATYPE(dev::Secret) Q_DECLARE_METATYPE(dev::KeyPair) -Q_DECLARE_METATYPE(QmlAccount*) -Q_DECLARE_METATYPE(QmlEthereum*) +//Q_DECLARE_METATYPE(QmlAccount*) +//Q_DECLARE_METATYPE(QmlEthereum*) class QmlU256Helper: public QObject { @@ -75,7 +75,7 @@ public: Q_INVOKABLE QString stringOf(dev::Address _a) const { return QString::fromStdString(dev::toHex(_a.asArray())); } Q_INVOKABLE QString toAbridged(dev::Address _a) const { return QString::fromStdString(_a.abridged()); } }; - +#if 0 class QmlAccount: public QObject { Q_OBJECT @@ -155,7 +155,7 @@ private: Q_PROPERTY(bool listening READ isListening WRITE setListening) Q_PROPERTY(bool mining READ isMining WRITE setMining) }; - +#endif #if 0 template T to(QVariant const& _s) { if (_s.type() != QVariant::String) return T(); auto s = _s.toString().toLatin1(); assert(s.size() == sizeof(T)); return *(T*)s.data(); } template QVariant toQJS(T const& _s) { QLatin1String ret((char*)&_s, sizeof(T)); assert(QVariant(QString(ret)).toString().toLatin1().size() == sizeof(T)); assert(*(T*)(QVariant(QString(ret)).toString().toLatin1().data()) == _s); return QVariant(QString(ret)); } diff --git a/libwebthree/WebThree.cpp b/libwebthree/WebThree.cpp index 93f81dd0a..55e82a36f 100644 --- a/libwebthree/WebThree.cpp +++ b/libwebthree/WebThree.cpp @@ -35,102 +35,50 @@ using namespace dev::p2p; using namespace dev::eth; using namespace dev::shh; -WebThreeDirect::WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean, std::set const& _interfaces, unsigned short _listenPort, std::string const& _publicIP, bool _upnp, dev::u256 _networkId, bool _localNetworking): +WebThreeDirect::WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean, std::set const& _interfaces, NetworkPreferences const& _n): m_clientVersion(_clientVersion), - m_net(m_clientVersion, _listenPort, _publicIP, _upnp, _localNetworking) + m_net(_clientVersion, _n) { if (_dbPath.size()) Defaults::setDBPath(_dbPath); if (_interfaces.count("eth")) - m_ethereum.reset(new eth::Client(&m_net, _dbPath, _forceClean, _networkId)); + m_ethereum.reset(new eth::Client(&m_net, _dbPath, _forceClean)); // if (_interfaces.count("shh")) // m_whisper = new eth::Whisper(m_net.get()); - - static const char* c_threadName = "net"; - - UpgradableGuard l(x_work); - { - UpgradeGuard ul(l); - - 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) - workNet(); - m_workState.store(Deleted, std::memory_order_release); - })); - - } } WebThreeDirect::~WebThreeDirect() { - UpgradableGuard l(x_work); - - 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(); - } - if (m_work) - { - UpgradeGuard ul(l); - m_work.reset(nullptr); - } } std::vector WebThreeDirect::peers() { - ReadGuard l(x_work); return m_net.peers(); } size_t WebThreeDirect::peerCount() const { - ReadGuard l(x_work); return m_net.peerCount(); } void WebThreeDirect::setIdealPeerCount(size_t _n) { - ReadGuard l(x_work); return m_net.setIdealPeerCount(_n); } bytes WebThreeDirect::savePeers() { - ReadGuard l(x_work); return m_net.savePeers(); } void WebThreeDirect::restorePeers(bytesConstRef _saved) { - ReadGuard l(x_work); return m_net.restorePeers(_saved); } void WebThreeDirect::connect(std::string const& _seedHost, unsigned short _port) { - ReadGuard l(x_work); m_net.connect(_seedHost, _port); } - -void WebThreeDirect::workNet() -{ - // 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. - { - ReadGuard l(x_work); - m_net.process(); // must be in guard for now since it uses the blockchain. - } - this_thread::sleep_for(chrono::milliseconds(1)); -} - diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index cf02c3122..06717ee26 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -66,7 +66,7 @@ class WebThreeDirect public: /// Constructor for private instance. If there is already another process on the machine using @a _dbPath, then this will throw an exception. /// ethereum() may be safely static_cast()ed to a eth::Client*. - WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean = false, std::set const& _interfaces = {"eth", "shh"}, unsigned short _listenPort = 30303, std::string const& _publicIP = std::string(), bool _upnp = true, dev::u256 _networkId = 0, bool _localNetworking = false); + WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean = false, std::set const& _interfaces = {"eth", "shh"}, p2p::NetworkPreferences const& _n = p2p::NetworkPreferences()); /// Destructor. ~WebThreeDirect(); @@ -104,25 +104,23 @@ public: /// Sets the ideal number of peers. void setIdealPeerCount(size_t _n); + bool haveNetwork() const { return m_net.isStarted(); } + + void setNetworkPreferences(p2p::NetworkPreferences const& _n) { auto had = haveNetwork(); if (had) stopNetwork(); m_net.setNetworkPreferences(_n); if (had) startNetwork(); } + /// Start the network subsystem. - void startNetwork() { setIdealPeerCount(5); } + void startNetwork() { m_net.start(); } /// Stop the network subsystem. - void stopNetwork() { setIdealPeerCount(0); } + void stopNetwork() { m_net.stop(); } private: - /// Do some work on the network. - void workNet(); - std::string m_clientVersion; ///< Our end-application client's name/version. std::unique_ptr m_ethereum; ///< Main interface for Ethereum ("eth") protocol. std::unique_ptr m_whisper; ///< Main interface for Whisper ("shh") protocol. p2p::Host m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. - std::unique_ptr m_work; ///< The network thread. - mutable boost::shared_mutex x_work; ///< Lock for the network existance. - std::atomic m_workState; }; @@ -137,7 +135,7 @@ class RPCMaster {}; class EthereumSlave: public eth::Interface { public: - EthereumSlave(RPCSlave* _c) {} + EthereumSlave(RPCSlave*) {} // TODO: implement all of the virtuals with the RLPClient link. }; @@ -145,7 +143,7 @@ public: class EthereumMaster { public: - EthereumMaster(RPCMaster* _m) {} + EthereumMaster(RPCMaster*) {} // TODO: implement the master-end of whatever the RLPClient link will send over. }; @@ -155,7 +153,7 @@ public: class WhisperSlave: public shh::Interface { public: - WhisperSlave(RPCSlave* _c) {} + WhisperSlave(RPCSlave*) {} // TODO: implement all of the virtuals with the RLPClient link. }; @@ -163,7 +161,7 @@ public: class WhisperMaster { public: - WhisperMaster(RPCMaster* _m) {} + WhisperMaster(RPCMaster*) {} // TODO: implement the master-end of whatever the RLPClient link will send over. }; diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 02a883dbc..20c42a9b8 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -42,11 +42,14 @@ void mine(Client& c, int numBlocks) void connectClients(Client& c1, Client& c2) { + // TODO: Move to WebThree. eth::Client no longer handles networking. +#if 0 short c1Port = 20000; short c2Port = 21000; c1.startNetwork(c1Port); c2.startNetwork(c2Port); c2.connect("127.0.0.1", c1Port); +#endif } } diff --git a/test/peer.cpp b/test/peer.cpp index 821aab514..a99ce7201 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -46,7 +46,7 @@ int peerTest(int argc, char** argv) remoteHost = argv[i]; } - Host ph("Test", listenPort); + Host ph("Test", NetworkPreferences(listenPort)); if (!remoteHost.empty()) ph.connect(remoteHost, remotePort); diff --git a/third/CMakeLists.txt b/third/CMakeLists.txt index a68b90813..77b2bb496 100644 --- a/third/CMakeLists.txt +++ b/third/CMakeLists.txt @@ -52,7 +52,7 @@ else () endif () qt5_use_modules(${EXECUTEABLE} Core)# Gui Widgets Network WebKit WebKitWidgets) -target_link_libraries(${EXECUTEABLE} qethereum ethereum evm ethcore secp256k1 gmp ${CRYPTOPP_LS} serpent lll evmface devcore) +target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore secp256k1 gmp ${CRYPTOPP_LS} serpent lll evmface devcore) if (APPLE) # First have qt5 install plugins and frameworks diff --git a/third/MainWin.cpp b/third/MainWin.cpp index 7bbdd721b..5e147bbda 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -38,49 +39,14 @@ #include #include #include +#include #include "BuildInfo.h" #include "MainWin.h" #include "ui_Main.h" using namespace std; - -// types -using dev::bytes; -using dev::bytesConstRef; -using dev::h160; -using dev::h256; -using dev::u160; -using dev::u256; -using dev::Address; -using dev::eth::BlockInfo; -using dev::eth::Client; -using dev::eth::Instruction; -using dev::KeyPair; -using dev::eth::NodeMode; -using dev::eth::BlockChain; -using dev::p2p::PeerInfo; -using dev::RLP; -using dev::Secret; -using dev::eth::Transaction; -using dev::eth::Executive; - -// functions -using dev::toHex; -using dev::fromHex; -using dev::left160; -using dev::right160; -using dev::simpleDebugOut; -using dev::toLog2; -using dev::toString; -using dev::operator<<; -using dev::eth::units; -using dev::eth::compileLLL; -using dev::eth::disassemble; -using dev::eth::formatBalance; -using dev::eth::sha3; - -// vars -using dev::g_logPost; -using dev::g_logVerbosity; +using namespace dev; +using namespace dev::eth; +using namespace dev::p2p; static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr) { @@ -133,12 +99,12 @@ Main::Main(QWidget *parent) : connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); - m_client.reset(new Client("Third")); + m_web3.reset(new WebThreeDirect("Third", getDataDir() + "/Third", false, {"eth", "shh"})); connect(ui->webView, &QWebView::loadStarted, [this]() { // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. - m_ethereum = new QEthereum(this, m_client.get(), owned()); + m_ethereum = new QEthereum(this, ethereum(), owned()); QWebFrame* f = ui->webView->page()->mainFrame(); f->disconnect(SIGNAL(javaScriptWindowObjectCleared())); @@ -181,6 +147,11 @@ Main::~Main() writeSettings(); } +eth::Client* Main::ethereum() const +{ + return m_web3->ethereum(); +} + void Main::onKeysChanged() { installBalancesWatch(); @@ -188,14 +159,14 @@ void Main::onKeysChanged() unsigned Main::installWatch(dev::eth::MessageFilter const& _tf, std::function const& _f) { - auto ret = m_client->installWatch(_tf); + auto ret = ethereum()->installWatch(_tf); m_handlers[ret] = _f; return ret; } unsigned Main::installWatch(dev::h256 _tf, std::function const& _f) { - auto ret = m_client->installWatch(_tf); + auto ret = ethereum()->installWatch(_tf); m_handlers[ret] = _f; return ret; } @@ -209,14 +180,14 @@ void Main::installWatches() void Main::installNameRegWatch() { - m_client->uninstallWatch(m_nameRegFilter); - m_nameRegFilter = installWatch(dev::eth::MessageFilter().altered((u160)m_client->stateAt(c_config, 0)), [=](){ onNameRegChange(); }); + ethereum()->uninstallWatch(m_nameRegFilter); + m_nameRegFilter = installWatch(dev::eth::MessageFilter().altered((u160)ethereum()->stateAt(c_config, 0)), [=](){ onNameRegChange(); }); } void Main::installCurrenciesWatch() { - m_client->uninstallWatch(m_currenciesFilter); - m_currenciesFilter = installWatch(dev::eth::MessageFilter().altered((u160)m_client->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); }); + ethereum()->uninstallWatch(m_currenciesFilter); + m_currenciesFilter = installWatch(dev::eth::MessageFilter().altered((u160)ethereum()->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); }); } void Main::installBalancesWatch() @@ -224,9 +195,9 @@ void Main::installBalancesWatch() dev::eth::MessageFilter tf; vector
altCoins; - Address coinsAddr = right160(m_client->stateAt(c_config, 1)); - for (unsigned i = 0; i < m_client->stateAt(coinsAddr, 0); ++i) - altCoins.push_back(right160(m_client->stateAt(coinsAddr, i + 1))); + Address coinsAddr = right160(ethereum()->stateAt(c_config, 1)); + for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) + altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); for (auto i: m_myKeys) { tf.altered(i.address()); @@ -234,7 +205,7 @@ void Main::installBalancesWatch() tf.altered(c, (u160)i.address()); } - m_client->uninstallWatch(m_balancesFilter); + ethereum()->uninstallWatch(m_balancesFilter); m_balancesFilter = installWatch(tf, [=](){ onBalancesChange(); }); } @@ -295,8 +266,8 @@ QString Main::pretty(dev::Address _a) const { h256 n; - if (h160 nameReg = (u160)m_client->stateAt(c_config, 0)) - n = m_client->stateAt(nameReg, (u160)(_a)); + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0)) + n = ethereum()->stateAt(nameReg, (u160)(_a)); return fromRaw(n); } @@ -322,8 +293,8 @@ Address Main::fromString(QString const& _a) const memset(n.data() + sn.size(), 0, 32 - sn.size()); if (_a.size()) { - if (h160 nameReg = (u160)m_client->stateAt(c_config, 0)) - if (h256 a = m_client->stateAt(nameReg, n)) + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0)) + if (h256 a = ethereum()->stateAt(nameReg, n)) return right160(a); } if (_a.size() == 40) @@ -351,11 +322,11 @@ QString Main::lookup(QString const& _a) const */ h256 ret; - if (h160 dnsReg = (u160)m_client->stateAt(c_config, 4, 0)) - ret = m_client->stateAt(dnsReg, n); + if (h160 dnsReg = (u160)ethereum()->stateAt(c_config, 4, 0)) + ret = ethereum()->stateAt(dnsReg, n); /* if (!ret) - if (h160 nameReg = (u160)m_client->stateAt(c_config, 0, 0)) - ret = m_client->stateAt(nameReg, n2); + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0, 0)) + ret = ethereum()->stateAt(nameReg, n2); */ if (ret && !((u256)ret >> 32)) return QString("%1.%2.%3.%4").arg((int)ret[28]).arg((int)ret[29]).arg((int)ret[30]).arg((int)ret[31]); @@ -385,7 +356,7 @@ void Main::writeSettings() s.setValue("address", b); s.setValue("url", ui->urlEdit->text()); - bytes d = m_client->savePeers(); + bytes d = m_web3->savePeers(); if (d.size()) m_peers = QByteArray((char*)d.data(), (int)d.size()); s.setValue("peers", m_peers); @@ -416,7 +387,7 @@ void Main::readSettings(bool _skipGeometry) m_myKeys.append(KeyPair(k)); } } - m_client->setAddress(m_myKeys.back().address()); + ethereum()->setAddress(m_myKeys.back().address()); m_peers = s.value("peers").toByteArray(); ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html on_urlEdit_returnPressed(); @@ -466,8 +437,8 @@ void Main::on_urlEdit_returnPressed() void Main::refreshMining() { - dev::eth::MineProgress p = m_client->miningProgress(); - ui->mineStatus->setText(m_client->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); + dev::eth::MineProgress p = ethereum()->miningProgress(); + ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); } void Main::refreshBalances() @@ -477,18 +448,18 @@ void Main::refreshBalances() ui->ourAccounts->clear(); u256 totalBalance = 0; map> altCoins; - Address coinsAddr = right160(m_client->stateAt(c_config, 1)); - for (unsigned i = 0; i < m_client->stateAt(coinsAddr, 0); ++i) - altCoins[right160(m_client->stateAt(coinsAddr, m_client->stateAt(coinsAddr, i + 1)))] = make_pair(fromRaw(m_client->stateAt(coinsAddr, i + 1)), 0); + Address coinsAddr = right160(ethereum()->stateAt(c_config, 1)); + for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) + altCoins[right160(ethereum()->stateAt(coinsAddr, ethereum()->stateAt(coinsAddr, i + 1)))] = make_pair(fromRaw(ethereum()->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)) + u256 b = ethereum()->balanceAt(i.address()); + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)ethereum()->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()); + c.second.second += (u256)ethereum()->stateAt(c.first, (u160)i.address()); } QString b; @@ -500,7 +471,7 @@ void Main::refreshBalances() void Main::refreshNetwork() { - auto ps = m_client->peers(); + auto ps = m_web3->peers(); ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); } @@ -514,8 +485,8 @@ void Main::refreshAll() void Main::refreshBlockCount() { cwatch << "refreshBlockCount()"; - auto d = m_client->blockChain().details(); - auto diff = BlockInfo(m_client->blockChain().block()).difficulty; + auto d = ethereum()->blockChain().details(); + auto diff = BlockInfo(ethereum()->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(dev::eth::c_protocolVersion).arg(dev::eth::c_databaseVersion)); } @@ -543,7 +514,7 @@ void Main::timerEvent(QTimerEvent*) m_ethereum->poll(); for (auto const& i: m_handlers) - if (m_client->checkWatch(i.first)) + if (ethereum()->checkWatch(i.first)) i.second(); } @@ -574,7 +545,7 @@ void Main::ensureNetwork() { string n = string("Third/v") + dev::Version; n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM); - m_client->setClientVersion(n); + web3()->setClientVersion(n); int pocnumber = QString(dev::Version).section('.', 1, 1).toInt(); string defPeer; @@ -583,13 +554,16 @@ void Main::ensureNetwork() else if (pocnumber == 6) defPeer = "54.76.56.74"; - if (!m_client->haveNetwork()) - m_client->startNetwork(30303, defPeer); + if (!web3()->haveNetwork()) + { + web3()->startNetwork(); + web3()->connect(defPeer); + } else - if (!m_client->peerCount()) - m_client->connect(defPeer); + if (!m_web3->peerCount()) + m_web3->connect(defPeer); if (m_peers.size()) - m_client->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); + m_web3->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); } void Main::on_connect_triggered() @@ -600,7 +574,7 @@ void Main::on_connect_triggered() { string host = s.section(":", 0, 0).toStdString(); unsigned short port = s.section(":", 1).toInt(); - m_client->connect(host, port); + web3()->connect(host, port); } } @@ -608,11 +582,11 @@ void Main::on_mine_triggered() { if (ui->mine->isChecked()) { - m_client->setAddress(m_myKeys.last().address()); - m_client->startMining(); + ethereum()->setAddress(m_myKeys.last().address()); + ethereum()->startMining(); } else - m_client->stopMining(); + ethereum()->stopMining(); } // extra bits needed to link on VS diff --git a/third/MainWin.h b/third/MainWin.h index 76505d603..df05d6828 100644 --- a/third/MainWin.h +++ b/third/MainWin.h @@ -36,7 +36,8 @@ namespace Ui { class Main; } -namespace dev { namespace eth { +namespace dev { class WebThreeDirect; +namespace eth { class Client; class State; class MessageFilter; @@ -52,7 +53,8 @@ public: explicit Main(QWidget *parent = 0); ~Main(); - dev::eth::Client* client() { return m_client.get(); } + dev::WebThreeDirect* web3() const { return m_web3.get(); } + dev::eth::Client* ethereum() const; QList const& owned() const { return m_myKeys; } @@ -111,7 +113,7 @@ private: std::unique_ptr ui; - std::unique_ptr m_client; + std::unique_ptr m_web3; QList m_myKeys; diff --git a/walleth/MainWin.cpp b/walleth/MainWin.cpp index ade04c4fa..3fa5a9388 100644 --- a/walleth/MainWin.cpp +++ b/walleth/MainWin.cpp @@ -61,7 +61,7 @@ Main::Main(QWidget *parent) : g_qmlMain = this; - m_client.reset(new Client("Walleth", Address(), dev::eth::getDataDir() + "/Walleth")); + m_client.reset(new Client("Walleth", Address(), dev::getDataDir() + "/Walleth")); g_qmlClient = m_client.get();