diff --git a/eth/main.cpp b/eth/main.cpp index 06611deaa..2c5d938aa 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -138,6 +138,7 @@ void help() << " -a,--address Set the coinbase (mining payout) address to addr (default: auto)." << endl << " -m,--mining Enable mining, optionally for a specified number of blocks (default: off)" << endl << " -f,--force-mining Mine even when there are no transactions to mine (default: off)" << endl + << " --mine-on-wrong-chain Mine even when we know it's the wrong chain (default: off)" << endl << " -C,--cpu When mining, use the CPU." << endl << " -G,--opencl When mining use the GPU via OpenCL." << endl << " --opencl-platform When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl @@ -279,6 +280,7 @@ int main(int argc, char** argv) bool upnp = true; WithExisting killChain = WithExisting::Trust; bool jit = false; + string sentinel; /// Networking params. string clientName; @@ -294,6 +296,7 @@ int main(int argc, char** argv) /// Mining params unsigned mining = 0; bool forceMining = false; + bool mineOnWrongChain = false; Address signingKey; Address sessionKey; Address beneficiary = signingKey; @@ -376,6 +379,10 @@ int main(int argc, char** argv) mode = OperationMode::Export; filename = argv[++i]; } + else if (arg == "--sentinel" && i + 1 < argc) + sentinel = argv[++i]; + else if (arg == "--mine-on-wrong-chain") + mineOnWrongChain = true; else if (arg == "--format" && i + 1 < argc) { string m = argv[++i]; @@ -671,6 +678,8 @@ int main(int argc, char** argv) nodeMode == NodeMode::Full ? set{"eth"/*, "shh"*/} : set(), netPrefs, &nodesState); + web3.ethereum()->setMineOnBadChain(mineOnWrongChain); + web3.ethereum()->setSentinel(sentinel); auto toNumber = [&](string const& s) -> unsigned { if (s == "latest") diff --git a/libdevcore/Log.cpp b/libdevcore/Log.cpp index f28a2c6b9..1e5c2d8ab 100644 --- a/libdevcore/Log.cpp +++ b/libdevcore/Log.cpp @@ -40,6 +40,14 @@ mutex x_logOverride; /// or equal to the currently output verbosity (g_logVerbosity). static map s_logOverride; +bool isLogVisible(std::type_info const* _ch, bool _default) +{ + Guard l(x_logOverride); + if (s_logOverride.count(_ch)) + return s_logOverride[_ch]; + return _default; +} + LogOverrideAux::LogOverrideAux(std::type_info const* _ch, bool _value): m_ch(_ch) { diff --git a/libdevcore/Log.h b/libdevcore/Log.h index ce0db17fe..e732ac73c 100644 --- a/libdevcore/Log.h +++ b/libdevcore/Log.h @@ -73,6 +73,9 @@ public: LogOverride(bool _value): LogOverrideAux(&typeid(Channel), _value) {} }; +bool isChannelVisible(std::type_info const* _ch, bool _default); +template bool isChannelVisible() { return isChannelVisible(&typeid(Channel), Channel::verbosity <= g_logVerbosity); } + /// Temporary changes system's verbosity for specific function. Restores the old verbosity when function returns. /// Not thread-safe, use with caution! struct VerbosityHolder diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index ae03be9a4..ea4e65ac4 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -310,6 +310,18 @@ Client::~Client() stopWorking(); } +static const Address c_canary("0x"); + +bool Client::isChainBad() const +{ + return stateAt(c_canary, 0) != 0; +} + +bool Client::isUpgradeNeeded() const +{ + return stateAt(c_canary, 0) == 2; +} + void Client::setNetworkId(u256 _n) { if (auto h = m_host.lock()) @@ -552,6 +564,9 @@ ProofOfWork::WorkPackage Client::getWork() bool oldShould = shouldServeWork(); m_lastGetWork = chrono::system_clock::now(); + if (!m_mineOnBadChain && isChainBad()) + return ProofOfWork::WorkPackage(); + // if this request has made us bother to serve work, prep it now. if (!oldShould && shouldServeWork()) onPostStateChanged(); @@ -709,11 +724,22 @@ bool Client::remoteActive() const void Client::onPostStateChanged() { - cnote << "Post state changed"; + cnote << "Post state changed."; + rejigMining(); + m_remoteWorking = false; +} + +void Client::startMining() +{ + m_wouldMine = true; + rejigMining(); +} - if (m_bq.items().first == 0 && (isMining() || remoteActive())) +void Client::rejigMining() +{ + if ((wouldMine() || remoteActive()) && !m_bq.items().first && (!isChainBad() || mineOnBadChain()) /*&& (forceMining() || transactionsWaiting())*/) { - cnote << "Restarting mining..."; + cnote << "Rejigging mining..."; DEV_WRITE_GUARDED(x_working) m_working.commitToMine(m_bc); DEV_READ_GUARDED(x_working) @@ -722,20 +748,21 @@ void Client::onPostStateChanged() m_postMine = m_working; m_miningInfo = m_postMine.info(); } - m_farm.setWork(m_miningInfo); - Ethash::ensurePrecomputed(m_bc.number()); - } - m_remoteWorking = false; -} + if (m_wouldMine) + { + m_farm.setWork(m_miningInfo); + if (m_turboMining) + m_farm.startGPU(); + else + m_farm.startCPU(); -void Client::startMining() -{ - if (m_turboMining) - m_farm.startGPU(); - else - m_farm.startCPU(); - onPostStateChanged(); + m_farm.setWork(m_miningInfo); + Ethash::ensurePrecomputed(m_bc.number()); + } + } + if (!m_wouldMine) + m_farm.stop(); } void Client::noteChanged(h256Hash const& _filters) diff --git a/libethereum/Client.h b/libethereum/Client.h index aed54f131..d2447e932 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -174,14 +174,26 @@ public: /// Enable/disable GPU mining. void setTurboMining(bool _enable = true) { m_turboMining = _enable; if (isMining()) startMining(); } + /// Check to see if we'd mine on an apparently bad chain. + bool mineOnBadChain() const { return m_mineOnBadChain; } + /// Set true if you want to mine even when the canary says you're on the wrong chain. + void setMineOnBadChain(bool _v) { m_mineOnBadChain = _v; } + + /// @returns true if the canary says that the chain is bad. + bool isChainBad() const; + /// @returns true if the canary says that the client should be upgraded. + bool isUpgradeNeeded() const; + /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread void startMining() override; /// Stop mining. /// NOT thread-safe - void stopMining() override { m_farm.stop(); } + void stopMining() override { m_wouldMine = false; rejigMining(); } /// Are we mining now? bool isMining() const override { return m_farm.isMining(); } + /// Are we mining now? + bool wouldMine() const override { return m_wouldMine; } /// The hashrate... uint64_t hashrate() const override; /// Check the progress of the mining. @@ -251,6 +263,9 @@ private: /// Called when Worker is exiting. void doneWorking() override; + /// Called when wouldMine(), turboMining(), isChainBad(), forceMining(), pendingTransactions() have changed. + void rejigMining(); + /// Magically called when the chain has changed. An import route is provided. /// Called by either submitWork() or in our main thread through syncBlockQueue(). void onChainChanged(ImportRoute const& _ir); @@ -308,8 +323,10 @@ private: Handler m_tqReady; Handler m_bqReady; + bool m_wouldMine = false; ///< True if we /should/ be mining. bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping. bool m_forceMining = false; ///< Mine even when there are no transactions pending? + bool m_mineOnBadChain = false; ///< Mine even when the canary says it's a bad chain. bool m_paranoia = false; ///< Should we be paranoid about our state? mutable std::chrono::system_clock::time_point m_lastGarbageCollection; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 8dc666bb5..6a2c76c0b 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -400,17 +400,16 @@ h256s ClientBase::pendingHashes() const return h256s() + postMine().pendingHashes(); } - StateDiff ClientBase::diff(unsigned _txi, h256 _block) const { State st = asOf(_block); - return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1), true); } StateDiff ClientBase::diff(unsigned _txi, BlockNumber _block) const { State st = asOf(_block); - return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1), true); } Addresses ClientBase::addresses(BlockNumber _block) const diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 2b271b1df..6a38ba357 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -154,6 +154,7 @@ public: virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::startMining")); } virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::stopMining")); } virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::isMining")); } + virtual bool wouldMine() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::wouldMine")); } virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::hashrate")); } virtual MiningProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::miningProgress")); } virtual ProofOfWork::WorkPackage getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::getWork")); } diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 7c9b730ea..4c1a65c7c 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -276,6 +276,13 @@ void EthereumHost::estimatePeerHashes(EthereumPeer* _peer) _peer->m_expectedHashes = blockCount; } +void EthereumHost::noteRude(p2p::NodeId const& _id, std::string const& _client) +{ + cwarn << "RUDE node/client: " << _id << _client; + m_rudeNodes.insert(_id); + m_rudeClients.insert(_client); +} + void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) { Guard l(x_sync); @@ -574,7 +581,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer) }); if (otherPeerSync) { - /// Downloading from other peer with v60 protocol, nothing ese we can do + /// Downloading from other peer with v60 protocol, nothing else we can do _peer->setIdle(); return; } @@ -599,6 +606,10 @@ void EthereumHost::continueSync(EthereumPeer* _peer) bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const { + // Early exit if this peer has proved unreliable. + if (_peer->isRude()) + return false; + auto td = _peer->m_totalDifficulty; auto lh = m_syncingLatestHash; auto ctd = m_chain.details().totalDifficulty; @@ -611,6 +622,10 @@ bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const { + // Early exit if this peer has proved unreliable. + if (_peer->isRude()) + return false; + h256 c = m_chain.currentHash(); unsigned n = m_chain.number(); u256 td = m_chain.details().totalDifficulty; diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 8ca815a17..a05e3f62f 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -71,7 +71,9 @@ public: DownloadMan const& downloadMan() const { return m_man; } bool isSyncing() const { Guard l(x_sync); return isSyncing_UNSAFE(); } - bool isBanned(p2p::NodeId _id) const { return !!m_banned.count(_id); } + bool isBanned(p2p::NodeId const& _id) const { return !!m_banned.count(_id); } + void noteRude(p2p::NodeId const& _id, std::string const& _client); + bool isRude(p2p::NodeId const& _id, std::string const& _client) const { return m_rudeClients.count(_client) && m_rudeNodes.count(_id); } void noteNewTransactions() { m_newTransactions = true; } void noteNewBlocks() { m_newBlocks = true; } @@ -147,6 +149,9 @@ private: h256 m_syncingLatestHash; ///< Latest block's hash, as of the current sync. u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty, as of the current sync. h256s m_hashes; ///< List of hashes with unknown block numbers. Used for v60 chain downloading and catching up to a particular unknown + + std::unordered_set m_rudeNodes; ///< Nodes that were impolite while syncing. We avoid syncing from these if possible. + std::unordered_set m_rudeClients; ///< Nodes that were impolite while syncing. We avoid syncing from these if possible. }; } diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index a332e5b93..534d76795 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -40,6 +40,8 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, Cap m_hashSub(host()->hashDownloadMan()), m_peerCapabilityVersion(_cap.second) { + m_isRude = host()->isRude(session()->id(), session()->info().clientVersion); + session()->addNote("manners", m_isRude ? "RUDE" : "nice"); m_syncHashNumber = host()->chain().number() + 1; requestStatus(); } @@ -52,8 +54,16 @@ EthereumPeer::~EthereumPeer() void EthereumPeer::abortSync() { - if (isSyncing()) - setIdle(); + if (m_asking != Asking::Nothing) + setRude(); + setIdle(); +} + +void EthereumPeer::setRude() +{ + m_isRude = true; + host()->noteRude(session()->id(), session()->info().clientVersion); + session()->addNote("manners", m_isRude ? "RUDE" : "nice"); } EthereumHost* EthereumPeer::host() const diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index 94dc60501..bf62559ee 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -82,6 +82,12 @@ public: /// Request blocks. Uses block download manager. void requestBlocks(); + /// Check if this node is rude. + bool isRude() const { return m_isRude; } + + /// Set that it's a rude node. + void setRude(); + private: using p2p::Capability::sealAndSend; @@ -101,7 +107,7 @@ private: void setAsking(Asking _g); /// Do we presently need syncing with this peer? - bool needsSyncing() const { return !!m_latestHash; } + bool needsSyncing() const { return !isRude() && !!m_latestHash; } /// Are we presently syncing with this peer? bool isSyncing() const; @@ -145,6 +151,8 @@ private: h256Hash m_knownBlocks; ///< Blocks that the peer already knows about (that don't need to be sent to them). Mutex x_knownTransactions; h256Hash m_knownTransactions; ///< Transactions that the peer already knows of. + + bool m_isRude; ///< True if this node has been rude in the past. }; } diff --git a/libethereum/Interface.h b/libethereum/Interface.h index f7253ad29..636f73fbf 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -196,6 +196,8 @@ public: virtual void stopMining() = 0; /// Are we mining now? virtual bool isMining() const = 0; + /// Would we like to mine now? + virtual bool wouldMine() const = 0; /// Current hash rate. virtual uint64_t hashrate() const = 0; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index d19cc556a..cb514e8f8 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -158,6 +158,7 @@ State::State(State const& _s): m_transactions(_s.m_transactions), m_receipts(_s.m_receipts), m_transactionSet(_s.m_transactionSet), + m_touched(_s.m_touched), m_cache(_s.m_cache), m_previousBlock(_s.m_previousBlock), m_currentBlock(_s.m_currentBlock), @@ -206,7 +207,7 @@ State::~State() { } -StateDiff State::diff(State const& _c) const +StateDiff State::diff(State const& _c, bool _quick) const { StateDiff ret; @@ -217,10 +218,20 @@ StateDiff State::diff(State const& _c) const auto trie = SecureTrieDB(const_cast(&m_db), rootHash()); auto trieD = SecureTrieDB(const_cast(&_c.m_db), _c.rootHash()); - for (auto i: trie) - ads.insert(i.first), trieAds.insert(i.first); - for (auto i: trieD) - ads.insert(i.first), trieAdsD.insert(i.first); + if (_quick) + { + trieAds = m_touched; + trieAdsD = _c.m_touched; + (ads += m_touched) += _c.m_touched; + } + else + { + for (auto i: trie) + ads.insert(i.first), trieAds.insert(i.first); + for (auto i: trieD) + ads.insert(i.first), trieAdsD.insert(i.first); + } + for (auto i: m_cache) ads.insert(i.first); for (auto i: _c.m_cache) @@ -272,7 +283,7 @@ void State::ensureCached(std::unordered_map& _cache, Address _ void State::commit() { - dev::eth::commit(m_cache, m_db, m_state); + m_touched += dev::eth::commit(m_cache, m_db, m_state); m_cache.clear(); } @@ -450,6 +461,7 @@ void State::resetCurrent() m_receipts.clear(); m_transactionSet.clear(); m_cache.clear(); + m_touched.clear(); m_currentBlock = BlockInfo(); m_currentBlock.coinbaseAddress = m_ourAddress; m_currentBlock.timestamp = max(m_previousBlock.timestamp + 1, (u256)time(0)); @@ -1219,8 +1231,10 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per if (!e.execute()) #if ETH_VMTRACE { - (void)_onOp; - e.go(e.simpleTrace()); + if (isChannelVisible()) + e.go(e.simpleTrace()); + else + e.go(_onOp); } #else e.go(_onOp); diff --git a/libethereum/State.h b/libethereum/State.h index da9d6b23a..9ecab05b5 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -310,10 +310,12 @@ public: State fromPending(unsigned _i) const; /// @returns the StateDiff caused by the pending transaction of index @a _i. - StateDiff pendingDiff(unsigned _i) const { return fromPending(_i).diff(fromPending(_i + 1)); } + StateDiff pendingDiff(unsigned _i) const { return fromPending(_i).diff(fromPending(_i + 1), true); } /// @return the difference between this state (origin) and @a _c (destination). - StateDiff diff(State const& _c) const; + /// @param _quick if true doesn't check all addresses possible (/very/ slow for a full chain) + /// but rather only those touched by the transactions in creating the two States. + StateDiff diff(State const& _c, bool _quick = false) const; /// Sync our state with the block chain. /// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue. @@ -387,6 +389,7 @@ private: TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts. h256Hash m_transactionSet; ///< The set of transaction hashes that we've included in the state. OverlayDB m_lastTx; + AddressHash m_touched; ///< Tracks all addresses touched by transactions so far. mutable std::unordered_map m_cache; ///< Our address cache. This stores the states of each address that has (or at least might have) been changed. @@ -410,8 +413,9 @@ private: std::ostream& operator<<(std::ostream& _out, State const& _s); template -void commit(std::unordered_map const& _cache, DB& _db, SecureTrieDB& _state) +AddressHash commit(std::unordered_map const& _cache, DB& _db, SecureTrieDB& _state) { + AddressHash ret; for (auto const& i: _cache) if (i.second.isDirty()) { @@ -450,7 +454,9 @@ void commit(std::unordered_map const& _cache, DB& _db, SecureT _state.insert(i.first, &s.out()); } + ret.insert(i.first); } + return ret; } }