diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 89768f559..b18236115 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -500,7 +500,7 @@ void Main::writeSettings() s.setValue("privateChain", m_privateChain); s.setValue("verbosity", ui->verbosity->value()); - bytes d = m_webThree->savePeers(); + bytes d = m_webThree->saveNodes(); if (d.size()) m_peers = QByteArray((char*)d.data(), (int)d.size()); s.setValue("peers", m_peers); @@ -1542,7 +1542,7 @@ void Main::on_net_triggered() web3()->startNetwork(); ui->downloadView->setDownloadMan(ethereum()->downloadMan()); if (m_peers.size() && ui->usePast->isChecked()) - web3()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); + web3()->restoreNodes(bytesConstRef((byte*)m_peers.data(), m_peers.size())); } else { diff --git a/libdevcore/RangeMask.h b/libdevcore/RangeMask.h index e6a66776c..7fda90f68 100644 --- a/libdevcore/RangeMask.h +++ b/libdevcore/RangeMask.h @@ -47,6 +47,7 @@ public: RangeMask(T _begin, T _end): m_all(_begin, _end) {} RangeMask(Range const& _c): m_all(_c) {} + RangeMask unionedWith(RangeMask const& _m) const { return operator+(_m); } RangeMask operator+(RangeMask const& _m) const { return RangeMask(*this) += _m; } RangeMask lowest(T _items) const @@ -57,7 +58,9 @@ public: return ret; } - RangeMask operator~() const + RangeMask operator~() const { return inverted(); } + + RangeMask inverted() const { RangeMask ret(m_all); T last = m_all.first; @@ -72,13 +75,23 @@ public: return ret; } - RangeMask& operator+=(RangeMask const& _m) + RangeMask& invert() { return *this = inverted(); } + + template RangeMask operator-(S const& _m) const { auto ret = *this; return ret -= _m; } + template RangeMask& operator-=(S const& _m) { return invert().unionWith(_m).invert(); } + + RangeMask& operator+=(RangeMask const& _m) { return unionWith(_m); } + + RangeMask& unionWith(RangeMask const& _m) { + m_all.first = std::min(_m.m_all.first, m_all.first); + m_all.second = std::max(_m.m_all.second, m_all.second); for (auto const& i: _m.m_ranges) - operator+=(i); + unionWith(i); return *this; } - RangeMask& operator+=(UnsignedRange const& _m) + RangeMask& operator+=(Range const& _m) { return unionWith(_m); } + RangeMask& unionWith(Range const& _m) { for (auto i = _m.first; i < _m.second;) { @@ -130,7 +143,8 @@ public: return *this; } - RangeMask& operator+=(T _i) + RangeMask& operator+=(T _m) { return unionWith(_m); } + RangeMask& unionWith(T _i) { return operator+=(Range(_i, _i + 1)); } @@ -165,6 +179,7 @@ public: } std::pair const& all() const { return m_all; } + void extendAll(T _max) { m_all.second = _max; } class const_iterator { diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h index d4583dc9b..16e2684fc 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -473,7 +473,7 @@ template void GenericTrieDB::insert(bytesConstRef _key, bytesCons assert(rv.size()); bytes b = mergeAt(RLP(rv), NibbleSlice(_key), _value); - // mergeAt won't attempt to delete the node is it's less than 32 bytes + // mergeAt won't attempt to delete the node if it's less than 32 bytes // However, we know it's the root node and thus always hashed. // So, if it's less than 32 (and thus should have been deleted but wasn't) then we delete it here. if (rv.size() < 32) diff --git a/libethcore/Exceptions.cpp b/libethcore/Exceptions.cpp index 2936e9353..c6f35763e 100644 --- a/libethcore/Exceptions.cpp +++ b/libethcore/Exceptions.cpp @@ -22,14 +22,17 @@ #include "Exceptions.h" #include +using namespace std; using namespace dev; using namespace dev::eth; -const char* InvalidBlockFormat::what() const noexcept { return ("Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")").c_str(); } -const char* UncleInChain::what() const noexcept { return ("Uncle in block already mentioned: Uncles " + toString(m_uncles) + " (" + m_block.abridged() + ")").c_str(); } -const char* InvalidTransactionsHash::what() const noexcept { return ("Invalid transactions hash: header says: " + toHex(m_head.ref()) + " block is:" + toHex(m_real.ref())).c_str(); } -const char* InvalidGasLimit::what() const noexcept { return ("Invalid gas limit (provided: " + toString(provided) + " valid:" + toString(valid) + ")").c_str(); } -const char* InvalidMinGasPrice::what() const noexcept { return ("Invalid minimum gas price (provided: " + toString(provided) + " limit:" + toString(limit) + ")").c_str(); } -const char* InvalidNonce::what() const noexcept { return ("Invalid nonce (r: " + toString(required) + " c:" + toString(candidate) + ")").c_str(); } -const char* InvalidBlockNonce::what() const noexcept { return ("Invalid nonce (h: " + toString(h) + " n:" + toString(n) + " d:" + toString(d) + ")").c_str(); } +#define ETH_RETURN_STRING(S) static string s_what; s_what = S; return s_what.c_str(); + +const char* InvalidBlockFormat::what() const noexcept { ETH_RETURN_STRING("Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"); } +const char* UncleInChain::what() const noexcept { ETH_RETURN_STRING("Uncle in block already mentioned: Uncles " + toString(m_uncles) + " (" + m_block.abridged() + ")"); } +const char* InvalidTransactionsHash::what() const noexcept { ETH_RETURN_STRING("Invalid transactions hash: header says: " + toHex(m_head.ref()) + " block is:" + toHex(m_real.ref())); } +const char* InvalidGasLimit::what() const noexcept { ETH_RETURN_STRING("Invalid gas limit (provided: " + toString(provided) + " valid:" + toString(valid) + ")"); } +const char* InvalidMinGasPrice::what() const noexcept { ETH_RETURN_STRING("Invalid minimum gas price (provided: " + toString(provided) + " limit:" + toString(limit) + ")"); } +const char* InvalidNonce::what() const noexcept { ETH_RETURN_STRING("Invalid nonce (r: " + toString(required) + " c:" + toString(candidate) + ")"); } +const char* InvalidBlockNonce::what() const noexcept { ETH_RETURN_STRING("Invalid nonce (h: " + toString(h) + " n:" + toString(n) + " d:" + toString(d) + ")"); } diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index f9207f795..8a4723696 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -48,7 +48,7 @@ class InvalidBlockFormat: public dev::Exception { public: InvalidBlockFormat(int struct InvalidUnclesHash: virtual dev::Exception {}; struct InvalidUncle: virtual dev::Exception {}; struct UncleTooOld: virtual dev::Exception {}; -class UncleInChain: public dev::Exception { public: UncleInChain(h256Set _uncles, h256 _block): m_uncles(_uncles), m_block(_block) {} h256Set m_uncles; h256 m_block;virtual const char* what() const noexcept; }; +class UncleInChain: public dev::Exception { public: UncleInChain(h256Set _uncles, h256 _block): m_uncles(_uncles), m_block(_block) {} h256Set m_uncles; h256 m_block; virtual const char* what() const noexcept; }; struct DuplicateUncleNonce: virtual dev::Exception {}; struct InvalidStateRoot: virtual dev::Exception {}; class InvalidTransactionsHash: public dev::Exception { public: InvalidTransactionsHash(h256 _head, h256 _real): m_head(_head), m_real(_real) {} h256 m_head; h256 m_real; virtual const char* what() const noexcept; }; diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index a4ccf5383..2add925c6 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -72,7 +72,7 @@ public: DownloadMan const& downloadMan() const { return m_man; } bool isSyncing() const { return !!m_syncer; } - bool isBanned(h512 _id) const { return !!m_banned.count(_id); } + bool isBanned(p2p::NodeId _id) const { return !!m_banned.count(_id); } private: /// Session is tell us that we may need (re-)syncing with the peer. @@ -116,7 +116,7 @@ private: h256 m_latestBlockSent; h256Set m_transactionsSent; - std::set m_banned; + std::set m_banned; }; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 047b9dcd5..e65b957a1 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -75,7 +75,9 @@ void ripemd160Code(bytesConstRef _in, bytesRef _out) { h256 ret; ripemd160(_in, bytesRef(ret.data(), 32)); - memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret))); + memset(_out.data(), 0, std::min(12, _out.size())); + if (_out.size() > 12) + memcpy(_out.data() + 12, &ret, min(_out.size() - 12, sizeof(ret))); } const std::map State::c_precompiled = @@ -1284,8 +1286,8 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s) { auto it = _s.m_cache.find(i); AddressState* cache = it != _s.m_cache.end() ? &it->second : nullptr; - auto rlpString = trie.at(i); - RLP r(dtr.count(i) ? rlpString : ""); + string rlpString = dtr.count(i) ? trie.at(i) : ""; + RLP r(rlpString); assert(cache || r); if (cache && !cache->isAlive()) @@ -1298,7 +1300,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s) stringstream contout; - if ((!cache || cache->codeBearing()) && (!r || r[3].toHash() != EmptySHA3)) + if ((cache && cache->codeBearing()) || (!cache && r && !r[3].isEmpty())) { std::map mem; std::set back; diff --git a/libp2p/CMakeLists.txt b/libp2p/CMakeLists.txt index ab852fc57..c3cdc70b9 100644 --- a/libp2p/CMakeLists.txt +++ b/libp2p/CMakeLists.txt @@ -17,6 +17,7 @@ file(GLOB HEADERS "*.h") include_directories(..) +target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} secp256k1) if(MINIUPNPC_LS) diff --git a/libp2p/Common.h b/libp2p/Common.h index 895b76404..7ef25ca6e 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -44,6 +44,8 @@ class RLPStream; namespace p2p { +using NodeId = h512; + bool isPrivateAddress(bi::address const& _addressToCheck); class UPnP; @@ -82,8 +84,9 @@ enum DisconnectReason TooManyPeers, DuplicatePeer, IncompatibleProtocol, - InvalidIdentity, + NullIdentity, ClientQuit, + UnexpectedIdentity, UserReason = 0x10 }; @@ -96,6 +99,7 @@ typedef std::vector CapDescs; struct PeerInfo { + NodeId id; std::string clientVersion; std::string host; unsigned short port; diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index b509ecc2a..848d37dfa 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -63,11 +63,11 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool m_netPrefs(_n), m_acceptor(m_ioService), m_socket(m_ioService), - m_id(h512::random()) + m_key(KeyPair::create()) { populateAddresses(); m_lastPeersRequest = chrono::steady_clock::time_point::min(); - clog(NetNote) << "Id:" << m_id.abridged(); + clog(NetNote) << "Id:" << id().abridged(); if (_start) start(); } @@ -109,11 +109,16 @@ void Host::start() determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); ensureAccepting(); - m_incomingPeers.clear(); + m_nodes.clear(); m_freePeers.clear(); + m_nodesList.clear(); + m_ready.reset(); + + if (!m_public.address().is_unspecified()) + noteNode(id(), m_public, Origin::Perfect, false); m_lastPeersRequest = chrono::steady_clock::time_point::min(); - clog(NetNote) << "Id:" << m_id.abridged(); + clog(NetNote) << "Id:" << id().abridged(); for (auto const& h: m_capabilities) h.second->onStarting(); @@ -149,9 +154,15 @@ unsigned Host::protocolVersion() const void Host::registerPeer(std::shared_ptr _s, CapDescs const& _caps) { + if (!_s->m_node || !_s->m_node->id) + { + cwarn << "Attempting to register a peer without node information!"; + return; + } + { - Guard l(x_peers); - m_peers[_s->m_id] = _s; + RecursiveGuard l(x_peers); + m_peers[_s->m_node->id] = _s; } unsigned o = (unsigned)UserPacket; for (auto const& i: _caps) @@ -167,7 +178,7 @@ void Host::disconnectPeers() for (unsigned n = 0;; n = 0) { { - Guard l(x_peers); + RecursiveGuard l(x_peers); for (auto i: m_peers) if (auto p = i.second.lock()) { @@ -230,7 +241,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) else { m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), (unsigned short)p); - m_addresses.push_back(m_public.address().to_v4()); + m_addresses.push_back(m_public.address()); } } else @@ -239,7 +250,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) m_public = bi::tcp::endpoint(_publicAddress.size() ? bi::address::from_string(_publicAddress) : m_peerAddresses.size() ? m_peerAddresses[0] : bi::address(), m_listenPort); - m_addresses.push_back(m_public.address().to_v4()); + m_addresses.push_back(m_public.address()); } } @@ -312,33 +323,83 @@ void Host::populateAddresses() clog(NetNote) << "Couldn't resolve: " << host; } } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + char host[NI_MAXHOST]; + if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) + continue; + try + { + auto it = r.resolve({host, "30303"}); + bi::tcp::endpoint ep = it->endpoint(); + bi::address ad = ep.address(); + m_addresses.push_back(ad.to_v6()); + bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); + if (!isLocal) + m_peerAddresses.push_back(ad); + clog(NetNote) << "Address: " << host << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); + } + catch (...) + { + clog(NetNote) << "Couldn't resolve: " << host; + } + } } freeifaddrs(ifaddr); #endif } -std::map Host::potentialPeers() +shared_ptr Host::noteNode(NodeId _id, bi::tcp::endpoint const& _a, Origin _o, bool _ready, NodeId _oldId) { - std::map ret; - if (!m_public.address().is_unspecified()) - ret.insert(make_pair(m_id, m_public)); - Guard l(x_peers); - for (auto i: m_peers) - if (auto j = i.second.lock()) + RecursiveGuard l(x_peers); + unsigned i; + if (!m_nodes.count(_id)) + { + shared_ptr old; + if (m_nodes.count(_oldId)) { - 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)); + old = m_nodes[_oldId]; + i = old->index; + m_nodes.erase(_oldId); + m_nodesList[i] = _id; + m_nodes[id()] = make_shared(); + } + else + { + i = m_nodesList.size(); + m_nodesList.push_back(_id); + m_nodes[_id] = make_shared(); } + m_nodes[_id]->address = m_public; + m_nodes[_id]->index = i; + m_nodes[_id]->id = _id; + m_nodes[_id]->idOrigin = _o; + } + else + { + i = m_nodes[_id]->index; + m_nodes[_id]->idOrigin = max(m_nodes[_id]->idOrigin, _o); + } + m_ready.extendAll(i); + m_private.extendAll(i); + if (_ready) + m_ready += i; + else + m_ready -= i; + if (!_a.port() || (isPrivateAddress(_a.address()) && !m_netPrefs.localNetworking)) + m_private += i; + else + m_private -= i; + return m_nodes[_id]; +} + +Nodes Host::potentialPeers(RangeMask const& _known) +{ + RecursiveGuard l(x_peers); + Nodes ret; + for (auto i: m_ready - (m_private + _known)) + ret.push_back(*m_nodes[m_nodesList[i]]); return ret; } @@ -359,7 +420,7 @@ void Host::ensureAccepting() } catch (...){} bi::address remoteAddress = m_socket.remote_endpoint().address(); // Port defaults to 0 - we let the hello tell us which port the peer listens to - auto p = std::make_shared(this, std::move(m_socket), remoteAddress); + auto p = std::make_shared(this, std::move(m_socket), bi::tcp::endpoint(remoteAddress, 0)); p->start(); } catch (Exception const& _e) @@ -415,26 +476,15 @@ void Host::connect(std::string const& _addr, unsigned short _port) noexcept void Host::connect(bi::tcp::endpoint const& _ep) { - clog(NetConnect) << "Attempting connection to " << _ep; + clog(NetConnect) << "Attempting single-shot connection to " << _ep; bi::tcp::socket* s = new bi::tcp::socket(m_ioService); s->async_connect(_ep, [=](boost::system::error_code const& ec) { if (ec) - { clog(NetConnect) << "Connection refused to " << _ep << " (" << ec.message() << ")"; - for (auto i = m_incomingPeers.begin(); i != m_incomingPeers.end(); ++i) - if (i->second.first == _ep && i->second.second < 3) - { - m_freePeers.push_back(i->first); - goto OK; - } - // for-else - clog(NetConnect) << "Giving up."; - OK:; - } else { - auto p = make_shared(this, std::move(*s), _ep.address(), _ep.port()); + auto p = make_shared(this, std::move(*s), _ep); clog(NetConnect) << "Connected to " << _ep; p->start(); } @@ -442,9 +492,35 @@ void Host::connect(bi::tcp::endpoint const& _ep) }); } -bool Host::havePeer(h512 _id) const +void Node::connect(Host* _h) { - Guard l(x_peers); + clog(NetConnect) << "Attempting connection to node" << id.abridged() << "@" << address; + _h->m_ready -= index; + bi::tcp::socket* s = new bi::tcp::socket(_h->m_ioService); + s->async_connect(address, [=](boost::system::error_code const& ec) + { + if (ec) + { + clog(NetConnect) << "Connection refused to node" << id.abridged() << "@" << address << "(" << ec.message() << ")"; + failedAttempts++; + lastAttempted = std::chrono::system_clock::now(); + _h->m_ready += index; + } + else + { + clog(NetConnect) << "Connected to" << id.abridged() << "@" << address; + failedAttempts = 0; + lastConnected = std::chrono::system_clock::now(); + auto p = make_shared(_h, std::move(*s), _h->node(id), true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism. + p->start(); + } + delete s; + }); +} + +bool Host::havePeer(NodeId _id) const +{ + RecursiveGuard l(x_peers); // Remove dead peers from list. for (auto i = m_peers.begin(); i != m_peers.end();) @@ -458,7 +534,7 @@ bool Host::havePeer(h512 _id) const void Host::growPeers() { - Guard l(x_peers); + RecursiveGuard l(x_peers); while (m_peers.size() < m_idealPeerCount) { if (m_freePeers.empty()) @@ -483,16 +559,15 @@ void Host::growPeers() } auto x = time(0) % m_freePeers.size(); - m_incomingPeers[m_freePeers[x]].second++; if (!m_peers.count(m_freePeers[x])) - connect(m_incomingPeers[m_freePeers[x]].first); + connect(m_nodes[m_freePeers[x]]->address); m_freePeers.erase(m_freePeers.begin() + x); } } void Host::prunePeers() { - Guard l(x_peers); + RecursiveGuard l(x_peers); // We'll keep at most twice as many as is ideal, halfing what counts as "too young to kill" until we get there. for (unsigned old = 15000; m_peers.size() > m_idealPeerCount * 2 && old > 100; old /= 2) while (m_peers.size() > m_idealPeerCount) @@ -506,7 +581,7 @@ void Host::prunePeers() if (/*(m_mode != NodeMode::Host || p->m_caps != 0x01) &&*/ chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers. { ++agedPeers; - if ((!worst || p->m_rating < worst->m_rating || (p->m_rating == worst->m_rating && p->m_connect > worst->m_connect))) // kill older ones + if ((!worst || p->rating() < worst->rating() || (p->rating() == worst->rating() && p->m_connect > worst->m_connect))) // kill older ones worst = p; } if (!worst || agedPeers <= m_idealPeerCount) @@ -524,10 +599,12 @@ void Host::prunePeers() std::vector Host::peers(bool _updatePing) const { - Guard l(x_peers); + RecursiveGuard l(x_peers); if (_updatePing) + { const_cast(this)->pingAll(); - this_thread::sleep_for(chrono::milliseconds(200)); + this_thread::sleep_for(chrono::milliseconds(200)); + } std::vector ret; for (auto& i: m_peers) if (auto j = i.second.lock()) @@ -545,36 +622,82 @@ void Host::doWork() void Host::pingAll() { - Guard l(x_peers); + RecursiveGuard l(x_peers); for (auto& i: m_peers) if (auto j = i.second.lock()) j->ping(); } -bytes Host::savePeers() const +bytes Host::saveNodes() const { - Guard l(x_peers); - RLPStream ret; - int n = 0; - for (auto& i: m_peers) - if (auto p = i.second.lock()) - if (p->m_socket.is_open() && p->endpoint().port()) - { - ret.appendList(3) << p->endpoint().address().to_v4().to_bytes() << p->endpoint().port() << p->m_id; - n++; - } - return RLPStream(n).appendRaw(ret.out(), n).out(); + RLPStream nodes; + int count = 0; + { + RecursiveGuard l(x_peers); + for (auto const& i: m_nodes) + { + Node const& n = *(i.second); + nodes.appendList(4); + if (n.address.address().is_v4()) + nodes << n.address.address().to_v4().to_bytes(); + else + nodes << n.address.address().to_v6().to_bytes(); + nodes << n.address.port() << n.id << (int)n.idOrigin + << std::chrono::duration_cast(n.lastConnected.time_since_epoch()).count() + << std::chrono::duration_cast(n.lastAttempted.time_since_epoch()).count() + << n.failedAttempts << n.lastDisconnect << n.score << n.rating; + count++; + } + } + RLPStream ret(3); + ret << 0 << m_key.secret(); + ret.appendList(count).appendRaw(nodes.out(), count); + return ret.out(); } -void Host::restorePeers(bytesConstRef _b) +void Host::restoreNodes(bytesConstRef _b) { - for (auto i: RLP(_b)) - { - auto k = (h512)i[2]; - if (!m_incomingPeers.count(k)) + RecursiveGuard l(x_peers); + RLP r(_b); + if (r.itemCount() == 2 && r[0].isInt()) + switch (r[0].toInt()) { - m_incomingPeers.insert(make_pair(k, make_pair(bi::tcp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()), 0))); - m_freePeers.push_back(k); + case 0: + m_key = KeyPair(r[1].toHash()); + for (auto i: r[2]) + { + auto id = (NodeId)i[2]; + auto o = (Origin)i[3].toInt(); + if (!m_nodes.count(id)) + { + bi::tcp::endpoint ep; + if (i[0].size() == 4) + ep = bi::tcp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()); + else + ep = bi::tcp::endpoint(bi::address_v6(i[0].toArray()), i[1].toInt()); + auto n = noteNode(id, ep, o, true); + n->lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt())); + n->lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt())); + n->failedAttempts = i[6].toInt(); + n->lastDisconnect = (int)i[7].toInt(); + n->score = (int)i[8].toInt(); + n->rating = (int)i[9].toInt(); + } + } + default:; + } + else + for (auto i: r) + { + auto id = (NodeId)i[2]; + if (!m_nodes.count(id)) + { + bi::tcp::endpoint ep; + if (i[0].size() == 4) + ep = bi::tcp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()); + else + ep = bi::tcp::endpoint(bi::address_v6(i[0].toArray()), i[1].toInt()); + auto n = noteNode(id, ep, Origin::Self, true); + } } - } } diff --git a/libp2p/Host.h b/libp2p/Host.h index b4ba9c2d4..b97ff17ba 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -28,8 +28,11 @@ #include #include #include +#include #include #include +#include +#include #include "HostCapability.h" #include "Common.h" namespace ba = boost::asio; @@ -43,6 +46,36 @@ class RLPStream; namespace p2p { +class Host; + +enum class Origin +{ + Unknown, + Self, + SelfThird, + PerfectThird, + Perfect, +}; + +struct Node +{ + NodeId id; ///< Their id/public key. + unsigned index; ///< Index into m_nodesList + bi::tcp::endpoint address; ///< As reported from the node itself. + int score = 0; ///< All time cumulative. + int rating = 0; ///< Trending. + std::chrono::system_clock::time_point lastConnected; + std::chrono::system_clock::time_point lastAttempted; + unsigned failedAttempts = 0; + int lastDisconnect = -1; ///< Reason for disconnect that happened last. + + Origin idOrigin = Origin::Unknown; ///< Thirdparty + + void connect(Host* _h); +}; + +using Nodes = std::vector; + 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) {} @@ -61,6 +94,7 @@ class Host: public Worker { friend class Session; friend class HostCapabilityFace; + friend class Node; public: /// Start server, listening for connections on the given port. @@ -88,7 +122,7 @@ public: void connect(bi::tcp::endpoint const& _ep); /// @returns true iff we have the a peer of the given id. - bool havePeer(h512 _id) const; + bool havePeer(NodeId _id) const; /// Set ideal number of peers. void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } @@ -97,7 +131,7 @@ public: std::vector peers(bool _updatePing = false) const; /// Get number of peers connected; equivalent to, but faster than, peers().size(). - size_t peerCount() const { Guard l(x_peers); return m_peers.size(); } + size_t peerCount() const { RecursiveGuard l(x_peers); return m_peers.size(); } /// Ping the peers, to update the latency information. void pingAll(); @@ -106,10 +140,10 @@ public: unsigned short listenPort() const { return m_public.port(); } /// Serialise the set of known peers. - bytes savePeers() const; + bytes saveNodes() const; /// Deserialise the data and populate the set of known peers. - void restorePeers(bytesConstRef _b); + void restoreNodes(bytesConstRef _b); void setNetworkPreferences(NetworkPreferences const& _p) { stop(); m_netPrefs = _p; start(); } @@ -117,14 +151,13 @@ public: void stop(); bool isStarted() const { return isWorking(); } - h512 id() const { return m_id; } + NodeId id() const { return m_key.pub(); } void registerPeer(std::shared_ptr _s, CapDescs const& _caps); -private: - /// Called when the session has provided us with a new peer we can connect to. - void noteNewPeers() {} + std::shared_ptr node(NodeId _id) const { if (m_nodes.count(_id)) return m_nodes.at(_id); return std::shared_ptr(); } +private: void seal(bytes& _b); void populateAddresses(); void determinePublic(std::string const& _publicAddress, bool _upnp); @@ -138,36 +171,51 @@ private: /// This won't touch alter the blockchain. virtual void doWork(); - std::map potentialPeers(); + std::shared_ptr noteNode(NodeId _id, bi::tcp::endpoint const& _a, Origin _o, bool _ready, NodeId _oldId = h256()); + Nodes potentialPeers(RangeMask const& _known); + + std::string m_clientVersion; ///< Our version string. + + NetworkPreferences m_netPrefs; ///< Network settings. + + static const int NetworkStopped = -1; ///< The value meaning we're not actually listening. + int m_listenPort = NetworkStopped; ///< What port are we listening on? + + ba::io_service m_ioService; ///< IOService for network stuff. + bi::tcp::acceptor m_acceptor; ///< Listening acceptor. + bi::tcp::socket m_socket; ///< Listening socket. - std::string m_clientVersion; + UPnP* m_upnp = nullptr; ///< UPnP helper. + bi::tcp::endpoint m_public; ///< Our public listening endpoint. + KeyPair m_key; ///< Our unique ID. - NetworkPreferences m_netPrefs; + mutable RecursiveMutex x_peers; - static const int NetworkStopped = -1; - int m_listenPort = NetworkStopped; + /// The nodes to which we are currently connected. + /// Mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. + mutable std::map> m_peers; - ba::io_service m_ioService; - bi::tcp::acceptor m_acceptor; - bi::tcp::socket m_socket; + /// Nodes to which we may connect (or to which we have connected). + /// TODO: does this need a lock? + std::map > m_nodes; - UPnP* m_upnp = nullptr; - bi::tcp::endpoint m_public; - h512 m_id; + /// A list of node IDs. This contains every index from m_nodes; the order is guaranteed to remain the same. + std::vector m_nodesList; - mutable std::mutex x_peers; - mutable std::map> m_peers; // mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. + RangeMask m_ready; ///< Indices into m_nodesList over to which nodes we are not currently connected, connecting or otherwise ignoring. + RangeMask m_private; ///< Indices into m_nodesList over to which nodes are private. - std::map> m_incomingPeers; // TODO: does this need a lock? - std::vector m_freePeers; + std::vector m_freePeers;// TODO: Kill + std::chrono::steady_clock::time_point m_lastPeersRequest;// TODO: Kill - std::chrono::steady_clock::time_point m_lastPeersRequest; - unsigned m_idealPeerCount = 5; + unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. - std::vector m_addresses; - std::vector m_peerAddresses; + // Our addresses. + std::vector m_addresses; ///< Addresses for us. + std::vector m_peerAddresses; ///< Addresses that peers (can) know us by. - std::map> m_capabilities; + // Our capabilities. + std::map> m_capabilities; ///< Each of the capabilities we support. bool m_accepting = false; }; diff --git a/libp2p/HostCapability.cpp b/libp2p/HostCapability.cpp index 2f295afd0..0728bef2c 100644 --- a/libp2p/HostCapability.cpp +++ b/libp2p/HostCapability.cpp @@ -34,7 +34,7 @@ void HostCapabilityFace::seal(bytes& _b) std::vector > HostCapabilityFace::peers() const { - Guard l(m_host->x_peers); + RecursiveGuard l(m_host->x_peers); std::vector > ret; for (auto const& i: m_host->m_peers) if (std::shared_ptr p = i.second.lock()) diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index d6a45bfc7..260f6ae51 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -36,19 +36,35 @@ using namespace dev::p2p; #endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " -Session::Session(Host* _s, bi::tcp::socket _socket, bi::address _peerAddress, unsigned short _peerPort): +Session::Session(Host* _s, bi::tcp::socket _socket, bi::tcp::endpoint const& _manual): m_server(_s), m_socket(std::move(_socket)), - m_listenPort(_peerPort), - m_rating(0) + m_node(nullptr), + m_manualEndpoint(_manual) { m_disconnect = std::chrono::steady_clock::time_point::max(); m_connect = std::chrono::steady_clock::now(); - m_info = PeerInfo({"?", _peerAddress.to_string(), m_listenPort, std::chrono::steady_clock::duration(0), CapDescSet(), 0, map()}); + + m_info = PeerInfo({NodeId(), "?", m_manualEndpoint.address().to_string(), m_manualEndpoint.port(), std::chrono::steady_clock::duration(0), CapDescSet(), 0, map()}); +} + +Session::Session(Host* _s, bi::tcp::socket _socket, std::shared_ptr const& _n, bool _force): + m_server(_s), + m_socket(std::move(_socket)), + m_node(_n), + m_manualEndpoint(_n->address), + m_force(_force) +{ + m_disconnect = std::chrono::steady_clock::time_point::max(); + m_connect = std::chrono::steady_clock::now(); + m_info = PeerInfo({m_node->id, "?", _n->address.address().to_string(), _n->address.port(), std::chrono::steady_clock::duration(0), CapDescSet(), 0, map()}); } Session::~Session() { + if (id()) + m_server->noteNode(id(), m_manualEndpoint, Origin::Unknown, true); + // Read-chain finished for one reason or another. for (auto& i: m_capabilities) i.second.reset(); @@ -61,30 +77,72 @@ Session::~Session() catch (...){} } +NodeId Session::id() const +{ + return m_node ? m_node->id : NodeId(); +} + +void Session::addRating(unsigned _r) +{ + if (m_node) + { + m_node->rating += _r; + m_node->score += _r; + } +} + +int Session::rating() const +{ + return m_node->rating; +} + bi::tcp::endpoint Session::endpoint() const { - if (m_socket.is_open()) + if (m_socket.is_open() && m_node) try { - return bi::tcp::endpoint(m_socket.remote_endpoint().address(), m_listenPort); + return bi::tcp::endpoint(m_socket.remote_endpoint().address(), m_node->address.port()); } - catch (...){} + catch (...) {} - return bi::tcp::endpoint(); + if (m_node) + return m_node->address; + + return m_manualEndpoint; +} + +template vector randomSelection(vector const& _t, unsigned _n) +{ + if (_t.size() <= _n) + return _t; + vector ret = _t; + while (ret.size() > _n) + { + auto i = ret.begin(); + advance(i, rand() % ret.size()); + ret.erase(i); + } + return ret; } bool Session::interpret(RLP const& _r) { clogS(NetRight) << _r; + try // Generic try-catch block designed to capture RLP format errors - TODO: give decent diagnostics, make a bit more specific over what is caught. + { + switch ((PacketType)_r[0].toInt()) { case HelloPacket: { + if (m_node) + m_node->lastDisconnect = -1; + m_protocolVersion = _r[1].toInt(); auto clientVersion = _r[2].toString(); auto caps = _r[3].toVector(); - m_listenPort = _r[4].toInt(); - m_id = _r[5].toHash(); + auto listenPort = _r[4].toInt(); + auto id = _r[5].toHash(); // clang error (previously: ... << hex << caps ...) // "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments" @@ -92,32 +150,44 @@ bool Session::interpret(RLP const& _r) for (auto cap: caps) capslog << "(" << hex << cap.first << "," << hex << cap.second << ")"; - clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << m_id.abridged() << showbase << capslog.str() << dec << m_listenPort; + clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << id.abridged() << showbase << capslog.str() << dec << listenPort; - if (m_server->havePeer(m_id)) + if (m_server->havePeer(id)) { // Already connected. - clogS(NetWarn) << "Already have peer id" << m_id.abridged();// << "at" << l->endpoint() << "rather than" << endpoint(); + clogS(NetWarn) << "Already connected to a peer with id" << id.abridged(); disconnect(DuplicatePeer); return false; } - if (!m_id) + + if (m_node && m_node->id != id) { - disconnect(InvalidIdentity); - return false; + if (m_force || m_node->idOrigin <= Origin::SelfThird) + // SECURITY: We're forcing through the new ID, despite having been told + clogS(NetWarn) << "Connected to node, but their ID has changed since last time. This could indicate a MitM attack. Allowing anyway..."; + else + { + clogS(NetWarn) << "Connected to node, but their ID has changed since last time. This could indicate a MitM attack. Disconnecting."; + disconnect(UnexpectedIdentity); + return false; + } } - if (m_protocolVersion != m_server->protocolVersion()) + + if (!id) { - disconnect(IncompatibleProtocol); + disconnect(NullIdentity); return false; } - try - { m_info = PeerInfo({clientVersion, m_socket.remote_endpoint().address().to_string(), m_listenPort, std::chrono::steady_clock::duration(), _r[3].toSet(), (unsigned)m_socket.native_handle(), map() }); } - catch (...) + + m_node = m_server->noteNode(id, bi::tcp::endpoint(m_socket.remote_endpoint().address(), listenPort), Origin::Self, false, m_node->id == id ? NodeId() : m_node->id); + m_knownNodes.unionWith(m_node->index); + + if (m_protocolVersion != m_server->protocolVersion()) { - disconnect(BadProtocol); + disconnect(IncompatibleProtocol); return false; } + m_info = PeerInfo({id, clientVersion, m_socket.remote_endpoint().address().to_string(), listenPort, std::chrono::steady_clock::duration(), _r[3].toSet(), (unsigned)m_socket.native_handle(), map() }); m_server->registerPeer(shared_from_this(), caps); break; @@ -150,16 +220,20 @@ bool Session::interpret(RLP const& _r) case GetPeersPacket: { clogS(NetTriviaSummary) << "GetPeers"; - auto peers = m_server->potentialPeers(); + auto peers = m_server->potentialPeers(m_knownNodes); + if (peers.empty()) + break; RLPStream s; - prep(s, PeersPacket, peers.size()); - for (auto i: peers) + prep(s, PeersPacket, min(10, peers.size())); + auto rs = randomSelection(peers, 10); + for (auto const& i: rs) { - clogS(NetTriviaDetail) << "Sending peer " << i.first.abridged() << i.second; - if (i.second.address().is_v4()) - s.appendList(3) << bytesConstRef(i.second.address().to_v4().to_bytes().data(), 4) << i.second.port() << i.first; + clogS(NetTriviaDetail) << "Sending peer " << i.id.abridged() << i.address; + if (i.address.address().is_v4()) + s.appendList(3) << bytesConstRef(i.address.address().to_v4().to_bytes().data(), 4) << i.address.port() << i.id; else// if (i.second.address().is_v6()) - assumed - s.appendList(3) << bytesConstRef(i.second.address().to_v6().to_bytes().data(), 16) << i.second.port() << i.first; + s.appendList(3) << bytesConstRef(i.address.address().to_v6().to_bytes().data(), 16) << i.address.port() << i.id; + m_knownNodes.unionWith(i.index); } sealAndSend(s); break; @@ -179,28 +253,51 @@ bool Session::interpret(RLP const& _r) return false; } 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); + NodeId id = _r[i][2].toHash(); + clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")" << isPrivateAddress(peerAddress) << this->id().abridged() << isPrivateAddress(endpoint().address()) << m_server->m_nodes.count(id) << (m_server->m_nodes.count(id) ? isPrivateAddress(m_server->m_nodes.at(id)->address.address()) : -1); if (isPrivateAddress(peerAddress) && !m_server->m_netPrefs.localNetworking) - goto CONTINUE; + goto CONTINUE; // Private address. Ignore. + + if (!id) + goto CONTINUE; // Null identity. Ignore. + + if (m_server->id() == id) + goto CONTINUE; // Just our info - we already have that. + + if (id == this->id()) + goto CONTINUE; // Just their info - we already have that. // check that it's not us or one we already know: - 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))) + if (m_server->m_nodes.count(id)) + { + // Already got this node. + // See if it's any better that ours or not... + // This could be the public address of a known node. + // SECURITY: remove this in beta - it's only for lazy connections and presents an easy attack vector. + if (m_server->m_nodes.count(id) && isPrivateAddress(m_server->m_nodes.at(id)->address.address())) + // Update address if the node if we now have a public IP for it. + m_server->m_nodes[id]->address = ep; goto CONTINUE; + } - // check that we're not already connected to addr: if (!ep.port()) - goto CONTINUE; + goto CONTINUE; // Zero port? Don't think so. + + // Avoid our random other addresses that they might end up giving us. for (auto i: m_server->m_addresses) if (ep.address() == i && ep.port() == m_server->listenPort()) goto CONTINUE; - for (auto i: m_server->m_incomingPeers) - if (i.second.first == ep) - goto CONTINUE; - m_server->m_incomingPeers[id] = make_pair(ep, 0); - m_server->m_freePeers.push_back(id); - m_server->noteNewPeers(); + + // Check that we don't already know about this addr:port combination. If we are, assume the original is best. + // SECURITY: Not a valid assumption in general. Should compare ID origins and pick the best or note uncertainty and weight each equally. + for (auto const& i: m_server->m_nodes) + if (i.second->address == ep) + goto CONTINUE; // Same address but a different node. + + // OK passed all our checks. Assume it's good. + addRating(1000); + m_server->noteNode(id, ep, m_node->idOrigin == Origin::Perfect ? Origin::PerfectThird : Origin::SelfThird, true); clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")"; CONTINUE:; } @@ -214,6 +311,12 @@ bool Session::interpret(RLP const& _r) return false; } } + } + catch (...) + { + disconnect(BadProtocol); + return false; + } return true; } @@ -347,6 +450,10 @@ void Session::dropped() void Session::disconnect(int _reason) { clogS(NetConnect) << "Disconnecting (reason:" << reasonOf((DisconnectReason)_reason) << ")"; + + if (m_node) + m_node->lastDisconnect = _reason; + if (m_socket.is_open()) { if (m_disconnect == chrono::steady_clock::time_point::max()) @@ -369,7 +476,7 @@ void Session::start() << m_server->m_clientVersion << m_server->caps() << m_server->m_public.port() - << m_server->m_id; + << m_server->id(); sealAndSend(s); ping(); getPeers(); diff --git a/libp2p/Session.h b/libp2p/Session.h index 1a0ae8730..ff05be81b 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -29,6 +29,7 @@ #include #include #include +#include #include "Common.h" namespace dev @@ -37,6 +38,8 @@ namespace dev namespace p2p { +class Node; + /** * @brief The Session class * @todo Document fully. @@ -47,7 +50,8 @@ class Session: public std::enable_shared_from_this friend class HostCapabilityFace; public: - Session(Host* _server, bi::tcp::socket _socket, bi::address _peerAddress, unsigned short _peerPort = 0); + Session(Host* _server, bi::tcp::socket _socket, std::shared_ptr const& _n, bool _force = false); + Session(Host* _server, bi::tcp::socket _socket, bi::tcp::endpoint const& _manual); virtual ~Session(); void start(); @@ -57,7 +61,7 @@ public: bool isOpen() const { return m_socket.is_open(); } - h512 id() const { return m_id; } + NodeId id() const; unsigned socketId() const { return m_socket.native_handle(); } bi::tcp::endpoint endpoint() const; ///< for other peers to connect to. @@ -71,7 +75,8 @@ public: void sendDestroy(bytes& _msg); void send(bytesConstRef _msg); - void addRating(unsigned _r) { m_rating += _r; } + int rating() const; + void addRating(unsigned _r); void addNote(std::string const& _k, std::string const& _v) { m_info.notes[_k] = _v; } @@ -95,23 +100,22 @@ private: std::mutex m_writeLock; std::deque m_writeQueue; - mutable bi::tcp::socket m_socket; ///< Mutable to ask for native_handle(). + mutable bi::tcp::socket m_socket; ///< Mutable to ask for native_handle(). std::array m_data; PeerInfo m_info; - h512 m_id; bytes m_incoming; unsigned m_protocolVersion; - unsigned short m_listenPort; ///< Port that the remote client is listening on for connections. Useful for giving to peers. + std::shared_ptr m_node; + bi::tcp::endpoint m_manualEndpoint; + bool m_force = false; /// If true, ignore IDs being different. This could open you up to MitM attacks. std::chrono::steady_clock::time_point m_ping; std::chrono::steady_clock::time_point m_connect; std::chrono::steady_clock::time_point m_disconnect; - unsigned m_rating; - std::map> m_capabilities; - std::set m_knownPeers; + RangeMask m_knownNodes; ///< Nodes we already know about as indices into Host's nodesList. These shouldn't be resent to peer. bool m_willBeDeleted = false; ///< True if we already posted a deleter on the strand. }; diff --git a/libwebthree/WebThree.cpp b/libwebthree/WebThree.cpp index 46725de1a..1515a8707 100644 --- a/libwebthree/WebThree.cpp +++ b/libwebthree/WebThree.cpp @@ -68,14 +68,14 @@ void WebThreeDirect::setIdealPeerCount(size_t _n) return m_net.setIdealPeerCount(_n); } -bytes WebThreeDirect::savePeers() +bytes WebThreeDirect::saveNodes() { - return m_net.savePeers(); + return m_net.saveNodes(); } -void WebThreeDirect::restorePeers(bytesConstRef _saved) +void WebThreeDirect::restoreNodes(bytesConstRef _saved) { - return m_net.restorePeers(_saved); + return m_net.restoreNodes(_saved); } void WebThreeDirect::connect(std::string const& _seedHost, unsigned short _port) diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index d4f23ca26..fd0991eaa 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -96,10 +96,10 @@ public: bool haveNetwork() { return peerCount() != 0; } /// Save peers - dev::bytes savePeers(); + dev::bytes saveNodes(); /// Restore peers - void restorePeers(bytesConstRef _saved); + void restoreNodes(bytesConstRef _saved); /// Sets the ideal number of peers. void setIdealPeerCount(size_t _n); diff --git a/libwhisper/Common.h b/libwhisper/Common.h index 251a089b7..26e8b9da8 100644 --- a/libwhisper/Common.h +++ b/libwhisper/Common.h @@ -35,7 +35,6 @@ namespace shh /* this makes these symbols ambiguous on VS2013 using h256 = dev::h256; -using h512 = dev::h512; using h256s = dev::h256s; using bytes = dev::bytes; using RLPStream = dev::RLPStream; diff --git a/third/MainWin.cpp b/third/MainWin.cpp index f86dc0434..399fc8396 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -365,10 +365,10 @@ void Main::writeSettings() s.setValue("address", b); s.setValue("url", ui->urlEdit->text()); - bytes d = m_web3->savePeers(); + bytes d = m_web3->saveNodes(); if (d.size()) - m_peers = QByteArray((char*)d.data(), (int)d.size()); - s.setValue("peers", m_peers); + m_nodes = QByteArray((char*)d.data(), (int)d.size()); + s.setValue("peers", m_nodes); s.setValue("geometry", saveGeometry()); s.setValue("windowState", saveState()); @@ -397,7 +397,7 @@ void Main::readSettings(bool _skipGeometry) } } ethereum()->setAddress(m_myKeys.back().address()); - m_peers = s.value("peers").toByteArray(); + m_nodes = s.value("peers").toByteArray(); ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html on_urlEdit_returnPressed(); } @@ -571,8 +571,8 @@ void Main::ensureNetwork() else if (!m_web3->peerCount()) m_web3->connect(defPeer); - if (m_peers.size()) - m_web3->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); + if (m_nodes.size()) + m_web3->restoreNodes(bytesConstRef((byte*)m_nodes.data(), m_nodes.size())); } void Main::on_connect_triggered() diff --git a/third/MainWin.h b/third/MainWin.h index 0afab773f..fcb7ab304 100644 --- a/third/MainWin.h +++ b/third/MainWin.h @@ -127,7 +127,7 @@ private: unsigned m_currenciesFilter = (unsigned)-1; unsigned m_balancesFilter = (unsigned)-1; - QByteArray m_peers; + QByteArray m_nodes; QStringList m_servers; QNetworkAccessManager m_webCtrl;