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/Common.cpp b/libdevcore/Common.cpp index 551b7af87..fd1868539 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.1"; } 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/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) { 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/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/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/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/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/libethcore/_libethcore.cpp b/libethcore/_libethcore.cpp index 8a3831584..477034b9e 100644 --- a/libethcore/_libethcore.cpp +++ b/libethcore/_libethcore.cpp @@ -3,4 +3,5 @@ #include "BlockInfo.cpp" #include "CommonEth.cpp" #include "Dagger.cpp" +#include "Exceptions.cpp" #endif diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index 3477a692d..bb5055104 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. @@ -60,6 +60,7 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) { cwarn << "Ignoring malformed block: " << diagnostic_information(_e); return false; + return ImportResult::Malformed; } #endif @@ -67,7 +68,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 +78,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 +89,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 +100,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/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/CommonNet.h b/libethereum/CommonNet.h index c53b644e4..7e4821bb4 100644 --- a/libethereum/CommonNet.h +++ b/libethereum/CommonNet.h @@ -50,25 +50,33 @@ class TransactionQueue; class EthereumHost; class EthereumPeer; -enum EthereumPacket +enum { - StatusPacket = 0x10, + StatusPacket = 0, GetTransactionsPacket, TransactionsPacket, GetBlockHashesPacket, BlockHashesPacket, GetBlocksPacket, BlocksPacket, + NewBlockPacket, + PacketCount }; -enum class Grabbing +enum class Asking { State, Hashes, - Chain, - ChainHelper, + Blocks, Nothing }; +enum class Syncing +{ + Waiting, + Executing, + Done +}; + } } diff --git a/libethereum/DownloadMan.cpp b/libethereum/DownloadMan.cpp index 3c8dae58e..be33f5187 100644 --- a/libethereum/DownloadMan.cpp +++ b/libethereum/DownloadMan.cpp @@ -66,10 +66,12 @@ h256Set DownloadSub::nextFetch(unsigned _n) return m_remaining; } -void DownloadSub::noteBlock(h256 _hash) +bool DownloadSub::noteBlock(h256 _hash) { Guard l(m_fetch); if (m_man && m_indices.count(_hash)) m_man->m_blocksGot += m_indices[_hash]; + bool ret = !!m_remaining.count(_hash); m_remaining.erase(_hash); + return ret; } diff --git a/libethereum/DownloadMan.h b/libethereum/DownloadMan.h index 6f4c4bafb..1902f3db1 100644 --- a/libethereum/DownloadMan.h +++ b/libethereum/DownloadMan.h @@ -48,8 +48,8 @@ public: /// Finished last fetch - grab the next bunch of block hashes to download. h256Set nextFetch(unsigned _n); - /// Note that we've received a particular block. - void noteBlock(h256 _hash); + /// Note that we've received a particular block. @returns true if we had asked for it but haven't received it yet. + bool noteBlock(h256 _hash); /// Nothing doing here. void doneFetch() { resetFetch(); } diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index e7e80c28a..8e92fa9e6 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -52,10 +52,10 @@ 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) +bool EthereumHost::ensureInitialised() { if (!m_latestBlockSent) { @@ -63,155 +63,109 @@ 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; } return false; } -void EthereumHost::noteHavePeerState(EthereumPeer* _who) +void EthereumHost::noteNeedsSyncing(EthereumPeer* _who) { - clog(NetAllDetail) << "Have peer state."; - - // TODO: FIX: BUG: Better state management! - // if already downloading hash-chain, ignore. - if (m_grabbing != Grabbing::Nothing) + if (isSyncing()) { - for (auto const& i: peers()) - if (i->cap()->m_grabbing == m_grabbing || m_grabbing == Grabbing::State) - { - clog(NetAllDetail) << "Already downloading chain. Just set to help out."; - _who->ensureGettingChain(); - return; - } - m_grabbing = Grabbing::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->tryGrabbingHashChain(); -} - -void EthereumHost::updateGrabbing(Grabbing _g) -{ - m_grabbing = _g; - if (_g == Grabbing::Nothing) - readyForSync(); - else if (_g == Grabbing::Chain) - for (auto j: peers()) - j->cap()->ensureGettingChain(); + else + // otherwise check to see if we should be downloading... + _who->attemptSync(); } -void EthereumHost::noteHaveChain(EthereumPeer* _from) +void EthereumHost::changeSyncer(EthereumPeer* _syncer) { - auto td = _from->m_totalDifficulty; - - if (_from->m_neededBlocks.empty()) + m_syncer = _syncer; + if (isSyncing()) { - _from->setGrabbing(Grabbing::Nothing); - updateGrabbing(Grabbing::Nothing); - return; + 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); } - - clog(NetNote) << "Hash-chain COMPLETE:" << _from->m_totalDifficulty << "vs" << m_chain.details().totalDifficulty << ";" << _from->m_neededBlocks.size() << " blocks, ends" << _from->m_neededBlocks.back().abridged(); - - if (td < m_chain.details().totalDifficulty || (td == m_chain.details().totalDifficulty && m_chain.currentHash() == _from->m_latestHash)) + else { - clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; - _from->setGrabbing(Grabbing::Nothing); - updateGrabbing(Grabbing::Nothing); - return; - } - - clog(NetNote) << "Difficulty of hashchain HIGHER. Replacing fetch queue [latest now" << _from->m_latestHash.abridged() << ", was" << m_latestBlockSent.abridged() << "]"; - - // Looks like it's the best yet for total difficulty. Set to download. - m_man.resetToChain(_from->m_neededBlocks); - m_latestBlockSent = _from->m_latestHash; - - _from->setGrabbing(Grabbing::Chain); - updateGrabbing(Grabbing::Chain); -} - -void EthereumHost::readyForSync() -{ - // start grabbing next hash chain if there is one. - for (auto j: peers()) - { - j->cap()->tryGrabbingHashChain(); - if (j->cap()->m_grabbing == Grabbing::Hashes) + // start grabbing next hash chain if there is one. + for (auto j: peers()) { - m_grabbing = Grabbing::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(Grabbing::Nothing); + // 1/100th for each useful block hash. + _who->addRating(m_man.chain().size() / 100); m_man.reset(); } - if (_who->m_grabbing == Grabbing::Chain) + else 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(Grabbing::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() { - 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 = (m_grabbing == Grabbing::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()) - { - auto ep = p->cap(); - if (ep) + if (auto ep = p->cap()) { 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; @@ -220,75 +174,39 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash } ep->clearKnownTransactions(); - if (n) + 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; } - } -} - -void EthereumHost::reset() -{ - m_grabbing = Grabbing::Nothing; - - m_man.resetToChain(h256s()); - - m_incomingTransactions.clear(); - m_incomingBlocks.clear(); - - m_latestBlockSent = h256(); - m_transactionsSent.clear(); } -void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) +void EthereumHost::maintainBlocks(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) + if (!isSyncing() && m_chain.isKnown(m_latestBlockSent) && m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty) { - RLPStream ts; - EthereumPeer::prep(ts); + // TODO: clean up + h256s hs; + hs.push_back(_currentHash); 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; - } - - ts.appendList(1 + c).append(BlocksPacket).appendRaw(bs, c); - bytes b; - ts.swapOut(b); - seal(b); + clog(NetMessageSummary) << "Sending" << hs.size() << "new blocks (current is" << _currentHash << ", was" << m_latestBlockSent << ")"; for (auto j: peers()) { auto p = j->cap(); + + RLPStream ts; + p->prep(ts, NewBlockPacket, 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/EthereumHost.h b/libethereum/EthereumHost.h index 07ef92513..a4ccf5383 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -70,26 +70,22 @@ public: void reset(); DownloadMan const& downloadMan() const { return m_man; } - bool isSyncing() const { return m_grabbing == Grabbing::Chain; } + bool isSyncing() const { return !!m_syncer; } + + bool isBanned(h512 _id) const { return !!m_banned.count(_id); } 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); - /// Session has finished getting the chain of hashes. - void noteHaveChain(EthereumPeer* _who); + /// 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(); - /// 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); + void maintainTransactions(h256 _currentBlock); + void maintainBlocks(h256 _currentBlock); /// Get a bunch of needed blocks. /// Removes them from our list of needed blocks. @@ -100,13 +96,12 @@ 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(); } - void readyForSync(); - void updateGrabbing(Grabbing _g); + 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. @@ -114,16 +109,14 @@ private: u256 m_networkId; - 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; + 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 b45abfbff..5817aa434 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -27,24 +27,34 @@ #include #include "BlockChain.h" #include "EthereumHost.h" +#include "TransactionQueue.h" +#include "BlockQueue.h" using namespace std; 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): - Capability(_s, _h), +EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i): + Capability(_s, _h, _i), m_sub(host()->m_man) { - setGrabbing(Grabbing::State); - sendStatus(); + transition(Asking::State); } EthereumPeer::~EthereumPeer() { - giveUpOnFetch(); + abortSync(); +} + +void EthereumPeer::abortSync() +{ + if (isSyncing()) + transition(Asking::Nothing, true); } EthereumHost* EthereumPeer::host() const @@ -52,36 +62,207 @@ EthereumHost* EthereumPeer::host() const return static_cast(Capability::hostCapability()); } -void EthereumPeer::sendStatus() +/* + * Possible asking/syncing states for two peers: + */ + +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); - s.appendList(6) << StatusPacket - << host()->protocolVersion() - << host()->networkId() - << host()->m_chain.details().totalDifficulty - << host()->m_chain.currentHash() - << host()->m_chain.genesisHash(); - sealAndSend(s); + if (_a == Asking::State) + { + if (m_asking == Asking::Nothing) + { + setAsking(Asking::State, false); + prep(s, StatusPacket, 5) + << 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) + { + if (isSyncing()) + clogS(NetWarn) << "Bad state: not asking for Hashes, yet syncing!"; + + m_syncingLatestHash = m_latestHash; + m_syncingTotalDifficulty = m_totalDifficulty; + resetNeedsSyncing(); + + setAsking(_a, true); + prep(s, GetBlockHashesPacket, 2) << m_syncingLatestHash << c_maxHashesAsk; + m_syncingNeededBlocks = h256s(1, m_syncingLatestHash); + sealAndSend(s); + return; + } + else if (m_asking == Asking::Hashes) + { + if (!isSyncing()) + clogS(NetWarn) << "Bad state: asking for Hashes yet not syncing!"; + + setAsking(_a, true); + prep(s, GetBlockHashesPacket, 2) << m_syncingNeededBlocks.back() << c_maxHashesAsk; + sealAndSend(s); + return; + } + } + else if (_a == Asking::Blocks) + { + 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() << "]"; + + host()->m_man.resetToChain(m_syncingNeededBlocks); + host()->m_latestBlockSent = m_syncingLatestHash; + + } + else + { + clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; + m_syncingLatestHash = h256(); + 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); // will kick off other peers to help if available. + auto blocks = m_sub.nextFetch(c_maxBlocksAsk); + if (blocks.size()) + { + prep(s, GetBlocksPacket, blocks.size()); + 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 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()) + host()->noteDoneBlocks(this, _force); + + // 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..."; + + setAsking(Asking::Nothing, false); + } + else if (m_asking == Asking::State) + { + setAsking(Asking::Nothing, false); + // 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:" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : ""); } -void EthereumPeer::startInitialSync() +void EthereumPeer::setAsking(Asking _a, bool _isSyncing) { - // Grab transactions off them. + bool changedAsking = (m_asking != _a); + m_asking = _a; + + if (_isSyncing != (host()->m_syncer == this) || (_isSyncing && changedAsking)) + host()->changeSyncer(_isSyncing ? this : nullptr); + + if (!_isSyncing) { - RLPStream s; - prep(s).appendList(1); - s << GetTransactionsPacket; - sealAndSend(s); + m_syncingLatestHash = h256(); + m_syncingTotalDifficulty = 0; + m_syncingNeededBlocks.clear(); } - host()->noteHavePeerState(this); + 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; + + if (m_latestHash) + host()->noteNeedsSyncing(this); + + session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : "")); +} + +bool EthereumPeer::isSyncing() const +{ + return host()->m_syncer == this; +} + +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::tryGrabbingHashChain() +void EthereumPeer::attemptSync() { + 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 (!needsSyncing()) { clogS(NetAllDetail) << "Already synced with this peer."; return; @@ -95,47 +276,26 @@ void EthereumPeer::tryGrabbingHashChain() if (td >= m_totalDifficulty) { clogS(NetAllDetail) << "No. Our chain is better."; - setGrabbing(Grabbing::Nothing); - return; // All good - we have the better chain. + resetNeedsSyncing(); + transition(Asking::Nothing); } - - // Our chain isn't better - grab theirs. + else { clogS(NetAllDetail) << "Yes. Their chain is better."; - - host()->updateGrabbing(Grabbing::Hashes); - setGrabbing(Grabbing::Hashes); - 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::giveUpOnFetch() +bool EthereumPeer::interpret(unsigned _id, RLP const& _r) { - 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) - { - host()->noteDoneBlocks(this); - setGrabbing(Grabbing::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()) + switch (_id) { case StatusPacket: { 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(); @@ -148,10 +308,18 @@ bool EthereumPeer::interpret(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."); else - startInitialSync(); + { + // Grab transactions off them. + RLPStream s; + prep(s, GetTransactionsPacket); + sealAndSend(s); + transition(Asking::Nothing); + } break; } case GetTransactionsPacket: @@ -163,13 +331,14 @@ bool EthereumPeer::interpret(RLP const& _r) { clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; addRating(_r.itemCount() - 1); - lock_guard l(host()->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_knownTransactions.insert(sha3(_r[i].data())); + 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; } @@ -182,7 +351,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; @@ -193,14 +362,14 @@ 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; } if (_r.itemCount() == 1) { - host()->noteHaveChain(this); + transition(Asking::Blocks); return true; } for (unsigned i = 1; i < _r.itemCount(); ++i) @@ -208,17 +377,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: @@ -237,57 +403,107 @@ 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: { 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_grabbing == Grabbing::Chain || m_grabbing == Grabbing::ChainHelper) - giveUpOnFetch(); + // Got to this peer's latest block - just give up. + transition(Asking::Nothing); break; } - unsigned used = 0; + unsigned success = 0; + 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 (host()->noteBlock(h, _r[i].data())) - used++; - Guard l(x_knownBlocks); - m_knownBlocks.insert(h); - } - addRating(used); - unsigned knownParents = 0; - unsigned unknownParents = 0; - if (g_logVerbosity >= NetMessageSummary::verbosity) - { - unsigned ic = _r.itemCount(); - for (unsigned i = 1; i < ic; ++i) + if (m_sub.noteBlock(h)) { - 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 + addRating(10); + switch (host()->m_bq.import(_r[i].data(), host()->m_chain)) { - knownParents++; - clogS(NetAllDetail) << "Known parent" << bi.parentHash.abridged() << "of block" << h.abridged(); + 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; } } + else + { + addRating(0); // -1? + repeated++; + } + } + + 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); + break; + } + case NewBlockPacket: + { + auto h = BlockInfo::headerHash(_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: + addRating(100); + break; + case ImportResult::FutureTime: + //TODO: Rating dependent on how far in future it is. + 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 << knownParents << "known parents," << unknownParents << "unknown," << used << "used."; - if (m_grabbing == Grabbing::Chain || m_grabbing == Grabbing::ChainHelper) - continueGettingChain(); break; } default: @@ -295,42 +511,3 @@ bool EthereumPeer::interpret(RLP const& _r) } return true; } - -void EthereumPeer::ensureGettingChain() -{ - if (m_grabbing == Grabbing::ChainHelper) - 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); - - 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) - 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 - giveUpOnFetch(); -} - -void EthereumPeer::setGrabbing(Grabbing _g) -{ - 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" : "?"); -} diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index 052af3c7e..71bfc544f 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -42,54 +42,98 @@ namespace eth /** * @brief The EthereumPeer class * @todo Document fully. + * @todo make state transitions thread-safe. */ class EthereumPeer: public p2p::Capability { friend class EthereumHost; public: - EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h); + /// Basic constructor. + EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h, unsigned _i); + + /// Basic destructor. virtual ~EthereumPeer(); + /// What is our name? static std::string name() { return "eth"; } + /// 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: - virtual bool interpret(RLP const& _r); + using p2p::Capability::sealAndSend; - void sendStatus(); - void startInitialSync(); + /// Interpret an incoming message. + virtual bool interpret(unsigned _id, RLP const& _r); - void tryGrabbingHashChain(); + /// 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 ensureGettingChain(); - /// Ensure that we are waiting for a bunch of blocks from our peer. - void continueGettingChain(); + /// 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 giveUpOnFetch(); + /// Abort the sync operation. + void abortSync(); + /// Clear all known transactions. void clearKnownTransactions() { std::lock_guard l(x_knownTransactions); m_knownTransactions.clear(); } - void setGrabbing(Grabbing _g); - + + /// Update our asking state. + void setAsking(Asking _g, bool _isSyncing); + + /// 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; } + + /// 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; - Grabbing m_grabbing; + /// What, if anything, we last asked the other peer for. + Asking m_asking = Asking::Nothing; - h256 m_latestHash; ///< Peer's latest block's hash. + /// 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 that we know about or default null value if no need to sync. u256 m_totalDifficulty; ///< Peer's latest block's total difficulty. - h256s m_neededBlocks; ///< The blocks that we should download from this peer. + /// 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_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; + + /// Have we received a GetTransactions packet that we haven't yet answered? 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. - DownloadSub m_sub; }; } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 84fba15d6..61d179ed8 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -20,6 +20,7 @@ */ #include +#include #include #include "Executive.h" #include "State.h" @@ -217,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/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/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 a2c422e9b..047b9dcd5 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -40,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()) @@ -1060,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; @@ -1076,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); @@ -1089,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(); } @@ -1124,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; @@ -1138,8 +1189,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()); @@ -1158,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 19cc944a4..f78cb82cb 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -21,6 +21,8 @@ #pragma once +#include +#include #include #include #include @@ -40,8 +42,6 @@ struct Post u256 gas; }; -using PostList = std::list; - using OnOpFunc = std::function; /** @@ -86,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. @@ -105,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/libevm/VM.h b/libevm/VM.h index 04804ffd5..399df72dd 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; }; @@ -171,18 +172,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); @@ -572,12 +567,18 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::JUMP: require(1); 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); 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; @@ -590,6 +591,8 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::GAS: m_stack.push_back(m_gas); break; + case Instruction::JUMPDEST: + break; case Instruction::CREATE: { require(3); @@ -611,7 +614,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 +665,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..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; @@ -82,6 +83,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 }, @@ -148,9 +150,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 } }; @@ -209,6 +210,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 } }, @@ -275,9 +277,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..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 @@ -163,8 +164,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 }; diff --git a/liblll/Assembly.cpp b/liblll/Assembly.cpp index 9b6dee947..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() << "]"; @@ -172,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; @@ -414,6 +414,7 @@ bytes Assembly::assemble() const } case Tag: tagPos[(unsigned)i.m_data] = ret.size(); + ret.push_back((byte)Instruction::JUMPDEST); break; default:; } 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/Capability.cpp b/libp2p/Capability.cpp index 5ca674120..2dbfd76a8 100644 --- a/libp2p/Capability.cpp +++ b/libp2p/Capability.cpp @@ -21,20 +21,31 @@ #include "Capability.h" +#include #include "Session.h" 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) +{ + 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 bffd38c79..bd2ab7a95 100644 --- a/libp2p/Capability.h +++ b/libp2p/Capability.h @@ -34,21 +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); @@ -59,6 +61,7 @@ private: Session* m_session; HostCapabilityFace* m_host; bool m_enabled = true; + unsigned m_idOffset; }; } 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 a5f7e5d84..895b76404 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -24,6 +24,8 @@ #pragma once #include +#include +#include #include #include #include @@ -42,7 +44,7 @@ class RLPStream; namespace p2p { -bool isPrivateAddress(bi::address _addressToCheck); +bool isPrivateAddress(bi::address const& _addressToCheck); class UPnP; class Capability; @@ -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..05fa81f4c 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -36,8 +36,10 @@ #include #include #include +#include #include #include "Session.h" +#include "Common.h" #include "Capability.h" #include "UPnP.h" using namespace std; @@ -142,18 +144,22 @@ void Host::stop() unsigned Host::protocolVersion() const { - return 0; + return 1; } -void Host::registerPeer(std::shared_ptr _s, vector const& _caps) +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() @@ -457,7 +463,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/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..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() {} @@ -47,7 +48,10 @@ public: protected: virtual std::string name() const = 0; - virtual Capability* newPeerCapability(Session* _s) = 0; + virtual u256 version() const = 0; + CapDesc capDesc() const { return std::make_pair(name(), version()); } + virtual unsigned messageCount() const = 0; + virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset) = 0; virtual void onStarting() {} virtual void onStopping() {} @@ -66,10 +70,14 @@ public: virtual ~HostCapability() {} 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 Capability* newPeerCapability(Session* _s) { return new PeerCap(_s, this); } + virtual u256 version() const { return PeerCap::version(); } + 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 524e1e657..d6a45bfc7 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include "Host.h" #include "Capability.h" @@ -30,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): @@ -40,7 +44,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() @@ -72,22 +76,28 @@ 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: { 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(); - 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)) { // 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; } @@ -102,7 +112,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); @@ -130,7 +140,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,12 +152,14 @@ 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; - 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; @@ -156,7 +168,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); @@ -185,25 +206,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) @@ -240,7 +269,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 +282,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 +317,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; } @@ -323,8 +352,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 +364,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() @@ -363,7 +390,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 +407,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 +423,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; } diff --git a/libp2p/Session.h b/libp2p/Session.h index 934e548d4..1a0ae8730 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "Common.h" @@ -62,8 +63,9 @@ 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, PacketType _t, unsigned _args = 0); static RLPStream& prep(RLPStream& _s); void sealAndSend(RLPStream& _s); void sendDestroy(bytes& _msg); @@ -108,7 +110,8 @@ private: unsigned m_rating; - std::map> m_capabilities; + 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/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/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)) }"); \ } 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/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 e92e2cac3..a8e72a40e 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -28,13 +28,16 @@ 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): 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 +49,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 +98,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 b3fe2701b..3da246562 100644 --- a/libwhisper/WhisperPeer.h +++ b/libwhisper/WhisperPeer.h @@ -49,15 +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(); 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..a90b122e2 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -21,6 +21,7 @@ */ #include "vm.h" +#include #define FILL_TESTS @@ -44,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(); @@ -82,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)) @@ -95,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) 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; 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 +