From 3f61b506dbb064b39e74920f02569f8f7194dfcc Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Oct 2014 14:25:11 +0200 Subject: [PATCH 01/26] Remove incoming queue. Put things straight into actual queues. Make state items more fitting. --- libethereum/BlockQueue.cpp | 14 +++-- libethereum/BlockQueue.h | 17 ++++- libethereum/CommonNet.h | 10 ++- libethereum/EthereumHost.cpp | 39 +++--------- libethereum/EthereumHost.h | 7 --- libethereum/EthereumPeer.cpp | 118 ++++++++++++++++++++++++++--------- libethereum/EthereumPeer.h | 6 +- libp2p/Session.cpp | 14 ++--- 8 files changed, 141 insertions(+), 84 deletions(-) diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index a845965d5..67e48a59d 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -29,7 +29,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) +ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) { // Check if we already know this block. h256 h = BlockInfo::headerHash(_block); @@ -42,7 +42,7 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) { // Already know about this one. cblockq << "Already known."; - return false; + return ImportResult::AlreadyKnown; } // VERIFY: populates from the block and checks the block is internally coherent. @@ -59,7 +59,7 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) catch (Exception const& _e) { cwarn << "Ignoring malformed block: " << _e.description(); - return false; + return ImportResult::Malformed; } #endif @@ -67,7 +67,7 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) if (_bc.details(h)) { cblockq << "Already known in chain."; - return false; + return ImportResult::AlreadyInChain; } UpgradeGuard ul(l); @@ -77,6 +77,7 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) { m_future.insert(make_pair((unsigned)bi.timestamp, _block.toBytes())); cblockq << "OK - queued for future."; + return ImportResult::FutureTime; } else { @@ -87,6 +88,8 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) 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); + + return ImportResult::UnknownParent; } else { @@ -96,10 +99,9 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) m_readySet.insert(h); noteReadyWithoutWriteGuard(h); + return ImportResult::Success; } } - - return true; } void BlockQueue::tick(BlockChain const& _bc) diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index 20bc4ce59..210b9eeb0 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -37,6 +37,16 @@ class BlockChain; struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; }; #define cblockq dev::LogOutputStream() +enum class ImportResult +{ + Success = 0, + UnknownParent, + FutureTime, + AlreadyInChain, + AlreadyKnown, + Malformed +}; + /** * @brief A queue of blocks. Sits between network or other I/O and the BlockChain. * Sorts them ready for blockchain insertion (with the BlockChain::sync() method). @@ -46,7 +56,7 @@ class BlockQueue { public: /// Import a block into the queue. - bool import(bytesConstRef _tx, BlockChain const& _bc); + ImportResult import(bytesConstRef _tx, BlockChain const& _bc); /// Notes that time has moved on and some blocks that used to be "in the future" may no be valid. void tick(BlockChain const& _bc); @@ -67,6 +77,9 @@ public: /// 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(); } + /// Return first block with an unknown parent. + h256 firstUnknown() const { ReadGuard l(m_lock); return m_unknownSet.size() ? *m_unknownSet.begin() : h256(); } + private: void noteReadyWithoutWriteGuard(h256 _b); void notePresentWithoutWriteGuard(bytesConstRef _block); @@ -77,7 +90,7 @@ private: std::vector m_ready; ///< List of blocks, in correct order, ready for chain-import. std::set m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain. std::multimap> m_unknown; ///< For transactions that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears. - std::multimap m_future; ///< Set of blocks that are not yet valid. + std::multimap m_future; ///< Set of blocks that are not yet valid. }; } diff --git a/libethereum/CommonNet.h b/libethereum/CommonNet.h index c53b644e4..c7f6929e3 100644 --- a/libethereum/CommonNet.h +++ b/libethereum/CommonNet.h @@ -65,10 +65,16 @@ enum class Grabbing { State, Hashes, - Chain, - ChainHelper, + Blocks, Nothing }; +enum class Syncing +{ + Waiting, + Executing, + Done +}; + } } diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index e7e80c28a..61b0ec70a 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -70,9 +70,9 @@ bool EthereumHost::ensureInitialised(TransactionQueue& _tq) return false; } -void EthereumHost::noteHavePeerState(EthereumPeer* _who) +void EthereumHost::notePeerStateChanged(EthereumPeer* _who) { - clog(NetAllDetail) << "Have peer state."; + clog(NetAllDetail) << "Peer state changed."; // TODO: FIX: BUG: Better state management! @@ -80,7 +80,7 @@ void EthereumHost::noteHavePeerState(EthereumPeer* _who) if (m_grabbing != Grabbing::Nothing) { for (auto const& i: peers()) - if (i->cap()->m_grabbing == m_grabbing || m_grabbing == Grabbing::State) + if (i->cap()->m_grabbing == m_grabbing || m_grabbing == Grabbing::Presync) { clog(NetAllDetail) << "Already downloading chain. Just set to help out."; _who->ensureGettingChain(); @@ -205,9 +205,7 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash // Send any new transactions. for (auto const& p: peers()) - { - auto ep = p->cap(); - if (ep) + if (auto ep = p->cap()) { bytes b; unsigned n = 0; @@ -231,7 +229,6 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash } ep->m_requireTransactions = false; } - } } void EthereumHost::reset() @@ -249,36 +246,20 @@ void EthereumHost::reset() void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) { - // Import new blocks - { - lock_guard l(m_incomingLock); - for (auto it = m_incomingBlocks.rbegin(); it != m_incomingBlocks.rend(); ++it) - if (_bq.import(&*it, m_chain)) - {} - else{} // TODO: don't forward it. - m_incomingBlocks.clear(); - } - // If we've finished our initial sync send any new blocks. if (m_grabbing == Grabbing::Nothing && m_chain.isKnown(m_latestBlockSent) && m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty) { + // TODO: clean up + h256s hs; + hs.push_back(_currentHash); RLPStream ts; EthereumPeer::prep(ts); bytes bs; - unsigned c = 0; - for (auto h: m_chain.treeRoute(m_latestBlockSent, _currentHash, nullptr, false, true)) - { + for (auto h: hs) bs += m_chain.block(h); - ++c; - } - clog(NetMessageSummary) << "Sending" << c << "new blocks (current is" << _currentHash << ", was" << m_latestBlockSent << ")"; - if (c > 1000) - { - cwarn << "Gaa this would be an awful lot of new blocks. Not bothering"; - return; - } + clog(NetMessageSummary) << "Sending" << hs.size() << "new blocks (current is" << _currentHash << ", was" << m_latestBlockSent << ")"; - ts.appendList(1 + c).append(BlocksPacket).appendRaw(bs, c); + ts.appendList(1 + hs.size()).append(BlocksPacket).appendRaw(bs, hs.size()); bytes b; ts.swapOut(b); seal(b); diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 07ef92513..49b8077a8 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -85,9 +85,6 @@ private: /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. void doWork(); - /// 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); @@ -116,10 +113,6 @@ private: Grabbing m_grabbing = Grabbing::Nothing; // TODO: needs to be thread-safe & switch to just having a peer id. - mutable std::recursive_mutex m_incomingLock; - std::vector m_incomingTransactions; - std::vector m_incomingBlocks; - DownloadMan m_man; h256 m_latestBlockSent; diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index b45abfbff..9b8139289 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -38,7 +38,7 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h): Capability(_s, _h), m_sub(host()->m_man) { - setGrabbing(Grabbing::State); + setAsking(Asking::State, Syncing::Done); sendStatus(); } @@ -75,13 +75,19 @@ void EthereumPeer::startInitialSync() sealAndSend(s); } - host()->noteHavePeerState(this); + host()->notePeerStateChanged(this); } void EthereumPeer::tryGrabbingHashChain() { + if (m_asking != Asking::Nothing) + { + clogS(NetAllDetail) << "Can't synced with this peer - outstanding asks."; + return; + } + // if already done this, then ignore. - if (m_grabbing != Grabbing::State) + if (m_syncing == Syncing::Done) { clogS(NetAllDetail) << "Already synced with this peer."; return; @@ -95,7 +101,7 @@ void EthereumPeer::tryGrabbingHashChain() if (td >= m_totalDifficulty) { clogS(NetAllDetail) << "No. Our chain is better."; - setGrabbing(Grabbing::Nothing); + setAsking(Asking::Nothing, Syncing::Done); return; // All good - we have the better chain. } @@ -103,8 +109,8 @@ void EthereumPeer::tryGrabbingHashChain() { clogS(NetAllDetail) << "Yes. Their chain is better."; - host()->updateGrabbing(Grabbing::Hashes); - setGrabbing(Grabbing::Hashes); + host()->updateGrabbing(Asking::Hashes); + setAsking(Asking::Hashes, Syncing::Executing); RLPStream s; prep(s).appendList(3); s << GetBlockHashesPacket << m_latestHash << c_maxHashesAsk; @@ -118,10 +124,10 @@ void EthereumPeer::giveUpOnFetch() clogS(NetNote) << "Finishing fetch..."; // a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry. - if (m_grabbing == Grabbing::Chain || m_grabbing == Grabbing::ChainHelper) + if (m_asking == Asking::Blocks || m_asking == Asking::ChainHelper) { host()->noteDoneBlocks(this); - setGrabbing(Grabbing::Nothing); + setAsking(Asking::Nothing); } // NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary. @@ -163,12 +169,11 @@ bool EthereumPeer::interpret(RLP const& _r) { clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; addRating(_r.itemCount() - 1); - lock_guard l(host()->m_incomingLock); + RecursiveGuard l(m_incomingLock); + Guard l(x_knownTransactions); for (unsigned i = 1; i < _r.itemCount(); ++i) { - host()->addIncomingTransaction(_r[i].data().toBytes()); - - lock_guard l(x_knownTransactions); + m_incomingTransactions.push_back(_r[i].data().toBytes()); m_knownTransactions.insert(sha3(_r[i].data())); } break; @@ -193,7 +198,7 @@ bool EthereumPeer::interpret(RLP const& _r) { clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreHashes"); - if (m_grabbing != Grabbing::Hashes) + if (m_asking != Asking::Hashes) { cwarn << "Peer giving us hashes when we didn't ask for them."; break; @@ -247,21 +252,62 @@ bool EthereumPeer::interpret(RLP const& _r) if (_r.itemCount() == 1) { // Couldn't get any from last batch - probably got to this peer's latest block - just give up. - if (m_grabbing == Grabbing::Chain || m_grabbing == Grabbing::ChainHelper) + if (m_asking == Asking::Blocks || m_asking == Asking::ChainHelper) giveUpOnFetch(); break; } - unsigned used = 0; + unsigned success = 0; + unsigned got = 0; + unsigned bad = 0; + unsigned unknown = 0; + unsigned future = 0; + for (unsigned i = 1; i < _r.itemCount(); ++i) { auto h = BlockInfo::headerHash(_r[i].data()); m_sub.noteBlock(h); - if (host()->noteBlock(h, _r[i].data())) - used++; - Guard l(x_knownBlocks); - m_knownBlocks.insert(h); + + { + Guard l(x_knownBlocks); + m_knownBlocks.insert(h); + } + + switch (host()->m_bq.import(_r[i].data(), host()->m_chain)) + { + case ImportResult::Success: + success++; + break; + + case ImportResult::Malformed: + bad++; + break; + + case ImportResult::FutureTime: + future++; + break; + + case ImportResult::AlreadyInChain: + case ImportResult::AlreadyKnown: + got++; + break; + + case ImportResult::UnknownParent: + unknown++; + break; + } } + + if (unknown && m_asking == Asking::Nothing) + { + // TODO: kick off resync. + } + + if (bad) + { + // TODO: punish peer + } + addRating(used); unsigned knownParents = 0; unsigned unknownParents = 0; @@ -286,7 +332,7 @@ bool EthereumPeer::interpret(RLP const& _r) } } clogS(NetMessageSummary) << dec << knownParents << "known parents," << unknownParents << "unknown," << used << "used."; - if (m_grabbing == Grabbing::Chain || m_grabbing == Grabbing::ChainHelper) + if (m_asking == Asking::Blocks || m_asking == Asking::ChainHelper) continueGettingChain(); break; } @@ -298,20 +344,18 @@ bool EthereumPeer::interpret(RLP const& _r) void EthereumPeer::ensureGettingChain() { - if (m_grabbing == Grabbing::ChainHelper) + if (m_helping) return; // Already asked & waiting for some. - // Switch to ChainHelper otherwise, unless we're already the Chain grabber. - if (m_grabbing != Grabbing::Chain) - setGrabbing(Grabbing::ChainHelper); - + // Help otherwise, unless we're already the Chain grabber. + setHelping(true); continueGettingChain(); } void EthereumPeer::continueGettingChain() { // If we're getting the hashes already, then we shouldn't be asking for the chain. - if (m_grabbing == Grabbing::Hashes) + if (m_asking == Asking::Hashes) return; auto blocks = m_sub.nextFetch(c_maxBlocksAsk); @@ -329,8 +373,24 @@ void EthereumPeer::continueGettingChain() giveUpOnFetch(); } -void EthereumPeer::setGrabbing(Grabbing _g) +/* + * Possible asking/syncing states for two peers: + * state/ presync + * presync hashes + * presync chain (transiently) + * presync+ chain + * presync nothing + * hashes nothing + * chain hashes + * presync chain (transiently) + * presync+ chain + * presync nothing + */ + +void EthereumPeer::setAsking(Asking _a, Syncing _s) { - m_grabbing = _g; - session()->addNote("grab", _g == Grabbing::Nothing ? "nothing" : _g == Grabbing::State ? "state" : _g == Grabbing::Hashes ? "hashes" : _g == Grabbing::Chain ? "chain" : _g == Grabbing::ChainHelper ? "chainhelper" : "?"); + m_asking = _a; + m_syncing = _s; + session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?"); + session()->addNote("sync", _s == Syncing::Done ? "done" : _s == Syncing::Waiting ? "wait" : _s == Syncing::Executing ? "exec" : "?"); } diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index 052af3c7e..da020f503 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -71,12 +71,14 @@ private: void giveUpOnFetch(); void clearKnownTransactions() { std::lock_guard l(x_knownTransactions); m_knownTransactions.clear(); } - void setGrabbing(Grabbing _g); + void setAsking(Asking _g, bool _helping = false); + void setHelping(bool _helping = false) { setAsking(m_asking, _helping); } unsigned m_protocolVersion; u256 m_networkId; - Grabbing m_grabbing; + Asking m_asking; + Syncing m_syncing; h256 m_latestHash; ///< Peer's latest block's hash. u256 m_totalDifficulty; ///< Peer's latest block's total difficulty. diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index ea8db2127..84a33db65 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -87,7 +87,7 @@ bool Session::interpret(RLP const& _r) if (m_server->havePeer(m_id)) { // Already connected. - cwarn << "Already have peer id" << m_id.abridged();// << "at" << l->endpoint() << "rather than" << endpoint(); + clogS(NetWarn) << "Already have peer id" << m_id.abridged();// << "at" << l->endpoint() << "rather than" << endpoint(); disconnect(DuplicatePeer); return false; } @@ -240,7 +240,7 @@ void Session::sendDestroy(bytes& _msg) if (!checkPacket(bytesConstRef(&_msg))) { - cwarn << "INVALID PACKET CONSTRUCTED!"; + clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!"; } bytes buffer = bytes(std::move(_msg)); @@ -253,7 +253,7 @@ void Session::send(bytesConstRef _msg) if (!checkPacket(_msg)) { - cwarn << "INVALID PACKET CONSTRUCTED!"; + clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!"; } bytes buffer = bytes(_msg.toBytes()); @@ -288,7 +288,7 @@ void Session::write() // must check queue, as write callback can occur following dropped() if (ec) { - cwarn << "Error sending: " << ec.message(); + clogS(NetWarn) << "Error sending: " << ec.message(); dropped(); return; } @@ -363,7 +363,7 @@ void Session::doRead() if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) { // got here with length of 1241... - cwarn << "Error reading: " << ec.message(); + clogS(NetWarn) << "Error reading: " << ec.message(); dropped(); } else if (ec && length == 0) @@ -380,7 +380,7 @@ void Session::doRead() { if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91) { - cwarn << "INVALID SYNCHRONISATION TOKEN; expected = 22400891; received = " << toHex(bytesConstRef(m_incoming.data(), 4)); + clogS(NetWarn) << "INVALID SYNCHRONISATION TOKEN; expected = 22400891; received = " << toHex(bytesConstRef(m_incoming.data(), 4)); disconnect(BadProtocol); return; } @@ -396,7 +396,7 @@ void Session::doRead() if (!checkPacket(data)) { cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; - cwarn << "INVALID MESSAGE RECEIVED"; + clogS(NetWarn) << "INVALID MESSAGE RECEIVED"; disconnect(BadProtocol); return; } From 2a1eddc9c9c73d0055d9ee4a7345e8952eba7024 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Oct 2014 14:35:06 +0200 Subject: [PATCH 02/26] Quick fix for third. --- libdevcore/RLP.h | 2 +- third/MainWin.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libdevcore/RLP.h b/libdevcore/RLP.h index 7d223f73a..248a2e645 100644 --- a/libdevcore/RLP.h +++ b/libdevcore/RLP.h @@ -289,7 +289,7 @@ public: private: /// Disable construction from rvalue - explicit RLP(bytes const&& _d) {} + explicit RLP(bytes const&&) {} /// Single-byte data payload. bool isSingleByte() const { return !isNull() && m_data[0] < c_rlpDataImmLenStart; } diff --git a/third/MainWin.cpp b/third/MainWin.cpp index 10f07ef6a..f86dc0434 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -84,9 +84,10 @@ Main::Main(QWidget *parent) : ui->setupUi(this); cerr << "State root: " << BlockChain::genesis().stateRoot << endl; - cerr << "Block Hash: " << sha3(BlockChain::createGenesisBlock()) << endl; - cerr << "Block RLP: " << RLP(BlockChain::createGenesisBlock()) << endl; - cerr << "Block Hex: " << toHex(BlockChain::createGenesisBlock()) << endl; + auto gb = BlockChain::createGenesisBlock(); + cerr << "Block Hash: " << sha3(gb) << endl; + cerr << "Block RLP: " << RLP(gb) << endl; + cerr << "Block Hex: " << toHex(gb) << endl; cerr << "Network protocol version: " << dev::eth::c_protocolVersion << endl; cerr << "Client database version: " << dev::eth::c_databaseVersion << endl; From f73b46d2c1fcf82653a5eba59f5449c3a2da6711 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Oct 2014 22:58:51 +0200 Subject: [PATCH 03/26] Work on making states good and various docs. --- libethereum/CommonNet.h | 3 +- libethereum/EthereumHost.cpp | 42 ++++++++-------- libethereum/EthereumHost.h | 6 +-- libethereum/EthereumPeer.cpp | 96 ++++++++++++++++++++++-------------- libethereum/EthereumPeer.h | 30 ++++++++--- 5 files changed, 109 insertions(+), 68 deletions(-) diff --git a/libethereum/CommonNet.h b/libethereum/CommonNet.h index c7f6929e3..15b05bff3 100644 --- a/libethereum/CommonNet.h +++ b/libethereum/CommonNet.h @@ -59,9 +59,10 @@ enum EthereumPacket BlockHashesPacket, GetBlocksPacket, BlocksPacket, + NewBlockPacket, }; -enum class Grabbing +enum class Asking { State, Hashes, diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 61b0ec70a..4be0065aa 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -77,28 +77,28 @@ void EthereumHost::notePeerStateChanged(EthereumPeer* _who) // TODO: FIX: BUG: Better state management! // if already downloading hash-chain, ignore. - if (m_grabbing != Grabbing::Nothing) + if (m_grabbing != Asking::Nothing) { for (auto const& i: peers()) - if (i->cap()->m_grabbing == m_grabbing || m_grabbing == Grabbing::Presync) + if (i->cap()->m_grabbing == m_grabbing || m_grabbing == Asking::Presync) { clog(NetAllDetail) << "Already downloading chain. Just set to help out."; _who->ensureGettingChain(); return; } - m_grabbing = Grabbing::Nothing; + m_grabbing = Asking::Nothing; } // otherwise check to see if we should be downloading... _who->tryGrabbingHashChain(); } -void EthereumHost::updateGrabbing(Grabbing _g) +void EthereumHost::updateGrabbing(Asking _g) { m_grabbing = _g; - if (_g == Grabbing::Nothing) + if (_g == Asking::Nothing) readyForSync(); - else if (_g == Grabbing::Chain) + else if (_g == Asking::Chain) for (auto j: peers()) j->cap()->ensureGettingChain(); } @@ -109,8 +109,8 @@ void EthereumHost::noteHaveChain(EthereumPeer* _from) if (_from->m_neededBlocks.empty()) { - _from->setGrabbing(Grabbing::Nothing); - updateGrabbing(Grabbing::Nothing); + _from->setGrabbing(Asking::Nothing); + updateGrabbing(Asking::Nothing); return; } @@ -119,8 +119,8 @@ void EthereumHost::noteHaveChain(EthereumPeer* _from) if (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->setGrabbing(Grabbing::Nothing); - updateGrabbing(Grabbing::Nothing); + _from->setGrabbing(Asking::Nothing); + updateGrabbing(Asking::Nothing); return; } @@ -130,8 +130,8 @@ void EthereumHost::noteHaveChain(EthereumPeer* _from) m_man.resetToChain(_from->m_neededBlocks); m_latestBlockSent = _from->m_latestHash; - _from->setGrabbing(Grabbing::Chain); - updateGrabbing(Grabbing::Chain); + _from->setGrabbing(Asking::Chain); + updateGrabbing(Asking::Chain); } void EthereumHost::readyForSync() @@ -140,9 +140,9 @@ void EthereumHost::readyForSync() for (auto j: peers()) { j->cap()->tryGrabbingHashChain(); - if (j->cap()->m_grabbing == Grabbing::Hashes) + if (j->cap()->m_grabbing == Asking::Hashes) { - m_grabbing = Grabbing::Hashes; + m_grabbing = Asking::Hashes; return; } } @@ -155,15 +155,15 @@ void EthereumHost::noteDoneBlocks(EthereumPeer* _who) { // Done our chain-get. clog(NetNote) << "Chain download complete."; - updateGrabbing(Grabbing::Nothing); + updateGrabbing(Asking::Nothing); m_man.reset(); } - if (_who->m_grabbing == Grabbing::Chain) + if (_who->m_grabbing == Asking::Chain) { // Done our chain-get. clog(NetNote) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished."; // TODO: note that peer is BADBADBAD! - updateGrabbing(Grabbing::Nothing); + updateGrabbing(Asking::Nothing); m_man.reset(); } } @@ -192,7 +192,7 @@ void EthereumHost::doWork() void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash) { - bool resendAll = (m_grabbing == Grabbing::Nothing && m_chain.isKnown(m_latestBlockSent) && _currentHash != m_latestBlockSent); + bool resendAll = (m_grabbing == Asking::Nothing && m_chain.isKnown(m_latestBlockSent) && _currentHash != m_latestBlockSent); { lock_guard l(m_incomingLock); for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it) @@ -218,7 +218,7 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash } ep->clearKnownTransactions(); - if (n) + if (n || ep->m_requireTransactions) { RLPStream ts; EthereumPeer::prep(ts); @@ -233,7 +233,7 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash void EthereumHost::reset() { - m_grabbing = Grabbing::Nothing; + m_grabbing = Asking::Nothing; m_man.resetToChain(h256s()); @@ -247,7 +247,7 @@ void EthereumHost::reset() void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) { // If we've finished our initial sync send any new blocks. - if (m_grabbing == Grabbing::Nothing && m_chain.isKnown(m_latestBlockSent) && m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty) + if (m_grabbing == Asking::Nothing && m_chain.isKnown(m_latestBlockSent) && m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty) { // TODO: clean up h256s hs; diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 49b8077a8..8cbfbf678 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -70,7 +70,7 @@ public: void reset(); DownloadMan const& downloadMan() const { return m_man; } - bool isSyncing() const { return m_grabbing == Grabbing::Chain; } + bool isSyncing() const { return m_grabbing == Asking::Chain; } private: void noteHavePeerState(EthereumPeer* _who); @@ -103,7 +103,7 @@ private: virtual void onStopping() { stopWorking(); } void readyForSync(); - void updateGrabbing(Grabbing _g); + void updateGrabbing(Asking _g); BlockChain const& m_chain; TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. @@ -111,7 +111,7 @@ private: u256 m_networkId; - Grabbing m_grabbing = Grabbing::Nothing; // TODO: needs to be thread-safe & switch to just having a peer id. + Asking m_grabbing = Asking::Nothing; // TODO: needs to be thread-safe & switch to just having a peer id. DownloadMan m_man; diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 9b8139289..ee5db6a4b 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -44,7 +44,7 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h): EthereumPeer::~EthereumPeer() { - giveUpOnFetch(); + finishSync(); } EthereumHost* EthereumPeer::host() const @@ -119,7 +119,7 @@ void EthereumPeer::tryGrabbingHashChain() } } -void EthereumPeer::giveUpOnFetch() +void EthereumPeer::finishSync() { clogS(NetNote) << "Finishing fetch..."; @@ -169,12 +169,12 @@ bool EthereumPeer::interpret(RLP const& _r) { clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; addRating(_r.itemCount() - 1); - RecursiveGuard l(m_incomingLock); Guard l(x_knownTransactions); for (unsigned i = 1; i < _r.itemCount(); ++i) { - m_incomingTransactions.push_back(_r[i].data().toBytes()); m_knownTransactions.insert(sha3(_r[i].data())); + if (!_tq.import(_r[i].data())) // if we already had the transaction, then don't bother sending it on. + host()->m_transactionsSent.insert(sha3(*it)); } break; } @@ -245,23 +245,59 @@ bool EthereumPeer::interpret(RLP const& _r) sealAndSend(prep(s).appendList(n + 1).append(BlocksPacket).appendRaw(rlp, n)); break; } + case NewBlockPacket: + { + auto h = BlockInfo::headerHash(bd(_r[1].data())); + clogS(NetMessageSummary) << "NewBlock: " << h.abridged(); + + if (_r.itemCount() != 3) + disable("NewBlock without 2 data fields."); + else + { + switch (host()->m_bq.import(_r[1].data(), host()->m_chain)) + { + case ImportResult::Success: + case ImportResult::FutureTime: + addRating(1); + break; + + case ImportResult::Malformed: + disable("Malformed block received."); + break; + + case ImportResult::AlreadyInChain: + case ImportResult::AlreadyKnown: + break; + + case ImportResult::UnknownParent: + clogS(NetMessageSummary) << "Received block with no known parent. Resyncing..."; + setNeedsSyncing(h, _r[2].toInt()); + break; + } + Guard l(x_knownBlocks); + m_knownBlocks.insert(h); + } + break; + } case BlocksPacket: { clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); + if (m_asking != Asking::Blocks) + clogS(NetWarn) << "Unexpected Blocks received!"; + if (_r.itemCount() == 1) { - // Couldn't get any from last batch - probably got to this peer's latest block - just give up. - if (m_asking == Asking::Blocks || m_asking == Asking::ChainHelper) - giveUpOnFetch(); + // Got to this peer's latest block - just give up. + if (m_asking == Asking::Blocks) + finishSync(); break; } unsigned success = 0; - unsigned got = 0; - unsigned bad = 0; - unsigned unknown = 0; unsigned future = 0; + unsigned unknown = 0; + unsigned got = 0; for (unsigned i = 1; i < _r.itemCount(); ++i) { @@ -276,12 +312,13 @@ bool EthereumPeer::interpret(RLP const& _r) switch (host()->m_bq.import(_r[i].data(), host()->m_chain)) { case ImportResult::Success: + addRating(1); success++; break; case ImportResult::Malformed: - bad++; - break; + disable("Malformed block received."); + return true; case ImportResult::FutureTime: future++; @@ -298,17 +335,6 @@ bool EthereumPeer::interpret(RLP const& _r) } } - if (unknown && m_asking == Asking::Nothing) - { - // TODO: kick off resync. - } - - if (bad) - { - // TODO: punish peer - } - - addRating(used); unsigned knownParents = 0; unsigned unknownParents = 0; if (g_logVerbosity >= NetMessageSummary::verbosity) @@ -331,9 +357,9 @@ bool EthereumPeer::interpret(RLP const& _r) } } } - clogS(NetMessageSummary) << dec << knownParents << "known parents," << unknownParents << "unknown," << used << "used."; - if (m_asking == Asking::Blocks || m_asking == Asking::ChainHelper) - continueGettingChain(); + clogS(NetMessageSummary) << dec << success << "known parents," << unknownParents << "unknown," << used << "used."; + if (m_asking == Asking::Blocks) + continueSync(); break; } default: @@ -342,17 +368,15 @@ bool EthereumPeer::interpret(RLP const& _r) return true; } -void EthereumPeer::ensureGettingChain() +void EthereumPeer::ensureAskingBlocks() { - if (m_helping) + if (m_asking != Asking::Nothing) return; // Already asked & waiting for some. - // Help otherwise, unless we're already the Chain grabber. - setHelping(true); - continueGettingChain(); + continueSync(); } -void EthereumPeer::continueGettingChain() +void EthereumPeer::continueSync() { // If we're getting the hashes already, then we shouldn't be asking for the chain. if (m_asking == Asking::Hashes) @@ -370,7 +394,7 @@ void EthereumPeer::continueGettingChain() sealAndSend(s); } else - giveUpOnFetch(); + finishSync(); } /* @@ -387,10 +411,10 @@ void EthereumPeer::continueGettingChain() * presync nothing */ -void EthereumPeer::setAsking(Asking _a, Syncing _s) +void EthereumPeer::setAsking(Asking _a, bool _isSyncing) { m_asking = _a; - m_syncing = _s; + m_isSyncing = _isSyncing; session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?"); - session()->addNote("sync", _s == Syncing::Done ? "done" : _s == Syncing::Waiting ? "wait" : _s == Syncing::Executing ? "exec" : "?"); + session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? "needed" : "ok")); } diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index da020f503..d5628829c 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -64,26 +64,43 @@ private: void tryGrabbingHashChain(); /// Ensure that we are waiting for a bunch of blocks from our peer. - void ensureGettingChain(); + void ensureAskingBlocks(); /// Ensure that we are waiting for a bunch of blocks from our peer. - void continueGettingChain(); + void continueSync(); - void giveUpOnFetch(); + void finishSync(); void clearKnownTransactions() { std::lock_guard l(x_knownTransactions); m_knownTransactions.clear(); } - void setAsking(Asking _g, bool _helping = false); - void setHelping(bool _helping = false) { setAsking(m_asking, _helping); } + void setAsking(Asking _g, bool _isSyncing); + + void setNeedsSyncing(h256 _latestHash, u256 _td) { m_latestHash = _latestHash; m_totalDifficulty = _td; } + bool needsSyncing() const { return !!m_latestHash; } + bool isSyncing() const { return m_isSyncing; } + /// Peer's protocol version. unsigned m_protocolVersion; + /// Peer's network id. u256 m_networkId; + /// What, if anything, we last asked the other peer for. Asking m_asking; - Syncing m_syncing; + /// Whether this peer is in the process of syncing or not. Only one peer can be syncing at once. + bool m_isSyncing = false; + + /// These are determined through either a Status message or from NewBlock. h256 m_latestHash; ///< Peer's latest block's hash. u256 m_totalDifficulty; ///< Peer's latest block's total difficulty. + /// Once a sync is started on this peer, they are cleared. + + /// This is built as we ask for hashes. Once no more hashes are given, we present this to the + /// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks. h256s m_neededBlocks; ///< The blocks that we should download from this peer. + /// Once we're asking for blocks, this becomes in use. + DownloadSub m_sub; + + /// Have we received a GetTransactions packet that we haven't yet answered? bool m_requireTransactions; Mutex x_knownBlocks; @@ -91,7 +108,6 @@ private: std::set m_knownTransactions; std::mutex x_knownTransactions; - DownloadSub m_sub; }; } From 10f3655270dc2732a3d7e572f385ab5f152348fd Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Oct 2014 23:00:15 +0200 Subject: [PATCH 04/26] New addresses whack existing. --- libethereum/State.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index a2c422e9b..10f283f77 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1138,8 +1138,6 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, } Address newAddress = right160(sha3(rlpList(_sender, transactionsFrom(_sender) - 1))); - while (addressInUse(newAddress)) - newAddress = (u160)newAddress + 1; // Set up new account... m_cache[newAddress] = AddressState(0, balance(newAddress) + _endowment, h256(), h256()); From 6df207655c4f0db9257e296c2852c20bc32ec850 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 00:37:31 +0200 Subject: [PATCH 05/26] More work on the peer state transition system --- libethereum/EthereumHost.cpp | 39 ++--- libethereum/EthereumHost.h | 6 +- libethereum/EthereumPeer.cpp | 329 +++++++++++++++++++---------------- libethereum/EthereumPeer.h | 18 +- 4 files changed, 206 insertions(+), 186 deletions(-) diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 4be0065aa..2c87d887a 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -90,48 +90,43 @@ void EthereumHost::notePeerStateChanged(EthereumPeer* _who) } // otherwise check to see if we should be downloading... - _who->tryGrabbingHashChain(); + _who->attemptSyncing(); } -void EthereumHost::updateGrabbing(Asking _g) +void EthereumHost::updateGrabbing(Asking _g, EthereumPeer* _ignore) { m_grabbing = _g; if (_g == Asking::Nothing) readyForSync(); - else if (_g == Asking::Chain) + else if (_g == Asking::Blocks) for (auto j: peers()) - j->cap()->ensureGettingChain(); + if (j->cap().get() != _ignore && j->cap()->m_asking == Asking::Nothing) + j->cap()->transition(Asking::Blocks); } -void EthereumHost::noteHaveChain(EthereumPeer* _from) +bool EthereumHost::shouldGrabBlocks(EthereumPeer* _from) { - auto td = _from->m_totalDifficulty; + auto td = _from->m_syncingTotalDifficulty; + auto lh = _from->m_syncingLatestHash; - if (_from->m_neededBlocks.empty()) + if (_from->m_syncingNeededBlocks.empty()) { - _from->setGrabbing(Asking::Nothing); updateGrabbing(Asking::Nothing); - return; + return false; } - clog(NetNote) << "Hash-chain COMPLETE:" << _from->m_totalDifficulty << "vs" << m_chain.details().totalDifficulty << ";" << _from->m_neededBlocks.size() << " blocks, ends" << _from->m_neededBlocks.back().abridged(); + clog(NetNote) << "Hash-chain COMPLETE:" << td << "vs" << m_chain.details().totalDifficulty << ";" << _from->m_syncingNeededBlocks.size() << " blocks, ends" << _from->m_syncingNeededBlocks.back().abridged(); - if (td < m_chain.details().totalDifficulty || (td == m_chain.details().totalDifficulty && m_chain.currentHash() == _from->m_latestHash)) + if (td < m_chain.details().totalDifficulty || (td == m_chain.details().totalDifficulty && m_chain.currentHash() == lh)) { clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; - _from->setGrabbing(Asking::Nothing); updateGrabbing(Asking::Nothing); - return; + return false; } - clog(NetNote) << "Difficulty of hashchain HIGHER. Replacing fetch queue [latest now" << _from->m_latestHash.abridged() << ", was" << m_latestBlockSent.abridged() << "]"; + clog(NetNote) << "Difficulty of hashchain HIGHER. Replacing fetch queue [latest now" << lh.abridged() << ", was" << m_latestBlockSent.abridged() << "]"; - // Looks like it's the best yet for total difficulty. Set to download. - m_man.resetToChain(_from->m_neededBlocks); - m_latestBlockSent = _from->m_latestHash; - - _from->setGrabbing(Asking::Chain); - updateGrabbing(Asking::Chain); + return true; } void EthereumHost::readyForSync() @@ -139,7 +134,7 @@ void EthereumHost::readyForSync() // start grabbing next hash chain if there is one. for (auto j: peers()) { - j->cap()->tryGrabbingHashChain(); + j->cap()->attemptSyncing(); if (j->cap()->m_grabbing == Asking::Hashes) { m_grabbing = Asking::Hashes; @@ -158,7 +153,7 @@ void EthereumHost::noteDoneBlocks(EthereumPeer* _who) updateGrabbing(Asking::Nothing); m_man.reset(); } - if (_who->m_grabbing == Asking::Chain) + if (_who->isSyncing()) { // Done our chain-get. clog(NetNote) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished."; diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 8cbfbf678..b42ae7fa7 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -77,8 +77,10 @@ private: /// 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); + /// Session has finished getting the chain of hashes. - void noteHaveChain(EthereumPeer* _who); + bool shouldGrabBlocks(EthereumPeer* _who); + /// Called when the peer can no longer provide us with any needed blocks. void noteDoneBlocks(EthereumPeer* _who); @@ -103,7 +105,7 @@ private: virtual void onStopping() { stopWorking(); } void readyForSync(); - void updateGrabbing(Asking _g); + void updateGrabbing(Asking _g, EthereumPeer* _ignore); BlockChain const& m_chain; TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index ee5db6a4b..20a9fef52 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -38,7 +38,7 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h): Capability(_s, _h), m_sub(host()->m_man) { - setAsking(Asking::State, Syncing::Done); + setAsking(Asking::State, false); sendStatus(); } @@ -65,20 +65,133 @@ void EthereumPeer::sendStatus() sealAndSend(s); } -void EthereumPeer::startInitialSync() +/* + * Possible asking/syncing states for two peers: + */ + +void EthereumPeer::transition(Asking _a) { - // Grab transactions off them. + RLPStream s; + prep(s); + if (_a == Asking::Hashes) { - RLPStream s; - prep(s).appendList(1); - s << GetTransactionsPacket; - sealAndSend(s); + if (m_asking == Asking::State || m_asking == Asking::Nothing) + { + if (isSyncing()) + clogS(NetWarn) << "Bad state: not asking for Hashes, yet syncing!"; + + m_syncingLatestHash = m_latestHash; + m_syncingTotalDifficulty = m_totalDifficulty; + m_latestHash = h256(); + + setAsking(_a, true); + s.appendList(3) << GetBlockHashesPacket << m_syncingLatestHash << c_maxHashesAsk; + m_syncingNeededBlocks = h256s(1, m_syncingLatestHash); + host()->updateGrabbing(Asking::Hashes); + sealAndSend(s); + return; + } + else if (m_asking == Asking::Hashes) + { + if (!isSyncing()) + clogS(NetWarn) << "Bad state: asking for Hashes yet not syncing!"; + + setAsking(_a, true); + s.appendList(3) << GetBlockHashesPacket << m_syncingNeededBlocks.back() << c_maxHashesAsk; + sealAndSend(s); + return; + } + } + else if (_a == Asking::Blocks) + { + if (m_asking == Asking::Hashes) + { + if (host()->shouldGrabBlocks(this)) + { + host()->m_man.resetToChain(m_syncingNeededBlocks); + host()->m_latestBlockSent = m_syncingLatestHash; + + host()->updateGrabbing(Asking::Blocks, this); + } + else + { + setAsking(Asking::Nothing, false); + return; + } + } + // run through into... + if (m_asking == Asking::Nothing || m_asking == Asking::Hashes || m_asking == Asking::Blocks) + { + // Looks like it's the best yet for total difficulty. Set to download. + setAsking(Asking::Blocks, true); + auto blocks = m_sub.nextFetch(c_maxBlocksAsk); + if (blocks.size()) + { + s.appendList(blocks.size() + 1) << GetBlocksPacket; + for (auto const& i: blocks) + s << i; + sealAndSend(s); + } + else + transition(Asking::Nothing); + return; + } } + else if (_a == Asking::Nothing) + { + if (m_asking == Asking::Blocks) + { + clogS(NetNote) << "Finishing block fetch..."; + + // a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry. + if (isSyncing()) + host()->noteDoneBlocks(this); + // NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary. + m_sub.doneFetch(); + + setAsking(Asking::Nothing, false); + } + else if (m_asking == Asking::Hashes) + { + clogS(NetNote) << "Finishing hashes fetch..."; + + if (isSyncing()) + host()->noteDoneBlocks(this); + + setAsking(Asking::Nothing, false); + } + else if (m_asking == Asking::State) + { + setAsking(Asking::Nothing, false); + // TODO: Just got the state - should check to see if we can be of help downloading the chain if any. + // TODO: Otherwise, should put ourselves up for sync. + } + // Otherwise it's fine. We don't care if it's Nothing->Nothing. + return; + } + + clogS(NetWarn) << "Invalid state transition:" << (int)_a << "from" << (int)m_asking << "/" << boolalpha << isSyncing() << needsSyncing(); +} + +void EthereumPeer::setAsking(Asking _a, bool _isSyncing) +{ + m_asking = _a; + m_isSyncing = _isSyncing; + session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?"); + session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : "")); +} + +void EthereumPeer::setNeedsSyncing(h256 _latestHash, u256 _td) +{ + m_latestHash = _latestHash; + m_totalDifficulty = _td; + + // TODO: should be "noteNeedsSyncing" or some such. host()->notePeerStateChanged(this); } -void EthereumPeer::tryGrabbingHashChain() +void EthereumPeer::attemptSyncing() { if (m_asking != Asking::Nothing) { @@ -87,7 +200,7 @@ void EthereumPeer::tryGrabbingHashChain() } // if already done this, then ignore. - if (m_syncing == Syncing::Done) + if (!needsSyncing()) { clogS(NetAllDetail) << "Already synced with this peer."; return; @@ -101,39 +214,15 @@ void EthereumPeer::tryGrabbingHashChain() if (td >= m_totalDifficulty) { clogS(NetAllDetail) << "No. Our chain is better."; - setAsking(Asking::Nothing, Syncing::Done); - return; // All good - we have the better chain. + transition(Asking::Nothing); } - - // Our chain isn't better - grab theirs. + else { clogS(NetAllDetail) << "Yes. Their chain is better."; - - host()->updateGrabbing(Asking::Hashes); - setAsking(Asking::Hashes, Syncing::Executing); - RLPStream s; - prep(s).appendList(3); - s << GetBlockHashesPacket << m_latestHash << c_maxHashesAsk; - m_neededBlocks = h256s(1, m_latestHash); - sealAndSend(s); + transition(Asking::Hashes); } } -void EthereumPeer::finishSync() -{ - clogS(NetNote) << "Finishing fetch..."; - - // a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry. - if (m_asking == Asking::Blocks || m_asking == Asking::ChainHelper) - { - host()->noteDoneBlocks(this); - setAsking(Asking::Nothing); - } - - // NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary. - m_sub.doneFetch(); -} - bool EthereumPeer::interpret(RLP const& _r) { switch (_r[0].toInt()) @@ -142,8 +231,8 @@ bool EthereumPeer::interpret(RLP const& _r) { m_protocolVersion = _r[1].toInt(); m_networkId = _r[2].toInt(); - m_totalDifficulty = _r[3].toInt(); - m_latestHash = _r[4].toHash(); + auto totalDifficulty = _r[3].toInt(); + auto latestHash = _r[4].toHash(); auto genesisHash = _r[5].toHash(); clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged(); @@ -157,7 +246,15 @@ bool EthereumPeer::interpret(RLP const& _r) else if (session()->info().clientVersion.find("/v0.6.9/") != string::npos) disable("Blacklisted client version."); else - startInitialSync(); + { + // Grab transactions off them. + RLPStream s; + prep(s).appendList(1); + s << GetTransactionsPacket; + sealAndSend(s); + + setNeedsSyncing(latestHash, totalDifficulty); + } break; } case GetTransactionsPacket: @@ -205,7 +302,7 @@ bool EthereumPeer::interpret(RLP const& _r) } if (_r.itemCount() == 1) { - host()->noteHaveChain(this); + transition(Asking::Blocks); return true; } for (unsigned i = 1; i < _r.itemCount(); ++i) @@ -213,17 +310,14 @@ bool EthereumPeer::interpret(RLP const& _r) auto h = _r[i].toHash(); if (host()->m_chain.isKnown(h)) { - host()->noteHaveChain(this); + transition(Asking::Blocks); return true; } else - m_neededBlocks.push_back(h); + m_syncingNeededBlocks.push_back(h); } // run through - ask for more. - RLPStream s; - prep(s).appendList(3); - s << GetBlockHashesPacket << m_neededBlocks.back() << c_maxHashesAsk; - sealAndSend(s); + transition(Asking::Hashes); break; } case GetBlocksPacket: @@ -245,40 +339,6 @@ bool EthereumPeer::interpret(RLP const& _r) sealAndSend(prep(s).appendList(n + 1).append(BlocksPacket).appendRaw(rlp, n)); break; } - case NewBlockPacket: - { - auto h = BlockInfo::headerHash(bd(_r[1].data())); - clogS(NetMessageSummary) << "NewBlock: " << h.abridged(); - - if (_r.itemCount() != 3) - disable("NewBlock without 2 data fields."); - else - { - switch (host()->m_bq.import(_r[1].data(), host()->m_chain)) - { - case ImportResult::Success: - case ImportResult::FutureTime: - addRating(1); - break; - - case ImportResult::Malformed: - disable("Malformed block received."); - break; - - case ImportResult::AlreadyInChain: - case ImportResult::AlreadyKnown: - break; - - case ImportResult::UnknownParent: - clogS(NetMessageSummary) << "Received block with no known parent. Resyncing..."; - setNeedsSyncing(h, _r[2].toInt()); - break; - } - Guard l(x_knownBlocks); - m_knownBlocks.insert(h); - } - break; - } case BlocksPacket: { clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); @@ -289,8 +349,7 @@ bool EthereumPeer::interpret(RLP const& _r) if (_r.itemCount() == 1) { // Got to this peer's latest block - just give up. - if (m_asking == Asking::Blocks) - finishSync(); + transition(Asking::Nothing); break; } @@ -335,31 +394,44 @@ bool EthereumPeer::interpret(RLP const& _r) } } - unsigned knownParents = 0; - unsigned unknownParents = 0; - if (g_logVerbosity >= NetMessageSummary::verbosity) + clogS(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known."; + + if (m_asking == Asking::Blocks) + transition(Asking::Blocks); + break; + } + case NewBlockPacket: + { + auto h = BlockInfo::headerHash(bd(_r[1].data())); + clogS(NetMessageSummary) << "NewBlock: " << h.abridged(); + + if (_r.itemCount() != 3) + disable("NewBlock without 2 data fields."); + else { - unsigned ic = _r.itemCount(); - for (unsigned i = 1; i < ic; ++i) + switch (host()->m_bq.import(_r[1].data(), host()->m_chain)) { - auto h = BlockInfo::headerHash(_r[i].data()); - BlockInfo bi(_r[i].data()); - Guard l(x_knownBlocks); - if (!host()->m_chain.details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash)) - { - unknownParents++; - clogS(NetAllDetail) << "Unknown parent" << bi.parentHash.abridged() << "of block" << h.abridged(); - } - else - { - knownParents++; - clogS(NetAllDetail) << "Known parent" << bi.parentHash.abridged() << "of block" << h.abridged(); - } + case ImportResult::Success: + case ImportResult::FutureTime: + addRating(1); + break; + + case ImportResult::Malformed: + disable("Malformed block received."); + break; + + case ImportResult::AlreadyInChain: + case ImportResult::AlreadyKnown: + break; + + case ImportResult::UnknownParent: + clogS(NetMessageSummary) << "Received block with no known parent. Resyncing..."; + setNeedsSyncing(h, _r[2].toInt()); + break; } + Guard l(x_knownBlocks); + m_knownBlocks.insert(h); } - clogS(NetMessageSummary) << dec << success << "known parents," << unknownParents << "unknown," << used << "used."; - if (m_asking == Asking::Blocks) - continueSync(); break; } default: @@ -367,54 +439,3 @@ bool EthereumPeer::interpret(RLP const& _r) } return true; } - -void EthereumPeer::ensureAskingBlocks() -{ - if (m_asking != Asking::Nothing) - return; // Already asked & waiting for some. - - continueSync(); -} - -void EthereumPeer::continueSync() -{ - // If we're getting the hashes already, then we shouldn't be asking for the chain. - if (m_asking == Asking::Hashes) - return; - - auto blocks = m_sub.nextFetch(c_maxBlocksAsk); - - if (blocks.size()) - { - RLPStream s; - prep(s); - s.appendList(blocks.size() + 1) << GetBlocksPacket; - for (auto const& i: blocks) - s << i; - sealAndSend(s); - } - else - finishSync(); -} - -/* - * Possible asking/syncing states for two peers: - * state/ presync - * presync hashes - * presync chain (transiently) - * presync+ chain - * presync nothing - * hashes nothing - * chain hashes - * presync chain (transiently) - * presync+ chain - * presync nothing - */ - -void EthereumPeer::setAsking(Asking _a, bool _isSyncing) -{ - m_asking = _a; - m_isSyncing = _isSyncing; - session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?"); - session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? "needed" : "ok")); -} diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index d5628829c..92b56301d 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -42,6 +42,7 @@ namespace eth /** * @brief The EthereumPeer class * @todo Document fully. + * @todo make state transitions thread-safe. */ class EthereumPeer: public p2p::Capability { @@ -59,21 +60,20 @@ private: virtual bool interpret(RLP const& _r); void sendStatus(); - void startInitialSync(); - void tryGrabbingHashChain(); + void transition(Asking _wantState); + + void attemptSyncing(); /// Ensure that we are waiting for a bunch of blocks from our peer. void ensureAskingBlocks(); - /// Ensure that we are waiting for a bunch of blocks from our peer. - void continueSync(); void finishSync(); void clearKnownTransactions() { std::lock_guard l(x_knownTransactions); m_knownTransactions.clear(); } void setAsking(Asking _g, bool _isSyncing); - void setNeedsSyncing(h256 _latestHash, u256 _td) { m_latestHash = _latestHash; m_totalDifficulty = _td; } + void setNeedsSyncing(h256 _latestHash, u256 _td); bool needsSyncing() const { return !!m_latestHash; } bool isSyncing() const { return m_isSyncing; } @@ -89,13 +89,15 @@ private: bool m_isSyncing = false; /// These are determined through either a Status message or from NewBlock. - h256 m_latestHash; ///< Peer's latest block's hash. + h256 m_latestHash; ///< Peer's latest block's hash that we know about or default null value if no need to sync. u256 m_totalDifficulty; ///< Peer's latest block's total difficulty. - /// Once a sync is started on this peer, they are cleared. + /// Once a sync is started on this peer, they are cleared and moved into m_syncing*. /// This is built as we ask for hashes. Once no more hashes are given, we present this to the /// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks. - h256s m_neededBlocks; ///< The blocks that we should download from this peer. + h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer. + h256 m_syncingLatestHash; ///< Peer's latest block's hash, as of the current sync. + u256 m_syncingTotalDifficulty; ///< Peer's latest block's total difficulty, as of the current sync. /// Once we're asking for blocks, this becomes in use. DownloadSub m_sub; From 611caef125367602a1ee7b9572cc37da225d6d6c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 15:43:49 +0200 Subject: [PATCH 06/26] Compilable etheruem network rewrite. --- libethereum/EthereumHost.cpp | 140 +++++++++++------------------------ libethereum/EthereumHost.h | 22 +++--- libethereum/EthereumPeer.cpp | 126 ++++++++++++++++++++++--------- libethereum/EthereumPeer.h | 48 ++++++++---- 4 files changed, 175 insertions(+), 161 deletions(-) diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 2c87d887a..3cdc442bf 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -52,7 +52,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu EthereumHost::~EthereumHost() { for (auto const& i: peers()) - i->cap()->giveUpOnFetch(); + i->cap()->abortSync(); } bool EthereumHost::ensureInitialised(TransactionQueue& _tq) @@ -70,108 +70,76 @@ bool EthereumHost::ensureInitialised(TransactionQueue& _tq) return false; } -void EthereumHost::notePeerStateChanged(EthereumPeer* _who) +void EthereumHost::noteNeedsSyncing(EthereumPeer* _who) { - clog(NetAllDetail) << "Peer state changed."; - - // TODO: FIX: BUG: Better state management! - // if already downloading hash-chain, ignore. - if (m_grabbing != Asking::Nothing) + if (isSyncing()) { - for (auto const& i: peers()) - if (i->cap()->m_grabbing == m_grabbing || m_grabbing == Asking::Presync) - { - clog(NetAllDetail) << "Already downloading chain. Just set to help out."; - _who->ensureGettingChain(); - return; - } - m_grabbing = Asking::Nothing; + clog(NetAllDetail) << "Sync in progress: Just set to help out."; + if (m_syncer->m_asking == Asking::Blocks) + _who->transition(Asking::Blocks); } - - // otherwise check to see if we should be downloading... - _who->attemptSyncing(); + else + // otherwise check to see if we should be downloading... + _who->attemptSync(); } -void EthereumHost::updateGrabbing(Asking _g, EthereumPeer* _ignore) +void EthereumHost::updateSyncer(EthereumPeer* _syncer) { - m_grabbing = _g; - if (_g == Asking::Nothing) - readyForSync(); - else if (_g == Asking::Blocks) + if (_syncer) + { for (auto j: peers()) - if (j->cap().get() != _ignore && j->cap()->m_asking == Asking::Nothing) + if (j->cap().get() != _syncer && j->cap()->m_asking == Asking::Nothing) j->cap()->transition(Asking::Blocks); -} - -bool EthereumHost::shouldGrabBlocks(EthereumPeer* _from) -{ - auto td = _from->m_syncingTotalDifficulty; - auto lh = _from->m_syncingLatestHash; - - if (_from->m_syncingNeededBlocks.empty()) - { - updateGrabbing(Asking::Nothing); - return false; } - - clog(NetNote) << "Hash-chain COMPLETE:" << td << "vs" << m_chain.details().totalDifficulty << ";" << _from->m_syncingNeededBlocks.size() << " blocks, ends" << _from->m_syncingNeededBlocks.back().abridged(); - - if (td < m_chain.details().totalDifficulty || (td == m_chain.details().totalDifficulty && m_chain.currentHash() == lh)) - { - clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; - updateGrabbing(Asking::Nothing); - return false; - } - - clog(NetNote) << "Difficulty of hashchain HIGHER. Replacing fetch queue [latest now" << lh.abridged() << ", was" << m_latestBlockSent.abridged() << "]"; - - return true; -} - -void EthereumHost::readyForSync() -{ - // start grabbing next hash chain if there is one. - for (auto j: peers()) + else { - j->cap()->attemptSyncing(); - if (j->cap()->m_grabbing == Asking::Hashes) + // start grabbing next hash chain if there is one. + for (auto j: peers()) { - m_grabbing = Asking::Hashes; - return; + j->cap()->attemptSync(); + if (isSyncing()) + return; } + clog(NetNote) << "No more peers to sync with."; } - clog(NetNote) << "No more peers to sync with."; } -void EthereumHost::noteDoneBlocks(EthereumPeer* _who) +void EthereumHost::noteDoneBlocks(EthereumPeer* _who, bool _clemency) { if (m_man.isComplete()) { // Done our chain-get. clog(NetNote) << "Chain download complete."; - updateGrabbing(Asking::Nothing); + // 1/100th for each useful block hash. + _who->addRating(m_man.chain().size() / 100); m_man.reset(); } if (_who->isSyncing()) { - // Done our chain-get. - clog(NetNote) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished."; - // TODO: note that peer is BADBADBAD! - updateGrabbing(Asking::Nothing); + if (_clemency) + clog(NetNote) << "Chain download failed. Aborted while incomplete."; + else + { + // Done our chain-get. + clog(NetNote) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished."; + + m_banned.insert(_who->session()->id()); // We know who you are! + _who->disable("Peer sent hashes but was unable to provide the blocks."); + } m_man.reset(); } } -bool EthereumHost::noteBlock(h256 _hash, bytesConstRef _data) +void EthereumHost::reset() { - if (!m_chain.details(_hash)) - { - lock_guard l(m_incomingLock); - m_incomingBlocks.push_back(_data.toBytes()); - return true; - } - return false; + if (m_syncer) + m_syncer->abortSync(); + + m_man.resetToChain(h256s()); + + m_latestBlockSent = h256(); + m_transactionsSent.clear(); } void EthereumHost::doWork() @@ -187,16 +155,7 @@ void EthereumHost::doWork() void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash) { - bool resendAll = (m_grabbing == Asking::Nothing && m_chain.isKnown(m_latestBlockSent) && _currentHash != m_latestBlockSent); - { - lock_guard l(m_incomingLock); - for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it) - if (_tq.import(&*it)) - {}//ret = true; // just putting a transaction in the queue isn't enough to change the state - it might have an invalid nonce... - else - m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on. - m_incomingTransactions.clear(); - } + bool resendAll = (!isSyncing() && m_chain.isKnown(m_latestBlockSent) && _currentHash != m_latestBlockSent); // Send any new transactions. for (auto const& p: peers()) @@ -226,23 +185,10 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash } } -void EthereumHost::reset() -{ - m_grabbing = Asking::Nothing; - - m_man.resetToChain(h256s()); - - m_incomingTransactions.clear(); - m_incomingBlocks.clear(); - - m_latestBlockSent = h256(); - m_transactionsSent.clear(); -} - void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) { // If we've finished our initial sync send any new blocks. - if (m_grabbing == Asking::Nothing && m_chain.isKnown(m_latestBlockSent) && m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty) + if (!isSyncing() && m_chain.isKnown(m_latestBlockSent) && m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty) { // TODO: clean up h256s hs; diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index b42ae7fa7..92e2c8c32 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -70,19 +70,16 @@ public: void reset(); DownloadMan const& downloadMan() const { return m_man; } - bool isSyncing() const { return m_grabbing == Asking::Chain; } + bool isSyncing() const { return !!m_syncer; } -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); + bool isBanned(h512 _id) const { return m_banned.count(_id); } - /// Session has finished getting the chain of hashes. - bool shouldGrabBlocks(EthereumPeer* _who); +private: + /// Session is tell us that we may need (re-)syncing with the peer. + void noteNeedsSyncing(EthereumPeer* _who); /// Called when the peer can no longer provide us with any needed blocks. - void noteDoneBlocks(EthereumPeer* _who); + void noteDoneBlocks(EthereumPeer* _who, bool _clemency); /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. void doWork(); @@ -104,8 +101,7 @@ private: virtual void onStarting() { startWorking(); } virtual void onStopping() { stopWorking(); } - void readyForSync(); - void updateGrabbing(Asking _g, EthereumPeer* _ignore); + void updateSyncer(EthereumPeer* _ignore); BlockChain const& m_chain; TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. @@ -113,12 +109,14 @@ private: u256 m_networkId; - Asking m_grabbing = Asking::Nothing; // TODO: needs to be thread-safe & switch to just having a peer id. + EthereumPeer* m_syncer = nullptr; // TODO: switch to weak_ptr DownloadMan m_man; h256 m_latestBlockSent; h256Set m_transactionsSent; + + std::set m_banned; }; } diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 20a9fef52..175c0bbae 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -27,6 +27,8 @@ #include #include "BlockChain.h" #include "EthereumHost.h" +#include "TransactionQueue.h" +#include "BlockQueue.h" using namespace std; using namespace dev; using namespace dev::eth; @@ -38,13 +40,18 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h): Capability(_s, _h), m_sub(host()->m_man) { - setAsking(Asking::State, false); - sendStatus(); + transition(Asking::State); } EthereumPeer::~EthereumPeer() { - finishSync(); + abortSync(); +} + +void EthereumPeer::abortSync() +{ + if (isSyncing()) + transition(Asking::Nothing, true); } EthereumHost* EthereumPeer::host() const @@ -54,26 +61,46 @@ EthereumHost* EthereumPeer::host() const void EthereumPeer::sendStatus() { - RLPStream s; - prep(s); - s.appendList(6) << StatusPacket - << host()->protocolVersion() - << host()->networkId() - << host()->m_chain.details().totalDifficulty - << host()->m_chain.currentHash() - << host()->m_chain.genesisHash(); - sealAndSend(s); } /* * Possible asking/syncing states for two peers: */ -void EthereumPeer::transition(Asking _a) +string toString(Asking _a) +{ + switch (_a) + { + case Asking::Blocks: return "Blocks"; + case Asking::Hashes: return "Hashes"; + case Asking::Nothing: return "Nothing"; + case Asking::State: return "State"; + } + return "?"; +} + +void EthereumPeer::transition(Asking _a, bool _force) { + clogS(NetMessageSummary) << "Transition!" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : ""); + RLPStream s; prep(s); - if (_a == Asking::Hashes) + if (_a == Asking::State) + { + if (m_asking == Asking::Nothing) + { + setAsking(Asking::State, false); + s.appendList(6) << StatusPacket + << host()->protocolVersion() + << host()->networkId() + << host()->m_chain.details().totalDifficulty + << host()->m_chain.currentHash() + << host()->m_chain.genesisHash(); + sealAndSend(s); + return; + } + } + else if (_a == Asking::Hashes) { if (m_asking == Asking::State || m_asking == Asking::Nothing) { @@ -87,7 +114,6 @@ void EthereumPeer::transition(Asking _a) setAsking(_a, true); s.appendList(3) << GetBlockHashesPacket << m_syncingLatestHash << c_maxHashesAsk; m_syncingNeededBlocks = h256s(1, m_syncingLatestHash); - host()->updateGrabbing(Asking::Hashes); sealAndSend(s); return; } @@ -106,15 +132,17 @@ void EthereumPeer::transition(Asking _a) { if (m_asking == Asking::Hashes) { - if (host()->shouldGrabBlocks(this)) + if (shouldGrabBlocks()) { + clog(NetNote) << "Difficulty of hashchain HIGHER. Grabbing" << m_syncingNeededBlocks.size() << "blocks [latest now" << m_syncingLatestHash.abridged() << ", was" << host()->m_latestBlockSent.abridged() << "]"; + host()->m_man.resetToChain(m_syncingNeededBlocks); host()->m_latestBlockSent = m_syncingLatestHash; - - host()->updateGrabbing(Asking::Blocks, this); } else { + clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; + setAsking(Asking::Nothing, false); return; } @@ -145,7 +173,7 @@ void EthereumPeer::transition(Asking _a) // a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry. if (isSyncing()) - host()->noteDoneBlocks(this); + host()->noteDoneBlocks(this, _force); // NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary. m_sub.doneFetch(); @@ -156,28 +184,28 @@ void EthereumPeer::transition(Asking _a) { clogS(NetNote) << "Finishing hashes fetch..."; - if (isSyncing()) - host()->noteDoneBlocks(this); - setAsking(Asking::Nothing, false); } else if (m_asking == Asking::State) { setAsking(Asking::Nothing, false); - // TODO: Just got the state - should check to see if we can be of help downloading the chain if any. - // TODO: Otherwise, should put ourselves up for sync. + // Just got the state - should check to see if we can be of help downloading the chain if any. + // Otherwise, should put ourselves up for sync. + setNeedsSyncing(m_latestHash, m_totalDifficulty); } // Otherwise it's fine. We don't care if it's Nothing->Nothing. return; } - clogS(NetWarn) << "Invalid state transition:" << (int)_a << "from" << (int)m_asking << "/" << boolalpha << isSyncing() << needsSyncing(); + clogS(NetWarn) << "Invalid state transition:" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : ""); } void EthereumPeer::setAsking(Asking _a, bool _isSyncing) { m_asking = _a; - m_isSyncing = _isSyncing; + if (_isSyncing != (host()->m_syncer == this)) + host()->updateSyncer(_isSyncing ? this : nullptr); + session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?"); session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : "")); } @@ -187,11 +215,32 @@ void EthereumPeer::setNeedsSyncing(h256 _latestHash, u256 _td) m_latestHash = _latestHash; m_totalDifficulty = _td; - // TODO: should be "noteNeedsSyncing" or some such. - host()->notePeerStateChanged(this); + host()->noteNeedsSyncing(this); +} + +bool EthereumPeer::isSyncing() const +{ + return host()->m_syncer == this; } -void EthereumPeer::attemptSyncing() +bool EthereumPeer::shouldGrabBlocks() const +{ + auto td = m_syncingTotalDifficulty; + auto lh = m_syncingLatestHash; + auto ctd = host()->m_chain.details().totalDifficulty; + + if (m_syncingNeededBlocks.empty()) + return false; + + clog(NetNote) << "Should grab blocks? " << td << "vs" << ctd << ";" << m_syncingNeededBlocks.size() << " blocks, ends" << m_syncingNeededBlocks.back().abridged(); + + if (td < ctd || (td == ctd && host()->m_chain.currentHash() == lh)) + return false; + + return true; +} + +void EthereumPeer::attemptSync() { if (m_asking != Asking::Nothing) { @@ -231,8 +280,8 @@ bool EthereumPeer::interpret(RLP const& _r) { m_protocolVersion = _r[1].toInt(); m_networkId = _r[2].toInt(); - auto totalDifficulty = _r[3].toInt(); - auto latestHash = _r[4].toHash(); + m_totalDifficulty = _r[3].toInt(); + m_latestHash = _r[4].toHash(); auto genesisHash = _r[5].toHash(); clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged(); @@ -245,6 +294,8 @@ bool EthereumPeer::interpret(RLP const& _r) disable("Invalid network identifier."); else if (session()->info().clientVersion.find("/v0.6.9/") != string::npos) disable("Blacklisted client version."); + else if (host()->isBanned(session()->id())) + disable("Peer banned for previous bad behaviour."); else { // Grab transactions off them. @@ -252,8 +303,7 @@ bool EthereumPeer::interpret(RLP const& _r) prep(s).appendList(1); s << GetTransactionsPacket; sealAndSend(s); - - setNeedsSyncing(latestHash, totalDifficulty); + transition(Asking::Nothing); } break; } @@ -269,9 +319,11 @@ bool EthereumPeer::interpret(RLP const& _r) Guard l(x_knownTransactions); for (unsigned i = 1; i < _r.itemCount(); ++i) { - m_knownTransactions.insert(sha3(_r[i].data())); - if (!_tq.import(_r[i].data())) // if we already had the transaction, then don't bother sending it on. - host()->m_transactionsSent.insert(sha3(*it)); + auto h = sha3(_r[i].data()); + m_knownTransactions.insert(h); + if (!host()->m_tq.import(_r[i].data())) + // if we already had the transaction, then don't bother sending it on. + host()->m_transactionsSent.insert(h); } break; } @@ -402,7 +454,7 @@ bool EthereumPeer::interpret(RLP const& _r) } case NewBlockPacket: { - auto h = BlockInfo::headerHash(bd(_r[1].data())); + auto h = BlockInfo::headerHash(_r[1].data()); clogS(NetMessageSummary) << "NewBlock: " << h.abridged(); if (_r.itemCount() != 3) diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index 92b56301d..a736516c2 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -49,41 +49,59 @@ class EthereumPeer: public p2p::Capability friend class EthereumHost; public: + /// Basic constructor. EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h); + + /// Basic destructor. virtual ~EthereumPeer(); + /// What is our name? static std::string name() { return "eth"; } + /// What is the ethereum subprotocol host object. EthereumHost* host() const; private: + /// Interpret an incoming message. virtual bool interpret(RLP const& _r); + /// Send our status to peer. void sendStatus(); - void transition(Asking _wantState); - - void attemptSyncing(); + /// Transition state in a particular direction. + void transition(Asking _wantState, bool _force = false); - /// Ensure that we are waiting for a bunch of blocks from our peer. - void ensureAskingBlocks(); + /// Attempt to begin syncing with this peer; first check the peer has a more difficlult chain to download, then start asking for hashes, then move to blocks. + void attemptSync(); - void finishSync(); + /// Abort the sync operation. + void abortSync(); + /// Clear all known transactions. void clearKnownTransactions() { std::lock_guard l(x_knownTransactions); m_knownTransactions.clear(); } + + /// Update our asking state. void setAsking(Asking _g, bool _isSyncing); + /// Update our syncing requirements state. void setNeedsSyncing(h256 _latestHash, u256 _td); + + /// Do we presently need syncing with this peer? bool needsSyncing() const { return !!m_latestHash; } - bool isSyncing() const { return m_isSyncing; } - + + /// Are we presently syncing with this peer? + bool isSyncing() const; + + /// Check whether the session should bother grabbing the peer's blocks. + bool shouldGrabBlocks() const; + /// Peer's protocol version. unsigned m_protocolVersion; /// Peer's network id. u256 m_networkId; /// What, if anything, we last asked the other peer for. - Asking m_asking; + Asking m_asking = Asking::Nothing; /// Whether this peer is in the process of syncing or not. Only one peer can be syncing at once. bool m_isSyncing = false; @@ -95,9 +113,9 @@ private: /// This is built as we ask for hashes. Once no more hashes are given, we present this to the /// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks. - h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer. - h256 m_syncingLatestHash; ///< Peer's latest block's hash, as of the current sync. - u256 m_syncingTotalDifficulty; ///< Peer's latest block's total difficulty, as of the current sync. + h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer. + h256 m_syncingLatestHash; ///< Peer's latest block's hash, as of the current sync. + u256 m_syncingTotalDifficulty; ///< Peer's latest block's total difficulty, as of the current sync. /// Once we're asking for blocks, this becomes in use. DownloadSub m_sub; @@ -106,9 +124,9 @@ private: bool m_requireTransactions; Mutex x_knownBlocks; - std::set m_knownBlocks; - std::set m_knownTransactions; - std::mutex x_knownTransactions; + h256Set m_knownBlocks; ///< Blocks that the peer already knows about (that don't need to be sent to them). + Mutex x_knownTransactions; + h256Set m_knownTransactions; ///< Transactions that the peer already knows of. }; From d7465f7b5521cea7aa0a410b6312f71eba015b40 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 16:01:38 +0200 Subject: [PATCH 07/26] Syncing fixes. --- libethereum/EthereumHost.cpp | 16 ++++++++-------- libethereum/EthereumHost.h | 6 +++--- libethereum/EthereumPeer.cpp | 19 +++++++++++++------ libethereum/EthereumPeer.h | 3 --- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 3cdc442bf..4bd34f201 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -55,7 +55,7 @@ EthereumHost::~EthereumHost() i->cap()->abortSync(); } -bool EthereumHost::ensureInitialised(TransactionQueue& _tq) +bool EthereumHost::ensureInitialised() { if (!m_latestBlockSent) { @@ -63,7 +63,7 @@ bool EthereumHost::ensureInitialised(TransactionQueue& _tq) m_latestBlockSent = m_chain.currentHash(); clog(NetNote) << "Initialising: latest=" << m_latestBlockSent.abridged(); - for (auto const& i: _tq.transactions()) + for (auto const& i: m_tq.transactions()) m_transactionsSent.insert(i.first); return true; } @@ -144,16 +144,16 @@ void EthereumHost::reset() void EthereumHost::doWork() { - bool netChange = ensureInitialised(m_tq); + bool netChange = ensureInitialised(); auto h = m_chain.currentHash(); - maintainTransactions(m_tq, h); - maintainBlocks(m_bq, h); + maintainTransactions(h); + maintainBlocks(h); // return netChange; // TODO: Figure out what to do with netChange. (void)netChange; } -void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash) +void EthereumHost::maintainTransactions(h256 _currentHash) { bool resendAll = (!isSyncing() && m_chain.isKnown(m_latestBlockSent) && _currentHash != m_latestBlockSent); @@ -163,7 +163,7 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash { bytes b; unsigned n = 0; - for (auto const& i: _tq.transactions()) + for (auto const& i: m_tq.transactions()) if ((!m_transactionsSent.count(i.first) && !ep->m_knownTransactions.count(i.first)) || ep->m_requireTransactions || resendAll) { b += i.second; @@ -185,7 +185,7 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash } } -void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) +void EthereumHost::maintainBlocks(h256 _currentHash) { // If we've finished our initial sync send any new blocks. if (!isSyncing() && m_chain.isKnown(m_latestBlockSent) && m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty) diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 92e2c8c32..2ce5b9971 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -84,8 +84,8 @@ private: /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. void doWork(); - void maintainTransactions(TransactionQueue& _tq, h256 _currentBlock); - void maintainBlocks(BlockQueue& _bq, h256 _currentBlock); + void maintainTransactions(h256 _currentBlock); + void maintainBlocks(h256 _currentBlock); /// Get a bunch of needed blocks. /// Removes them from our list of needed blocks. @@ -96,7 +96,7 @@ private: bool isInitialised() const { return m_latestBlockSent; } /// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first. - bool ensureInitialised(TransactionQueue& _tq); + bool ensureInitialised(); virtual void onStarting() { startWorking(); } virtual void onStopping() { stopWorking(); } diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 175c0bbae..c80912c30 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -59,10 +59,6 @@ EthereumHost* EthereumPeer::host() const return static_cast(Capability::hostCapability()); } -void EthereumPeer::sendStatus() -{ -} - /* * Possible asking/syncing states for two peers: */ @@ -132,17 +128,21 @@ void EthereumPeer::transition(Asking _a, bool _force) { if (m_asking == Asking::Hashes) { + if (!isSyncing()) + clogS(NetWarn) << "Bad state: asking for Hashes yet not syncing!"; if (shouldGrabBlocks()) { clog(NetNote) << "Difficulty of hashchain HIGHER. Grabbing" << m_syncingNeededBlocks.size() << "blocks [latest now" << m_syncingLatestHash.abridged() << ", was" << host()->m_latestBlockSent.abridged() << "]"; + m_syncingLatestHash = h256(); + host()->m_man.resetToChain(m_syncingNeededBlocks); host()->m_latestBlockSent = m_syncingLatestHash; } else { clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; - + m_latestHash = h256(); setAsking(Asking::Nothing, false); return; } @@ -169,7 +169,7 @@ void EthereumPeer::transition(Asking _a, bool _force) { if (m_asking == Asking::Blocks) { - clogS(NetNote) << "Finishing block fetch..."; + clogS(NetNote) << "Finishing blocks fetch..."; // a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry. if (isSyncing()) @@ -205,6 +205,12 @@ void EthereumPeer::setAsking(Asking _a, bool _isSyncing) m_asking = _a; if (_isSyncing != (host()->m_syncer == this)) host()->updateSyncer(_isSyncing ? this : nullptr); + if (!_isSyncing) + { + m_syncingLatestHash = h256(); + m_syncingTotalDifficulty = 0; + m_syncingNeededBlocks.clear(); + } session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?"); session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : "")); @@ -263,6 +269,7 @@ void EthereumPeer::attemptSync() if (td >= m_totalDifficulty) { clogS(NetAllDetail) << "No. Our chain is better."; + m_latestHash = h256(); transition(Asking::Nothing); } else diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index a736516c2..3a625f369 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -65,9 +65,6 @@ private: /// Interpret an incoming message. virtual bool interpret(RLP const& _r); - /// Send our status to peer. - void sendStatus(); - /// Transition state in a particular direction. void transition(Asking _wantState, bool _force = false); From 4f5b15345282768fe78a8331ac69e6d9d60ad457 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 16:40:13 +0200 Subject: [PATCH 08/26] Better state management. --- libethereum/EthereumHost.cpp | 14 ++++++++------ libethereum/EthereumHost.h | 2 +- libethereum/EthereumPeer.cpp | 25 ++++++++++++++++--------- libethereum/EthereumPeer.h | 1 + 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 4bd34f201..c5c3e90cd 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -84,13 +84,15 @@ void EthereumHost::noteNeedsSyncing(EthereumPeer* _who) _who->attemptSync(); } -void EthereumHost::updateSyncer(EthereumPeer* _syncer) +void EthereumHost::changeSyncer(EthereumPeer* _syncer) { - if (_syncer) + m_syncer = _syncer; + if (isSyncing()) { - for (auto j: peers()) - if (j->cap().get() != _syncer && j->cap()->m_asking == Asking::Nothing) - j->cap()->transition(Asking::Blocks); + if (_syncer->m_asking == Asking::Blocks) + for (auto j: peers()) + if (j->cap().get() != _syncer && j->cap()->m_asking == Asking::Nothing) + j->cap()->transition(Asking::Blocks); } else { @@ -115,7 +117,7 @@ void EthereumHost::noteDoneBlocks(EthereumPeer* _who, bool _clemency) _who->addRating(m_man.chain().size() / 100); m_man.reset(); } - if (_who->isSyncing()) + else if (_who->isSyncing()) { if (_clemency) clog(NetNote) << "Chain download failed. Aborted while incomplete."; diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 2ce5b9971..12ce28f23 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -101,7 +101,7 @@ private: virtual void onStarting() { startWorking(); } virtual void onStopping() { stopWorking(); } - void updateSyncer(EthereumPeer* _ignore); + void changeSyncer(EthereumPeer* _ignore); BlockChain const& m_chain; TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index c80912c30..45e850de6 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -105,7 +105,7 @@ void EthereumPeer::transition(Asking _a, bool _force) m_syncingLatestHash = m_latestHash; m_syncingTotalDifficulty = m_totalDifficulty; - m_latestHash = h256(); + resetNeedsSyncing(); setAsking(_a, true); s.appendList(3) << GetBlockHashesPacket << m_syncingLatestHash << c_maxHashesAsk; @@ -134,15 +134,14 @@ void EthereumPeer::transition(Asking _a, bool _force) { clog(NetNote) << "Difficulty of hashchain HIGHER. Grabbing" << m_syncingNeededBlocks.size() << "blocks [latest now" << m_syncingLatestHash.abridged() << ", was" << host()->m_latestBlockSent.abridged() << "]"; - m_syncingLatestHash = h256(); - host()->m_man.resetToChain(m_syncingNeededBlocks); host()->m_latestBlockSent = m_syncingLatestHash; + } else { clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; - m_latestHash = h256(); + m_syncingLatestHash = h256(); setAsking(Asking::Nothing, false); return; } @@ -151,7 +150,7 @@ void EthereumPeer::transition(Asking _a, bool _force) if (m_asking == Asking::Nothing || m_asking == Asking::Hashes || m_asking == Asking::Blocks) { // Looks like it's the best yet for total difficulty. Set to download. - setAsking(Asking::Blocks, true); + setAsking(Asking::Blocks, true); // will kick off other peers to help if available. auto blocks = m_sub.nextFetch(c_maxBlocksAsk); if (blocks.size()) { @@ -202,9 +201,12 @@ void EthereumPeer::transition(Asking _a, bool _force) void EthereumPeer::setAsking(Asking _a, bool _isSyncing) { + bool changedAsking = (m_asking != _a); m_asking = _a; - if (_isSyncing != (host()->m_syncer == this)) - host()->updateSyncer(_isSyncing ? this : nullptr); + + if (_isSyncing != (host()->m_syncer == this) || (_isSyncing && changedAsking)) + host()->changeSyncer(_isSyncing ? this : nullptr); + if (!_isSyncing) { m_syncingLatestHash = h256(); @@ -221,7 +223,10 @@ void EthereumPeer::setNeedsSyncing(h256 _latestHash, u256 _td) m_latestHash = _latestHash; m_totalDifficulty = _td; - host()->noteNeedsSyncing(this); + if (m_latestHash) + host()->noteNeedsSyncing(this); + + session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : "")); } bool EthereumPeer::isSyncing() const @@ -269,7 +274,7 @@ void EthereumPeer::attemptSync() if (td >= m_totalDifficulty) { clogS(NetAllDetail) << "No. Our chain is better."; - m_latestHash = h256(); + resetNeedsSyncing(); transition(Asking::Nothing); } else @@ -287,6 +292,8 @@ bool EthereumPeer::interpret(RLP const& _r) { m_protocolVersion = _r[1].toInt(); m_networkId = _r[2].toInt(); + + // a bit dirty as we're misusing these to communicate the values to transition, but harmless. m_totalDifficulty = _r[3].toInt(); m_latestHash = _r[4].toHash(); auto genesisHash = _r[5].toHash(); diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index 3a625f369..00788d019 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -82,6 +82,7 @@ private: /// Update our syncing requirements state. void setNeedsSyncing(h256 _latestHash, u256 _td); + void resetNeedsSyncing() { setNeedsSyncing(h256(), 0); } /// Do we presently need syncing with this peer? bool needsSyncing() const { return !!m_latestHash; } From 20ad46c481f3ab07323031aa885bf7e394baee92 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 17:44:26 +0200 Subject: [PATCH 09/26] PoC-7: versioned subprotocols. --- libdevcore/Common.cpp | 2 +- libethcore/CommonEth.cpp | 2 +- libethereum/EthereumPeer.h | 3 +++ libp2p/Capability.h | 1 + libp2p/Common.h | 8 +++++++- libp2p/Host.cpp | 3 ++- libp2p/Host.h | 13 +++++++------ libp2p/HostCapability.cpp | 2 +- libp2p/HostCapability.h | 4 ++++ libp2p/Session.cpp | 6 +++--- libp2p/Session.h | 4 ++-- libwhisper/WhisperPeer.h | 1 + 12 files changed, 33 insertions(+), 16 deletions(-) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 551b7af87..8a8687bfa 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.6.11"; +char const* Version = "0.7.0"; } diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index 44297e0ba..1526da1b0 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -34,7 +34,7 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 33; +const unsigned c_protocolVersion = 34; const unsigned c_databaseVersion = 2; static const vector> g_units = diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index 00788d019..bad12d3b7 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -58,6 +58,9 @@ public: /// What is our name? static std::string name() { return "eth"; } + /// What is our version? + static u256 version() { return c_protocolVersion; } + /// What is the ethereum subprotocol host object. EthereumHost* host() const; diff --git a/libp2p/Capability.h b/libp2p/Capability.h index bffd38c79..00cccaeef 100644 --- a/libp2p/Capability.h +++ b/libp2p/Capability.h @@ -39,6 +39,7 @@ public: /// Must return the capability name. static std::string name() { return ""; } + static u256 version() { return 0; } Session* session() const { return m_session; } HostCapabilityFace* hostCapability() const { return m_host; } diff --git a/libp2p/Common.h b/libp2p/Common.h index a5f7e5d84..bb1b1e2e0 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -24,6 +24,8 @@ #pragma once #include +#include +#include #include #include #include @@ -88,13 +90,17 @@ enum DisconnectReason /// @returns the string form of the given disconnection reason. std::string reasonOf(DisconnectReason _r); +typedef std::pair CapDesc; +typedef std::set CapDescSet; +typedef std::vector CapDescs; + struct PeerInfo { std::string clientVersion; std::string host; unsigned short port; std::chrono::steady_clock::duration lastPing; - std::set caps; + std::set caps; unsigned socket; std::map notes; }; diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 43758f0f5..d13cbf4f9 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -38,6 +38,7 @@ #include #include #include "Session.h" +#include "Common.h" #include "Capability.h" #include "UPnP.h" using namespace std; @@ -145,7 +146,7 @@ unsigned Host::protocolVersion() const return 0; } -void Host::registerPeer(std::shared_ptr _s, vector const& _caps) +void Host::registerPeer(std::shared_ptr _s, CapDescs const& _caps) { { Guard l(x_peers); diff --git a/libp2p/Host.h b/libp2p/Host.h index f5f2f9e97..b4ba9c2d4 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -31,6 +31,7 @@ #include #include #include "HostCapability.h" +#include "Common.h" namespace ba = boost::asio; namespace bi = boost::asio::ip; @@ -75,11 +76,11 @@ public: unsigned protocolVersion() const; /// Register a peer-capability; all new peer connections will have this capability. - template std::shared_ptr registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr(_t); m_capabilities[T::staticName()] = ret; return ret; } + template std::shared_ptr registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; } - bool haveCapability(std::string const& _name) const { return m_capabilities.count(_name) != 0; } - std::vector caps() const { std::vector ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } - template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(T::staticName())); } catch (...) { return nullptr; } } + bool haveCapability(CapDesc const& _name) const { return m_capabilities.count(_name) != 0; } + CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } + template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(T::staticName(), T::staticVersion()))); } catch (...) { return nullptr; } } /// Connect to a peer explicitly. static std::string pocHost(); @@ -118,7 +119,7 @@ public: h512 id() const { return m_id; } - void registerPeer(std::shared_ptr _s, std::vector const& _caps); + 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. @@ -166,7 +167,7 @@ private: std::vector m_addresses; std::vector m_peerAddresses; - std::map> m_capabilities; + std::map> m_capabilities; bool m_accepting = false; }; diff --git a/libp2p/HostCapability.cpp b/libp2p/HostCapability.cpp index a3a47cd5c..2f295afd0 100644 --- a/libp2p/HostCapability.cpp +++ b/libp2p/HostCapability.cpp @@ -38,7 +38,7 @@ std::vector > HostCapabilityFace::peers() const std::vector > ret; for (auto const& i: m_host->m_peers) if (std::shared_ptr p = i.second.lock()) - if (p->m_capabilities.count(name())) + if (p->m_capabilities.count(capDesc())) ret.push_back(p); return ret; } diff --git a/libp2p/HostCapability.h b/libp2p/HostCapability.h index 1c532788b..f07900034 100644 --- a/libp2p/HostCapability.h +++ b/libp2p/HostCapability.h @@ -47,6 +47,8 @@ public: protected: virtual std::string name() const = 0; + virtual u256 version() const = 0; + CapDesc capDesc() const { return std::make_pair(name(), version()); } virtual Capability* newPeerCapability(Session* _s) = 0; virtual void onStarting() {} @@ -66,9 +68,11 @@ public: virtual ~HostCapability() {} static std::string staticName() { return PeerCap::name(); } + static u256 staticVersion() { return PeerCap::version(); } protected: virtual std::string name() const { return PeerCap::name(); } + virtual u256 version() const { return PeerCap::version(); } virtual Capability* newPeerCapability(Session* _s) { return new PeerCap(_s, this); } }; diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index c50990476..71780ff6b 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -40,7 +40,7 @@ Session::Session(Host* _s, bi::tcp::socket _socket, bi::address _peerAddress, un { 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), set(), 0, map()}); + m_info = PeerInfo({"?", _peerAddress.to_string(), m_listenPort, std::chrono::steady_clock::duration(0), CapDescSet(), 0, map()}); } Session::~Session() @@ -78,7 +78,7 @@ bool Session::interpret(RLP const& _r) { m_protocolVersion = _r[1].toInt(); auto clientVersion = _r[2].toString(); - auto caps = _r[3].toVector(); + auto caps = _r[3].toVector(); m_listenPort = _r[4].toInt(); m_id = _r[5].toHash(); @@ -102,7 +102,7 @@ bool Session::interpret(RLP const& _r) 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() }); } + { 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 (...) { disconnect(BadProtocol); diff --git a/libp2p/Session.h b/libp2p/Session.h index 934e548d4..9b04154ee 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -62,7 +62,7 @@ public: bi::tcp::endpoint endpoint() const; ///< for other peers to connect to. template - std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(PeerCap::name())); } catch (...) { return nullptr; } } + std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(PeerCap::name(), PeerCap::version()))); } catch (...) { return nullptr; } } static RLPStream& prep(RLPStream& _s); void sealAndSend(RLPStream& _s); @@ -108,7 +108,7 @@ private: unsigned m_rating; - std::map> m_capabilities; + std::map> m_capabilities; bool m_willBeDeleted = false; ///< True if we already posted a deleter on the strand. }; diff --git a/libwhisper/WhisperPeer.h b/libwhisper/WhisperPeer.h index b3fe2701b..ae20cae68 100644 --- a/libwhisper/WhisperPeer.h +++ b/libwhisper/WhisperPeer.h @@ -53,6 +53,7 @@ public: virtual ~WhisperPeer(); static std::string name() { return "shh"; } + static u256 version() { return 1; } WhisperHost* host() const; From 875b0251144ee24ef5b69d17e4ff68f303513f44 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 18:00:10 +0200 Subject: [PATCH 10/26] PoC-7: Remove POST, move CALLSTATELESS -> CALLCODE = 0xf3. --- libevm/VM.h | 33 ++------------------------------- libevmface/Instruction.cpp | 6 ++---- libevmface/Instruction.h | 3 +-- 3 files changed, 5 insertions(+), 37 deletions(-) diff --git a/libevm/VM.h b/libevm/VM.h index 04804ffd5..33a40e6c6 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -171,18 +171,12 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); break; - case Instruction::CALLSTATELESS: + case Instruction::CALLCODE: require(7); runGas = c_callGas + m_stack[m_stack.size() - 1]; newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); break; - case Instruction::POST: - require(5); - runGas = c_callGas + m_stack[m_stack.size() - 1]; - newTempSize = memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]); - break; - case Instruction::CREATE: { require(3); @@ -611,7 +605,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; } case Instruction::CALL: - case Instruction::CALLSTATELESS: + case Instruction::CALLCODE: { require(7); @@ -662,29 +656,6 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con } case Instruction::STOP: return bytesConstRef(); - case Instruction::POST: - { - require(5); - - u256 gas = m_stack.back(); - m_stack.pop_back(); - u160 receiveAddress = asAddress(m_stack.back()); - m_stack.pop_back(); - u256 value = m_stack.back(); - m_stack.pop_back(); - - unsigned inOff = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned inSize = (unsigned)m_stack.back(); - m_stack.pop_back(); - - if (_ext.balance(_ext.myAddress) >= value) - { - _ext.subBalance(value); - _ext.post(receiveAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas); - } - break; - } default: BOOST_THROW_EXCEPTION(BadInstruction()); } diff --git a/libevmface/Instruction.cpp b/libevmface/Instruction.cpp index f8c7f5078..b9ade2c2d 100644 --- a/libevmface/Instruction.cpp +++ b/libevmface/Instruction.cpp @@ -148,9 +148,8 @@ const std::map dev::eth::c_instructions = { "SWAP16", Instruction::SWAP16 }, { "CREATE", Instruction::CREATE }, { "CALL", Instruction::CALL }, - { "CALLSTATELESS", Instruction::CALLSTATELESS }, + { "CALLCODE", Instruction::CALLCODE }, { "RETURN", Instruction::RETURN }, - { "POST", Instruction::POST }, { "SUICIDE", Instruction::SUICIDE } }; @@ -275,9 +274,8 @@ static const std::map c_instructionInfo = { Instruction::SWAP16, { "SWAP16", 0, 17, 17 } }, { Instruction::CREATE, { "CREATE", 0, 3, 1 } }, { Instruction::CALL, { "CALL", 0, 7, 1 } }, - { Instruction::CALLSTATELESS, { "CALLSTATELESS",0, 7, 1 } }, + { Instruction::CALLCODE, { "CALLCODE",0, 7, 1 } }, { Instruction::RETURN, { "RETURN", 0, 2, 0 } }, - { Instruction::POST, { "POST", 0, 5, 0 } }, { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} } }; diff --git a/libevmface/Instruction.h b/libevmface/Instruction.h index 6c87b7a76..001d778a8 100644 --- a/libevmface/Instruction.h +++ b/libevmface/Instruction.h @@ -163,8 +163,7 @@ enum class Instruction: uint8_t CREATE = 0xf0, ///< create a new account with associated code CALL, ///< message-call into an account RETURN, ///< halt execution returning output data - POST, ///< asynchronous call without output (adds a message to the post queue) - CALLSTATELESS, + CALLCODE, SUICIDE = 0xff ///< halt execution and register account for later deletion }; From 4c08cda6fd630ba442155132beff32f919b282ea Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 18:43:01 +0200 Subject: [PATCH 11/26] PoC-7: JUMPDEST implemented. --- libevm/VM.cpp | 2 ++ libevm/VM.h | 15 +++++++++++++++ libevmface/Instruction.cpp | 4 +++- libevmface/Instruction.h | 1 + liblll/Assembly.cpp | 26 +++++++++++++++++++++++++- 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/libevm/VM.cpp b/libevm/VM.cpp index e47237d5a..4a3f70d5e 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -29,4 +29,6 @@ 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 33a40e6c6..86acd8c26 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -39,6 +39,7 @@ struct VMException: virtual Exception {}; struct StepsDone: virtual VMException {}; struct BreakPointHit: virtual VMException {}; struct BadInstruction: virtual VMException {}; +struct BadJumpDestination: virtual VMException {}; struct OutOfGas: virtual VMException {}; class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; @@ -83,6 +84,8 @@ private: u256 m_curPC = 0; bytes m_temp; u256s m_stack; + bool m_jumpLatch = false; + u256Set m_destinations; }; } @@ -565,11 +568,17 @@ 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(); 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(); m_stack.pop_back(); @@ -584,6 +593,12 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::GAS: 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: { require(3); diff --git a/libevmface/Instruction.cpp b/libevmface/Instruction.cpp index b9ade2c2d..bb1ef3e98 100644 --- a/libevmface/Instruction.cpp +++ b/libevmface/Instruction.cpp @@ -82,6 +82,7 @@ const std::map dev::eth::c_instructions = { "PC", Instruction::PC }, { "MSIZE", Instruction::MSIZE }, { "GAS", Instruction::GAS }, + { "JUMPDEST", Instruction::JUMPDEST }, { "PUSH1", Instruction::PUSH1 }, { "PUSH2", Instruction::PUSH2 }, { "PUSH3", Instruction::PUSH3 }, @@ -208,6 +209,7 @@ static const std::map c_instructionInfo = { Instruction::PC, { "PC", 0, 0, 1 } }, { Instruction::MSIZE, { "MSIZE", 0, 0, 1 } }, { Instruction::GAS, { "GAS", 0, 0, 1 } }, + { Instruction::JUMPDEST, { "JUMPDEST", 0, 1, 0 } }, { Instruction::PUSH1, { "PUSH1", 1, 0, 1 } }, { Instruction::PUSH2, { "PUSH2", 2, 0, 1 } }, { Instruction::PUSH3, { "PUSH3", 3, 0, 1 } }, @@ -274,7 +276,7 @@ static const std::map c_instructionInfo = { Instruction::SWAP16, { "SWAP16", 0, 17, 17 } }, { Instruction::CREATE, { "CREATE", 0, 3, 1 } }, { Instruction::CALL, { "CALL", 0, 7, 1 } }, - { Instruction::CALLCODE, { "CALLCODE",0, 7, 1 } }, + { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1 } }, { Instruction::RETURN, { "RETURN", 0, 2, 0 } }, { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} } }; diff --git a/libevmface/Instruction.h b/libevmface/Instruction.h index 001d778a8..753bd0ad6 100644 --- a/libevmface/Instruction.h +++ b/libevmface/Instruction.h @@ -92,6 +92,7 @@ enum class Instruction: uint8_t PC, ///< get the program counter MSIZE, ///< get the size of active memory GAS, ///< get the amount of available gas + JUMPDEST, ///< set a potential jump destination PUSH1 = 0x60, ///< place 1 byte item on stack PUSH2, ///< place 2 byte item on stack diff --git a/liblll/Assembly.cpp b/liblll/Assembly.cpp index 9b6dee947..11ee9122c 100644 --- a/liblll/Assembly.cpp +++ b/liblll/Assembly.cpp @@ -149,6 +149,17 @@ 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) @@ -353,9 +364,11 @@ 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(); @@ -393,6 +406,11 @@ 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: @@ -424,6 +442,12 @@ 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); @@ -442,5 +466,5 @@ bytes Assembly::assemble() const } } } - return ret; + return preret + ret; } From 72449d349aa85883de61d4f65a11fd6276927d61 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 19:49:15 +0200 Subject: [PATCH 12/26] PoC-7: Dynamic message IDs. --- libethereum/CommonNet.h | 5 +++-- libethereum/EthereumHost.cpp | 20 +++++++------------- libethereum/EthereumPeer.cpp | 25 ++++++++++++------------- libethereum/EthereumPeer.h | 9 +++++++-- libp2p/Capability.cpp | 14 +++++++++++--- libp2p/Capability.h | 14 ++++++++------ libp2p/Host.cpp | 8 ++++++-- libp2p/HostCapability.h | 8 ++++++-- libp2p/Session.cpp | 27 ++++++++++++++++----------- libp2p/Session.h | 1 + libwhisper/Common.h | 5 +++-- libwhisper/WhisperPeer.cpp | 13 +++++-------- libwhisper/WhisperPeer.h | 5 +++-- 13 files changed, 88 insertions(+), 66 deletions(-) diff --git a/libethereum/CommonNet.h b/libethereum/CommonNet.h index 15b05bff3..7e4821bb4 100644 --- a/libethereum/CommonNet.h +++ b/libethereum/CommonNet.h @@ -50,9 +50,9 @@ class TransactionQueue; class EthereumHost; class EthereumPeer; -enum EthereumPacket +enum { - StatusPacket = 0x10, + StatusPacket = 0, GetTransactionsPacket, TransactionsPacket, GetBlockHashesPacket, @@ -60,6 +60,7 @@ enum EthereumPacket GetBlocksPacket, BlocksPacket, NewBlockPacket, + PacketCount }; enum class Asking diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index c5c3e90cd..bc5f11936 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -177,11 +177,8 @@ void EthereumHost::maintainTransactions(h256 _currentHash) if (n || ep->m_requireTransactions) { RLPStream ts; - EthereumPeer::prep(ts); - ts.appendList(n + 1) << TransactionsPacket; - ts.appendRaw(b, n).swapOut(b); - seal(b); - ep->send(&b); + ep->prep(ts, TransactionsPacket, n).appendRaw(b, n); + ep->sealAndSend(ts); } ep->m_requireTransactions = false; } @@ -195,24 +192,21 @@ void EthereumHost::maintainBlocks(h256 _currentHash) // TODO: clean up h256s hs; hs.push_back(_currentHash); - RLPStream ts; - EthereumPeer::prep(ts); bytes bs; for (auto h: hs) bs += m_chain.block(h); clog(NetMessageSummary) << "Sending" << hs.size() << "new blocks (current is" << _currentHash << ", was" << m_latestBlockSent << ")"; - ts.appendList(1 + hs.size()).append(BlocksPacket).appendRaw(bs, hs.size()); - bytes b; - ts.swapOut(b); - seal(b); - for (auto j: peers()) { auto p = j->cap(); + + RLPStream ts; + p->prep(ts, BlocksPacket, hs.size()).appendRaw(bs, hs.size()); + Guard l(p->x_knownBlocks); if (!p->m_knownBlocks.count(_currentHash)) - p->send(&b); + p->sealAndSend(ts); p->m_knownBlocks.clear(); } m_latestBlockSent = _currentHash; diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 45e850de6..875f86f06 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -36,8 +36,8 @@ using namespace p2p; #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " -EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h): - Capability(_s, _h), +EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i): + Capability(_s, _h, _i), m_sub(host()->m_man) { transition(Asking::State); @@ -80,13 +80,12 @@ void EthereumPeer::transition(Asking _a, bool _force) clogS(NetMessageSummary) << "Transition!" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : ""); RLPStream s; - prep(s); if (_a == Asking::State) { if (m_asking == Asking::Nothing) { setAsking(Asking::State, false); - s.appendList(6) << StatusPacket + prep(s, StatusPacket, 5) << host()->protocolVersion() << host()->networkId() << host()->m_chain.details().totalDifficulty @@ -108,7 +107,7 @@ void EthereumPeer::transition(Asking _a, bool _force) resetNeedsSyncing(); setAsking(_a, true); - s.appendList(3) << GetBlockHashesPacket << m_syncingLatestHash << c_maxHashesAsk; + prep(s, GetBlockHashesPacket, 2) << m_syncingLatestHash << c_maxHashesAsk; m_syncingNeededBlocks = h256s(1, m_syncingLatestHash); sealAndSend(s); return; @@ -119,7 +118,7 @@ void EthereumPeer::transition(Asking _a, bool _force) clogS(NetWarn) << "Bad state: asking for Hashes yet not syncing!"; setAsking(_a, true); - s.appendList(3) << GetBlockHashesPacket << m_syncingNeededBlocks.back() << c_maxHashesAsk; + prep(s, GetBlockHashesPacket, 2) << m_syncingNeededBlocks.back() << c_maxHashesAsk; sealAndSend(s); return; } @@ -154,7 +153,7 @@ void EthereumPeer::transition(Asking _a, bool _force) auto blocks = m_sub.nextFetch(c_maxBlocksAsk); if (blocks.size()) { - s.appendList(blocks.size() + 1) << GetBlocksPacket; + prep(s, GetBlocksPacket, blocks.size()); for (auto const& i: blocks) s << i; sealAndSend(s); @@ -284,9 +283,9 @@ void EthereumPeer::attemptSync() } } -bool EthereumPeer::interpret(RLP const& _r) +bool EthereumPeer::interpret(unsigned _id, RLP const& _r) { - switch (_r[0].toInt()) + switch (_id) { case StatusPacket: { @@ -314,8 +313,7 @@ bool EthereumPeer::interpret(RLP const& _r) { // Grab transactions off them. RLPStream s; - prep(s).appendList(1); - s << GetTransactionsPacket; + prep(s, GetTransactionsPacket); sealAndSend(s); transition(Asking::Nothing); } @@ -350,7 +348,7 @@ bool EthereumPeer::interpret(RLP const& _r) unsigned c = min(host()->m_chain.number(later), limit); RLPStream s; - prep(s).appendList(1 + c).append(BlockHashesPacket); + prep(s, BlockHashesPacket, c); h256 p = host()->m_chain.details(later).parent; for (unsigned i = 0; i < c && p; ++i, p = host()->m_chain.details(p).parent) s << p; @@ -402,7 +400,8 @@ bool EthereumPeer::interpret(RLP const& _r) } } RLPStream s; - sealAndSend(prep(s).appendList(n + 1).append(BlocksPacket).appendRaw(rlp, n)); + prep(s, BlocksPacket, n).appendRaw(rlp, n); + sealAndSend(s); break; } case BlocksPacket: diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index bad12d3b7..71bfc544f 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -50,7 +50,7 @@ class EthereumPeer: public p2p::Capability public: /// Basic constructor. - EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h); + EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h, unsigned _i); /// Basic destructor. virtual ~EthereumPeer(); @@ -61,12 +61,17 @@ public: /// What is our version? static u256 version() { return c_protocolVersion; } + /// How many message types do we have? + static unsigned messageCount() { return PacketCount; } + /// What is the ethereum subprotocol host object. EthereumHost* host() const; private: + using p2p::Capability::sealAndSend; + /// Interpret an incoming message. - virtual bool interpret(RLP const& _r); + virtual bool interpret(unsigned _id, RLP const& _r); /// Transition state in a particular direction. void transition(Asking _wantState, bool _force = false); diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp index 5ca674120..3ca91d4ca 100644 --- a/libp2p/Capability.cpp +++ b/libp2p/Capability.cpp @@ -21,20 +21,28 @@ #include "Capability.h" +#include #include "Session.h" using namespace std; using namespace dev; using namespace dev::p2p; +#define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " + +Capability::Capability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset): m_session(_s), m_host(_h), m_idOffset(_idOffset) +{ + clogS(NetConnect) << "New session for capability" << m_host->name() << "; idOffset:" << m_idOffset; +} + void Capability::disable(std::string const& _problem) { - clog(NetConnect) << "Disabling capability '" << m_host->name() << "'. Reason:" << _problem; + clogS(NetConnect) << "Disabling capability '" << m_host->name() << "'. Reason:" << _problem; m_enabled = false; } -RLPStream& Capability::prep(RLPStream& _s) +RLPStream& Capability::prep(RLPStream& _s, unsigned _id, unsigned _args) { - return Session::prep(_s); + return Session::prep(_s).appendList(_args + 1).append(_id + m_idOffset); } void Capability::sealAndSend(RLPStream& _s) diff --git a/libp2p/Capability.h b/libp2p/Capability.h index 00cccaeef..bd2ab7a95 100644 --- a/libp2p/Capability.h +++ b/libp2p/Capability.h @@ -34,22 +34,23 @@ class Capability friend class Session; public: - Capability(Session* _s, HostCapabilityFace* _h): m_session(_s), m_host(_h) {} + Capability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset); virtual ~Capability() {} - /// Must return the capability name. - static std::string name() { return ""; } + // Implement these in the derived class. +/* static std::string name() { return ""; } static u256 version() { return 0; } - + static unsigned messageCount() { return 0; } +*/ Session* session() const { return m_session; } HostCapabilityFace* hostCapability() const { return m_host; } protected: - virtual bool interpret(RLP const&) = 0; + virtual bool interpret(unsigned _id, RLP const&) = 0; void disable(std::string const& _problem); - static RLPStream& prep(RLPStream& _s); + RLPStream& prep(RLPStream& _s, unsigned _id, unsigned _args = 0); void sealAndSend(RLPStream& _s); void sendDestroy(bytes& _msg); void send(bytesConstRef _msg); @@ -60,6 +61,7 @@ private: Session* m_session; HostCapabilityFace* m_host; bool m_enabled = true; + unsigned m_idOffset; }; } diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index d13cbf4f9..baa7ca6e9 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -152,9 +152,13 @@ void Host::registerPeer(std::shared_ptr _s, CapDescs const& _caps) Guard l(x_peers); m_peers[_s->m_id] = _s; } + unsigned o = (unsigned)UserPacket; for (auto const& i: _caps) if (haveCapability(i)) - _s->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(_s.get())); + { + _s->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(_s.get(), o)); + o += m_capabilities[i]->messageCount(); + } } void Host::disconnectPeers() @@ -458,7 +462,7 @@ void Host::growPeers() { RLPStream s; bytes b; - (Session::prep(s).appendList(1) << GetPeersPacket).swapOut(b); + Session::prep(s, GetPeersPacket).swapOut(b); seal(b); for (auto const& i: m_peers) if (auto p = i.second.lock()) diff --git a/libp2p/HostCapability.h b/libp2p/HostCapability.h index f07900034..da454860a 100644 --- a/libp2p/HostCapability.h +++ b/libp2p/HostCapability.h @@ -36,6 +36,7 @@ class HostCapabilityFace friend class Host; template friend class HostCapability; friend class Capability; + friend class Session; public: HostCapabilityFace() {} @@ -49,7 +50,8 @@ protected: virtual std::string name() const = 0; virtual u256 version() const = 0; CapDesc capDesc() const { return std::make_pair(name(), version()); } - virtual Capability* newPeerCapability(Session* _s) = 0; + virtual unsigned messageCount() const = 0; + virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset) = 0; virtual void onStarting() {} virtual void onStopping() {} @@ -69,11 +71,13 @@ public: static std::string staticName() { return PeerCap::name(); } static u256 staticVersion() { return PeerCap::version(); } + static unsigned staticMessageCount() { return PeerCap::messageCount(); } protected: virtual std::string name() const { return PeerCap::name(); } virtual u256 version() const { return PeerCap::version(); } - virtual Capability* newPeerCapability(Session* _s) { return new PeerCap(_s, this); } + virtual unsigned messageCount() const { return PeerCap::messageCount(); } + virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset) { return new PeerCap(_s, this, _idOffset); } }; } diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 71780ff6b..0b939882f 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -72,7 +72,7 @@ bi::tcp::endpoint Session::endpoint() const bool Session::interpret(RLP const& _r) { clogS(NetRight) << _r; - switch (_r[0].toInt()) + switch ((PacketType)_r[0].toInt()) { case HelloPacket: { @@ -130,7 +130,7 @@ bool Session::interpret(RLP const& _r) { clogS(NetTriviaSummary) << "Ping"; RLPStream s; - sealAndSend(prep(s).appendList(1) << PongPacket); + sealAndSend(prep(s, PongPacket)); break; } case PongPacket: @@ -142,8 +142,7 @@ bool Session::interpret(RLP const& _r) clogS(NetTriviaSummary) << "GetPeers"; auto peers = m_server->potentialPeers(); RLPStream s; - prep(s).appendList(peers.size() + 1); - s << PeersPacket; + prep(s, PeersPacket, peers.size()); for (auto i: peers) { clogS(NetTriviaDetail) << "Sending peer " << i.first.abridged() << i.second; @@ -185,25 +184,33 @@ bool Session::interpret(RLP const& _r) } break; default: + { + auto id = _r[0].toInt(); for (auto const& i: m_capabilities) - if (i.second->m_enabled && i.second->interpret(_r)) + if (i.second->m_enabled && id >= i.second->m_idOffset && id - i.second->m_idOffset < i.second->hostCapability()->messageCount() && i.second->interpret(id - i.second->m_idOffset, _r)) return true; return false; } + } return true; } void Session::ping() { RLPStream s; - sealAndSend(prep(s).appendList(1) << PingPacket); + sealAndSend(prep(s, PingPacket)); m_ping = std::chrono::steady_clock::now(); } void Session::getPeers() { RLPStream s; - sealAndSend(prep(s).appendList(1) << GetPeersPacket); + sealAndSend(prep(s, GetPeersPacket)); +} + +RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args) +{ + return prep(_s).appendList(_args + 1).append((unsigned)_id); } RLPStream& Session::prep(RLPStream& _s) @@ -323,8 +330,7 @@ void Session::disconnect(int _reason) if (m_disconnect == chrono::steady_clock::time_point::max()) { RLPStream s; - prep(s); - s.appendList(2) << DisconnectPacket << _reason; + prep(s, DisconnectPacket, 1) << _reason; sealAndSend(s); m_disconnect = chrono::steady_clock::now(); } @@ -336,8 +342,7 @@ void Session::disconnect(int _reason) void Session::start() { RLPStream s; - prep(s); - s.appendList(6) << HelloPacket + prep(s, HelloPacket, 5) << m_server->protocolVersion() << m_server->m_clientVersion << m_server->caps() diff --git a/libp2p/Session.h b/libp2p/Session.h index 9b04154ee..0498ede9b 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -64,6 +64,7 @@ public: template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(PeerCap::name(), PeerCap::version()))); } catch (...) { return nullptr; } } + static RLPStream& prep(RLPStream& _s, PacketType _t, unsigned _args = 0); static RLPStream& prep(RLPStream& _s); void sealAndSend(RLPStream& _s); void sendDestroy(bytes& _msg); diff --git a/libwhisper/Common.h b/libwhisper/Common.h index ba4285f2b..251a089b7 100644 --- a/libwhisper/Common.h +++ b/libwhisper/Common.h @@ -51,10 +51,11 @@ class Whisper; enum WhisperPacket { - StatusPacket = 0x20, + StatusPacket = 0, MessagesPacket, AddFilterPacket, - RemoveFilterPacket + RemoveFilterPacket, + PacketCount }; } diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index e92e2cac3..25bd58271 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -30,11 +30,10 @@ using namespace dev::p2p; using namespace dev::shh; #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " -WhisperPeer::WhisperPeer(Session* _s, HostCapabilityFace* _h): Capability(_s, _h) +WhisperPeer::WhisperPeer(Session* _s, HostCapabilityFace* _h, unsigned _i): Capability(_s, _h, _i) { RLPStream s; - prep(s); - sealAndSend(s.appendList(2) << StatusPacket << host()->protocolVersion()); + sealAndSend(prep(s, StatusPacket, 1) << host()->protocolVersion()); } WhisperPeer::~WhisperPeer() @@ -46,9 +45,9 @@ WhisperHost* WhisperPeer::host() const return static_cast(Capability::hostCapability()); } -bool WhisperPeer::interpret(RLP const& _r) +bool WhisperPeer::interpret(unsigned _id, RLP const& _r) { - switch (_r[0].toInt()) + switch (_id) { case StatusPacket: { @@ -95,9 +94,7 @@ void WhisperPeer::sendMessages() if (n) { RLPStream s; - prep(s); - s.appendList(n + 1) << MessagesPacket; - s.appendRaw(amalg.out(), n); + prep(s, MessagesPacket, n).appendRaw(amalg.out(), n); sealAndSend(s); } else diff --git a/libwhisper/WhisperPeer.h b/libwhisper/WhisperPeer.h index ae20cae68..3da246562 100644 --- a/libwhisper/WhisperPeer.h +++ b/libwhisper/WhisperPeer.h @@ -49,16 +49,17 @@ class WhisperPeer: public Capability friend class WhisperHost; public: - WhisperPeer(Session* _s, HostCapabilityFace* _h); + WhisperPeer(Session* _s, HostCapabilityFace* _h, unsigned _i); virtual ~WhisperPeer(); static std::string name() { return "shh"; } static u256 version() { return 1; } + static unsigned messageCount() { return PacketCount; } WhisperHost* host() const; private: - virtual bool interpret(RLP const&); + virtual bool interpret(unsigned _id, RLP const&); void sendMessages(); From 784ea95f2721554bb19201580a111564d2ff5f76 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 20:04:57 +0200 Subject: [PATCH 13/26] PoC-7: IPv6. --- libp2p/Common.cpp | 13 +++++++++++-- libp2p/Common.h | 2 +- libp2p/Session.cpp | 16 ++++++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp index 21f23696a..540f285c1 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -27,8 +27,7 @@ using namespace dev::p2p; // Helper function to determine if an address falls within one of the reserved ranges // For V4: // Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*" -// Not implemented yet for V6 -bool p2p::isPrivateAddress(bi::address _addressToCheck) +bool p2p::isPrivateAddress(bi::address const& _addressToCheck) { if (_addressToCheck.is_v4()) { @@ -41,6 +40,16 @@ bool p2p::isPrivateAddress(bi::address _addressToCheck) if (bytesToCheck[0] == 192 && bytesToCheck[1] == 168) return true; } + else if (_addressToCheck.is_v6()) + { + bi::address_v6 v6Address = _addressToCheck.to_v6(); + bi::address_v6::bytes_type bytesToCheck = v6Address.to_bytes(); + if (bytesToCheck[0] == 0xfd && bytesToCheck[1] == 0) + return true; + if (!bytesToCheck[0] && !bytesToCheck[1] && !bytesToCheck[2] && !bytesToCheck[3] && !bytesToCheck[4] && !bytesToCheck[5] && !bytesToCheck[6] && !bytesToCheck[7] + && !bytesToCheck[8] && !bytesToCheck[9] && !bytesToCheck[10] && !bytesToCheck[11] && !bytesToCheck[12] && !bytesToCheck[13] && !bytesToCheck[14] && (bytesToCheck[15] == 0 || bytesToCheck[15] == 1)) + return true; + } return false; } diff --git a/libp2p/Common.h b/libp2p/Common.h index bb1b1e2e0..895b76404 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -44,7 +44,7 @@ class RLPStream; namespace p2p { -bool isPrivateAddress(bi::address _addressToCheck); +bool isPrivateAddress(bi::address const& _addressToCheck); class UPnP; class Capability; diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 0b939882f..681e24d29 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -146,7 +146,10 @@ bool Session::interpret(RLP const& _r) for (auto i: peers) { clogS(NetTriviaDetail) << "Sending peer " << i.first.abridged() << i.second; - s.appendList(3) << bytesConstRef(i.second.address().to_v4().to_bytes().data(), 4) << i.second.port() << i.first; + 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); break; @@ -155,7 +158,16 @@ bool Session::interpret(RLP const& _r) clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; for (unsigned i = 1; i < _r.itemCount(); ++i) { - bi::address_v4 peerAddress(_r[i][0].toHash>().asArray()); + bi::address peerAddress; + if (_r[i][0].size() == 16) + peerAddress = bi::address_v6(_r[i][0].toHash>().asArray()); + else if (_r[i][0].size() == 4) + peerAddress = bi::address_v4(_r[i][0].toHash>().asArray()); + else + { + disconnect(BadProtocol); + 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); From cad19f652f3278798f344551f20fc5d90303535b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 20:15:38 +0200 Subject: [PATCH 14/26] p2p protocol version bump. --- libp2p/Host.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index baa7ca6e9..410699a55 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -143,7 +143,7 @@ void Host::stop() unsigned Host::protocolVersion() const { - return 0; + return 1; } void Host::registerPeer(std::shared_ptr _s, CapDescs const& _caps) From f1228c461a99e40bef09c5d15d7448d23aa9510e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 20:56:36 +0200 Subject: [PATCH 15/26] Predeclare all the stream operators. --- libdevcore/CommonIO.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 98c5a96c7..11422db9a 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -55,6 +55,18 @@ std::string memDump(bytes const& _b, unsigned _w = 8, bool _html = false); template struct StreamOut { static S& bypass(S& _out, T const& _t) { _out << _t; return _out; } }; template struct StreamOut { static S& bypass(S& _out, uint8_t const& _t) { _out << (int)_t; return _out; } }; +template inline std::ostream& operator<<(std::ostream& _out, std::vector const& _e); +template inline std::ostream& operator<<(std::ostream& _out, std::array const& _e); +template inline std::ostream& operator<<(std::ostream& _out, std::pair const& _e); +template inline std::ostream& operator<<(std::ostream& _out, std::list const& _e); +template inline std::ostream& operator<<(std::ostream& _out, std::tuple const& _e); +template inline std::ostream& operator<<(std::ostream& _out, std::map const& _e); +template inline std::ostream& operator<<(std::ostream& _out, std::unordered_map const& _e); +template inline std::ostream& operator<<(std::ostream& _out, std::set const& _e); +template inline std::ostream& operator<<(std::ostream& _out, std::unordered_set const& _e); +template inline std::ostream& operator<<(std::ostream& _out, std::multimap const& _e); +template _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p); + template inline S& streamout(S& _out, std::vector const& _e) { From c9582e490c6baa92211c4e56d03bcc79d7f61235 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 21:08:58 +0200 Subject: [PATCH 16/26] Windows build fix. --- libethereum/EthereumHost.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 12ce28f23..a4ccf5383 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(h512 _id) const { return !!m_banned.count(_id); } private: /// Session is tell us that we may need (re-)syncing with the peer. From 8bf5251d5a38ca44c6c99eee65e2df35dcf0756d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 22:43:42 +0200 Subject: [PATCH 17/26] Can grab block info from JS. Stephan will be pleased. --- libethereum/Client.cpp | 23 +++++++--- libethereum/Client.h | 68 +++++++++++++++-------------- libethereum/Interface.h | 9 +++- libp2p/Session.h | 1 + libqethereum/QEthereum.cpp | 87 ++++++++++++++++++++++++++++++++++++++ libqethereum/QEthereum.h | 20 ++++----- 6 files changed, 158 insertions(+), 50 deletions(-) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index cf26df6fb..d22f7d873 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -232,12 +232,9 @@ void Client::appendFromNewBlock(h256 _block, h256Set& o_changed) const void Client::setForceMining(bool _enable) { m_forceMining = _enable; - if (!m_host.lock()) - { - ReadGuard l(x_miners); - for (auto& m: m_miners) - m.noteStateChange(); - } + ReadGuard l(x_miners); + for (auto& m: m_miners) + m.noteStateChange(); } void Client::setMiningThreads(unsigned _threads) @@ -550,6 +547,20 @@ bytes Client::codeAt(Address _a, int _block) const return asOf(_block).code(_a); } +Transaction Client::transaction(h256 _blockHash, unsigned _i) const +{ + auto bl = m_bc.block(_blockHash); + RLP b(bl); + return Transaction(b[1][_i].data()); +} + +BlockInfo Client::uncle(h256 _blockHash, unsigned _i) const +{ + auto bl = m_bc.block(_blockHash); + RLP b(bl); + return BlockInfo::fromHeader(b[2][_i].data()); +} + PastMessages Client::messages(MessageFilter const& _f) const { PastMessages ret; diff --git a/libethereum/Client.h b/libethereum/Client.h index d00bf53ba..e7bbc1ca3 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -119,23 +119,23 @@ public: explicit Client(p2p::Host* _host, std::string const& _dbPath = std::string(), bool _forceClean = false, u256 _networkId = 0); /// Destructor. - ~Client(); + virtual ~Client(); /// Submits the given message-call transaction. - void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo); + virtual void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo); /// Submits a new contract-creation transaction. /// @returns the new contract's address (assuming it all goes through). - Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo); + virtual Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo); /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. - void inject(bytesConstRef _rlp); + virtual void inject(bytesConstRef _rlp); /// Blocks until all pending transactions have been processed. - void flushTransactions(); + virtual void flushTransactions(); /// Makes the given call. Nothing is recorded into the state. - bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo); + virtual bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo); // Informational stuff @@ -147,20 +147,20 @@ public: using Interface::codeAt; using Interface::storageAt; - u256 balanceAt(Address _a, int _block) const; - u256 countAt(Address _a, int _block) const; - u256 stateAt(Address _a, u256 _l, int _block) const; - bytes codeAt(Address _a, int _block) const; - std::map storageAt(Address _a, int _block) const; + virtual u256 balanceAt(Address _a, int _block) const; + virtual u256 countAt(Address _a, int _block) const; + virtual u256 stateAt(Address _a, u256 _l, int _block) const; + virtual bytes codeAt(Address _a, int _block) const; + virtual std::map storageAt(Address _a, int _block) const; - unsigned installWatch(MessageFilter const& _filter); - unsigned installWatch(h256 _filterId); - void uninstallWatch(unsigned _watchId); - bool peekWatch(unsigned _watchId) const { std::lock_guard l(m_filterLock); try { return m_watches.at(_watchId).changes != 0; } catch (...) { return false; } } - bool checkWatch(unsigned _watchId) { std::lock_guard l(m_filterLock); bool ret = false; try { ret = m_watches.at(_watchId).changes != 0; m_watches.at(_watchId).changes = 0; } catch (...) {} return ret; } + virtual unsigned installWatch(MessageFilter const& _filter); + virtual unsigned installWatch(h256 _filterId); + virtual void uninstallWatch(unsigned _watchId); + virtual bool peekWatch(unsigned _watchId) const { std::lock_guard l(m_filterLock); try { return m_watches.at(_watchId).changes != 0; } catch (...) { return false; } } + virtual bool checkWatch(unsigned _watchId) { std::lock_guard l(m_filterLock); bool ret = false; try { ret = m_watches.at(_watchId).changes != 0; m_watches.at(_watchId).changes = 0; } catch (...) {} return ret; } - PastMessages messages(unsigned _watchId) const { try { std::lock_guard l(m_filterLock); return messages(m_filters.at(m_watches.at(_watchId).id).filter); } catch (...) { return PastMessages(); } } - PastMessages messages(MessageFilter const& _filter) const; + virtual PastMessages messages(unsigned _watchId) const { try { std::lock_guard l(m_filterLock); return messages(m_filters.at(m_watches.at(_watchId).id).filter); } catch (...) { return PastMessages(); } } + virtual PastMessages messages(MessageFilter const& _filter) const; // [EXTRA API]: @@ -169,19 +169,25 @@ public: /// Get a map containing each of the pending transactions. /// @TODO: Remove in favour of transactions(). - Transactions pending() const { return m_postMine.pending(); } + virtual Transactions pending() const { return m_postMine.pending(); } + + virtual h256 hashFromNumber(unsigned _number) const { return m_bc.numberHash(_number); } + virtual BlockInfo blockInfo(h256 _hash) const { return BlockInfo(m_bc.block(_hash)); } + virtual BlockDetails blockDetails(h256 _hash) const { return m_bc.details(_hash); } + virtual Transaction transaction(h256 _blockHash, unsigned _i) const; + virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const; /// Differences between transactions. using Interface::diff; - StateDiff diff(unsigned _txi, h256 _block) const; - StateDiff diff(unsigned _txi, int _block) const; + virtual StateDiff diff(unsigned _txi, h256 _block) const; + virtual StateDiff diff(unsigned _txi, int _block) const; /// Get a list of all active addresses. using Interface::addresses; - std::vector
addresses(int _block) const; + virtual std::vector
addresses(int _block) const; /// Get the remaining gas limit in this block. - u256 gasLimitRemaining() const { return m_postMine.gasLimitRemaining(); } + virtual u256 gasLimitRemaining() const { return m_postMine.gasLimitRemaining(); } // [PRIVATE API - only relevant for base clients, not available in general] @@ -210,23 +216,23 @@ public: void setTurboMining(bool _enable = true) { m_turboMining = _enable; } /// Set the coinbase address. - void setAddress(Address _us) { m_preMine.setAddress(_us); } + virtual void setAddress(Address _us) { m_preMine.setAddress(_us); } /// Get the coinbase address. - Address address() const { return m_preMine.address(); } + virtual Address address() const { return m_preMine.address(); } /// Stops mining and sets the number of mining threads (0 for automatic). - void setMiningThreads(unsigned _threads = 0); + virtual void setMiningThreads(unsigned _threads = 0); /// Get the effective number of mining threads. - unsigned miningThreads() const { ReadGuard l(x_miners); return m_miners.size(); } + virtual unsigned miningThreads() const { ReadGuard l(x_miners); return m_miners.size(); } /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread - void startMining() { startWorking(); ReadGuard l(x_miners); for (auto& m: m_miners) m.start(); } + virtual void startMining() { startWorking(); ReadGuard l(x_miners); for (auto& m: m_miners) m.start(); } /// Stop mining. /// NOT thread-safe - void stopMining() { ReadGuard l(x_miners); for (auto& m: m_miners) m.stop(); } + virtual void stopMining() { ReadGuard l(x_miners); for (auto& m: m_miners) m.stop(); } /// Are we mining now? - bool isMining() { ReadGuard l(x_miners); return m_miners.size() && m_miners[0].isRunning(); } + virtual bool isMining() { ReadGuard l(x_miners); return m_miners.size() && m_miners[0].isRunning(); } /// Check the progress of the mining. - MineProgress miningProgress() const; + virtual MineProgress miningProgress() const; /// Get and clear the mining history. std::list miningHistory(); diff --git a/libethereum/Interface.h b/libethereum/Interface.h index a5df3e5b1..7ae650590 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -29,6 +29,7 @@ #include "MessageFilter.h" #include "Transaction.h" #include "AccountDiff.h" +#include "BlockDetails.h" #include "Miner.h" namespace dev @@ -95,7 +96,13 @@ public: virtual bool peekWatch(unsigned _watchId) const = 0; virtual bool checkWatch(unsigned _watchId) = 0; - // TODO: Block query API. + // [BLOCK QUERY API] + + virtual h256 hashFromNumber(unsigned _number) const = 0; + virtual BlockInfo blockInfo(h256 _hash) const = 0; + virtual BlockDetails blockDetails(h256 _hash) const = 0; + virtual Transaction transaction(h256 _blockHash, unsigned _i) const = 0; + virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const = 0; // [EXTRA API]: diff --git a/libp2p/Session.h b/libp2p/Session.h index 0498ede9b..2103d6db6 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -110,6 +110,7 @@ private: unsigned m_rating; std::map> m_capabilities; + std::set m_knownPeers; bool m_willBeDeleted = false; ///< True if we already posted a deleter on the strand. }; diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index 262835553..a244b9c9b 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -336,6 +336,93 @@ static QString toJson(dev::eth::PastMessages const& _pms) return QString::fromUtf8(QJsonDocument(jsonArray).toJson()); } +static QString toJson(dev::eth::BlockInfo const& _bi, dev::eth::BlockDetails const& _bd) +{ + QJsonObject v; + v["hash"] = toQJS(_bi.hash); + + v["parentHash"] = toQJS(_bi.parentHash); + v["sha3Uncles"] = toQJS(_bi.sha3Uncles); + v["miner"] = toQJS(_bi.coinbaseAddress); + v["stateRoot"] = toQJS(_bi.stateRoot); + v["transactionsRoot"] = toQJS(_bi.transactionsRoot); + v["difficulty"] = toQJS(_bi.difficulty); + v["number"] = (int)_bi.number; + v["minGasPrice"] = toQJS(_bi.minGasPrice); + v["gasLimit"] = (int)_bi.gasLimit; + v["gasUsed"] = (int)_bi.gasUsed; + v["timestamp"] = (int)_bi.timestamp; + v["extraData"] = ::fromBinary(_bi.extraData); + v["nonce"] = toQJS(_bi.nonce); + + QJsonArray children; + for (auto c: _bd.children) + children.append(toQJS(c)); + v["children"] = children; + v["totalDifficulty"] = toQJS(_bd.totalDifficulty); + v["bloom"] = toQJS(_bd.bloom); + return QString::fromUtf8(QJsonDocument(v).toJson()); +} + +static QString toJson(dev::eth::BlockInfo const& _bi) +{ + QJsonObject v; + v["hash"] = toQJS(_bi.hash); + + v["parentHash"] = toQJS(_bi.parentHash); + v["sha3Uncles"] = toQJS(_bi.sha3Uncles); + v["miner"] = toQJS(_bi.coinbaseAddress); + v["stateRoot"] = toQJS(_bi.stateRoot); + v["transactionsRoot"] = toQJS(_bi.transactionsRoot); + v["difficulty"] = toQJS(_bi.difficulty); + v["number"] = (int)_bi.number; + v["minGasPrice"] = toQJS(_bi.minGasPrice); + v["gasLimit"] = (int)_bi.gasLimit; + v["gasUsed"] = (int)_bi.gasUsed; + v["timestamp"] = (int)_bi.timestamp; + v["extraData"] = ::fromBinary(_bi.extraData); + v["nonce"] = toQJS(_bi.nonce); + + return QString::fromUtf8(QJsonDocument(v).toJson()); +} + +static QString toJson(dev::eth::Transaction const& _bi) +{ + QJsonObject v; + v["hash"] = toQJS(_bi.sha3()); + + v["input"] = ::fromBinary(_bi.data); + v["to"] = toQJS(_bi.receiveAddress); + v["from"] = toQJS(_bi.sender()); + v["gas"] = (int)_bi.gas; + v["gasPrice"] = toQJS(_bi.gasPrice); + v["nonce"] = toQJS(_bi.nonce); + v["value"] = toQJS(_bi.value); + + return QString::fromUtf8(QJsonDocument(v).toJson()); +} + +QString QEthereum::getUncle(QString _numberOrHash, int _i) const +{ + auto n = toU256(_numberOrHash); + auto h = n < m_client->number() ? m_client->hashFromNumber((unsigned)n) : ::toFixed<32>(_numberOrHash); + return m_client ? toJson(m_client->uncle(h, _i)) : ""; +} + +QString QEthereum::getTransaction(QString _numberOrHash, int _i) const +{ + auto n = toU256(_numberOrHash); + auto h = n < m_client->number() ? m_client->hashFromNumber((unsigned)n) : ::toFixed<32>(_numberOrHash); + return m_client ? toJson(m_client->transaction(h, _i)) : ""; +} + +QString QEthereum::getBlock(QString _numberOrHash) const +{ + auto n = toU256(_numberOrHash); + auto h = n < m_client->number() ? m_client->hashFromNumber((unsigned)n) : ::toFixed<32>(_numberOrHash); + return m_client ? toJson(m_client->blockInfo(h), m_client->blockDetails(h)) : ""; +} + QString QEthereum::getMessages(QString _json) const { return m_client ? toJson(m_client->messages(toMessageFilter(_json))) : ""; diff --git a/libqethereum/QEthereum.h b/libqethereum/QEthereum.h index 83ee53d43..94519e040 100644 --- a/libqethereum/QEthereum.h +++ b/libqethereum/QEthereum.h @@ -148,6 +148,10 @@ public: Q_INVOKABLE QString/*dev::u256*/ stateAt(QString/*dev::Address*/ _a, QString/*dev::u256*/ _p) const; Q_INVOKABLE QString/*dev::u256*/ codeAt(QString/*dev::Address*/ _a) const; + Q_INVOKABLE QString/*json*/ getBlock(QString _numberOrHash/*unsigned if < number(), hash otherwise*/) const; + Q_INVOKABLE QString/*json*/ getTransaction(QString _numberOrHash/*unsigned if < number(), hash otherwise*/, int _index) const; + Q_INVOKABLE QString/*json*/ getUncle(QString _numberOrHash/*unsigned if < number(), hash otherwise*/, int _index) const; + Q_INVOKABLE QString/*json*/ getMessages(QString _attribs/*json*/) const; Q_INVOKABLE QString doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice); @@ -255,20 +259,12 @@ private: frame->addToJavaScriptWindowObject("shh", eth, QWebFrame::ScriptOwnership); \ frame->evaluateJavaScript("eth.makeWatch = function(a) { var ww = eth.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { eth.killWatch(w); }; ret.changed = function(f) { eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(eth.watchMessages(this.w)) }; return ret; }"); \ frame->evaluateJavaScript("eth.watch = function(a) { return eth.makeWatch(JSON.stringify(a)) }"); \ - frame->evaluateJavaScript("eth.watchChain = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.watch('chain') INSTEAD.'); return eth.makeWatch('chain') }"); \ - frame->evaluateJavaScript("eth.watchPending = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.watch('pending') INSTEAD.'); return eth.makeWatch('pending') }"); \ - frame->evaluateJavaScript("eth.create = function(s, v, c, g, p, f) { env.warn('THIS CALL IS DEPRECATED. USE eth.transact INSTEAD.'); var v = eth.doCreate(s, v, c, g, p); if (f) f(v) }"); \ - frame->evaluateJavaScript("eth.transact = function(a_s, f_v, t, d, g, p, f) { if (t == null) { var r = eth.doTransact(JSON.stringify(a_s)); if (f_v) f_v(r); } else { env.warn('THIS FORM OF THIS CALL IS DEPRECATED.'); eth.doTransact(a_s, f_v, t, d, g, p); if (f) f() } }"); \ + frame->evaluateJavaScript("eth.transact = function(a, f) { var r = eth.doTransact(JSON.stringify(a)); if (f) f(r); }"); \ frame->evaluateJavaScript("eth.call = function(a, f) { var ret = eth.doCallJson(JSON.stringify(a)); if (f) f(ret); return ret; }"); \ frame->evaluateJavaScript("eth.messages = function(a) { return JSON.parse(eth.getMessages(JSON.stringify(a))); }"); \ - frame->evaluateJavaScript("eth.transactions = function(a) { env.warn('THIS CALL IS DEPRECATED. USE eth.messages INSTEAD.'); return JSON.parse(eth.getMessages(JSON.stringify(a))); }"); \ - frame->evaluateJavaScript("String.prototype.pad = function(l, r) { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.pad(this, l, r) }"); \ - frame->evaluateJavaScript("String.prototype.bin = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.toAscii(this) }"); \ - frame->evaluateJavaScript("String.prototype.unbin = function(l) { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.fromAscii(this) }"); \ - frame->evaluateJavaScript("String.prototype.unpad = function(l) { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.unpad(this) }"); \ - frame->evaluateJavaScript("String.prototype.dec = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.toDecimal(this) }"); \ - frame->evaluateJavaScript("String.prototype.fix = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.toFixed(this) }"); \ - frame->evaluateJavaScript("String.prototype.sha3 = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.sha3old(this) }"); \ + frame->evaluateJavaScript("eth.block = function(a) { return JSON.parse(eth.getBlock(a)); }"); \ + frame->evaluateJavaScript("eth.transaction = function(a) { return JSON.parse(eth.getTransaction(a)); }"); \ + frame->evaluateJavaScript("eth.uncle = function(a) { return JSON.parse(eth.getUncle(a)); }"); \ frame->evaluateJavaScript("shh.makeWatch = function(a) { var ww = shh.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { shh.killWatch(w); }; ret.changed = function(f) { shh.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(shh.watchMessages(this.w)) }; return ret; }"); \ frame->evaluateJavaScript("shh.watch = function(a) { return shh.makeWatch(JSON.stringify(a)) }"); \ } From d93cccc463afc2439dcb0de91207fb552ba125e7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 7 Oct 2014 23:07:56 +0200 Subject: [PATCH 18/26] More attempts to fix for the ultra-pedantic clang compiler. --- exp/main.cpp | 1 + libdevcore/CommonIO.cpp | 2 +- libdevcore/Exceptions.h | 1 - libethcore/Exceptions.cpp | 35 +++++++++++++++++++++++++++++++++++ libethcore/Exceptions.h | 35 ++++++++++++++++++++++++++++------- libethereum/Executive.cpp | 1 + libethereum/Miner.cpp | 8 ++++++++ libethereum/Miner.h | 2 +- libethereum/State.cpp | 1 + libevm/ExtVMFace.h | 2 ++ libevmface/Instruction.cpp | 1 + liblll/CodeFragment.cpp | 1 + libp2p/Host.cpp | 1 + libp2p/Session.cpp | 1 + libp2p/Session.h | 1 + libp2p/UPnP.cpp | 1 + test/hexPrefix.cpp | 3 ++- test/rlp.cpp | 1 + test/trie.cpp | 1 + test/vm.cpp | 1 + 20 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 libethcore/Exceptions.cpp diff --git a/exp/main.cpp b/exp/main.cpp index 1f29ab207..59c622b6e 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 5797e44dc..3d2eccf13 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -19,7 +19,7 @@ * @date 2014 */ -#include "Common.h" +#include "CommonIO.h" #include #include "Exceptions.h" diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index 0ecd2734c..1a54814c6 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -25,7 +25,6 @@ #include #include #include -#include "CommonIO.h" #include "CommonData.h" #include "FixedHash.h" diff --git a/libethcore/Exceptions.cpp b/libethcore/Exceptions.cpp new file mode 100644 index 000000000..2936e9353 --- /dev/null +++ b/libethcore/Exceptions.cpp @@ -0,0 +1,35 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Exceptions.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Exceptions.h" +#include + +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(); } + diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index 6cf974e78..f9207f795 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -1,3 +1,24 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Exceptions.h + * @author Gav Wood + * @date 2014 + */ + #pragma once #include @@ -23,23 +44,23 @@ struct FeeTooSmall: virtual dev::Exception {}; struct TooMuchGasUsed: virtual dev::Exception {}; struct ExtraDataTooBig: virtual dev::Exception {}; struct InvalidSignature: virtual dev::Exception {}; -class InvalidBlockFormat: public dev::Exception { public: InvalidBlockFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual const char* what() const noexcept { return ("Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")").c_str(); } }; +class InvalidBlockFormat: public dev::Exception { public: InvalidBlockFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual const char* what() const noexcept; }; 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 { return ("Uncle in block already mentioned: Uncles " + toString(m_uncles) + " (" + m_block.abridged() + ")").c_str(); } }; +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 { return ("Invalid transactions hash: header says: " + toHex(m_head.ref()) + " block is:" + toHex(m_real.ref())).c_str(); } }; +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; }; struct InvalidTransaction: virtual dev::Exception {}; struct InvalidDifficulty: virtual dev::Exception {}; -class InvalidGasLimit: public dev::Exception { public: InvalidGasLimit(u256 _provided = 0, u256 _valid = 0): provided(_provided), valid(_valid) {} u256 provided; u256 valid; virtual const char* what() const noexcept { return ("Invalid gas limit (provided: " + toString(provided) + " valid:" + toString(valid) + ")").c_str(); } }; -class InvalidMinGasPrice: public dev::Exception { public: InvalidMinGasPrice(u256 _provided = 0, u256 _limit = 0): provided(_provided), limit(_limit) {} u256 provided; u256 limit; virtual const char* what() const noexcept { return ("Invalid minimum gas price (provided: " + toString(provided) + " limit:" + toString(limit) + ")").c_str(); } }; +class InvalidGasLimit: public dev::Exception { public: InvalidGasLimit(u256 _provided = 0, u256 _valid = 0): provided(_provided), valid(_valid) {} u256 provided; u256 valid; virtual const char* what() const noexcept; }; +class InvalidMinGasPrice: public dev::Exception { public: InvalidMinGasPrice(u256 _provided = 0, u256 _limit = 0): provided(_provided), limit(_limit) {} u256 provided; u256 limit; virtual const char* what() const noexcept; }; struct InvalidTransactionGasUsed: virtual dev::Exception {}; struct InvalidTransactionStateRoot: virtual dev::Exception {}; struct InvalidTimestamp: virtual dev::Exception {}; -class InvalidNonce: public dev::Exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; virtual const char* what() const noexcept { return ("Invalid nonce (r: " + toString(required) + " c:" + toString(candidate) + ")").c_str(); } }; -class InvalidBlockNonce: public dev::Exception { public: InvalidBlockNonce(h256 _h = h256(), h256 _n = h256(), u256 _d = 0): h(_h), n(_n), d(_d) {} h256 h; h256 n; u256 d; virtual const char* what() const noexcept { return ("Invalid nonce (h: " + toString(h) + " n:" + toString(n) + " d:" + toString(d) + ")").c_str(); } }; +class InvalidNonce: public dev::Exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; virtual const char* what() const noexcept; }; +class InvalidBlockNonce: public dev::Exception { public: InvalidBlockNonce(h256 _h = h256(), h256 _n = h256(), u256 _d = 0): h(_h), n(_n), d(_d) {} h256 h; h256 n; u256 d; virtual const char* what() const noexcept; }; struct InvalidParentHash: virtual dev::Exception {}; struct InvalidNumber: virtual dev::Exception {}; struct InvalidContractAddress: virtual dev::Exception {}; diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 84fba15d6..fda4a1a02 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -20,6 +20,7 @@ */ #include +#include #include #include "Executive.h" #include "State.h" diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp index 9b651e2c5..638947a89 100644 --- a/libethereum/Miner.cpp +++ b/libethereum/Miner.cpp @@ -21,6 +21,8 @@ */ #include "Miner.h" + +#include #include "State.h" using namespace std; using namespace dev; @@ -32,6 +34,12 @@ Miner::Miner(MinerHost* _host, unsigned _id): { } +void Miner::setup(MinerHost* _host, unsigned _id) +{ + m_host = _host; + setName("miner-" + toString(_id)); +} + void Miner::doWork() { // Do some mining. diff --git a/libethereum/Miner.h b/libethereum/Miner.h index 763249b7d..358a0428b 100644 --- a/libethereum/Miner.h +++ b/libethereum/Miner.h @@ -94,7 +94,7 @@ public: ~Miner() { stop(); } /// Setup its basics. - void setup(MinerHost* _host, unsigned _id = 0) { m_host = _host; setName("miner-" + toString(_id)); } + void setup(MinerHost* _host, unsigned _id = 0); /// Start mining. void start() { startWorking(); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 10f283f77..13cd48f92 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 19cc944a4..2fe6b9c18 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -21,6 +21,8 @@ #pragma once +#include +#include #include #include #include diff --git a/libevmface/Instruction.cpp b/libevmface/Instruction.cpp index bb1ef3e98..7bf845b06 100644 --- a/libevmface/Instruction.cpp +++ b/libevmface/Instruction.cpp @@ -22,6 +22,7 @@ #include "Instruction.h" #include +#include #include using namespace std; using namespace dev; diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index 533d00a96..56b5d440f 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "CompilerState.h" #include "Parser.h" diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 410699a55..05fa81f4c 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include "Session.h" #include "Common.h" diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 681e24d29..5444c204b 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include "Host.h" #include "Capability.h" diff --git a/libp2p/Session.h b/libp2p/Session.h index 2103d6db6..1a0ae8730 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "Common.h" diff --git a/libp2p/UPnP.cpp b/libp2p/UPnP.cpp index 5ac3e9427..211b185bb 100644 --- a/libp2p/UPnP.cpp +++ b/libp2p/UPnP.cpp @@ -31,6 +31,7 @@ #endif #include #include +#include #include using namespace std; using namespace dev; diff --git a/test/hexPrefix.cpp b/test/hexPrefix.cpp index 9b65db0e4..6ced839dd 100644 --- a/test/hexPrefix.cpp +++ b/test/hexPrefix.cpp @@ -22,8 +22,9 @@ #include #include "JsonSpiritHeaders.h" -#include #include +#include +#include #include using namespace std; diff --git a/test/rlp.cpp b/test/rlp.cpp index c3f9dda2f..95d40ada7 100644 --- a/test/rlp.cpp +++ b/test/rlp.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include "JsonSpiritHeaders.h" diff --git a/test/trie.cpp b/test/trie.cpp index cdebd09fc..fb74ebe20 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -23,6 +23,7 @@ #include #include #include "JsonSpiritHeaders.h" +#include #include #include "TrieHash.h" #include "MemTrie.h" diff --git a/test/vm.cpp b/test/vm.cpp index feba4ef75..0f9073719 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -21,6 +21,7 @@ */ #include "vm.h" +#include #define FILL_TESTS From 81c16c7290a510d3de043bc7866d9d7dc60f60e5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 8 Oct 2014 00:07:03 +0200 Subject: [PATCH 19/26] PoC-7 crypto-contracts. --- libdevcrypto/SHA3.cpp | 16 ++++++++++ libdevcrypto/SHA3.h | 6 ++++ libethereum/Executive.cpp | 5 +-- libethereum/ExtVM.h | 18 ++--------- libethereum/State.cpp | 65 +++++++++++++++++++++++++++++++++------ libethereum/State.h | 12 ++++++-- libevm/ExtVMFace.h | 9 ------ test/vm.cpp | 6 ++-- 8 files changed, 94 insertions(+), 43 deletions(-) diff --git a/libdevcrypto/SHA3.cpp b/libdevcrypto/SHA3.cpp index 4c5ee9fae..58d5329ef 100644 --- a/libdevcrypto/SHA3.cpp +++ b/libdevcrypto/SHA3.cpp @@ -58,6 +58,22 @@ void sha3(bytesConstRef _input, bytesRef _output) ctx.Final(_output.data()); } +void ripemd160(bytesConstRef _input, bytesRef _output) +{ + CryptoPP::RIPEMD160 ctx; + ctx.Update((byte*)_input.data(), _input.size()); + assert(_output.size() >= 32); + ctx.Final(_output.data()); +} + +void sha256(bytesConstRef _input, bytesRef _output) +{ + CryptoPP::SHA256 ctx; + ctx.Update((byte*)_input.data(), _input.size()); + assert(_output.size() >= 32); + ctx.Final(_output.data()); +} + bytes sha3Bytes(bytesConstRef _input) { bytes ret(32); diff --git a/libdevcrypto/SHA3.h b/libdevcrypto/SHA3.h index f3837fcc9..caadfeaf9 100644 --- a/libdevcrypto/SHA3.h +++ b/libdevcrypto/SHA3.h @@ -60,7 +60,13 @@ inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)) extern h256 EmptySHA3; +// Other crypto convenience routines + bytes aesDecrypt(bytesConstRef _cipher, std::string const& _password, unsigned _rounds = 2000, bytesConstRef _salt = bytesConstRef()); +void sha256(bytesConstRef _input, bytesRef _output); + +void ripemd160(bytesConstRef _input, bytesRef _output); + } } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index fda4a1a02..61d179ed8 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -218,15 +218,12 @@ u256 Executive::gas() const return m_vm ? m_vm->gas() : m_endGas; } -void Executive::finalize(OnOpFunc const& _onOp) +void Executive::finalize(OnOpFunc const&) { if (m_t.isCreation() && m_newAddress && m_out.size()) // non-reverted creation - put code in place. m_s.m_cache[m_newAddress].setCode(m_out); - if (m_ext) - m_endGas += m_ext->doPosts(_onOp); - // cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")"; m_s.addBalance(m_sender, m_endGas * m_t.gasPrice); diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index b4db1975e..3b4b7161f 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -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, &posts, 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, level + 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, &posts, 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, level + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; @@ -98,20 +98,6 @@ public: /// @TODO check call site for the parent manifest being discarded. void revert() { if (m_ms) *m_ms = Manifest(); m_s.m_cache = m_origCache; } - /// Execute any posts we have left. - u256 doPosts(OnOpFunc const& _onOp = OnOpFunc()) - { - u256 ret; - while (posts.size()) - { - Post& p = posts.front(); - call(p.to, p.value, &p.data, &p.gas, bytesRef(), _onOp, p.from); - ret += p.gas; - posts.pop_front(); - } - return ret; - } - State& state() const { return m_s; } /// @note not a part of the main API; just for use by tracing/debug stuff. diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 13cd48f92..047b9dcd5 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -41,6 +41,50 @@ using namespace dev::eth; static const u256 c_blockReward = 1500 * finney; +void ecrecoverCode(bytesConstRef _in, bytesRef _out) +{ + struct inType + { + h256 hash; + h256 v; + h256 r; + h256 s; + } in; + + h256 ret; + + memcpy(&in, _in.data(), min(_in.size(), sizeof(in))); + + byte pubkey[65]; + int pubkeylen = 65; + secp256k1_start(); + if (secp256k1_ecdsa_recover_compact(in.hash.data(), 32, in.r.data(), pubkey, &pubkeylen, 0, (int)(u256)in.v - 27)) + ret = dev::eth::sha3(bytesConstRef(&(pubkey[1]), 64)); + + memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret))); +} + +void sha256Code(bytesConstRef _in, bytesRef _out) +{ + h256 ret; + sha256(_in, bytesRef(ret.data(), 32)); + memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret))); +} + +void ripemd160Code(bytesConstRef _in, bytesRef _out) +{ + h256 ret; + ripemd160(_in, bytesRef(ret.data(), 32)); + memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret))); +} + +const std::map State::c_precompiled = +{ + { 1, { 500, ecrecoverCode }}, + { 2, { 100, sha256Code }}, + { 3, { 100, ripemd160Code }} +}; + OverlayDB State::openDB(std::string _path, bool _killExisting) { if (_path.empty()) @@ -1061,7 +1105,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) return e.gasUsed(); } -bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set
* o_suicides, PostList* o_posts, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { if (!_originAddress) _originAddress = _senderAddress; @@ -1077,7 +1121,16 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA o_ms->input = _data.toBytes(); } - if (addressHasCode(_codeAddress)) + auto it = !(_codeAddress & ~h160(0xffffffff)) ? c_precompiled.find((unsigned)(u160)_codeAddress) : c_precompiled.end(); + if (it != c_precompiled.end()) + { + if (*_gas >= it->second.gas) + { + *_gas -= it->second.gas; + it->second.exec(_data, _out); + } + } + else if (addressHasCode(_codeAddress)) { VM vm(*_gas); ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), o_ms, _level); @@ -1090,9 +1143,6 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA if (o_suicides) for (auto i: evm.suicides) o_suicides->insert(i); - if (o_posts) - for (auto i: evm.posts) - o_posts->push_back(i); if (o_ms) o_ms->output = out.toBytes(); } @@ -1125,7 +1175,7 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA return true; } -h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set
* o_suicides, PostList* o_posts, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +h160 State::create(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; @@ -1157,9 +1207,6 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, if (o_suicides) for (auto i: evm.suicides) o_suicides->insert(i); - if (o_posts) - for (auto i: evm.posts) - o_posts->push_back(i); } catch (OutOfGas const& /*_e*/) { diff --git a/libethereum/State.h b/libethereum/State.h index a716eb610..a28d8155c 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -69,6 +69,12 @@ struct TransactionReceipt Manifest changes; }; +struct PrecompiledAddress +{ + unsigned gas; + std::function exec; +}; + /** * @brief Model of the current state of the ledger. * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). @@ -277,12 +283,12 @@ private: // We assume all instrinsic fees are paid up before this point. /// Execute a contract-creation transaction. - h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set
* o_suicides = nullptr, PostList* o_posts = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Execute a call. /// @a _gas points to the amount of gas to use for the call, and will lower it accordingly. /// @returns false if the call ran out of gas before completion. true otherwise. - bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set
* o_suicides = nullptr, PostList* o_posts = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); @@ -321,6 +327,8 @@ private: static std::string c_defaultPath; + static const std::map c_precompiled; + friend std::ostream& operator<<(std::ostream& _out, State const& _s); }; diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 2fe6b9c18..f78cb82cb 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -42,8 +42,6 @@ struct Post u256 gas; }; -using PostList = std::list; - using OnOpFunc = std::function; /** @@ -88,15 +86,9 @@ public: /// Make a new message call. bool call(Address, u256, bytesConstRef, u256*, bytesRef, OnOpFunc const&, Address, Address) { return false; } - /// Post a new message call. - void post(Address _to, u256 _value, bytesConstRef _data, u256 _gas) { posts.push_back(Post({myAddress, _to, _value, _data.toBytes(), _gas})); } - /// Revert any changes made (by any of the other calls). void revert() {} - /// Execute any posts that may exist, including those that are incurred as a result of earlier posts. - void doPosts() {} - Address myAddress; ///< Address associated with executing code (a contract, or contract-to-be). Address caller; ///< Address which sent the message (either equal to origin or a contract). Address origin; ///< Original transactor. @@ -107,7 +99,6 @@ public: BlockInfo previousBlock; ///< The previous block's information. BlockInfo currentBlock; ///< The current block's information. std::set
suicides; ///< Any accounts that have suicided. - std::list posts; ///< Any posts that have been made. }; } diff --git a/test/vm.cpp b/test/vm.cpp index 0f9073719..a90b122e2 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -45,7 +45,7 @@ h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFun m_s.noteSending(myAddress); m_ms.internal.resize(m_ms.internal.size() + 1); - auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _init, origin, &suicides, &posts, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1); + auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); @@ -83,7 +83,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, { 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, &posts, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1); + auto na = m_s.create(myAddress, 0, gasPrice, _gas, 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)) @@ -96,7 +96,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, } 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, &posts, &(m_ms.internal.back()), OnOpFunc(), 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); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); if (!ret) From a37d214ecbcf6df99064cc13975481449a05e92e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 8 Oct 2014 00:12:20 +0200 Subject: [PATCH 20/26] Undefine clogS for windows builds. --- libethereum/EthereumPeer.cpp | 3 +++ libp2p/Capability.cpp | 3 +++ libp2p/Session.cpp | 3 +++ libwhisper/Interface.cpp | 3 +++ libwhisper/Message.cpp | 1 - libwhisper/WhisperHost.cpp | 3 +++ libwhisper/WhisperPeer.cpp | 4 ++++ 7 files changed, 19 insertions(+), 1 deletion(-) diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 875f86f06..21fe5fbba 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -34,6 +34,9 @@ using namespace dev; using namespace dev::eth; using namespace p2p; +#if defined(clogS) +#undef clogS +#endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i): diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp index 3ca91d4ca..2dbfd76a8 100644 --- a/libp2p/Capability.cpp +++ b/libp2p/Capability.cpp @@ -27,6 +27,9 @@ using namespace std; using namespace dev; using namespace dev::p2p; +#if defined(clogS) +#undef clogS +#endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " Capability::Capability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset): m_session(_s), m_host(_h), m_idOffset(_idOffset) diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 5444c204b..12f48f3c9 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -31,6 +31,9 @@ using namespace std; using namespace dev; using namespace dev::p2p; +#if defined(clogS) +#undef clogS +#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): diff --git a/libwhisper/Interface.cpp b/libwhisper/Interface.cpp index 4ccf85689..e5f5cd268 100644 --- a/libwhisper/Interface.cpp +++ b/libwhisper/Interface.cpp @@ -29,6 +29,9 @@ using namespace dev; using namespace dev::p2p; using namespace dev::shh; +#if defined(clogS) +#undef clogS +#endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " bool MessageFilter::matches(Message const& _m) const diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp index d7fed1811..8a3b8f435 100644 --- a/libwhisper/Message.cpp +++ b/libwhisper/Message.cpp @@ -25,4 +25,3 @@ using namespace std; using namespace dev; using namespace dev::p2p; using namespace dev::shh; -#define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index e5e32e72f..8ed88bca8 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -28,6 +28,9 @@ using namespace dev; using namespace dev::p2p; using namespace dev::shh; +#if defined(clogS) +#undef clogS +#endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " WhisperHost::WhisperHost() diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index 25bd58271..a8e72a40e 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -28,6 +28,10 @@ using namespace std; using namespace dev; using namespace dev::p2p; using namespace dev::shh; + +#if defined(clogS) +#undef clogS +#endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " WhisperPeer::WhisperPeer(Session* _s, HostCapabilityFace* _h, unsigned _i): Capability(_s, _h, _i) From d489ef3357d76c097aa8c736bdc8a67fac79c4c1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 8 Oct 2014 11:46:12 +0200 Subject: [PATCH 21/26] Actually use NewBlock --- libethereum/DownloadMan.cpp | 4 ++- libethereum/DownloadMan.h | 4 +-- libethereum/EthereumHost.cpp | 2 +- libethereum/EthereumPeer.cpp | 62 +++++++++++++++++++----------------- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/libethereum/DownloadMan.cpp b/libethereum/DownloadMan.cpp index 3c8dae58e..e2c457404 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/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 21fe5fbba..610050ad0 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -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: From f2e178108ce208f29a34a40ed56e12fb59dc2f9c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 8 Oct 2014 11:52:13 +0200 Subject: [PATCH 22/26] Version bump --- libdevcore/Common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 8a8687bfa..fd1868539 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.1"; } From 0cadc14cce26394d88c2099a40b9af7252236925 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 8 Oct 2014 11:52:55 +0200 Subject: [PATCH 23/26] Blacklist version. --- libethereum/EthereumPeer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 610050ad0..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."); From 9f832c659d662a790d6200bfd7b00a7dc7138bbf Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 8 Oct 2014 12:22:08 +0200 Subject: [PATCH 24/26] fix for clang builds --- libp2p/Session.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 12f48f3c9..d6a45bfc7 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -86,7 +86,13 @@ bool Session::interpret(RLP const& _r) m_listenPort = _r[4].toInt(); m_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 << ")"; + + clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << m_id.abridged() << showbase << capslog.str() << dec << m_listenPort; if (m_server->havePeer(m_id)) { From e491090e7cc0c1f0fe1db18b70a04b56006afe0c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 8 Oct 2014 14:43:32 +0200 Subject: [PATCH 25/26] PoC-7 JUMPDEST done the intended way. Windows pedantic build fix. --- libethereum/DownloadMan.cpp | 2 +- libevm/VM.cpp | 2 -- libevm/VM.h | 18 ++++++------------ liblll/Assembly.cpp | 31 ++++--------------------------- 4 files changed, 11 insertions(+), 42 deletions(-) diff --git a/libethereum/DownloadMan.cpp b/libethereum/DownloadMan.cpp index e2c457404..be33f5187 100644 --- a/libethereum/DownloadMan.cpp +++ b/libethereum/DownloadMan.cpp @@ -71,7 +71,7 @@ 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); + bool ret = !!m_remaining.count(_hash); m_remaining.erase(_hash); return ret; } 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..399df72dd 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; }; } @@ -568,19 +566,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 ((Instruction)_ext.getCode(nextPC) != 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 ((Instruction)_ext.getCode(nextPC) != Instruction::JUMPDEST) + BOOST_THROW_EXCEPTION(BadJumpDestination()); + } m_stack.pop_back(); m_stack.pop_back(); break; @@ -594,10 +592,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: { diff --git a/liblll/Assembly.cpp b/liblll/Assembly.cpp index 11ee9122c..695c9ffab 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: @@ -432,6 +414,7 @@ bytes Assembly::assemble() const } case Tag: tagPos[(unsigned)i.m_data] = ret.size(); + ret.push_back((byte)Instruction::JUMPDEST); 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; } From 1fac4812f5818f24ce1a9b0298f28483e43e5e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 8 Oct 2014 15:31:23 +0200 Subject: [PATCH 26/26] LibEthereum.vcxproj updated: missing Exceptions.cpp file added --- libethcore/_libethcore.cpp | 1 + windows/LibEthereum.vcxproj | 6 ++++++ windows/LibEthereum.vcxproj.filters | 3 +++ 3 files changed, 10 insertions(+) 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/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 +