From 0d3f298e4532e378cb8b190fd130d8373ff435ab Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 19 Sep 2014 12:02:34 -0500 Subject: [PATCH] Blocks come down in order (well... unless a peer bugs out). Peer hash-chains downloaded one-at-once. KillChain works again. Local networking option. Don't resend blocks during sync. --- alethzero/Main.ui | 9 +++ alethzero/MainWin.cpp | 5 +- eth/main.cpp | 12 ++- libdevcore/Common.cpp | 2 +- libethcore/CommonEth.cpp | 2 +- libethereum/BlockChain.cpp | 47 +++++++++-- libethereum/BlockChain.h | 8 ++ libethereum/BlockQueue.cpp | 9 ++- libethereum/BlockQueue.h | 5 +- libethereum/Client.cpp | 30 ++++++- libethereum/Client.h | 4 +- libethereum/CommonNet.h | 16 +++- libethereum/EthereumHost.cpp | 142 +++++++++++++++++++++++---------- libethereum/EthereumHost.h | 12 ++- libethereum/EthereumPeer.cpp | 47 +++++++++-- libethereum/EthereumPeer.h | 4 + libethereum/State.cpp | 5 ++ libethereum/State.h | 2 + libethereum/TransactionQueue.h | 2 + libwebthree/WebThree.h | 3 +- 20 files changed, 291 insertions(+), 75 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 7ae434ff5..dbc4bb144 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -132,6 +132,7 @@ + @@ -1731,6 +1732,14 @@ font-size: 14pt Reserved Debug 1 + + + true + + + Enable Local Addresses + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 37c33de02..5842664b3 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -192,7 +192,7 @@ Main::~Main() dev::p2p::NetworkPreferences Main::netPrefs() const { - return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), false); + return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked()); } void Main::onKeysChanged() @@ -494,6 +494,7 @@ void Main::writeSettings() s.setValue("upnp", ui->upnp->isChecked()); s.setValue("forceAddress", ui->forceAddress->text()); s.setValue("usePast", ui->usePast->isChecked()); + s.setValue("localNetworking", ui->localNetworking->isChecked()); s.setValue("forceMining", ui->forceMining->isChecked()); s.setValue("paranoia", ui->paranoia->isChecked()); s.setValue("showAll", ui->showAll->isChecked()); @@ -543,6 +544,7 @@ void Main::readSettings(bool _skipGeometry) ui->upnp->setChecked(s.value("upnp", true).toBool()); ui->forceAddress->setText(s.value("forceAddress", "").toString()); ui->usePast->setChecked(s.value("usePast", true).toBool()); + ui->localNetworking->setChecked(s.value("localNetworking", true).toBool()); ui->forceMining->setChecked(s.value("forceMining", false).toBool()); on_forceMining_triggered(); ui->paranoia->setChecked(s.value("paranoia", false).toBool()); @@ -1419,6 +1421,7 @@ void Main::on_killBlockchain_triggered() writeSettings(); ui->mine->setChecked(false); ui->net->setChecked(false); + web3()->stopNetwork(); ethereum()->killChain(); m_ethereum->setClient(ethereum()); readSettings(true); diff --git a/eth/main.cpp b/eth/main.cpp index cebec34e9..3ea02f744 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -112,7 +112,8 @@ void help() << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl << " -m,--mining Enable mining, optionally for a specified number of blocks (Default: off)" << endl << " -n,--upnp Use upnp for NAT (default: on)." << endl - << " -o,--mode Start a full node or a peer node (Default: full)." << endl + << " -L,--local-networking Use peers whose addresses are local." << endl + << " -o,--mode Start a full node or a peer node (Default: full)." << endl << " -p,--port Connect to remote port (default: 30303)." << endl << " -r,--remote Connect to remote host (default: none)." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl @@ -188,6 +189,7 @@ int main(int argc, char** argv) #endif string publicIP; bool upnp = true; + bool useLocal = false; bool forceMining = false; string clientName; @@ -233,10 +235,12 @@ int main(int argc, char** argv) upnp = false; else { - cerr << "Invalid UPnP option: " << m << endl; + cerr << "Invalid -n/--upnp option: " << m << endl; return -1; } } + else if (arg == "-L" || arg == "--local-networking") + useLocal = true; else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) clientName = argv[++i]; else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) @@ -256,7 +260,7 @@ int main(int argc, char** argv) mining = i; else { - cerr << "Unknown mining option: " << m << endl; + cerr << "Unknown -m/--mining option: " << m << endl; return -1; } } @@ -300,7 +304,7 @@ int main(int argc, char** argv) cout << credits(); - NetworkPreferences netPrefs(listenPort, publicIP, upnp, false); + NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); dev::WebThreeDirect web3("Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), dbPath, false, mode == NodeMode::Full ? set{"eth", "shh"} : set{}, netPrefs); web3.setIdealPeerCount(peers); eth::Client& c = *web3.ethereum(); diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 325ba274e..ad779e35d 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.6.8b"; +char const* Version = "0.6.8c"; } diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index f96fb5480..eed8b4bb9 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -34,7 +34,7 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 32; +const unsigned c_protocolVersion = 33; const unsigned c_databaseVersion = 1; static const vector> g_units = diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 716add2ae..181f4fd64 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -108,6 +108,20 @@ bytes BlockChain::createGenesisBlock() } BlockChain::BlockChain(std::string _path, bool _killExisting) +{ + // Initialise with the genesis as the last block on the longest chain. + m_genesisHash = BlockChain::genesis().hash; + m_genesisBlock = BlockChain::createGenesisBlock(); + + open(_path, _killExisting); +} + +BlockChain::~BlockChain() +{ + close(); +} + +void BlockChain::open(std::string _path, bool _killExisting) { if (_path.empty()) _path = Defaults::get()->m_dbPath; @@ -127,10 +141,6 @@ BlockChain::BlockChain(std::string _path, bool _killExisting) if (!m_extrasDB) throw DatabaseAlreadyOpen(); - // Initialise with the genesis as the last block on the longest chain. - m_genesisHash = BlockChain::genesis().hash; - m_genesisBlock = BlockChain::createGenesisBlock(); - if (!details(m_genesisHash)) { // Insert details of genesis block. @@ -150,11 +160,16 @@ BlockChain::BlockChain(std::string _path, bool _killExisting) cnote << "Opened blockchain DB. Latest: " << currentHash(); } -BlockChain::~BlockChain() +void BlockChain::close() { cnote << "Closing blockchain DB"; delete m_extrasDB; delete m_db; + m_lastBlockHash = m_genesisHash; + m_details.clear(); + m_blooms.clear(); + m_traces.clear(); + m_cache.clear(); } template @@ -245,21 +260,23 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) auto newHash = BlockInfo::headerHash(_block); // Check block doesn't already exist first! - if (details(newHash)) + if (isKnown(newHash)) { clog(BlockChainNote) << newHash << ": Not new."; throw AlreadyHaveBlock(); } // Work out its number as the parent's number + 1 - auto pd = details(bi.parentHash); - if (!pd) + if (!isKnown(bi.parentHash)) { clog(BlockChainNote) << newHash << ": Unknown parent " << bi.parentHash; // We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on. throw UnknownParent(); } + auto pd = details(bi.parentHash); + assert(pd); + // Check it's not crazy if (bi.timestamp > (u256)time(0)) { @@ -432,6 +449,20 @@ h256Set BlockChain::allUnclesFrom(h256 _parent) const return ret; } +bool BlockChain::isKnown(h256 _hash) const +{ + if (_hash == m_genesisHash) + return true; + { + ReadGuard l(x_cache); + if (m_cache.count(_hash)) + return true; + } + string d; + m_db->Get(m_readOptions, ldb::Slice((char const*)&_hash, 32), &d); + return d.size(); +} + bytes BlockChain::block(h256 _hash) const { if (_hash == m_genesisHash) diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index c0b3988a9..cf33a5104 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -70,6 +70,8 @@ public: BlockChain(std::string _path, bool _killExisting = false); ~BlockChain(); + void reopen(std::string _path, bool _killExisting = false) { close(); open(_path, _killExisting); } + /// (Potentially) renders invalid existing bytesConstRef returned by lastBlock. /// To be called from main loop every 100ms or so. void process(); @@ -85,6 +87,9 @@ public: /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. h256s import(bytes const& _block, OverlayDB const& _stateDB); + /// Returns true if the given block is known (though not necessarily a part of the canon chain). + bool isKnown(h256 _hash) const; + /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. BlockDetails details(h256 _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); } BlockDetails details() const { return details(currentHash()); } @@ -143,6 +148,9 @@ public: h256s treeRoute(h256 _from, h256 _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; private: + void open(std::string _path, bool _killExisting = false); + void close(); + template T queryExtras(h256 _h, std::map& _m, boost::shared_mutex& _x, T const& _n) const { { diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index 21aeb5a72..a845965d5 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -74,21 +74,24 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) // Check it's not in the future if (bi.timestamp > (u256)time(0)) + { m_future.insert(make_pair((unsigned)bi.timestamp, _block.toBytes())); + cblockq << "OK - queued for future."; + } else { // We now know it. - if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.details(bi.parentHash)) + if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash)) { // We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on. -// cnote << "OK - queued for future."; + cblockq << "OK - queued as unknown parent:" << bi.parentHash.abridged(); m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknownSet.insert(h); } else { // If valid, append to blocks. -// cnote << "OK - ready for chain insertion."; + cblockq << "OK - ready for chain insertion."; m_ready.push_back(_block.toBytes()); m_readySet.insert(h); diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index 5a4a86ad6..20bc4ce59 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -34,7 +34,7 @@ namespace eth class BlockChain; -struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 7; }; +struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; }; #define cblockq dev::LogOutputStream() /** @@ -64,6 +64,9 @@ public: /// Get information on the items queued. std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_unknown.size()); } + /// Clear everything. + void clear() { WriteGuard l(m_lock); m_readySet.clear(); m_drainingSet.clear(); m_ready.clear(); m_unknownSet.clear(); m_unknown.clear(); m_future.clear(); } + private: void noteReadyWithoutWriteGuard(h256 _b); void notePresentWithoutWriteGuard(bytesConstRef _block); diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index f0513716b..d64c9fafb 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -99,7 +99,35 @@ void Client::flushTransactions() void Client::killChain() { - // TODO + bool wasMining = isMining(); + if (wasMining) + stopMining(); + stopWorking(); + + m_tq.clear(); + m_bq.clear(); + m_miners.clear(); + m_preMine = State(); + m_postMine = State(); + + { + WriteGuard l(x_stateDB); + m_stateDB = OverlayDB(); + m_stateDB = State::openDB(Defaults::dbPath(), true); + } + m_bc.reopen(Defaults::dbPath(), true); + + m_preMine = State(Address(), m_stateDB); + m_postMine = State(Address(), m_stateDB); + + if (auto h = m_host.lock()) + h->reset(); + + doWork(); + + startWorking(); + if (wasMining) + startMining(); } void Client::clearPending() diff --git a/libethereum/Client.h b/libethereum/Client.h index 2c29175c2..6415defb0 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -271,13 +271,13 @@ private: BlockChain m_bc; ///< Maintains block database. TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). - // TODO: remove in favour of copying m_stateDB as required and thread-safing/copying State. Have a good think about what state objects there should be. Probably want 4 (pre, post, mining, user-visible). + mutable boost::shared_mutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine. OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. State m_preMine; ///< The present state of the client. State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). - std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. + std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. std::vector m_miners; mutable boost::shared_mutex x_miners; diff --git a/libethereum/CommonNet.h b/libethereum/CommonNet.h index 3bb45b55d..6774c02ae 100644 --- a/libethereum/CommonNet.h +++ b/libethereum/CommonNet.h @@ -33,11 +33,17 @@ namespace dev namespace eth { +#if ETH_DEBUG +static const unsigned c_maxHashes = 4; ///< Maximum number of hashes BlockHashes will ever send. +static const unsigned c_maxHashesAsk = 4; ///< Maximum number of hashes GetBlockHashes will ever ask for. +static const unsigned c_maxBlocks = 2; ///< Maximum number of blocks Blocks will ever send. +static const unsigned c_maxBlocksAsk = 2; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). +#else static const unsigned c_maxHashes = 256; ///< Maximum number of hashes BlockHashes will ever send. static const unsigned c_maxHashesAsk = 256; ///< Maximum number of hashes GetBlockHashes will ever ask for. static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send. static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). - +#endif class OverlayDB; class BlockChain; class TransactionQueue; @@ -55,5 +61,13 @@ enum EthereumPacket BlocksPacket, }; +enum class Grabbing +{ + State, + Hashes, + Chain, + Nothing +}; + } } diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 13a02a146..2add39482 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -41,7 +41,7 @@ using namespace p2p; EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId): HostCapability(), - Worker("ethsync"), + Worker ("ethsync"), m_chain (_ch), m_tq (_tq), m_bq (_bq), @@ -72,11 +72,11 @@ h256Set EthereumHost::neededBlocks(h256Set const& _exclude) m_blocksNeeded.erase(it); } } - if (!ret.size()) + if (ret.empty()) for (auto i = m_blocksOnWay.begin(); ret.size() < c_maxBlocksAsk && i != m_blocksOnWay.end() && !_exclude.count(*i); ++i) ret.insert(*i); - - clog(NetMessageSummary) << "Asking for" << ret.size() << "blocks that we don't yet have." << m_blocksNeeded.size() << "blocks still needed," << m_blocksOnWay.size() << "blocks on way."; + if (ret.size()) + clog(NetMessageSummary) << "Asking for" << ret.size() << "blocks that we don't yet have." << m_blocksNeeded.size() << "blocks still needed," << m_blocksOnWay.size() << "total blocks on way."; return ret; } @@ -95,6 +95,83 @@ bool EthereumHost::ensureInitialised(TransactionQueue& _tq) return false; } +void EthereumHost::noteHavePeerState(EthereumPeer* _who) +{ + clog(NetAllDetail) << "Have peer state."; + + // if already downloading hash-chain, ignore. + if (m_grabbing != Grabbing::Nothing) + { + clog(NetAllDetail) << "Already downloading chain. Just set to help out."; + _who->restartGettingChain(); + return; + } + + // otherwise check to see if we should be downloading... + _who->tryGrabbingHashChain(); +} + +void EthereumHost::updateGrabbing(Grabbing _g) +{ + m_grabbing = _g; + if (_g == Grabbing::Nothing) + readyForSync(); + else if (_g == Grabbing::Chain) + for (auto j: peers()) + j->cap()->restartGettingChain(); +} + +void EthereumHost::noteHaveChain(EthereumPeer* _from) +{ + auto td = _from->m_totalDifficulty; + + if (_from->m_neededBlocks.empty()) + { + _from->m_grabbing = Grabbing::Nothing; + updateGrabbing(Grabbing::Nothing); + return; + } + + clog(NetNote) << "Hash-chain COMPLETE:" << _from->m_totalDifficulty << "vs" << m_chain.details().totalDifficulty << "," << m_totalDifficultyOfNeeded << ";" << _from->m_neededBlocks.size() << " blocks, ends" << _from->m_neededBlocks.back().abridged(); + + if ((m_totalDifficultyOfNeeded && (td < m_totalDifficultyOfNeeded || (td == m_totalDifficultyOfNeeded && m_latestBlockSent == _from->m_latestHash))) || td < m_chain.details().totalDifficulty || (td == m_chain.details().totalDifficulty && m_chain.currentHash() == _from->m_latestHash)) + { + clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; + _from->m_grabbing = Grabbing::Nothing; + updateGrabbing(Grabbing::Nothing); + return; + } + + clog(NetNote) << "Difficulty of hashchain HIGHER. Replacing fetch queue [latest now" << _from->m_latestHash.abridged() << ", was" << m_latestBlockSent.abridged() << "]"; + + // Looks like it's the best yet for total difficulty. Set to download. + { + Guard l(x_blocksNeeded); + m_blocksNeeded = _from->m_neededBlocks; + m_blocksOnWay.clear(); + m_totalDifficultyOfNeeded = td; + m_latestBlockSent = _from->m_latestHash; + } + + _from->m_grabbing = Grabbing::Chain; + updateGrabbing(Grabbing::Chain); +} + +void EthereumHost::readyForSync() +{ + // start grabbing next hash chain if there is one. + for (auto j: peers()) + { + j->cap()->tryGrabbingHashChain(); + if (j->cap()->m_grabbing == Grabbing::Hashes) + { + m_grabbing = Grabbing::Hashes; + return; + } + } + clog(NetNote) << "No more peers to sync with."; +} + void EthereumHost::noteDoneBlocks() { if (m_blocksOnWay.empty()) @@ -104,7 +181,7 @@ void EthereumHost::noteDoneBlocks() clog(NetNote) << "No more blocks coming. Missing" << m_blocksNeeded.size() << "blocks."; else clog(NetNote) << "No more blocks to get."; - m_latestBlockSent = m_chain.currentHash(); + updateGrabbing(Grabbing::Nothing); } } @@ -129,6 +206,7 @@ void EthereumHost::doWork() maintainBlocks(m_bq, h); // return netChange; // TODO: Figure out what to do with netChange. + (void)netChange; } void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash) @@ -175,6 +253,21 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash } } +void EthereumHost::reset() +{ + m_grabbing = Grabbing::Nothing; + + m_incomingTransactions.clear(); + m_incomingBlocks.clear(); + + m_totalDifficultyOfNeeded = 0; + m_blocksNeeded.clear(); + m_blocksOnWay.clear(); + + m_latestBlockSent = h256(); + m_transactionsSent.clear(); +} + void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) { // Import new blocks @@ -187,14 +280,8 @@ void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) m_incomingBlocks.clear(); } - // If we've finished our initial sync... - { - Guard l(x_blocksNeeded); - if (m_blocksOnWay.size()) - return; - } - // ...send any new blocks. - if (m_latestBlockSent != _currentHash) + // If we've finished our initial sync send any new blocks. + if (m_grabbing == Grabbing::Nothing && m_chain.details(m_latestBlockSent) && m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty) { RLPStream ts; EthereumPeer::prep(ts); @@ -222,32 +309,3 @@ void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) m_latestBlockSent = _currentHash; } } - -void EthereumHost::noteHaveChain(EthereumPeer* _from) -{ - auto td = _from->m_totalDifficulty; - - if (_from->m_neededBlocks.empty()) - return; - - clog(NetNote) << "Hash-chain COMPLETE:" << _from->m_totalDifficulty << "vs" << m_chain.details().totalDifficulty << "," << m_totalDifficultyOfNeeded << ";" << _from->m_neededBlocks.size() << " blocks, ends" << _from->m_neededBlocks.back().abridged(); - - if ((m_totalDifficultyOfNeeded && td < m_totalDifficultyOfNeeded) || td < m_chain.details().totalDifficulty) - { - clog(NetNote) << "Difficulty of hashchain LOWER. Ignoring."; - return; - } - - clog(NetNote) << "Difficulty of hashchain HIGHER. Replacing fetch queue."; - - // Looks like it's the best yet for total difficulty. Set to download. - { - Guard l(x_blocksNeeded); - m_blocksNeeded = _from->m_neededBlocks; - m_blocksOnWay.clear(); - m_totalDifficultyOfNeeded = td; - } - - for (auto j: peers()) - j->cap()->restartGettingChain(); -} diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 418439114..c875d74fe 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -185,7 +185,10 @@ public: u256 networkId() const { return m_networkId; } void setNetworkId(u256 _n) { m_networkId = _n; } + void reset(); + private: + void noteHavePeerState(EthereumPeer* _who); /// Session wants to pass us a block that we might not have. /// @returns true if we didn't have it. bool noteBlock(h256 _hash, bytesConstRef _data); @@ -199,7 +202,7 @@ private: /// Called by peer to add incoming transactions. void addIncomingTransaction(bytes const& _bytes) { std::lock_guard l(m_incomingLock); m_incomingTransactions.push_back(_bytes); } - + void maintainTransactions(TransactionQueue& _tq, h256 _currentBlock); void maintainBlocks(BlockQueue& _bq, h256 _currentBlock); @@ -217,12 +220,17 @@ private: virtual void onStarting() { startWorking(); } virtual void onStopping() { stopWorking(); } + void readyForSync(); + void updateGrabbing(Grabbing _g); + BlockChain const& m_chain; TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. BlockQueue& m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). u256 m_networkId; + Grabbing m_grabbing = Grabbing::Nothing; + mutable std::recursive_mutex m_incomingLock; std::vector m_incomingTransactions; std::vector m_incomingBlocks; @@ -233,7 +241,7 @@ private: h256Set m_blocksOnWay; h256 m_latestBlockSent; - std::set m_transactionsSent; + h256Set m_transactionsSent; }; } diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index f8f944916..7ccf6939b 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -73,16 +73,35 @@ void EthereumPeer::startInitialSync() sealAndSend(s); } + host()->noteHavePeerState(this); +} + +void EthereumPeer::tryGrabbingHashChain() +{ + // if already done this, then ignore. + if (m_grabbing != Grabbing::State) + { + clogS(NetAllDetail) << "Already synced with this peer."; + return; + } + h256 c = host()->m_chain.currentHash(); unsigned n = host()->m_chain.number(); u256 td = max(host()->m_chain.details().totalDifficulty, host()->m_totalDifficultyOfNeeded); - clogS(NetAllDetail) << "Initial sync. Latest:" << c.abridged() << ", number:" << n << ", TD: max(" << host()->m_chain.details().totalDifficulty << "," << host()->m_totalDifficultyOfNeeded << ") versus " << m_totalDifficulty; + clogS(NetAllDetail) << "Attempt chain-grab? Latest:" << c.abridged() << ", number:" << n << ", TD: max(" << host()->m_chain.details().totalDifficulty << "," << host()->m_totalDifficultyOfNeeded << ") versus " << m_totalDifficulty; if (td > m_totalDifficulty) + { + clogS(NetAllDetail) << "No. Our chain is better."; + m_grabbing = Grabbing::Nothing; return; // All good - we have the better chain. + } // Our chain isn't better - grab theirs. { + clogS(NetAllDetail) << "Yes. Their chain is better."; + + m_grabbing = Grabbing::Hashes; RLPStream s; prep(s).appendList(3); s << GetBlockHashesPacket << m_latestHash << c_maxHashesAsk; @@ -94,6 +113,16 @@ void EthereumPeer::startInitialSync() void EthereumPeer::giveUpOnFetch() { clogS(NetNote) << "GIVE UP FETCH; can't get" << toString(m_askedBlocks); + + // a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry. + if (m_grabbing == Grabbing::Chain) + { + m_grabbing = Grabbing::Nothing; + host()->updateGrabbing(Grabbing::Nothing); + } + + // NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary. + if (m_askedBlocks.size()) { Guard l (host()->x_blocksNeeded); @@ -157,7 +186,7 @@ bool EthereumPeer::interpret(RLP const& _r) unsigned limit = _r[2].toInt(); clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")"; - unsigned c = min(host()->m_chain.number(later), limit); + unsigned c = min(max(1, host()->m_chain.number(later)) - 1, limit); RLPStream s; prep(s).appendList(1 + c).append(BlockHashesPacket); @@ -169,7 +198,13 @@ bool EthereumPeer::interpret(RLP const& _r) } case BlockHashesPacket: { - clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)"; + clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreHashes"); + + if (m_grabbing != Grabbing::Hashes) + { + cwarn << "Peer giving us hashes when we didn't ask for them."; + break; + } if (_r.itemCount() == 1) { host()->noteHaveChain(this); @@ -196,7 +231,7 @@ bool EthereumPeer::interpret(RLP const& _r) case GetBlocksPacket: { clogS(NetMessageSummary) << "GetBlocks (" << dec << (_r.itemCount() - 1) << "entries)"; - // TODO: return the requested blocks. + // return the requested blocks. bytes rlp; unsigned n = 0; for (unsigned i = 1; i < _r.itemCount() && i <= c_maxBlocks; ++i) @@ -214,7 +249,7 @@ bool EthereumPeer::interpret(RLP const& _r) } case BlocksPacket: { - clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)"; + clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); if (_r.itemCount() == 1 && !m_askedBlocksChanged) { @@ -302,7 +337,7 @@ void EthereumPeer::continueGettingChain() else { if (m_failedBlocks.size()) - clogS(NetMessageSummary) << "No blocks left to get. Peer doesn't seem to have" << m_failedBlocks.size() << "of our needed blocks."; + clogS(NetMessageSummary) << "No blocks left to get. Peer doesn't seem to have" << m_failedBlocks.size() << "of our needed blocks."; host()->noteDoneBlocks(); } } diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index f171767cd..7248fabd0 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -59,6 +59,8 @@ private: void sendStatus(); void startInitialSync(); + void tryGrabbingHashChain(); + /// Ensure that we are waiting for a bunch of blocks from our peer. void ensureGettingChain(); /// Ensure that we are waiting for a bunch of blocks from our peer. @@ -73,6 +75,8 @@ private: unsigned m_protocolVersion; u256 m_networkId; + Grabbing m_grabbing = Grabbing::State; + h256 m_latestHash; ///< Peer's latest block's hash. u256 m_totalDifficulty; ///< Peer's latest block's total difficulty. h256s m_neededBlocks; ///< The blocks that we should download from this peer. diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 093db299f..a74b6ba18 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -152,10 +152,15 @@ State& State::operator=(State const& _s) m_currentBlock = _s.m_currentBlock; m_ourAddress = _s.m_ourAddress; m_blockReward = _s.m_blockReward; + m_lastTx = _s.m_lastTx; paranoia("after state cloning (assignment op)", true); return *this; } +State::~State() +{ +} + struct CachedAddressState { CachedAddressState(std::string const& _rlp, AddressState const* _s, OverlayDB const* _o): rS(_rlp), r(rS), s(_s), o(_o) {} diff --git a/libethereum/State.h b/libethereum/State.h index 335f27168..fea8e06c7 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -90,6 +90,8 @@ public: /// Copy state object. State& operator=(State const& _s); + ~State(); + /// Set the coinbase address for any transactions we do. /// This causes a complete reset of current block. void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); } diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index 154eed9d2..22a420602 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -52,6 +52,8 @@ public: void setFuture(std::pair const& _t); void noteGood(std::pair const& _t); + void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } + private: mutable boost::shared_mutex m_lock; ///< General lock. std::set m_known; ///< Hashes of transactions in both sets. diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index da41f9e76..06717ee26 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -106,7 +106,7 @@ public: bool haveNetwork() const { return m_net.isStarted(); } - void setNetworkPreferences(p2p::NetworkPreferences const& _n) { auto had = haveNetwork(); if (had) stopNetwork(); m_netPrefs = _n; if (had) startNetwork(); } + void setNetworkPreferences(p2p::NetworkPreferences const& _n) { auto had = haveNetwork(); if (had) stopNetwork(); m_net.setNetworkPreferences(_n); if (had) startNetwork(); } /// Start the network subsystem. void startNetwork() { m_net.start(); } @@ -121,7 +121,6 @@ private: std::unique_ptr m_whisper; ///< Main interface for Whisper ("shh") protocol. p2p::Host m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. - p2p::NetworkPreferences m_netPrefs; };