diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c6ec18cc..e623c7319 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) // resurect once moved over to WebThree API. + add_subdirectory(neth) endif () if(QTQML) add_definitions(-DETH_QTQML) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 5842664b3..9b9ae69e9 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -97,26 +97,10 @@ Main::Main(QWidget *parent) : // ui->log->addItem(QString::fromStdString(s)); }; -#if 0&Ð_DEBUG - m_servers.append("192.168.0.10:30301"); -#else - int pocnumber = QString(dev::Version).section('.', 1, 1).toInt(); - if (pocnumber == 5) - m_servers.push_back("54.72.69.180:30303"); - else if (pocnumber == 6) - m_servers.push_back("54.76.56.74:30303"); - else - { - connect(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r) - { - m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts); - }); - QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc" + QString::number(pocnumber) + ".txt")); - r.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1712.0 Safari/537.36"); - m_webCtrl.get(r); - srand(time(0)); - } +#if ETH_DEBUG + m_servers.append("localhost:30300"); #endif + m_servers.append(QString::fromStdString(Host::pocHost() + ":30303")); cerr << "State root: " << BlockChain::genesis().stateRoot << endl; cerr << "Block Hash: " << sha3(BlockChain::createGenesisBlock()) << endl; @@ -141,7 +125,7 @@ Main::Main(QWidget *parent) : connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); - m_webThree.reset(new WebThreeDirect("AlethZero", getDataDir() + "/AlethZero", false, {"eth", "shh"})); + m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/AlethZero", false, {"eth", "shh"})); connect(ui->webView, &QWebView::loadStarted, [this]() { diff --git a/eth/main.cpp b/eth/main.cpp index 215350c09..a9ea43f14 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -99,6 +99,7 @@ void help() << "Usage eth [OPTIONS] " << endl << "Options:" << endl << " -a,--address Set the coinbase (mining payout) address to addr (default: auto)." << endl + << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl << " -c,--client-name Add a name to your client's version string (default: blank)." << endl << " -d,--db-path Load database from path (default: ~/.ethereum " << endl << " /Etherum or Library/Application Support/Ethereum)." << endl @@ -134,17 +135,8 @@ string credits(bool _interactive = false) if (_interactive) { - string vs = toString(dev::Version); - vs = vs.substr(vs.find_first_of('.') + 1)[0]; - int pocnumber = stoi(vs); - string m_servers; - if (pocnumber == 4) - m_servers = "54.72.31.55"; - else - m_servers = "54.72.69.180"; - cout << "Type 'netstart 30303' to start networking" << endl; - cout << "Type 'connect " << m_servers << " 30303' to connect" << endl; + cout << "Type 'connect " << Host::pocHost() << " 30303' to connect" << endl; cout << "Type 'exit' to quit" << endl << endl; } return cout.str(); @@ -185,9 +177,10 @@ int main(int argc, char** argv) unsigned peers = 5; bool interactive = false; #if ETH_JSONRPC - int jsonrpc = 8080; + int jsonrpc = -1; #endif string publicIP; + bool bootstrap = false; bool upnp = true; bool useLocal = false; bool forceMining = false; @@ -264,13 +257,15 @@ int main(int argc, char** argv) return -1; } } + else if (arg == "-b" || arg == "--bootstrap") + bootstrap = true; else if (arg == "-f" || arg == "--force-mining") forceMining = true; else if (arg == "-i" || arg == "--interactive") interactive = true; #if ETH_JSONRPC else if ((arg == "-j" || arg == "--json-rpc")) - jsonrpc = jsonrpc ? jsonrpc : 8080; + jsonrpc = jsonrpc == -1 ? 8080 : jsonrpc; else if (arg == "--json-rpc-port" && i + 1 < argc) jsonrpc = atoi(argv[++i]); #endif @@ -317,7 +312,11 @@ int main(int argc, char** argv) cout << "Address: " << endl << toHex(us.address().asArray()) << endl; web3.startNetwork(); - web3.connect(remoteHost, remotePort); + + if (bootstrap) + web3.connect(Host::pocHost()); + if (remoteHost.size()) + web3.connect(remoteHost, remotePort); #if ETH_JSONRPC auto_ptr jsonrpcServer; diff --git a/exp/main.cpp b/exp/main.cpp index a88023fae..1f29ab207 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -19,12 +19,14 @@ * @date 2014 * Ethereum client. */ +#include #include #include #include #include #include #include +#include #include using namespace std; using namespace dev; @@ -34,7 +36,25 @@ using namespace dev::shh; int main() { - RangeMask m(0, 100); + DownloadMan man; + DownloadSub s0(man); + DownloadSub s1(man); + DownloadSub s2(man); + man.resetToChain(h256s({u256(0), u256(1), u256(2), u256(3), u256(4), u256(5), u256(6), u256(7), u256(8)})); + cnote << s0.nextFetch(2); + cnote << s1.nextFetch(2); + cnote << s2.nextFetch(2); + s0.noteBlock(u256(0)); + s0.doneFetch(); + cnote << s0.nextFetch(2); + s1.noteBlock(u256(2)); + s1.noteBlock(u256(3)); + s1.doneFetch(); + cnote << s1.nextFetch(2); + s0.doneFetch(); + cnote << s0.nextFetch(2); + +/* RangeMask m(0, 100); cnote << m; m += UnsignedRange(3, 10); cnote << m; @@ -45,7 +65,7 @@ int main() cnote << ~m; cnote << (~m).lowest(10); for (auto i: (~m).lowest(10)) - cnote << i; + cnote << i;*/ return 0; } diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index ad779e35d..908948236 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.6.8c"; +char const* Version = "0.6.8d"; } diff --git a/libdevcore/RangeMask.cpp b/libdevcore/RangeMask.cpp index 5ba4a1f73..5317e00e8 100644 --- a/libdevcore/RangeMask.cpp +++ b/libdevcore/RangeMask.cpp @@ -1,5 +1,22 @@ -#include "RangeMask.h" +/* + 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. -RangeMask::RangeMask() -{ -} + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file RangeMask.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "RangeMask.h" diff --git a/libdevcore/RangeMask.h b/libdevcore/RangeMask.h index ba8594779..5d271cf3c 100644 --- a/libdevcore/RangeMask.h +++ b/libdevcore/RangeMask.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace dev { @@ -52,7 +53,7 @@ public: { RangeMask ret(m_all); for (auto i = m_ranges.begin(); i != m_ranges.end() && _items; ++i) - _items -= (ret.m_ranges[i->first] = std::min(i->first + _items, i->second)); + _items -= (ret.m_ranges[i->first] = std::min(i->first + _items, i->second)) - i->first; return ret; } @@ -140,6 +141,21 @@ public: return it != m_ranges.end() && it->first <= _i && it->second > _i; } + bool empty() const + { + return m_ranges.empty(); + } + + bool full() const + { + return m_ranges.size() == 1 && m_ranges.begin()->first == m_all.first && m_ranges.begin()->second == m_all.second; + } + + void clear() + { + m_ranges.clear(); + } + class const_iterator { friend class RangeMask; diff --git a/libethereum/CommonNet.h b/libethereum/CommonNet.h index 6774c02ae..4fdfa77a0 100644 --- a/libethereum/CommonNet.h +++ b/libethereum/CommonNet.h @@ -66,6 +66,7 @@ enum class Grabbing State, Hashes, Chain, + ChainHelper, Nothing }; diff --git a/libethereum/DownloadMan.cpp b/libethereum/DownloadMan.cpp new file mode 100644 index 000000000..09a3bcc00 --- /dev/null +++ b/libethereum/DownloadMan.cpp @@ -0,0 +1,75 @@ +/* + 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 DownloadMan.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "DownloadMan.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +DownloadSub::DownloadSub(DownloadMan& _man): m_man(&_man) +{ + WriteGuard l(m_man->x_subs); + m_man->m_subs.insert(this); +} + +DownloadSub::~DownloadSub() +{ + if (m_man) + { + WriteGuard l(m_man->x_subs); + m_man->m_subs.erase(this); + } +} + +h256Set DownloadSub::nextFetch(unsigned _n) +{ + Guard l(m_fetch); + + if (m_remaining.size()) + return m_remaining; + + m_asked.clear(); + m_indices.clear(); + m_remaining.clear(); + + if (!m_man) + return h256Set(); + + m_asked = (~(m_man->taken() + m_attempted)).lowest(_n); + if (m_asked.empty()) + m_asked = (~(m_man->taken(true) + m_attempted)).lowest(_n); + m_attempted += m_asked; + for (auto i: m_asked) + { + auto x = m_man->m_chain[i]; + m_remaining.insert(x); + m_indices[x] = i; + } + return m_remaining; +} + +void DownloadSub::noteBlock(h256 _hash) +{ + Guard l(m_fetch); + if (m_man && m_indices.count(_hash)) + m_man->m_blocksGot += m_indices[_hash]; + m_remaining.erase(_hash); +} diff --git a/libethereum/DownloadMan.h b/libethereum/DownloadMan.h new file mode 100644 index 000000000..e9cd37e3c --- /dev/null +++ b/libethereum/DownloadMan.h @@ -0,0 +1,128 @@ +/* + 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 DownloadMan.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace dev +{ + +namespace eth +{ + +class DownloadMan; + +class DownloadSub +{ + friend class DownloadMan; + +public: + DownloadSub(DownloadMan& _man); + ~DownloadSub(); + + /// Finished last fetch - grab the next bunch of block hashes to download. + h256Set nextFetch(unsigned _n); + + /// Note that we've received a particular block. + void noteBlock(h256 _hash); + + /// Nothing doing here. + void doneFetch() { resetFetch(); } + +private: + void resetFetch() // Called by DownloadMan when we need to reset the download. + { + Guard l(m_fetch); + m_remaining.clear(); + m_indices.clear(); + m_asked.clear(); + m_attempted.clear(); + } + + DownloadMan* m_man = nullptr; + + Mutex m_fetch; + h256Set m_remaining; + std::map m_indices; + RangeMask m_asked; + RangeMask m_attempted; +}; + +class DownloadMan +{ + friend class DownloadSub; + +public: + ~DownloadMan() + { + for (auto i: m_subs) + i->m_man = nullptr; + } + + void resetToChain(h256s const& _chain) + { + { + ReadGuard l(x_subs); + for (auto i: m_subs) + i->resetFetch(); + } + m_chain.clear(); + m_chain.reserve(_chain.size()); + for (auto i = _chain.rbegin(); i != _chain.rend(); ++i) + m_chain.push_back(*i); + m_blocksGot = RangeMask(0, m_chain.size()); + } + + RangeMask taken(bool _desperate = false) const + { + auto ret = m_blocksGot; + if (!_desperate) + { + ReadGuard l(x_subs); + for (auto i: m_subs) + ret += i->m_asked; + } + return ret; + } + + bool isComplete() const + { + return m_blocksGot.full(); + } + +private: + h256s m_chain; + RangeMask m_blocksGot; + + mutable SharedMutex x_subs; + std::set m_subs; +}; + +} + +} diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 2add39482..a2bac994e 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -15,9 +15,7 @@ along with cpp-ethereum. If not, see . */ /** @file EthereumHost.cpp - * @authors: - * Gav Wood - * Eric Lombrozo + * @author Gav Wood * @date 2014 */ @@ -34,6 +32,7 @@ #include "TransactionQueue.h" #include "BlockQueue.h" #include "EthereumPeer.h" +#include "DownloadMan.h" using namespace std; using namespace dev; using namespace dev::eth; @@ -56,30 +55,6 @@ EthereumHost::~EthereumHost() i->cap()->giveUpOnFetch(); } -h256Set EthereumHost::neededBlocks(h256Set const& _exclude) -{ - Guard l(x_blocksNeeded); - h256Set ret; - if (m_blocksNeeded.size()) - { - int s = m_blocksNeeded.size() - 1; - for (; ret.size() < c_maxBlocksAsk && s < (int)m_blocksNeeded.size() && s >= 0; --s) - if (!_exclude.count(m_blocksNeeded[s])) - { - auto it = m_blocksNeeded.begin() + s; - ret.insert(*it); - m_blocksOnWay.insert(*it); - m_blocksNeeded.erase(it); - } - } - if (ret.empty()) - for (auto i = m_blocksOnWay.begin(); ret.size() < c_maxBlocksAsk && i != m_blocksOnWay.end() && !_exclude.count(*i); ++i) - ret.insert(*i); - 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) @@ -103,7 +78,7 @@ void EthereumHost::noteHavePeerState(EthereumPeer* _who) if (m_grabbing != Grabbing::Nothing) { clog(NetAllDetail) << "Already downloading chain. Just set to help out."; - _who->restartGettingChain(); + _who->ensureGettingChain(); return; } @@ -118,7 +93,7 @@ void EthereumHost::updateGrabbing(Grabbing _g) readyForSync(); else if (_g == Grabbing::Chain) for (auto j: peers()) - j->cap()->restartGettingChain(); + j->cap()->ensureGettingChain(); } void EthereumHost::noteHaveChain(EthereumPeer* _from) @@ -145,13 +120,9 @@ void EthereumHost::noteHaveChain(EthereumPeer* _from) 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; - } + m_man.resetToChain(_from->m_neededBlocks); + m_totalDifficultyOfNeeded = td; + m_latestBlockSent = _from->m_latestHash; _from->m_grabbing = Grabbing::Chain; updateGrabbing(Grabbing::Chain); @@ -172,23 +143,25 @@ void EthereumHost::readyForSync() clog(NetNote) << "No more peers to sync with."; } -void EthereumHost::noteDoneBlocks() +void EthereumHost::noteDoneBlocks(EthereumPeer* _who) { - if (m_blocksOnWay.empty()) + if (m_man.isComplete()) { // Done our chain-get. - if (m_blocksNeeded.size()) - clog(NetNote) << "No more blocks coming. Missing" << m_blocksNeeded.size() << "blocks."; - else - clog(NetNote) << "No more blocks to get."; + clog(NetNote) << "Chain download complete."; + updateGrabbing(Grabbing::Nothing); + } + if (_who->m_grabbing == Grabbing::Chain) + { + // Done our chain-get. + clog(NetNote) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished."; + // TODO: note that peer is BADBADBAD! updateGrabbing(Grabbing::Nothing); } } bool EthereumHost::noteBlock(h256 _hash, bytesConstRef _data) { - Guard l(x_blocksNeeded); - m_blocksOnWay.erase(_hash); if (!m_chain.details(_hash)) { lock_guard l(m_incomingLock); @@ -257,12 +230,12 @@ void EthereumHost::reset() { m_grabbing = Grabbing::Nothing; + m_man.resetToChain(h256s()); + m_incomingTransactions.clear(); m_incomingBlocks.clear(); m_totalDifficultyOfNeeded = 0; - m_blocksNeeded.clear(); - m_blocksOnWay.clear(); m_latestBlockSent = h256(); m_transactionsSent.clear(); diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index b038e0785..b735532b8 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -30,10 +30,12 @@ #include #include #include +#include #include #include #include "CommonNet.h" #include "EthereumPeer.h" +#include "DownloadMan.h" namespace dev { @@ -46,44 +48,6 @@ namespace eth class TransactionQueue; class BlockQueue; -#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. @@ -113,7 +77,7 @@ private: /// Session has finished getting the chain of hashes. void noteHaveChain(EthereumPeer* _who); /// Called when the peer can no longer provide us with any needed blocks. - void noteDoneBlocks(); + void noteDoneBlocks(EthereumPeer* _who); /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. void doWork(); @@ -147,16 +111,15 @@ private: u256 m_networkId; - Grabbing m_grabbing = Grabbing::Nothing; + Grabbing m_grabbing = Grabbing::Nothing; // TODO: needs to be thread-safe & switch to just having a peer id. mutable std::recursive_mutex m_incomingLock; std::vector m_incomingTransactions; std::vector m_incomingBlocks; - mutable std::mutex x_blocksNeeded; u256 m_totalDifficultyOfNeeded; - h256s m_blocksNeeded; - h256Set m_blocksOnWay; + + DownloadMan m_man; h256 m_latestBlockSent; h256Set m_transactionsSent; diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 7ccf6939b..6f9ff226e 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -35,7 +35,8 @@ using namespace p2p; #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h): - Capability(_s, _h) + Capability(_s, _h), + m_sub(host()->m_man) { sendStatus(); } @@ -65,7 +66,7 @@ void EthereumPeer::sendStatus() void EthereumPeer::startInitialSync() { - // Grab trsansactions off them. + // Grab transactions off them. { RLPStream s; prep(s).appendList(1); @@ -90,7 +91,7 @@ void EthereumPeer::tryGrabbingHashChain() 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) + if (td >= m_totalDifficulty) { clogS(NetAllDetail) << "No. Our chain is better."; m_grabbing = Grabbing::Nothing; @@ -101,6 +102,7 @@ void EthereumPeer::tryGrabbingHashChain() { clogS(NetAllDetail) << "Yes. Their chain is better."; + host()->updateGrabbing(Grabbing::Hashes); m_grabbing = Grabbing::Hashes; RLPStream s; prep(s).appendList(3); @@ -112,29 +114,17 @@ void EthereumPeer::tryGrabbingHashChain() void EthereumPeer::giveUpOnFetch() { - clogS(NetNote) << "GIVE UP FETCH; can't get" << toString(m_askedBlocks); + clogS(NetNote) << "GIVE UP FETCH"; // 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) { + host()->noteDoneBlocks(this); m_grabbing = Grabbing::Nothing; - host()->updateGrabbing(Grabbing::Nothing); } + m_sub.doneFetch(); // NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary. - - if (m_askedBlocks.size()) - { - Guard l (host()->x_blocksNeeded); - host()->m_blocksNeeded.reserve(host()->m_blocksNeeded.size() + m_askedBlocks.size()); - for (auto i: m_askedBlocks) - { - m_failedBlocks.insert(i); - host()->m_blocksOnWay.erase(i); - host()->m_blocksNeeded.push_back(i); - } - m_askedBlocks.clear(); - } } bool EthereumPeer::interpret(RLP const& _r) @@ -251,20 +241,20 @@ bool EthereumPeer::interpret(RLP const& _r) { clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); - if (_r.itemCount() == 1 && !m_askedBlocksChanged) + if (_r.itemCount() == 1) { // Couldn't get any from last batch - probably got to this peer's latest block - just give up. + m_sub.doneFetch(); giveUpOnFetch(); } - m_askedBlocksChanged = false; unsigned used = 0; for (unsigned i = 1; i < _r.itemCount(); ++i) { auto h = BlockInfo::headerHash(_r[i].data()); + m_sub.noteBlock(h); if (host()->noteBlock(h, _r[i].data())) used++; - m_askedBlocks.erase(h); Guard l(x_knownBlocks); m_knownBlocks.insert(h); } @@ -281,12 +271,12 @@ bool EthereumPeer::interpret(RLP const& _r) if (!host()->m_chain.details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash)) { unknownParents++; - clogS(NetAllDetail) << "Unknown parent" << bi.parentHash << "of block" << h; + clogS(NetAllDetail) << "Unknown parent" << bi.parentHash.abridged() << "of block" << h.abridged(); } else { knownParents++; - clogS(NetAllDetail) << "Known parent" << bi.parentHash << "of block" << h; + clogS(NetAllDetail) << "Known parent" << bi.parentHash.abridged() << "of block" << h.abridged(); } } } @@ -300,21 +290,9 @@ bool EthereumPeer::interpret(RLP const& _r) return true; } -void EthereumPeer::restartGettingChain() -{ - if (m_askedBlocks.size()) - { - m_askedBlocksChanged = true; // So that we continue even if the Ask's reply is empty. - m_askedBlocks.clear(); // So that we restart once we get the Ask's reply. - m_failedBlocks.clear(); - } - else - ensureGettingChain(); -} - void EthereumPeer::ensureGettingChain() { - if (m_askedBlocks.size()) + if (m_grabbing == Grabbing::ChainHelper) return; // Already asked & waiting for some. continueGettingChain(); @@ -322,22 +300,20 @@ void EthereumPeer::ensureGettingChain() void EthereumPeer::continueGettingChain() { - if (!m_askedBlocks.size()) - m_askedBlocks = host()->neededBlocks(m_failedBlocks); + if (m_grabbing != Grabbing::Chain) + m_grabbing = Grabbing::ChainHelper; - if (m_askedBlocks.size()) + auto blocks = m_sub.nextFetch(c_maxBlocksAsk); + + if (blocks.size()) { RLPStream s; prep(s); - s.appendList(m_askedBlocks.size() + 1) << GetBlocksPacket; - for (auto i: m_askedBlocks) + s.appendList(blocks.size() + 1) << GetBlocksPacket; + for (auto const& i: blocks) s << i; sealAndSend(s); } else - { - 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(); - } + giveUpOnFetch(); } diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index 7248fabd0..f88a757d8 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -28,9 +28,11 @@ #include #include #include +#include #include #include #include "CommonNet.h" +#include "DownloadMan.h" namespace dev { @@ -65,8 +67,6 @@ private: void ensureGettingChain(); /// Ensure that we are waiting for a bunch of blocks from our peer. void continueGettingChain(); - /// Now getting a different chain so we need to make sure we restart. - void restartGettingChain(); void giveUpOnFetch(); @@ -80,10 +80,6 @@ private: 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. - h256Set m_failedBlocks; ///< Blocks that the peer doesn't seem to have. - - h256Set m_askedBlocks; ///< The blocks for which we sent the last GetBlocks for but haven't received a corresponding Blocks. - bool m_askedBlocksChanged = true; bool m_requireTransactions; @@ -91,6 +87,8 @@ private: std::set m_knownBlocks; std::set m_knownTransactions; std::mutex x_knownTransactions; + + DownloadSub m_sub; }; } diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 15727f9c1..cf76397bf 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include "Session.h" @@ -76,7 +77,9 @@ Host::~Host() void Host::start() { - stop(); + if (isWorking()) + stop(); + for (unsigned i = 0; i < 2; ++i) { bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : m_netPrefs.listenPort); @@ -103,6 +106,10 @@ void Host::start() determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); ensureAccepting(); + + m_incomingPeers.clear(); + m_freePeers.clear(); + m_lastPeersRequest = chrono::steady_clock::time_point::min(); clog(NetNote) << "Id:" << m_id.abridged(); @@ -311,8 +318,14 @@ std::map Host::potentialPeers() if (auto j = i.second.lock()) { auto ep = j->endpoint(); +// cnote << "Checking potential peer" << j->m_listenPort << j->endpoint() << isPrivateAddress(ep.address()) << ep.port() << j->m_id.abridged(); // Skip peers with a listen port of zero or are on a private network bool peerOnNet = (j->m_listenPort != 0 && (!isPrivateAddress(ep.address()) || m_netPrefs.localNetworking)); + if (!peerOnNet && m_incomingPeers.count(j->m_id)) + { + ep = m_incomingPeers.at(j->m_id).first; + 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)); } @@ -349,12 +362,20 @@ void Host::ensureAccepting() } } +string Host::pocHost() +{ + vector strs; + boost::split(strs, dev::Version, boost::is_any_of(".")); + return "poc-" + strs[1] + ".ethdev.com"; +} + 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)); + bi::tcp::resolver r(m_ioService); + connect(r.resolve({_addr, toString(_port)})->endpoint()); +// connect(bi::tcp::endpoint(bi::address::from_string(_addr), _port)); } catch (exception const& e) { @@ -435,8 +456,11 @@ void Host::growPeers() auto x = time(0) % m_freePeers.size(); m_incomingPeers[m_freePeers[x]].second++; - connect(m_incomingPeers[m_freePeers[x]].first); - m_freePeers.erase(m_freePeers.begin() + x); + if (!m_peers.count(m_freePeers[x])) + { + connect(m_incomingPeers[m_freePeers[x]].first); + m_freePeers.erase(m_freePeers.begin() + x); + } } } diff --git a/libp2p/Host.h b/libp2p/Host.h index e8b95e2a8..f5f2f9e97 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -82,6 +82,7 @@ public: template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(T::staticName())); } catch (...) { return nullptr; } } /// Connect to a peer explicitly. + static std::string pocHost(); void connect(std::string const& _addr, unsigned short _port = 30303) noexcept; void connect(bi::tcp::endpoint const& _ep); diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index f117b426e..252a1e8e6 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -159,13 +159,13 @@ 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(); + clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")" << isPrivateAddress(peerAddress) << m_id.abridged() << isPrivateAddress(endpoint().address()) << m_server->m_incomingPeers.count(id) << (m_server->m_incomingPeers.count(id) ? isPrivateAddress(m_server->m_incomingPeers.at(id).first.address()) : -1); + if (isPrivateAddress(peerAddress) && !m_server->m_netPrefs.localNetworking) goto CONTINUE; - clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")"; - // check that it's not us or one we already know: - if (id && (m_server->m_id == id || m_server->havePeer(id) || m_server->m_incomingPeers.count(id))) + if (!(m_id == id && isPrivateAddress(endpoint().address()) && (!m_server->m_incomingPeers.count(id) || isPrivateAddress(m_server->m_incomingPeers.at(id).first.address()))) && (!id || m_server->m_id == id || m_server->m_incomingPeers.count(id))) goto CONTINUE; // check that we're not already connected to addr: @@ -180,7 +180,7 @@ bool Session::interpret(RLP const& _r) m_server->m_incomingPeers[id] = make_pair(ep, 0); m_server->m_freePeers.push_back(id); m_server->noteNewPeers(); - clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id << ")"; + clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")"; CONTINUE:; } break; diff --git a/neth/CMakeLists.txt b/neth/CMakeLists.txt index 3e8dab70d..2b3f92947 100644 --- a/neth/CMakeLists.txt +++ b/neth/CMakeLists.txt @@ -11,6 +11,7 @@ set(EXECUTABLE neth) add_executable(${EXECUTABLE} ${SRC_LIST}) +target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} gmp) diff --git a/neth/main.cpp b/neth/main.cpp index e492d1a3c..d9818c72f 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -39,6 +39,7 @@ #include #include #endif +#include #include "BuildInfo.h" #undef KEY_EVENT // from windows.h @@ -413,7 +414,9 @@ int main(int argc, char** argv) if (!clientName.empty()) clientName += "/"; - Client c("NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), coinbase, dbPath); + WebThreeDirect web3("NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), dbPath); + // mode doesn't work anymore: see eth for how that should be structured. + Client& c = *web3.ethereum(); c.setForceMining(true); @@ -479,7 +482,12 @@ int main(int argc, char** argv) wmove(mainwin, 1, 4); if (!remoteHost.empty()) - c.startNetwork(listenPort, remoteHost, remotePort, mode, peers, publicIP, upnp); + { + web3.setIdealPeerCount(peers); + web3.setNetworkPreferences(NetworkPreferences(listenPort, publicIP, upnp)); + web3.startNetwork(); + web3.connect(remoteHost, remotePort); + } if (mining) c.startMining(); @@ -487,7 +495,7 @@ int main(int argc, char** argv) 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(); } @@ -528,18 +536,19 @@ int main(int argc, char** argv) { unsigned port; iss >> port; - c.startNetwork((short)port); + web3.setNetworkPreferences(NetworkPreferences((short)port, publicIP, upnp)); + 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") { @@ -560,7 +569,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(); } @@ -589,7 +598,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; @@ -925,7 +934,7 @@ int main(int argc, char** argv) // Peers y = 1; - for (PeerInfo const& i: c.peers()) + for (PeerInfo const& i: web3.peers()) { auto s = boost::format("%1% ms - %2%:%3% - %4%") % toString(chrono::duration_cast(i.lastPing).count()) % @@ -971,7 +980,7 @@ int main(int argc, char** argv) // Peers mvwprintw(peerswin, 0, x, "Peers: "); - mvwprintw(peerswin, 0, 9, toString(c.peers().size()).c_str()); + mvwprintw(peerswin, 0, 9, toString(web3.peers().size()).c_str()); // Mining flag if (c.isMining()) diff --git a/third/MainWin.cpp b/third/MainWin.cpp index 5e147bbda..be8420426 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -100,6 +100,7 @@ Main::Main(QWidget *parent) : connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); m_web3.reset(new WebThreeDirect("Third", getDataDir() + "/Third", false, {"eth", "shh"})); + m_web3->connect(Host::pocHost()); connect(ui->webView, &QWebView::loadStarted, [this]() {