diff --git a/alethzero/DownloadView.cpp b/alethzero/DownloadView.cpp index a6533023c..88a595566 100644 --- a/alethzero/DownloadView.cpp +++ b/alethzero/DownloadView.cpp @@ -45,7 +45,7 @@ void DownloadView::paintEvent(QPaintEvent*) double ratio = (double)rect().width() / rect().height(); if (ratio < 1) ratio = 1 / ratio; - double n = min(rect().width(), rect().height()) / ceil(sqrt(m_man->chain().size() / ratio)); + double n = min(16.0, min(rect().width(), rect().height()) / ceil(sqrt(m_man->chain().size() / ratio))); // QSizeF area(rect().width() / floor(rect().width() / n), rect().height() / floor(rect().height() / n)); QSizeF area(n, n); diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 5e86ee59a..5a6eb6f32 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -1480,6 +1480,40 @@ font-size: 14pt + + + QDockWidget::DockWidgetFeatureMask + + + Nodes + + + 2 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + + + + &Quit diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 5455c27ea..f55bf2d9f 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -207,6 +207,12 @@ unsigned Main::installWatch(dev::h256 _tf, std::function const& _f) return ret; } +void Main::uninstallWatch(unsigned _w) +{ + ethereum()->uninstallWatch(_w); + m_handlers.erase(_w); +} + void Main::installWatches() { installWatch(dev::eth::MessageFilter().altered(c_config, 0), [=](){ installNameRegWatch(); }); @@ -217,13 +223,13 @@ void Main::installWatches() void Main::installNameRegWatch() { - ethereum()->uninstallWatch(m_nameRegFilter); + uninstallWatch(m_nameRegFilter); m_nameRegFilter = installWatch(dev::eth::MessageFilter().altered((u160)ethereum()->stateAt(c_config, 0)), [=](){ onNameRegChange(); }); } void Main::installCurrenciesWatch() { - ethereum()->uninstallWatch(m_currenciesFilter); + uninstallWatch(m_currenciesFilter); m_currenciesFilter = installWatch(dev::eth::MessageFilter().altered((u160)ethereum()->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); }); } @@ -242,7 +248,7 @@ void Main::installBalancesWatch() tf.altered(c, (u160)i.address()); } - ethereum()->uninstallWatch(m_balancesFilter); + uninstallWatch(m_balancesFilter); m_balancesFilter = installWatch(tf, [=](){ onBalancesChange(); }); } @@ -500,7 +506,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); @@ -744,11 +750,34 @@ void Main::refreshBalances() void Main::refreshNetwork() { auto ps = web3()->peers(); - ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); ui->peers->clear(); for (PeerInfo const& i: ps) - ui->peers->addItem(QString("[%7] %3 ms - %1:%2 - %4 %5 %6").arg(i.host.c_str()).arg(i.port).arg(chrono::duration_cast(i.lastPing).count()).arg(i.clientVersion.c_str()).arg(QString::fromStdString(toString(i.caps))).arg(QString::fromStdString(toString(i.notes))).arg(i.socket)); + ui->peers->addItem(QString("[%8 %7] %3 ms - %1:%2 - %4 %5 %6") + .arg(QString::fromStdString(i.host)) + .arg(i.port) + .arg(chrono::duration_cast(i.lastPing).count()) + .arg(QString::fromStdString(i.clientVersion)) + .arg(QString::fromStdString(toString(i.caps))) + .arg(QString::fromStdString(toString(i.notes))) + .arg(i.socket) + .arg(QString::fromStdString(i.id.abridged()))); + + auto ns = web3()->nodes(); + ui->nodes->clear(); + for (p2p::Node const& i: ns) + if (!i.dead) + ui->nodes->addItem(QString("[%1%3] %2 - ( =%5s | /%4s | %6 | %7x ) - *%8 $%9") + .arg(QString::fromStdString(i.id.abridged())) + .arg(QString::fromStdString(toString(i.address))) + .arg(i.id == web3()->id() ? " self" : i.isOffline() ? " ripe" : " ----") + .arg(i.secondsSinceLastAttempted()) + .arg(i.secondsSinceLastConnected()) + .arg(QString::fromStdString(reasonOf(i.lastDisconnect))) + .arg(i.failedAttempts) + .arg(i.rating) + .arg((int)i.idOrigin) + ); } void Main::refreshAll() @@ -866,7 +895,7 @@ void Main::refreshBlockChain() string filter = ui->blockChainFilter->text().toLower().toStdString(); 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) + for (auto h = bc.currentHash(); bc.details(h) && i; h = bc.details(h).parent, --i) { auto d = bc.details(h); auto bm = blockMatch(filter, d, h, bc); @@ -906,6 +935,8 @@ void Main::refreshBlockChain() } n++; } + if (h == bc.genesisHash()) + break; } if (!ui->blocks->currentItem()) @@ -1132,7 +1163,10 @@ 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(ethereum()->blockChain().block(info.parentHash)).stateRoot << ""; + if (info.parentHash) + s << "
Pre: " << BlockInfo(ethereum()->blockChain().block(info.parentHash)).stateRoot << ""; + else + s << "
Pre: Nothing is before the Gensesis"; for (auto const& i: block[1]) s << "
" << sha3(i[0].data()).abridged() << ": " << i[1].toHash() << " [" << i[2].toInt() << " used]"; s << "
Post: " << info.stateRoot << ""; @@ -1257,10 +1291,10 @@ void Main::populateDebugger(dev::bytesConstRef _r) if (!m_codes.count(lastDataHash)) m_codes[lastDataHash] = ext.data.toBytes(); } - if (levels.size() < ext.level) + if (levels.size() < ext.depth) levels.push_back(&m_history.back()); else - levels.resize(ext.level); + levels.resize(ext.depth); m_history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); }; m_currentExecution->go(onOp); @@ -1539,10 +1573,10 @@ void Main::on_net_triggered() web3()->setIdealPeerCount(ui->idealPeers->value()); web3()->setNetworkPreferences(netPrefs()); ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : 0); + if (m_peers.size()/* && ui->usePast->isChecked()*/) + web3()->restoreNodes(bytesConstRef((byte*)m_peers.data(), m_peers.size())); web3()->startNetwork(); ui->downloadView->setDownloadMan(ethereum()->downloadMan()); - if (m_peers.size() && ui->usePast->isChecked()) - web3()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); } else { diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index c83848cdd..55b8b4e0a 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -183,6 +183,7 @@ private: unsigned installWatch(dev::eth::MessageFilter const& _tf, std::function const& _f); unsigned installWatch(dev::h256 _tf, std::function const& _f); + void uninstallWatch(unsigned _w); void keysChanged(); diff --git a/eth/main.cpp b/eth/main.cpp index 03be8b969..95d1c21fa 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #if ETH_JSONRPC @@ -166,6 +167,13 @@ string pretty(h160 _a, dev::eth::State _st) return ns; } +bool g_exit = false; + +void sighandler(int) +{ + g_exit = true; +} + int main(int argc, char** argv) { unsigned short listenPort = 30303; @@ -316,6 +324,9 @@ int main(int argc, char** argv) c->setAddress(coinbase); } + auto nodesState = contents(dbPath + "/nodeState.rlp"); + web3.restoreNodes(&nodesState); + cout << "Address: " << endl << toHex(us.address().asArray()) << endl; web3.startNetwork(); @@ -334,11 +345,15 @@ int main(int argc, char** argv) } #endif + signal(SIGABRT, &sighandler); + signal(SIGTERM, &sighandler); + signal(SIGINT, &sighandler); + if (interactive) { string logbuf; string l; - while (true) + while (!g_exit) { g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << "Press Enter" << flush; }; cout << logbuf << "Press Enter" << flush; @@ -612,7 +627,14 @@ int main(int argc, char** argv) Transaction t = state.pending()[index]; state = state.fromPending(index); bytes r = t.rlp(); - e.setup(&r); + try + { + e.setup(&r); + } + catch(Exception const& _e) + { + cwarn << diagnostic_information(_e); + } OnOpFunc oof; if (format == "pretty") @@ -627,7 +649,7 @@ int main(int argc, char** argv) f << " STORAGE" << endl; for (auto const& i: ext->state().storage(ext->myAddress)) f << showbase << hex << i.first << ": " << i.second << endl; - f << dec << ext->level << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << vm->gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32"; + f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << vm->gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32"; }; else if (format == "standard") oof = [&](uint64_t, Instruction instr, bigint, bigint, void* vvm, void const* vextVM) @@ -757,7 +779,7 @@ int main(int argc, char** argv) unsigned n =c->blockChain().details().number; if (mining) c->startMining(); - while (true) + while (!g_exit) { if ( c->isMining() &&c->blockChain().details().number - n == mining) c->stopMining(); @@ -765,9 +787,10 @@ int main(int argc, char** argv) } } else - while (true) + while (!g_exit) this_thread::sleep_for(chrono::milliseconds(1000)); + writeFile(dbPath + "/nodeState.rlp", web3.saveNodes()); return 0; } diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 8a8687bfa..b67e3e62c 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.7.0"; +char const* Version = "0.7.2"; } diff --git a/libdevcore/RangeMask.h b/libdevcore/RangeMask.h index e6a66776c..ac58ec8b1 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,16 +75,28 @@ 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;) { + assert(i >= m_all.first); + assert(i < m_all.second); // for each number, we find the element equal or next lower. this, if any, must contain the value. auto uit = m_ranges.upper_bound(i); auto it = uit == m_ranges.begin() ? m_ranges.end() : std::prev(uit); @@ -130,7 +145,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 +181,7 @@ public: } std::pair const& all() const { return m_all; } + void extendAll(T _i) { m_all = std::make_pair(std::min(m_all.first, _i), std::max(m_all.second, _i + 1)); } class const_iterator { diff --git a/libdevcrypto/MemoryDB.h b/libdevcrypto/MemoryDB.h index 59435d3bf..446a947ec 100644 --- a/libdevcrypto/MemoryDB.h +++ b/libdevcrypto/MemoryDB.h @@ -32,7 +32,7 @@ namespace dev namespace eth { -struct DBChannel: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 12; }; +struct DBChannel: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 18; }; #define dbdebug clog(DBChannel) diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h index d4583dc9b..3dd76899a 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -42,7 +42,7 @@ namespace dev namespace eth { -struct TrieDBChannel: public LogChannel { static const char* name() { return "-T-"; } static const int verbosity = 6; }; +struct TrieDBChannel: public LogChannel { static const char* name() { return "-T-"; } static const int verbosity = 17; }; #define tdebug clog(TrieDBChannel) struct InvalidTrie: virtual dev::Exception {}; @@ -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/libethcore/_libethcore.cpp b/libethcore/_libethcore.cpp index 8a3831584..477034b9e 100644 --- a/libethcore/_libethcore.cpp +++ b/libethcore/_libethcore.cpp @@ -3,4 +3,5 @@ #include "BlockInfo.cpp" #include "CommonEth.cpp" #include "Dagger.cpp" +#include "Exceptions.cpp" #endif diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 0a66a0979..8e0619a43 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -43,7 +43,9 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) for (it->SeekToFirst(); it->Valid(); it->Next()) if (it->key().ToString() != "best") { - BlockDetails d(RLP(it->value().ToString())); + string rlpString = it->value().ToString(); + RLP r(rlpString); + BlockDetails d(r); _out << toHex(it->key().ToString()) << ": " << d.number << " @ " << d.parent << (cmp == it->key().ToString() ? " BEST" : "") << std::endl; } delete it; @@ -437,7 +439,7 @@ void BlockChain::checkConsistency() if (p != h256()) { auto dp = details(p); - assert(contains(dp.children, h)); +// assert(contains(dp.children, h)); // WTF? assert(dp.number == dh.number - 1); } } diff --git a/libethereum/DownloadMan.cpp b/libethereum/DownloadMan.cpp index 3c8dae58e..be33f5187 100644 --- a/libethereum/DownloadMan.cpp +++ b/libethereum/DownloadMan.cpp @@ -66,10 +66,12 @@ h256Set DownloadSub::nextFetch(unsigned _n) return m_remaining; } -void DownloadSub::noteBlock(h256 _hash) +bool DownloadSub::noteBlock(h256 _hash) { Guard l(m_fetch); if (m_man && m_indices.count(_hash)) m_man->m_blocksGot += m_indices[_hash]; + bool ret = !!m_remaining.count(_hash); m_remaining.erase(_hash); + return ret; } diff --git a/libethereum/DownloadMan.h b/libethereum/DownloadMan.h index 6f4c4bafb..1902f3db1 100644 --- a/libethereum/DownloadMan.h +++ b/libethereum/DownloadMan.h @@ -48,8 +48,8 @@ public: /// 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); + /// Note that we've received a particular block. @returns true if we had asked for it but haven't received it yet. + bool noteBlock(h256 _hash); /// Nothing doing here. void doneFetch() { resetFetch(); } diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index bc5f11936..8e92fa9e6 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -202,7 +202,7 @@ void EthereumHost::maintainBlocks(h256 _currentHash) auto p = j->cap(); RLPStream ts; - p->prep(ts, BlocksPacket, hs.size()).appendRaw(bs, hs.size()); + p->prep(ts, NewBlockPacket, hs.size()).appendRaw(bs, hs.size()); Guard l(p->x_knownBlocks); if (!p->m_knownBlocks.count(_currentHash)) 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/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 21fe5fbba..5817aa434 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -308,7 +308,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) disable("Invalid protocol version."); else if (m_networkId != host()->networkId()) disable("Invalid network identifier."); - else if (session()->info().clientVersion.find("/v0.6.9/") != string::npos) + else if (session()->info().clientVersion.find("/v0.7.0/") != string::npos) disable("Blacklisted client version."); else if (host()->isBanned(session()->id())) disable("Peer banned for previous bad behaviour."); @@ -425,44 +425,46 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) unsigned future = 0; unsigned unknown = 0; unsigned got = 0; + unsigned repeated = 0; for (unsigned i = 1; i < _r.itemCount(); ++i) { auto h = BlockInfo::headerHash(_r[i].data()); - m_sub.noteBlock(h); - + if (m_sub.noteBlock(h)) { - Guard l(x_knownBlocks); - m_knownBlocks.insert(h); + addRating(10); + switch (host()->m_bq.import(_r[i].data(), host()->m_chain)) + { + case ImportResult::Success: + success++; + break; + + case ImportResult::Malformed: + disable("Malformed block received."); + return true; + + case ImportResult::FutureTime: + future++; + break; + + case ImportResult::AlreadyInChain: + case ImportResult::AlreadyKnown: + got++; + break; + + case ImportResult::UnknownParent: + unknown++; + break; + } } - - switch (host()->m_bq.import(_r[i].data(), host()->m_chain)) + else { - case ImportResult::Success: - addRating(1); - success++; - break; - - case ImportResult::Malformed: - disable("Malformed block received."); - return true; - - case ImportResult::FutureTime: - future++; - break; - - case ImportResult::AlreadyInChain: - case ImportResult::AlreadyKnown: - got++; - break; - - case ImportResult::UnknownParent: - unknown++; - break; + addRating(0); // -1? + repeated++; } } - clogS(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known."; + clogS(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received."; if (m_asking == Asking::Blocks) transition(Asking::Blocks); @@ -480,8 +482,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) switch (host()->m_bq.import(_r[1].data(), host()->m_chain)) { case ImportResult::Success: + addRating(100); + break; case ImportResult::FutureTime: - addRating(1); + //TODO: Rating dependent on how far in future it is. break; case ImportResult::Malformed: diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 61d179ed8..93900b51e 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -158,7 +158,7 @@ OnOpFunc Executive::simpleTrace() for (auto const& i: ext.state().storage(ext.myAddress)) o << showbase << hex << i.first << ": " << i.second << endl; dev::LogOutputStream(true) << o.str(); - dev::LogOutputStream(false) << " | " << dec << ext.level << " | " << ext.myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << dec << vm.gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32" << " ]"; + dev::LogOutputStream(false) << " | " << dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << dec << vm.gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32" << " ]"; }; } diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index 3b4b7161f..612164d13 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -39,8 +39,8 @@ class ExtVM: public ExtVMFace { public: /// Full constructor. - ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, Manifest* o_ms, unsigned _level = 0): - ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code, _s.m_previousBlock, _s.m_currentBlock), level(_level), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms) + ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, Manifest* o_ms, unsigned _depth = 0): + ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code, _s.m_previousBlock, _s.m_currentBlock, _depth), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms) { m_s.ensureCached(_myAddress, true, true); } @@ -61,7 +61,7 @@ public: m_s.noteSending(myAddress); if (m_ms) m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, level + 1); + auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; @@ -72,7 +72,7 @@ public: { if (m_ms) m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, level + 1); + auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; @@ -100,9 +100,6 @@ public: State& state() const { return m_s; } - /// @note not a part of the main API; just for use by tracing/debug stuff. - unsigned level = 0; - private: State& m_s; ///< A reference to the base state. std::map m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution. diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 047b9dcd5..105df0172 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 = @@ -1052,7 +1054,14 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) Manifest ms; Executive e(*this, &ms); - e.setup(_rlp); + try + { + e.setup(_rlp); + } + catch (Exception const & _e) + { + cwarn << diagnostic_information(_e); + } u256 startGasUsed = gasUsed(); @@ -1284,8 +1293,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 +1307,8 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s) stringstream contout; - if ((!cache || cache->codeBearing()) && (!r || r[3].toHash() != EmptySHA3)) + /// For POC6, 3rd value of account is code and will be empty if code is not present. + if ((cache && cache->codeBearing()) || (!cache && r && !r[3].isEmpty())) { std::map mem; std::set back; diff --git a/libethereum/State.h b/libethereum/State.h index a28d8155c..c73fc95dc 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -40,7 +40,7 @@ namespace dev { -namespace test{ class FakeExtVM;} +namespace test{ class FakeExtVM; class FakeState;} namespace eth { @@ -84,6 +84,7 @@ class State { friend class ExtVM; friend class test::FakeExtVM; + friend class test::FakeState; friend class Executive; public: diff --git a/libevm/ExtVMFace.cpp b/libevm/ExtVMFace.cpp index 7c938417a..da189d899 100644 --- a/libevm/ExtVMFace.cpp +++ b/libevm/ExtVMFace.cpp @@ -25,7 +25,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock): +ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, unsigned _depth): myAddress(_myAddress), caller(_caller), origin(_origin), @@ -34,6 +34,7 @@ ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 data(_data), code(_code), previousBlock(_previousBlock), - currentBlock(_currentBlock) + currentBlock(_currentBlock), + depth(_depth) {} diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index f78cb82cb..1b0f9eaf5 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -54,7 +54,7 @@ public: ExtVMFace() {} /// Full constructor. - ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock); + ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, unsigned _depth); /// Get the code at the given location in code ROM. byte getCode(u256 _n) const { return _n < code.size() ? code[(unsigned)_n] : 0; } @@ -99,6 +99,7 @@ public: BlockInfo previousBlock; ///< The previous block's information. BlockInfo currentBlock; ///< The current block's information. std::set
suicides; ///< Any accounts that have suicided. + unsigned depth; ///< Depth of the present call. }; } diff --git a/libevm/VM.cpp b/libevm/VM.cpp index 4a3f70d5e..e47237d5a 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -29,6 +29,4 @@ void VM::reset(u256 _gas) { m_gas = _gas; m_curPC = 0; - m_jumpLatch = false; - m_destinations.clear(); } diff --git a/libevm/VM.h b/libevm/VM.h index 86acd8c26..401e30baf 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -84,8 +84,6 @@ private: u256 m_curPC = 0; bytes m_temp; u256s m_stack; - bool m_jumpLatch = false; - u256Set m_destinations; }; } @@ -190,8 +188,116 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; } - default: + case Instruction::ADD: + case Instruction::MUL: + case Instruction::SUB: + case Instruction::DIV: + case Instruction::SDIV: + case Instruction::MOD: + case Instruction::SMOD: + case Instruction::EXP: + case Instruction::NEG: + case Instruction::LT: + case Instruction::GT: + case Instruction::SLT: + case Instruction::SGT: + case Instruction::EQ: + case Instruction::NOT: + case Instruction::AND: + case Instruction::OR: + case Instruction::XOR: + case Instruction::BYTE: + case Instruction::ADDMOD: + case Instruction::MULMOD: + case Instruction::ADDRESS: + case Instruction::ORIGIN: + case Instruction::CALLER: + case Instruction::CALLVALUE: + case Instruction::CALLDATALOAD: + case Instruction::CALLDATASIZE: + case Instruction::CODESIZE: + case Instruction::EXTCODESIZE: + case Instruction::GASPRICE: + case Instruction::PREVHASH: + case Instruction::COINBASE: + case Instruction::TIMESTAMP: + case Instruction::NUMBER: + case Instruction::DIFFICULTY: + case Instruction::GASLIMIT: + case Instruction::PUSH1: + case Instruction::PUSH2: + case Instruction::PUSH3: + case Instruction::PUSH4: + case Instruction::PUSH5: + case Instruction::PUSH6: + case Instruction::PUSH7: + case Instruction::PUSH8: + case Instruction::PUSH9: + case Instruction::PUSH10: + case Instruction::PUSH11: + case Instruction::PUSH12: + case Instruction::PUSH13: + case Instruction::PUSH14: + case Instruction::PUSH15: + case Instruction::PUSH16: + case Instruction::PUSH17: + case Instruction::PUSH18: + case Instruction::PUSH19: + case Instruction::PUSH20: + case Instruction::PUSH21: + case Instruction::PUSH22: + case Instruction::PUSH23: + case Instruction::PUSH24: + case Instruction::PUSH25: + case Instruction::PUSH26: + case Instruction::PUSH27: + case Instruction::PUSH28: + case Instruction::PUSH29: + case Instruction::PUSH30: + case Instruction::PUSH31: + case Instruction::PUSH32: + case Instruction::POP: + case Instruction::DUP1: + case Instruction::DUP2: + case Instruction::DUP3: + case Instruction::DUP4: + case Instruction::DUP5: + case Instruction::DUP6: + case Instruction::DUP7: + case Instruction::DUP8: + case Instruction::DUP9: + case Instruction::DUP10: + case Instruction::DUP11: + case Instruction::DUP12: + case Instruction::DUP13: + case Instruction::DUP14: + case Instruction::DUP15: + case Instruction::DUP16: + case Instruction::SWAP1: + case Instruction::SWAP2: + case Instruction::SWAP3: + case Instruction::SWAP4: + case Instruction::SWAP5: + case Instruction::SWAP6: + case Instruction::SWAP7: + case Instruction::SWAP8: + case Instruction::SWAP9: + case Instruction::SWAP10: + case Instruction::SWAP11: + case Instruction::SWAP12: + case Instruction::SWAP13: + case Instruction::SWAP14: + case Instruction::SWAP15: + case Instruction::SWAP16: + case Instruction::JUMP: + case Instruction::JUMPI: + case Instruction::PC: + case Instruction::MSIZE: + case Instruction::GAS: + case Instruction::JUMPDEST: break; + default: + BOOST_THROW_EXCEPTION(BadInstruction()); } newTempSize = (newTempSize + 31) / 32 * 32; @@ -568,19 +674,19 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::JUMP: require(1); - m_jumpLatch = true; - if (!m_destinations.count(m_stack.back())) - BOOST_THROW_EXCEPTION(BadJumpDestination()); nextPC = m_stack.back(); + if (nextPC && (Instruction)_ext.getCode(nextPC - 1) != Instruction::JUMPDEST) + BOOST_THROW_EXCEPTION(BadJumpDestination()); m_stack.pop_back(); break; case Instruction::JUMPI: require(2); - m_jumpLatch = true; - if (!m_destinations.count(m_stack.back())) - BOOST_THROW_EXCEPTION(BadJumpDestination()); if (m_stack[m_stack.size() - 2]) + { nextPC = m_stack.back(); + if (nextPC && (Instruction)_ext.getCode(nextPC - 1) != Instruction::JUMPDEST) + BOOST_THROW_EXCEPTION(BadJumpDestination()); + } m_stack.pop_back(); m_stack.pop_back(); break; @@ -594,10 +700,6 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con m_stack.push_back(m_gas); break; case Instruction::JUMPDEST: - require(1); - if (!m_jumpLatch) - m_destinations.insert(m_stack.back()); - m_stack.pop_back(); break; case Instruction::CREATE: { @@ -612,6 +714,8 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con if (_ext.balance(_ext.myAddress) >= endowment) { + if (_ext.depth == 1024) + BOOST_THROW_EXCEPTION(OutOfGas()); _ext.subBalance(endowment); m_stack.push_back((u160)_ext.create(endowment, &m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp)); } @@ -642,6 +746,8 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con if (_ext.balance(_ext.myAddress) >= value) { + if (_ext.depth == 1024) + BOOST_THROW_EXCEPTION(OutOfGas()); _ext.subBalance(value); m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, Address(), receiveAddress)); } @@ -671,8 +777,6 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con } case Instruction::STOP: return bytesConstRef(); - default: - BOOST_THROW_EXCEPTION(BadInstruction()); } } if (_steps == (uint64_t)-1) diff --git a/liblll/Assembly.cpp b/liblll/Assembly.cpp index 11ee9122c..c26a9a98e 100644 --- a/liblll/Assembly.cpp +++ b/liblll/Assembly.cpp @@ -129,7 +129,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) _out << " PUSH[tag" << i.data() << "]"; break; case Tag: - _out << " tag" << i.data() << ":"; + _out << " tag" << i.data() << ": JUMPDEST"; break; case PushData: _out << " PUSH*[" << hex << (unsigned)i.data() << "]"; @@ -149,17 +149,6 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) ostream& Assembly::streamOut(ostream& _out, string const& _prefix) const { - _out << _prefix << ".pre:" << endl; - for (AssemblyItem const& i: m_items) - switch (i.m_type) - { - case PushTag: - _out << _prefix << " PUSH [tag" << i.m_data << "]" << endl; - _out << _prefix << " JUMPDEST" << endl; - break; - default:; - } - _out << _prefix << ".code:" << endl; for (AssemblyItem const& i: m_items) switch (i.m_type) @@ -183,7 +172,7 @@ ostream& Assembly::streamOut(ostream& _out, string const& _prefix) const _out << _prefix << " PUSH #[$" << h256(i.m_data).abridged() << "]" << endl; break; case Tag: - _out << _prefix << "tag" << i.m_data << ": " << endl; + _out << _prefix << "tag" << i.m_data << ": " << endl << _prefix << " JUMPDEST" << endl; break; case PushData: _out << _prefix << " PUSH [" << hex << (unsigned)i.m_data << "]" << endl; @@ -364,11 +353,9 @@ bytes Assembly::assemble() const ret.reserve(totalBytes); vector tagPos(m_usedTags); map tagRef; - map pretagRef; multimap dataRef; unsigned bytesPerTag = dev::bytesRequired(totalBytes); byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag; - bytes preret; for (auto const& i: m_subs) m_data[i.first] = i.second.assemble(); @@ -406,11 +393,6 @@ bytes Assembly::assemble() const ret.push_back(tagPush); tagRef[ret.size()] = (unsigned)i.m_data; ret.resize(ret.size() + bytesPerTag); - - preret.push_back(tagPush); - pretagRef[preret.size()] = (unsigned)i.m_data; - preret.resize(preret.size() + bytesPerTag); - preret.push_back((byte)Instruction::JUMPDEST); break; } case PushData: case PushSub: @@ -431,6 +413,7 @@ bytes Assembly::assemble() const break; } case Tag: + ret.push_back((byte)Instruction::JUMPDEST); tagPos[(unsigned)i.m_data] = ret.size(); break; default:; @@ -442,12 +425,6 @@ bytes Assembly::assemble() const toBigEndian(tagPos[i.second], r); } - for (auto const& i: pretagRef) - { - bytesRef r(preret.data() + i.first, bytesPerTag); - toBigEndian(tagPos[i.second], r); - } - if (m_data.size()) { ret.push_back(0); @@ -466,5 +443,5 @@ bytes Assembly::assemble() const } } } - return preret + ret; + return ret; } 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.cpp b/libp2p/Common.cpp index 540f285c1..8a3b27ef0 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -64,7 +64,12 @@ std::string p2p::reasonOf(DisconnectReason _r) case TooManyPeers: return "Peer had too many connections."; case DuplicatePeer: return "Peer was already connected."; case IncompatibleProtocol: return "Peer protocol versions are incompatible."; + case NullIdentity: return "Null identity given."; case ClientQuit: return "Peer is exiting."; + case UnexpectedIdentity: return "Unexpected identity given."; + case LocalIdentity: return "Connected to ourselves."; + case UserReason: return "Subprotocol reason."; + case NoDisconnect: return "(No disconnect has happened.)"; default: return "Unknown reason."; } } diff --git a/libp2p/Common.h b/libp2p/Common.h index 895b76404..04154c9a0 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,11 +84,30 @@ enum DisconnectReason TooManyPeers, DuplicatePeer, IncompatibleProtocol, - InvalidIdentity, + NullIdentity, ClientQuit, - UserReason = 0x10 + UnexpectedIdentity, + LocalIdentity, + UserReason = 0x10, + NoDisconnect = 0xffff }; +inline bool isPermanentProblem(DisconnectReason _r) +{ + switch (_r) + { + case DisconnectRequested: + case DuplicatePeer: + case IncompatibleProtocol: + case NullIdentity: + case UnexpectedIdentity: + case LocalIdentity: + return true; + default: + return false; + } +} + /// @returns the string form of the given disconnection reason. std::string reasonOf(DisconnectReason _r); @@ -96,6 +117,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 05fa81f4c..44fa137e5 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -63,11 +63,10 @@ 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 +108,10 @@ void Host::start() determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); ensureAccepting(); - m_incomingPeers.clear(); - m_freePeers.clear(); + if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id())) + 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 +147,15 @@ unsigned Host::protocolVersion() const void Host::registerPeer(std::shared_ptr _s, CapDescs const& _caps) { + if (!_s->m_node || !_s->m_node->id) { - Guard l(x_peers); - m_peers[_s->m_id] = _s; + cwarn << "Attempting to register a peer without node information!"; + return; + } + + { + RecursiveGuard l(x_peers); + m_peers[_s->m_node->id] = _s; } unsigned o = (unsigned)UserPacket; for (auto const& i: _caps) @@ -167,7 +171,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()) { @@ -182,6 +186,7 @@ void Host::disconnectPeers() } delete m_upnp; + m_upnp = nullptr; } void Host::seal(bytes& _b) @@ -210,7 +215,10 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) if (m_upnp && m_upnp->isValid() && m_peerAddresses.size()) { clog(NetNote) << "External addr:" << m_upnp->externalIP(); - int p = m_upnp->addRedirect(m_peerAddresses[0].to_string().c_str(), m_listenPort); + int p; + for (auto const& addr : m_peerAddresses) + if ((p = m_upnp->addRedirect(addr.to_string().c_str(), m_listenPort))) + break; if (p) clog(NetNote) << "Punched through NAT and mapped local port" << m_listenPort << "onto external port" << p << "."; else @@ -226,7 +234,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 @@ -235,7 +243,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()); } } @@ -308,33 +316,92 @@ 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); + cnote << "Node:" << _id.abridged() << _a << (_ready ? "ready" : "used") << _oldId.abridged() << (m_nodes.count(_id) ? "[have]" : "[NEW]"); + if (!_a.port()) + { + cwarn << "PORT IS INVALID!"; + } + unsigned i; + if (!m_nodes.count(_id)) + { + 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)); + i = m_nodes[_oldId]->index; + m_nodes.erase(_oldId); + m_nodesList[i] = _id; + } + else + { + i = m_nodesList.size(); + m_nodesList.push_back(_id); } + m_nodes[_id] = make_shared(); + m_nodes[_id]->id = _id; + m_nodes[_id]->address = _a; + m_nodes[_id]->index = i; + 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; + + cnote << m_nodes[_id]->index << ":" << m_ready; + + m_hadNewNodes = true; + + return m_nodes[_id]; +} + +Nodes Host::potentialPeers(RangeMask const& _known) +{ + RecursiveGuard l(x_peers); + Nodes ret; + + auto ns = (m_netPrefs.localNetworking ? _known : (m_private + _known)).inverted(); + for (auto i: ns) + ret.push_back(*m_nodes[m_nodesList[i]]); return ret; } @@ -355,7 +422,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) @@ -411,26 +478,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(); } @@ -438,9 +494,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 << "from" << _h->id().abridged(); + _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();) @@ -452,43 +534,55 @@ bool Host::havePeer(h512 _id) const return !!m_peers.count(_id); } +unsigned cumulativeFallback(unsigned _failed) +{ + if (_failed < 5) + return _failed * 5; + else if (_failed < 15) + return 25 + (_failed - 5) * 10; + else + return 25 + 100 + (_failed - 15) * 20; +} + void Host::growPeers() { - Guard l(x_peers); - while (m_peers.size() < m_idealPeerCount) + RecursiveGuard l(x_peers); + int morePeers = (int)m_idealPeerCount - m_peers.size(); + if (morePeers > 0) { - if (m_freePeers.empty()) - { - if (chrono::steady_clock::now() > m_lastPeersRequest + chrono::seconds(10)) + auto toTry = m_ready; + if (!m_netPrefs.localNetworking) + toTry -= m_private; + set ns; + for (auto i: toTry) + if (chrono::system_clock::now() > m_nodes[m_nodesList[i]]->lastAttempted + chrono::seconds(cumulativeFallback(m_nodes[m_nodesList[i]]->failedAttempts))) + ns.insert(*m_nodes[m_nodesList[i]]); + + if (ns.size()) + for (Node const& i: ns) { - RLPStream s; - bytes b; - Session::prep(s, GetPeersPacket).swapOut(b); - seal(b); - for (auto const& i: m_peers) - if (auto p = i.second.lock()) - if (p->isOpen()) - p->send(&b); - m_lastPeersRequest = chrono::steady_clock::now(); + m_nodes[i.id]->connect(this); + if (!--morePeers) + return; } - - if (!m_accepting) - ensureAccepting(); - - break; + else + { + ensureAccepting(); + requestNodes(); } - - 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); - m_freePeers.erase(m_freePeers.begin() + x); } } +void Host::requestNodes() +{ + for (auto const& i: m_peers) + if (auto p = i.second.lock()) + p->ensureNodesRequested(); +} + 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) @@ -502,7 +596,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) @@ -520,10 +614,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()) @@ -536,41 +632,105 @@ void Host::doWork() { growPeers(); prunePeers(); + + if (m_hadNewNodes) + { + for (auto p: m_peers) + if (auto pp = p.second.lock()) + pp->serviceNodesRequest(); + + m_hadNewNodes = false; + } + m_ioService.poll(); } 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()) + RLPStream nodes; + int count = 0; + { + RecursiveGuard l(x_peers); + for (auto const& i: m_nodes) + { + Node const& n = *(i.second); + if (!n.dead && n.id != id() && !isPrivateAddress(n.address.address())) { - ret.appendList(3) << p->endpoint().address().to_v4().to_bytes() << p->endpoint().port() << p->m_id; - n++; + nodes.appendList(10); + 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 << (unsigned)n.lastDisconnect << n.score << n.rating; + count++; } - return RLPStream(n).appendRaw(ret.out(), n).out(); + } + } + 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() > 0 && r[0].isInt()) + switch (r[0].toInt()) + { + case 0: { - 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); + auto oldId = id(); + m_key = KeyPair(r[1].toHash()); + noteNode(id(), m_public, Origin::Perfect, false, oldId); + + for (auto i: r[2]) + { + bi::tcp::endpoint ep; + if (i[0].itemCount() == 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 id = (NodeId)i[2]; + if (!m_nodes.count(id)) + { + auto o = (Origin)i[3].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 = (DisconnectReason)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].itemCount() == 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..a7beb8c35 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,60 @@ 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. + bool dead = false; ///< If true, we believe this node is permanently dead - forget all about it. + std::chrono::system_clock::time_point lastConnected; + std::chrono::system_clock::time_point lastAttempted; + unsigned failedAttempts = 0; + DisconnectReason lastDisconnect = NoDisconnect; ///< Reason for disconnect that happened last. + + Origin idOrigin = Origin::Unknown; ///< How did we get to know this node's id? + + int secondsSinceLastConnected() const { return lastConnected == std::chrono::system_clock::time_point() ? -1 : (int)std::chrono::duration_cast(std::chrono::system_clock::now() - lastConnected).count(); } + int secondsSinceLastAttempted() const { return lastAttempted == std::chrono::system_clock::time_point() ? -1 : (int)std::chrono::duration_cast(std::chrono::system_clock::now() - lastAttempted).count(); } + + bool isOffline() const { return lastDisconnect == -1 || lastAttempted > lastConnected; } + bool operator<(Node const& _n) const + { + if (isOffline() != _n.isOffline()) + return isOffline(); + else if (isOffline()) + if (lastAttempted == _n.lastAttempted) + return failedAttempts < _n.failedAttempts; + else + return lastAttempted < _n.lastAttempted; + else + if (score == _n.score) + if (rating == _n.rating) + return failedAttempts < _n.failedAttempts; + else + return rating < _n.rating; + else + return score < _n.score; + } + + 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 +118,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 +146,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 +155,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 +164,12 @@ 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); + + Nodes nodes() const { RecursiveGuard l(x_peers); Nodes ret; for (auto const& i: m_nodes) ret.push_back(*i.second); return ret; } void setNetworkPreferences(NetworkPreferences const& _p) { stop(); m_netPrefs = _p; start(); } @@ -117,14 +177,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 +197,52 @@ 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); + + void requestNodes(); + + 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; + bool m_hadNewNodes = false; - static const int NetworkStopped = -1; - int m_listenPort = NetworkStopped; + mutable RecursiveMutex x_peers; - ba::io_service m_ioService; - bi::tcp::acceptor m_acceptor; - bi::tcp::socket m_socket; + /// 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; - UPnP* m_upnp = nullptr; - bi::tcp::endpoint m_public; - h512 m_id; + /// Nodes to which we may connect (or to which we have connected). + /// TODO: does this need a lock? + std::map > m_nodes; - 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. + /// A list of node IDs. This contains every index from m_nodes; the order is guaranteed to remain the same. + std::vector m_nodesList; - std::map> m_incomingPeers; // TODO: does this need a lock? - std::vector m_freePeers; + 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::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 12f48f3c9..3f224a72d 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_node || (!isPermanentProblem(m_node->lastDisconnect) && !m_node->dead))) + m_server->m_ready += m_node->index; + // Read-chain finished for one reason or another. for (auto& i: m_capabilities) i.second.reset(); @@ -61,57 +77,174 @@ 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 (...) {} + + if (m_node) + return m_node->address; + + return m_manualEndpoint; +} - return bi::tcp::endpoint(); +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; +} + +void Session::ensureNodesRequested() +{ + if (isOpen() && !m_weRequestedNodes) + { + m_weRequestedNodes = true; + RLPStream s; + sealAndSend(prep(s, GetPeersPacket)); + } +} + +void Session::serviceNodesRequest() +{ + if (!m_theyRequestedNodes) + return; + + auto peers = m_server->potentialPeers(m_knownNodes); + if (peers.empty()) + { + addNote("peers", "requested"); + return; + } + + // note this should cost them... + RLPStream s; + prep(s, PeersPacket, min(10, peers.size())); + auto rs = randomSelection(peers, 10); + for (auto const& i: rs) + { + 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.address.address().to_v6().to_bytes().data(), 16) << i.address.port() << i.id; + m_knownNodes.extendAll(i.index); + m_knownNodes.unionWith(i.index); + } + sealAndSend(s); + m_theyRequestedNodes = false; + addNote("peers", "done"); } 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 = NoDisconnect; + 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(); - clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << m_id.abridged() << showbase << hex << caps << dec << m_listenPort; + // clang error (previously: ... << hex << caps ...) + // "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments" + stringstream capslog; + for (auto cap: caps) + capslog << "(" << hex << cap.first << "," << hex << cap.second << ")"; - if (m_server->havePeer(m_id)) + clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << id.abridged() << showbase << capslog.str() << dec << listenPort; + + if (m_server->id() == id) { // Already connected. - clogS(NetWarn) << "Already have peer id" << m_id.abridged();// << "at" << l->endpoint() << "rather than" << endpoint(); - disconnect(DuplicatePeer); + clogS(NetWarn) << "Connected to ourself under a false pretext. We were told this peer was id" << m_info.id.abridged(); + disconnect(LocalIdentity); return false; } - if (!m_id) + + if (m_node && m_node->id != id) { - disconnect(InvalidIdentity); + 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_server->havePeer(id)) + { + m_node->dead = true; + disconnect(DuplicatePeer); + return false; + } + } + + if (m_server->havePeer(id)) + { + // Already connected. + clogS(NetWarn) << "Already connected to a peer with id" << id.abridged(); + disconnect(DuplicatePeer); 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 || m_node->id == id ? NodeId() : m_node->id); + m_knownNodes.extendAll(m_node->index); + 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; @@ -144,22 +277,13 @@ bool Session::interpret(RLP const& _r) case GetPeersPacket: { clogS(NetTriviaSummary) << "GetPeers"; - auto peers = m_server->potentialPeers(); - RLPStream s; - prep(s, PeersPacket, peers.size()); - for (auto i: peers) - { - 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; - 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; - } - sealAndSend(s); + m_theyRequestedNodes = true; + serviceNodesRequest(); break; } case PeersPacket: clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; + m_weRequestedNodes = false; for (unsigned i = 1; i < _r.itemCount(); ++i) { bi::address peerAddress; @@ -173,28 +297,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:; } @@ -208,6 +355,12 @@ bool Session::interpret(RLP const& _r) return false; } } + } + catch (...) + { + disconnect(BadProtocol); + return false; + } return true; } @@ -218,12 +371,6 @@ void Session::ping() m_ping = std::chrono::steady_clock::now(); } -void Session::getPeers() -{ - RLPStream s; - sealAndSend(prep(s, GetPeersPacket)); -} - RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args) { return prep(_s).appendList(_args + 1).append((unsigned)_id); @@ -338,9 +485,13 @@ void Session::dropped() catch (...) {} } -void Session::disconnect(int _reason) +void Session::disconnect(DisconnectReason _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()) @@ -363,11 +514,9 @@ 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(); - doRead(); } diff --git a/libp2p/Session.h b/libp2p/Session.h index 1a0ae8730..e76edd8b1 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,17 +50,18 @@ 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(); - void disconnect(int _reason); + void disconnect(DisconnectReason _reason); void ping(); 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,12 +75,16 @@ 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; } PeerInfo const& info() const { return m_info; } + void ensureNodesRequested(); + void serviceNodesRequest(); + private: void dropped(); void doRead(); @@ -84,7 +92,6 @@ private: void writeImpl(bytes& _buffer); void write(); - void getPeers(); bool interpret(RLP const& _r); /// @returns true iff the _msg forms a valid message for sending or receiving on the network. @@ -95,23 +102,25 @@ 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. + + bool m_theyRequestedNodes = false; + bool m_weRequestedNodes = false; 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/libp2p/UPnP.cpp b/libp2p/UPnP.cpp index 211b185bb..42868d67a 100644 --- a/libp2p/UPnP.cpp +++ b/libp2p/UPnP.cpp @@ -123,16 +123,18 @@ int UPnP::addRedirect(char const* _addr, int _port) // Try direct mapping first (port external, port internal). char port_str[16]; + char ext_port_str[16]; sprintf(port_str, "%d", _port); if (!UPNP_AddPortMapping(m_urls->controlURL, m_data->first.servicetype, port_str, port_str, _addr, "ethereum", "TCP", NULL, NULL)) return _port; // Failed - now try (random external, port internal) and cycle up to 10 times. + srand(time(NULL)); for (unsigned i = 0; i < 10; ++i) { - _port = rand() % 65535 - 1024 + 1024; - sprintf(port_str, "%d", _port); - if (!UPNP_AddPortMapping(m_urls->controlURL, m_data->first.servicetype, NULL, port_str, _addr, "ethereum", "TCP", NULL, NULL)) + _port = rand() % (65535 - 1024) + 1024; + sprintf(ext_port_str, "%d", _port); + if (!UPNP_AddPortMapping(m_urls->controlURL, m_data->first.servicetype, ext_port_str, port_str, _addr, "ethereum", "TCP", NULL, NULL)) return _port; } 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..32bbe0b31 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); @@ -108,6 +108,11 @@ public: void setNetworkPreferences(p2p::NetworkPreferences const& _n) { auto had = haveNetwork(); if (had) stopNetwork(); m_net.setNetworkPreferences(_n); if (had) startNetwork(); } + p2p::NodeId id() const { return m_net.id(); } + + /// Gets the nodes. + p2p::Nodes nodes() const { return m_net.nodes(); } + /// Start the network subsystem. void startNetwork() { m_net.start(); } 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/test/vm.cpp b/test/vm.cpp index a90b122e2..cc87866df 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -31,8 +31,8 @@ using namespace dev; using namespace dev::eth; using namespace dev::test; -FakeExtVM::FakeExtVM(eth::BlockInfo const& _previousBlock, eth::BlockInfo const& _currentBlock): - ExtVMFace(Address(), Address(), Address(), 0, 1, bytesConstRef(), bytesConstRef(), _previousBlock, _currentBlock) {} +FakeExtVM::FakeExtVM(eth::BlockInfo const& _previousBlock, eth::BlockInfo const& _currentBlock, unsigned _depth): /// TODO: XXX: remove the default argument & fix. + ExtVMFace(Address(), Address(), Address(), 0, 1, bytesConstRef(), bytesConstRef(), _previousBlock, _currentBlock, _depth) {} h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFunc) { @@ -41,7 +41,6 @@ h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFun t.gasPrice = gasPrice; t.gas = *_gas; t.data = _init.toBytes(); - callcreates.push_back(t); m_s.noteSending(myAddress); m_ms.internal.resize(m_ms.internal.size() + 1); @@ -56,57 +55,103 @@ h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFun get<3>(addresses[ret]) = m_s.code(ret); } + t.receiveAddress = ret; + callcreates.push_back(t); return ret; } -bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, OnOpFunc, Address, Address) +bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, OnOpFunc, Address _myAddressOverride = Address(), Address _codeAddressOverride = Address()) { + + u256 contractgas = 0xffff; + Transaction t; t.value = _value; t.gasPrice = gasPrice; t.gas = *_gas; t.data = _data.toVector(); t.receiveAddress = _receiveAddress; + callcreates.push_back(t); + + string codeOf_CodeAddress = _codeAddressOverride ? toHex(get<3>(addresses[_codeAddressOverride])) : toHex(get<3>(addresses[_receiveAddress]) ); + string sizeOfCode = toHex(toCompactBigEndian((codeOf_CodeAddress.size()+1)/2)); + + string codeOf_SenderAddress = toHex(get<3>(addresses[myAddress]) ); + string sizeOfSenderCode = toHex(toCompactBigEndian((codeOf_SenderAddress.size()+1)/2)); + + if (codeOf_SenderAddress.size()) + { + // create init code that returns given contract code + string initStringHex = "{ (CODECOPY 0 (- (CODESIZE) 0x" + sizeOfSenderCode + " ) 0x" + sizeOfSenderCode + ") (RETURN 0 0x" + sizeOfSenderCode +")}"; + bytes initBytes = compileLLL(initStringHex, true, NULL); + initBytes += fromHex(codeOf_SenderAddress); + bytesConstRef init(&initBytes); - string codeOf_receiveAddress = toHex(get<3>(addresses[_receiveAddress]) ); - string sizeOfCode = toHex(toCompactBigEndian((codeOf_receiveAddress.size()+1)/2)); + if (!m_s.addresses().count(myAddress)) + { + m_ms.internal.resize(m_ms.internal.size() + 1); + auto na = m_s.createNewAddress(myAddress, myAddress, balance(myAddress), gasPrice, &contractgas, init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1); + if (!m_ms.internal.back().from) + m_ms.internal.pop_back(); + if (na != myAddress) + { + cnote << "not able to call to : " << myAddress << "\n"; + cnote << "in FakeExtVM you can only make a call to " << na << "\n"; + BOOST_THROW_EXCEPTION(FakeExtVMFailure() << errinfo_comment("Address not callable in FakeExtVM\n") << errinfo_wrongAddress(myAddress)); + return false; + } + } + } - if (codeOf_receiveAddress.size()) + if (codeOf_CodeAddress.size()) { // create init code that returns given contract code string initStringHex = "{ (CODECOPY 0 (- (CODESIZE) 0x" + sizeOfCode + " ) 0x" + sizeOfCode + ") (RETURN 0 0x" + sizeOfCode +")}"; bytes initBytes = compileLLL(initStringHex, true, NULL); - initBytes += fromHex(codeOf_receiveAddress); + initBytes += fromHex(codeOf_CodeAddress); bytesConstRef init(&initBytes); - if (!m_s.addresses().count(_receiveAddress)) + if (!m_s.addresses().count(_codeAddressOverride ? _codeAddressOverride : _receiveAddress)) { m_s.noteSending(myAddress); m_ms.internal.resize(m_ms.internal.size() + 1); - auto na = m_s.create(myAddress, 0, gasPrice, _gas, init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1); + auto na = m_s.createNewAddress(_codeAddressOverride ? _codeAddressOverride : _receiveAddress, myAddress, balance(_codeAddressOverride ? _codeAddressOverride : _receiveAddress), gasPrice, &contractgas, init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); - if (!m_s.addresses().count(_receiveAddress)) + + if (na != (_codeAddressOverride ? _codeAddressOverride : _receiveAddress)) { - cnote << "not able to call to : " << _receiveAddress << "\n"; + cnote << "not able to call to : " << (_codeAddressOverride ? _codeAddressOverride : _receiveAddress) << "\n"; cnote << "in FakeExtVM you can only make a call to " << na << "\n"; - BOOST_THROW_EXCEPTION(FakeExtVMFailure() << errinfo_comment("Address not callable in FakeExtVM\n") << errinfo_wrongAddress(_receiveAddress)); + BOOST_THROW_EXCEPTION(FakeExtVMFailure() << errinfo_comment("Address not callable in FakeExtVM\n") << errinfo_wrongAddress(_codeAddressOverride ? _codeAddressOverride : _receiveAddress)); return false; } } m_ms.internal.resize(m_ms.internal.size() + 1); - auto ret = m_s.call(_receiveAddress, Address() ? Address() : _receiveAddress, Address() ? Address() : myAddress, _value, gasPrice, _data, _gas, _out, origin, &suicides, &(m_ms.internal.back()), OnOpFunc(), 1); + + auto ret = m_s.call(_receiveAddress,_codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _value, gasPrice, _data, _gas, _out, origin, &suicides, &(m_ms.internal.back()), OnOpFunc(), 1); + if (!m_ms.internal.back().from) m_ms.internal.pop_back(); + + // get correct balances, (also for sucicides in the call function) + for (auto const& f: addresses) + { + if (m_s.addressInUse(f.first)) + get<0>(addresses[f.first]) = m_s.balance(f.first); + } + if (!ret) return false; - if (get<0>(addresses[myAddress]) >= _value) - { - get<1>(addresses[myAddress])++; - get<0>(addresses[_receiveAddress]) += _value; + // do suicides + for (auto const& f: suicides) + addresses.erase(f); + // get storage + if ((get<0>(addresses[myAddress]) >= _value) && (suicides.find(_receiveAddress) == suicides.end())) + { for (auto const& j: m_s.storage(_receiveAddress)) { u256 adr(j.first); @@ -114,13 +159,10 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, get<2>(addresses[_receiveAddress])[adr] = j.second; } } - } else addresses.erase(_receiveAddress); // for the sake of comparison - callcreates.push_back(t); - return true; } @@ -235,30 +277,8 @@ mObject FakeExtVM::exportState() { mObject store; - string curKey; - u256 li = 0; - bool isOutOfRange = false; - mArray curVal; for (auto const& s: get<2>(a.second)) - { - if (!li || s.first > li + 8) - { - if (li || isOutOfRange) - store[curKey] = curVal; - li = s.first; - curKey = "0x"+toHex(toCompactBigEndian(li)); - curVal = mArray(); - } - else - for (; li != s.first; ++li) - curVal.push_back(0); - curVal.push_back("0x"+toHex(toCompactBigEndian(s.second))); - if ( toHex(toCompactBigEndian(li)) == "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - isOutOfRange = true; - ++li; - } - if (li || isOutOfRange) - store[curKey] = curVal; + store["0x"+toHex(toCompactBigEndian(s.first))] = "0x"+toHex(toCompactBigEndian(s.second)); o["storage"] = store; } o["code"] = "0x" + toHex(get<3>(a.second)); @@ -282,15 +302,7 @@ void FakeExtVM::importState(mObject& _object) get<0>(a) = toInt(o["balance"]); get<1>(a) = toInt(o["nonce"]); for (auto const& j: o["storage"].get_obj()) - { - u256 adr(j.first); - for (auto const& k: j.second.get_array()) - { - if ((toInt(k) != 0) || (j.second.get_array().size() == 1)) - get<2>(a)[adr] = toInt(k); - adr++; - } - } + get<2>(a)[toInt(j.first)] = toInt(j.second); if (o["code"].type() == str_type) if (o["code"].get_str().find_first_of("0x") != 0) @@ -402,6 +414,71 @@ void FakeExtVM::importCallCreates(mArray& _callcreates) } } +h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +{ + if (!_origin) + _origin = _sender; + + if (o_ms) + { + o_ms->from = _sender; + o_ms->to = Address(); + o_ms->value = _endowment; + o_ms->input = _code.toBytes(); + } + + // Set up new account... + m_cache[_newAddress] = AddressState(0, balance(_newAddress) + _endowment, h256(), h256()); + + // Execute init code. + VM vm(*_gas); + ExtVM evm(*this, _newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms, _level); + bool revert = false; + bytesConstRef out; + + try + { + out = vm.go(evm, _onOp); + if (o_ms) + o_ms->output = out.toBytes(); + if (o_suicides) + for (auto i: evm.suicides) + o_suicides->insert(i); + } + catch (OutOfGas const& /*_e*/) + { + clog(StateChat) << "Out of Gas! Reverting."; + revert = true; + } + catch (VMException const& _e) + { + clog(StateChat) << "VM Exception: " << diagnostic_information(_e); + } + catch (Exception const& _e) + { + clog(StateChat) << "Exception in VM: " << diagnostic_information(_e); + } + catch (std::exception const& _e) + { + clog(StateChat) << "std::exception in VM: " << _e.what(); + } + + // TODO: CHECK: IS THIS CORRECT?! (esp. given account created prior to revertion init.) + + // Write state out only in the case of a non-out-of-gas transaction. + if (revert) + evm.revert(); + + // Set code. + if (addressInUse(_newAddress)) + m_cache[_newAddress].setCode(out); + + *_gas = vm.gas(); + + return _newAddress; +} + + namespace dev { namespace test { @@ -542,7 +619,7 @@ void executeTests(const string& _name) BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + _name + "Filler.json is empty."); json_spirit::read_string(s, v); dev::test::doTests(v, true); - writeFile("../../../tests/" + _name + ".json", asBytes(json_spirit::write_string(v, true))); + writeFile("../../../tests/vmtests/" + _name + ".json", asBytes(json_spirit::write_string(v, true))); } catch (Exception const& _e) { @@ -558,7 +635,7 @@ void executeTests(const string& _name) { cnote << "Testing VM..." << _name; json_spirit::mValue v; - string s = asString(contents("../../../tests/" + _name + ".json")); + string s = asString(contents("../../../tests/vmtests/" + _name + ".json")); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + _name + ".json is empty. Have you cloned the 'tests' repo branch develop?"); json_spirit::read_string(s, v); dev::test::doTests(v, false); @@ -620,3 +697,4 @@ BOOST_AUTO_TEST_CASE(vmSystemOperationsTest) { dev::test::executeTests("vmSystemOperationsTest"); } + diff --git a/test/vm.h b/test/vm.h index 5bad9d7d4..34e0e855a 100644 --- a/test/vm.h +++ b/test/vm.h @@ -40,11 +40,18 @@ namespace dev { namespace test { struct FakeExtVMFailure : virtual Exception {}; +class FakeState: public eth::State +{ +public: + /// Execute a contract-creation transaction. + h160 createNewAddress(Address _newAddress, Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set
* o_suicides = nullptr, eth::Manifest* o_ms = nullptr, eth::OnOpFunc const& _onOp = eth::OnOpFunc(), unsigned _level = 0); +}; + class FakeExtVM: public eth::ExtVMFace { public: FakeExtVM() {} - FakeExtVM(eth::BlockInfo const& _previousBlock, eth::BlockInfo const& _currentBlock); + FakeExtVM(eth::BlockInfo const& _previousBlock, eth::BlockInfo const& _currentBlock, unsigned _depth = 0); u256 store(u256 _n) { return std::get<2>(addresses[myAddress])[_n]; } void setStore(u256 _n, u256 _v) { std::get<2>(addresses[myAddress])[_n] = _v; } @@ -63,6 +70,7 @@ public: byte toByte(json_spirit::mValue const& _v); void push(json_spirit::mObject& o, std::string const& _n, u256 _v); void push(json_spirit::mArray& a, u256 _v); + u256 doPosts(); json_spirit::mObject exportEnv(); void importEnv(json_spirit::mObject& _o); json_spirit::mObject exportState(); @@ -79,7 +87,7 @@ public: u256 gas; private: - eth::State m_s; + FakeState m_s; eth::Manifest m_ms; }; diff --git a/test/vmSystemOperationsTestFiller.json b/test/vmSystemOperationsTestFiller.json index 34235e040..de8a3ad1a 100644 --- a/test/vmSystemOperationsTestFiller.json +++ b/test/vmSystemOperationsTestFiller.json @@ -48,7 +48,7 @@ "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", - "value" : "1000000000000000000", + "value" : "100", "data" : "", "gasPrice" : "100000000000000", "gas" : "10000" @@ -76,7 +76,7 @@ "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", - "value" : "1000000000000000000", + "value" : "100", "data" : "", "gasPrice" : "100000000000000", "gas" : "10000" @@ -104,7 +104,7 @@ "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", - "value" : "1000000000000000000", + "value" : "100", "data" : "", "gasPrice" : "100000000000000", "gas" : "10000" @@ -638,7 +638,7 @@ "address" : "cd1722f3947def4cf144679da39c4c32bdc35681", "origin" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "caller" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", - "value" : "100000", + "value" : "23", "data" : "0xaa", "gasPrice" : "100000000000000", "gas" : "1000" @@ -668,7 +668,7 @@ "address" : "cd1722f3947def4cf144679da39c4c32bdc35681", "origin" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "caller" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", - "value" : "100000", + "value" : "23", "data" : "0xaa", "gasPrice" : "100000000000000", "gas" : "1000" @@ -698,7 +698,7 @@ "address" : "cd1722f3947def4cf144679da39c4c32bdc35681", "origin" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "caller" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", - "value" : "100000", + "value" : "23", "data" : "0xaa", "gasPrice" : "100000000000000", "gas" : "1000" @@ -777,9 +777,6 @@ } }, - - - "TestNameRegistrator": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -806,9 +803,149 @@ "gasPrice" : "100000000000000", "gas" : "10000" } - } + }, + "ABAcalls0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ (PC) ]] (CALL 1000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 24 0 0 0 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : " { [[ (PC) ]] (ADD 1 (CALL 500 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0)) } ", + "nonce" : "0", + "storage" : { + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "100000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000000000000" + } + }, + "ABAcalls0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ (PC) ]] (CALL (- (GAS) 1000) 0x945304eb96065b2a98b57a48a06ae28d285a71b5 24 0 0 0 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : " { [[ (PC) ]] (ADD 1 (CALL (- (GAS) 1000) 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0)) } ", + "nonce" : "0", + "storage" : { + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "100000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000000000000" + } + }, + + + "ABAcallsSuicide0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ (PC) ]] (CALL 1000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 24 0 0 0 0) (SUICIDE 0x945304eb96065b2a98b57a48a06ae28d285a71b5) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "{ [[ (PC) ]] (ADD 1 (CALL 500 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0)) } ", + "nonce" : "0", + "storage" : { + } + } + + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "100000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000000000000" + } + }, + + "ABAcallsSuicide1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ (PC) ]] (CALL 1000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 24 0 0 0 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "{ [[ (PC) ]] (ADD 1 (CALL 500 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0)) (SUICIDE 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6) } ", + "nonce" : "0", + "storage" : { + } + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "100000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000000000000" + } + } } 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; diff --git a/windows/LibEthereum.props b/windows/LibEthereum.props index 136a8edab..8c0bcac90 100644 --- a/windows/LibEthereum.props +++ b/windows/LibEthereum.props @@ -15,7 +15,7 @@ true false include/$(ProjectName);$(IntDir);../../cryptopp;..;../libethcore;../libethereum;../libethsupport;$(BoostDir);../../leveldb/include;../secp256k1;../../miniupnp - ETH_BUILD_PLATFORM=Windows/VS2013;ETH_BUILD_TYPE=$(Configuration)-$(Platform);STATICLIB;LEVELDB_PLATFORM_WINDOWS;USE_NUM_BOOST;USE_FIELD_10X26;USE_FIELD_INV_BUILTIN;_WIN32_WINNT=0x0501;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ETH_BUILD_PLATFORM=Windows/VS2013;ETH_BUILD_TYPE=$(Configuration)-$(Platform);ETH_MINIUPNPC=1;STATICLIB;LEVELDB_PLATFORM_WINDOWS;USE_NUM_BOOST;USE_FIELD_10X26;USE_FIELD_INV_BUILTIN;_WIN32_WINNT=0x0501;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true true Level3 diff --git a/windows/LibEthereum.vcxproj b/windows/LibEthereum.vcxproj index db1362363..8c08091ec 100644 --- a/windows/LibEthereum.vcxproj +++ b/windows/LibEthereum.vcxproj @@ -93,6 +93,12 @@ true true + + true + true + true + true + diff --git a/windows/LibEthereum.vcxproj.filters b/windows/LibEthereum.vcxproj.filters index 848b8308d..7d743d82d 100644 --- a/windows/LibEthereum.vcxproj.filters +++ b/windows/LibEthereum.vcxproj.filters @@ -190,6 +190,9 @@ libwebthree + + libethcore + diff --git a/windows/LibMiniUPnPc.vcxproj b/windows/LibMiniUPnPc.vcxproj index 7e5e44412..07489d0e8 100644 --- a/windows/LibMiniUPnPc.vcxproj +++ b/windows/LibMiniUPnPc.vcxproj @@ -106,7 +106,7 @@ Disabled MultiThreadedDebugDLL - _CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;MINIUPNP_STATICLIB;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) 4100;4244;4245;4267;4389;%(DisableSpecificWarnings) @@ -123,7 +123,7 @@ Disabled MultiThreadedDebugDLL - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) 4100;4244;4245;4267;4389;%(DisableSpecificWarnings) @@ -142,7 +142,7 @@ true true MultiThreadedDLL - _CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;MINIUPNP_STATICLIB;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) 4100;4244;4245;4267;4389;%(DisableSpecificWarnings) @@ -163,7 +163,7 @@ true true MultiThreadedDLL - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) 4100;4244;4245;4267;4389;%(DisableSpecificWarnings)