Browse Source

Merge branch 'develop' into develop-evmcc

cl-refactor
Paweł Bylica 10 years ago
parent
commit
aa716d4600
  1. 1
      exp/main.cpp
  2. 2
      libdevcore/Common.cpp
  3. 2
      libdevcore/CommonIO.cpp
  4. 12
      libdevcore/CommonIO.h
  5. 1
      libdevcore/Exceptions.h
  6. 2
      libdevcore/RLP.h
  7. 16
      libdevcrypto/SHA3.cpp
  8. 6
      libdevcrypto/SHA3.h
  9. 2
      libethcore/CommonEth.cpp
  10. 35
      libethcore/Exceptions.cpp
  11. 35
      libethcore/Exceptions.h
  12. 1
      libethcore/_libethcore.cpp
  13. 13
      libethereum/BlockQueue.cpp
  14. 17
      libethereum/BlockQueue.h
  15. 23
      libethereum/Client.cpp
  16. 68
      libethereum/Client.h
  17. 18
      libethereum/CommonNet.h
  18. 4
      libethereum/DownloadMan.cpp
  19. 4
      libethereum/DownloadMan.h
  20. 220
      libethereum/EthereumHost.cpp
  21. 35
      libethereum/EthereumHost.h
  22. 455
      libethereum/EthereumPeer.cpp
  23. 80
      libethereum/EthereumPeer.h
  24. 6
      libethereum/Executive.cpp
  25. 18
      libethereum/ExtVM.h
  26. 9
      libethereum/Interface.h
  27. 8
      libethereum/Miner.cpp
  28. 2
      libethereum/Miner.h
  29. 68
      libethereum/State.cpp
  30. 12
      libethereum/State.h
  31. 11
      libevm/ExtVMFace.h
  32. 42
      libevm/VM.h
  33. 9
      libevmface/Instruction.cpp
  34. 4
      libevmface/Instruction.h
  35. 5
      liblll/Assembly.cpp
  36. 1
      liblll/CodeFragment.cpp
  37. 17
      libp2p/Capability.cpp
  38. 15
      libp2p/Capability.h
  39. 13
      libp2p/Common.cpp
  40. 10
      libp2p/Common.h
  41. 14
      libp2p/Host.cpp
  42. 13
      libp2p/Host.h
  43. 2
      libp2p/HostCapability.cpp
  44. 12
      libp2p/HostCapability.h
  45. 75
      libp2p/Session.cpp
  46. 7
      libp2p/Session.h
  47. 1
      libp2p/UPnP.cpp
  48. 87
      libqethereum/QEthereum.cpp
  49. 20
      libqethereum/QEthereum.h
  50. 5
      libwhisper/Common.h
  51. 3
      libwhisper/Interface.cpp
  52. 1
      libwhisper/Message.cpp
  53. 3
      libwhisper/WhisperHost.cpp
  54. 17
      libwhisper/WhisperPeer.cpp
  55. 6
      libwhisper/WhisperPeer.h
  56. 3
      test/hexPrefix.cpp
  57. 1
      test/rlp.cpp
  58. 1
      test/trie.cpp
  59. 7
      test/vm.cpp
  60. 7
      third/MainWin.cpp
  61. 6
      windows/LibEthereum.vcxproj
  62. 3
      windows/LibEthereum.vcxproj.filters

1
exp/main.cpp

@ -24,6 +24,7 @@
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libdevcore/CommonIO.h>
#include <libp2p/All.h> #include <libp2p/All.h>
#include <libdevcore/RangeMask.h> #include <libdevcore/RangeMask.h>
#include <libethereum/DownloadMan.h> #include <libethereum/DownloadMan.h>

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using namespace dev;
namespace dev namespace dev
{ {
char const* Version = "0.6.11"; char const* Version = "0.7.1";
} }

2
libdevcore/CommonIO.cpp

@ -19,7 +19,7 @@
* @date 2014 * @date 2014
*/ */
#include "Common.h" #include "CommonIO.h"
#include <fstream> #include <fstream>
#include "Exceptions.h" #include "Exceptions.h"

12
libdevcore/CommonIO.h

@ -55,6 +55,18 @@ std::string memDump(bytes const& _b, unsigned _w = 8, bool _html = false);
template <class S, class T> struct StreamOut { static S& bypass(S& _out, T const& _t) { _out << _t; return _out; } }; template <class S, class T> struct StreamOut { static S& bypass(S& _out, T const& _t) { _out << _t; return _out; } };
template <class S> struct StreamOut<S, uint8_t> { static S& bypass(S& _out, uint8_t const& _t) { _out << (int)_t; return _out; } }; template <class S> struct StreamOut<S, uint8_t> { static S& bypass(S& _out, uint8_t const& _t) { _out << (int)_t; return _out; } };
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::vector<T> const& _e);
template <class T, unsigned Z> inline std::ostream& operator<<(std::ostream& _out, std::array<T, Z> const& _e);
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::pair<T, U> const& _e);
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::list<T> const& _e);
template <class T1, class T2, class T3> inline std::ostream& operator<<(std::ostream& _out, std::tuple<T1, T2, T3> const& _e);
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::map<T, U> const& _e);
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::unordered_map<T, U> const& _e);
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::set<T, U> const& _e);
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::unordered_set<T, U> const& _e);
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::multimap<T, U> const& _e);
template <class _S, class _T> _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p);
template <class S, class T> template <class S, class T>
inline S& streamout(S& _out, std::vector<T> const& _e) inline S& streamout(S& _out, std::vector<T> const& _e)
{ {

1
libdevcore/Exceptions.h

@ -25,7 +25,6 @@
#include <boost/exception/all.hpp> #include <boost/exception/all.hpp>
#include <boost/throw_exception.hpp> #include <boost/throw_exception.hpp>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include "CommonIO.h"
#include "CommonData.h" #include "CommonData.h"
#include "FixedHash.h" #include "FixedHash.h"

2
libdevcore/RLP.h

@ -289,7 +289,7 @@ public:
private: private:
/// Disable construction from rvalue /// Disable construction from rvalue
explicit RLP(bytes const&& _d) {} explicit RLP(bytes const&&) {}
/// Single-byte data payload. /// Single-byte data payload.
bool isSingleByte() const { return !isNull() && m_data[0] < c_rlpDataImmLenStart; } bool isSingleByte() const { return !isNull() && m_data[0] < c_rlpDataImmLenStart; }

16
libdevcrypto/SHA3.cpp

@ -58,6 +58,22 @@ void sha3(bytesConstRef _input, bytesRef _output)
ctx.Final(_output.data()); 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 sha3Bytes(bytesConstRef _input)
{ {
bytes ret(32); bytes ret(32);

6
libdevcrypto/SHA3.h

@ -60,7 +60,13 @@ inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input))
extern h256 EmptySHA3; extern h256 EmptySHA3;
// Other crypto convenience routines
bytes aesDecrypt(bytesConstRef _cipher, std::string const& _password, unsigned _rounds = 2000, bytesConstRef _salt = bytesConstRef()); 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);
} }
} }

2
libethcore/CommonEth.cpp

@ -34,7 +34,7 @@ namespace dev
namespace eth namespace eth
{ {
const unsigned c_protocolVersion = 33; const unsigned c_protocolVersion = 34;
const unsigned c_databaseVersion = 2; const unsigned c_databaseVersion = 2;
static const vector<pair<u256, string>> g_units = static const vector<pair<u256, string>> g_units =

35
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 <http://www.gnu.org/licenses/>.
*/
/** @file Exceptions.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Exceptions.h"
#include <libdevcore/CommonIO.h>
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(); }

35
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 <http://www.gnu.org/licenses/>.
*/
/** @file Exceptions.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once #pragma once
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
@ -23,23 +44,23 @@ struct FeeTooSmall: virtual dev::Exception {};
struct TooMuchGasUsed: virtual dev::Exception {}; struct TooMuchGasUsed: virtual dev::Exception {};
struct ExtraDataTooBig: virtual dev::Exception {}; struct ExtraDataTooBig: virtual dev::Exception {};
struct InvalidSignature: 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 InvalidUnclesHash: virtual dev::Exception {};
struct InvalidUncle: virtual dev::Exception {}; struct InvalidUncle: virtual dev::Exception {};
struct UncleTooOld: 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 DuplicateUncleNonce: virtual dev::Exception {};
struct InvalidStateRoot: 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 InvalidTransaction: virtual dev::Exception {};
struct InvalidDifficulty: 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 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 { return ("Invalid minimum gas price (provided: " + toString(provided) + " limit:" + toString(limit) + ")").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; };
struct InvalidTransactionGasUsed: virtual dev::Exception {}; struct InvalidTransactionGasUsed: virtual dev::Exception {};
struct InvalidTransactionStateRoot: virtual dev::Exception {}; struct InvalidTransactionStateRoot: virtual dev::Exception {};
struct InvalidTimestamp: 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 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 { return ("Invalid nonce (h: " + toString(h) + " n:" + toString(n) + " d:" + toString(d) + ")").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; };
struct InvalidParentHash: virtual dev::Exception {}; struct InvalidParentHash: virtual dev::Exception {};
struct InvalidNumber: virtual dev::Exception {}; struct InvalidNumber: virtual dev::Exception {};
struct InvalidContractAddress: virtual dev::Exception {}; struct InvalidContractAddress: virtual dev::Exception {};

1
libethcore/_libethcore.cpp

@ -3,4 +3,5 @@
#include "BlockInfo.cpp" #include "BlockInfo.cpp"
#include "CommonEth.cpp" #include "CommonEth.cpp"
#include "Dagger.cpp" #include "Dagger.cpp"
#include "Exceptions.cpp"
#endif #endif

13
libethereum/BlockQueue.cpp

@ -29,7 +29,7 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; 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. // Check if we already know this block.
h256 h = BlockInfo::headerHash(_block); h256 h = BlockInfo::headerHash(_block);
@ -42,7 +42,7 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
{ {
// Already know about this one. // Already know about this one.
cblockq << "Already known."; cblockq << "Already known.";
return false; return ImportResult::AlreadyKnown;
} }
// VERIFY: populates from the block and checks the block is internally coherent. // 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); cwarn << "Ignoring malformed block: " << diagnostic_information(_e);
return false; return false;
return ImportResult::Malformed;
} }
#endif #endif
@ -67,7 +68,7 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
if (_bc.details(h)) if (_bc.details(h))
{ {
cblockq << "Already known in chain."; cblockq << "Already known in chain.";
return false; return ImportResult::AlreadyInChain;
} }
UpgradeGuard ul(l); 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())); m_future.insert(make_pair((unsigned)bi.timestamp, _block.toBytes()));
cblockq << "OK - queued for future."; cblockq << "OK - queued for future.";
return ImportResult::FutureTime;
} }
else else
{ {
@ -87,6 +89,8 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
cblockq << "OK - queued as unknown parent:" << bi.parentHash.abridged(); cblockq << "OK - queued as unknown parent:" << bi.parentHash.abridged();
m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes())));
m_unknownSet.insert(h); m_unknownSet.insert(h);
return ImportResult::UnknownParent;
} }
else else
{ {
@ -96,10 +100,9 @@ bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
m_readySet.insert(h); m_readySet.insert(h);
noteReadyWithoutWriteGuard(h); noteReadyWithoutWriteGuard(h);
return ImportResult::Success;
} }
} }
return true;
} }
void BlockQueue::tick(BlockChain const& _bc) void BlockQueue::tick(BlockChain const& _bc)

17
libethereum/BlockQueue.h

@ -37,6 +37,16 @@ class BlockChain;
struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; }; struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; };
#define cblockq dev::LogOutputStream<dev::eth::BlockQueueChannel, true>() #define cblockq dev::LogOutputStream<dev::eth::BlockQueueChannel, true>()
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. * @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). * Sorts them ready for blockchain insertion (with the BlockChain::sync() method).
@ -46,7 +56,7 @@ class BlockQueue
{ {
public: public:
/// Import a block into the queue. /// 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. /// 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); void tick(BlockChain const& _bc);
@ -67,6 +77,9 @@ public:
/// Clear everything. /// 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(); } 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: private:
void noteReadyWithoutWriteGuard(h256 _b); void noteReadyWithoutWriteGuard(h256 _b);
void notePresentWithoutWriteGuard(bytesConstRef _block); void notePresentWithoutWriteGuard(bytesConstRef _block);
@ -77,7 +90,7 @@ private:
std::vector<bytes> m_ready; ///< List of blocks, in correct order, ready for chain-import. std::vector<bytes> m_ready; ///< List of blocks, in correct order, ready for chain-import.
std::set<h256> m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain. std::set<h256> m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain.
std::multimap<h256, std::pair<h256, bytes>> 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<h256, std::pair<h256, bytes>> 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<unsigned, bytes> m_future; ///< Set of blocks that are not yet valid. std::multimap<unsigned, bytes> m_future; ///< Set of blocks that are not yet valid.
}; };
} }

23
libethereum/Client.cpp

@ -232,12 +232,9 @@ void Client::appendFromNewBlock(h256 _block, h256Set& o_changed) const
void Client::setForceMining(bool _enable) void Client::setForceMining(bool _enable)
{ {
m_forceMining = _enable; m_forceMining = _enable;
if (!m_host.lock()) ReadGuard l(x_miners);
{ for (auto& m: m_miners)
ReadGuard l(x_miners); m.noteStateChange();
for (auto& m: m_miners)
m.noteStateChange();
}
} }
void Client::setMiningThreads(unsigned _threads) void Client::setMiningThreads(unsigned _threads)
@ -550,6 +547,20 @@ bytes Client::codeAt(Address _a, int _block) const
return asOf(_block).code(_a); 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 Client::messages(MessageFilter const& _f) const
{ {
PastMessages ret; PastMessages ret;

68
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); explicit Client(p2p::Host* _host, std::string const& _dbPath = std::string(), bool _forceClean = false, u256 _networkId = 0);
/// Destructor. /// Destructor.
~Client(); virtual ~Client();
/// Submits the given message-call transaction. /// 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. /// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through). /// @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. /// 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. /// Blocks until all pending transactions have been processed.
void flushTransactions(); virtual void flushTransactions();
/// Makes the given call. Nothing is recorded into the state. /// 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 // Informational stuff
@ -147,20 +147,20 @@ public:
using Interface::codeAt; using Interface::codeAt;
using Interface::storageAt; using Interface::storageAt;
u256 balanceAt(Address _a, int _block) const; virtual u256 balanceAt(Address _a, int _block) const;
u256 countAt(Address _a, int _block) const; virtual u256 countAt(Address _a, int _block) const;
u256 stateAt(Address _a, u256 _l, int _block) const; virtual u256 stateAt(Address _a, u256 _l, int _block) const;
bytes codeAt(Address _a, int _block) const; virtual bytes codeAt(Address _a, int _block) const;
std::map<u256, u256> storageAt(Address _a, int _block) const; virtual std::map<u256, u256> storageAt(Address _a, int _block) const;
unsigned installWatch(MessageFilter const& _filter); virtual unsigned installWatch(MessageFilter const& _filter);
unsigned installWatch(h256 _filterId); virtual unsigned installWatch(h256 _filterId);
void uninstallWatch(unsigned _watchId); virtual void uninstallWatch(unsigned _watchId);
bool peekWatch(unsigned _watchId) const { std::lock_guard<std::mutex> l(m_filterLock); try { return m_watches.at(_watchId).changes != 0; } catch (...) { return false; } } virtual bool peekWatch(unsigned _watchId) const { std::lock_guard<std::mutex> l(m_filterLock); try { return m_watches.at(_watchId).changes != 0; } catch (...) { return false; } }
bool checkWatch(unsigned _watchId) { std::lock_guard<std::mutex> l(m_filterLock); bool ret = false; try { ret = m_watches.at(_watchId).changes != 0; m_watches.at(_watchId).changes = 0; } catch (...) {} return ret; } virtual bool checkWatch(unsigned _watchId) { std::lock_guard<std::mutex> 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<std::mutex> l(m_filterLock); return messages(m_filters.at(m_watches.at(_watchId).id).filter); } catch (...) { return PastMessages(); } } virtual PastMessages messages(unsigned _watchId) const { try { std::lock_guard<std::mutex> 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(MessageFilter const& _filter) const;
// [EXTRA API]: // [EXTRA API]:
@ -169,19 +169,25 @@ public:
/// Get a map containing each of the pending transactions. /// Get a map containing each of the pending transactions.
/// @TODO: Remove in favour of 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. /// Differences between transactions.
using Interface::diff; using Interface::diff;
StateDiff diff(unsigned _txi, h256 _block) const; virtual StateDiff diff(unsigned _txi, h256 _block) const;
StateDiff diff(unsigned _txi, int _block) const; virtual StateDiff diff(unsigned _txi, int _block) const;
/// Get a list of all active addresses. /// Get a list of all active addresses.
using Interface::addresses; using Interface::addresses;
std::vector<Address> addresses(int _block) const; virtual std::vector<Address> addresses(int _block) const;
/// Get the remaining gas limit in this block. /// 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] // [PRIVATE API - only relevant for base clients, not available in general]
@ -210,23 +216,23 @@ public:
void setTurboMining(bool _enable = true) { m_turboMining = _enable; } void setTurboMining(bool _enable = true) { m_turboMining = _enable; }
/// Set the coinbase address. /// 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. /// 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). /// 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. /// 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. /// Start mining.
/// NOT thread-safe - call it & stopMining only from a single thread /// 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. /// Stop mining.
/// NOT thread-safe /// 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? /// 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. /// Check the progress of the mining.
MineProgress miningProgress() const; virtual MineProgress miningProgress() const;
/// Get and clear the mining history. /// Get and clear the mining history.
std::list<MineInfo> miningHistory(); std::list<MineInfo> miningHistory();

18
libethereum/CommonNet.h

@ -50,25 +50,33 @@ class TransactionQueue;
class EthereumHost; class EthereumHost;
class EthereumPeer; class EthereumPeer;
enum EthereumPacket enum
{ {
StatusPacket = 0x10, StatusPacket = 0,
GetTransactionsPacket, GetTransactionsPacket,
TransactionsPacket, TransactionsPacket,
GetBlockHashesPacket, GetBlockHashesPacket,
BlockHashesPacket, BlockHashesPacket,
GetBlocksPacket, GetBlocksPacket,
BlocksPacket, BlocksPacket,
NewBlockPacket,
PacketCount
}; };
enum class Grabbing enum class Asking
{ {
State, State,
Hashes, Hashes,
Chain, Blocks,
ChainHelper,
Nothing Nothing
}; };
enum class Syncing
{
Waiting,
Executing,
Done
};
} }
} }

4
libethereum/DownloadMan.cpp

@ -66,10 +66,12 @@ h256Set DownloadSub::nextFetch(unsigned _n)
return m_remaining; return m_remaining;
} }
void DownloadSub::noteBlock(h256 _hash) bool DownloadSub::noteBlock(h256 _hash)
{ {
Guard l(m_fetch); Guard l(m_fetch);
if (m_man && m_indices.count(_hash)) if (m_man && m_indices.count(_hash))
m_man->m_blocksGot += m_indices[_hash]; m_man->m_blocksGot += m_indices[_hash];
bool ret = !!m_remaining.count(_hash);
m_remaining.erase(_hash); m_remaining.erase(_hash);
return ret;
} }

4
libethereum/DownloadMan.h

@ -48,8 +48,8 @@ public:
/// Finished last fetch - grab the next bunch of block hashes to download. /// Finished last fetch - grab the next bunch of block hashes to download.
h256Set nextFetch(unsigned _n); h256Set nextFetch(unsigned _n);
/// Note that we've received a particular block. /// Note that we've received a particular block. @returns true if we had asked for it but haven't received it yet.
void noteBlock(h256 _hash); bool noteBlock(h256 _hash);
/// Nothing doing here. /// Nothing doing here.
void doneFetch() { resetFetch(); } void doneFetch() { resetFetch(); }

220
libethereum/EthereumHost.cpp

@ -52,10 +52,10 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
EthereumHost::~EthereumHost() EthereumHost::~EthereumHost()
{ {
for (auto const& i: peers()) for (auto const& i: peers())
i->cap<EthereumPeer>()->giveUpOnFetch(); i->cap<EthereumPeer>()->abortSync();
} }
bool EthereumHost::ensureInitialised(TransactionQueue& _tq) bool EthereumHost::ensureInitialised()
{ {
if (!m_latestBlockSent) if (!m_latestBlockSent)
{ {
@ -63,155 +63,109 @@ bool EthereumHost::ensureInitialised(TransactionQueue& _tq)
m_latestBlockSent = m_chain.currentHash(); m_latestBlockSent = m_chain.currentHash();
clog(NetNote) << "Initialising: latest=" << m_latestBlockSent.abridged(); 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); m_transactionsSent.insert(i.first);
return true; return true;
} }
return false; 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 already downloading hash-chain, ignore.
if (m_grabbing != Grabbing::Nothing) if (isSyncing())
{ {
for (auto const& i: peers()) clog(NetAllDetail) << "Sync in progress: Just set to help out.";
if (i->cap<EthereumPeer>()->m_grabbing == m_grabbing || m_grabbing == Grabbing::State) if (m_syncer->m_asking == Asking::Blocks)
{ _who->transition(Asking::Blocks);
clog(NetAllDetail) << "Already downloading chain. Just set to help out.";
_who->ensureGettingChain();
return;
}
m_grabbing = Grabbing::Nothing;
} }
else
// otherwise check to see if we should be downloading... // otherwise check to see if we should be downloading...
_who->tryGrabbingHashChain(); _who->attemptSync();
}
void EthereumHost::updateGrabbing(Grabbing _g)
{
m_grabbing = _g;
if (_g == Grabbing::Nothing)
readyForSync();
else if (_g == Grabbing::Chain)
for (auto j: peers())
j->cap<EthereumPeer>()->ensureGettingChain();
} }
void EthereumHost::noteHaveChain(EthereumPeer* _from) void EthereumHost::changeSyncer(EthereumPeer* _syncer)
{ {
auto td = _from->m_totalDifficulty; m_syncer = _syncer;
if (isSyncing())
if (_from->m_neededBlocks.empty())
{ {
_from->setGrabbing(Grabbing::Nothing); if (_syncer->m_asking == Asking::Blocks)
updateGrabbing(Grabbing::Nothing); for (auto j: peers())
return; if (j->cap<EthereumPeer>().get() != _syncer && j->cap<EthereumPeer>()->m_asking == Asking::Nothing)
j->cap<EthereumPeer>()->transition(Asking::Blocks);
} }
else
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))
{ {
clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; // start grabbing next hash chain if there is one.
_from->setGrabbing(Grabbing::Nothing); for (auto j: peers())
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<EthereumPeer>()->tryGrabbingHashChain();
if (j->cap<EthereumPeer>()->m_grabbing == Grabbing::Hashes)
{ {
m_grabbing = Grabbing::Hashes; j->cap<EthereumPeer>()->attemptSync();
return; 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()) if (m_man.isComplete())
{ {
// Done our chain-get. // Done our chain-get.
clog(NetNote) << "Chain download complete."; 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(); m_man.reset();
} }
if (_who->m_grabbing == Grabbing::Chain) else if (_who->isSyncing())
{ {
// Done our chain-get. if (_clemency)
clog(NetNote) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished."; clog(NetNote) << "Chain download failed. Aborted while incomplete.";
// TODO: note that peer is BADBADBAD! else
updateGrabbing(Grabbing::Nothing); {
// 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(); m_man.reset();
} }
} }
bool EthereumHost::noteBlock(h256 _hash, bytesConstRef _data) void EthereumHost::reset()
{ {
if (!m_chain.details(_hash)) if (m_syncer)
{ m_syncer->abortSync();
lock_guard<recursive_mutex> l(m_incomingLock);
m_incomingBlocks.push_back(_data.toBytes()); m_man.resetToChain(h256s());
return true;
} m_latestBlockSent = h256();
return false; m_transactionsSent.clear();
} }
void EthereumHost::doWork() void EthereumHost::doWork()
{ {
bool netChange = ensureInitialised(m_tq); bool netChange = ensureInitialised();
auto h = m_chain.currentHash(); auto h = m_chain.currentHash();
maintainTransactions(m_tq, h); maintainTransactions(h);
maintainBlocks(m_bq, h); maintainBlocks(h);
// return netChange; // return netChange;
// TODO: Figure out what to do with netChange. // TODO: Figure out what to do with netChange.
(void)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); bool resendAll = (!isSyncing() && m_chain.isKnown(m_latestBlockSent) && _currentHash != m_latestBlockSent);
{
lock_guard<recursive_mutex> 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();
}
// Send any new transactions. // Send any new transactions.
for (auto const& p: peers()) for (auto const& p: peers())
{ if (auto ep = p->cap<EthereumPeer>())
auto ep = p->cap<EthereumPeer>();
if (ep)
{ {
bytes b; bytes b;
unsigned n = 0; 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) if ((!m_transactionsSent.count(i.first) && !ep->m_knownTransactions.count(i.first)) || ep->m_requireTransactions || resendAll)
{ {
b += i.second; b += i.second;
@ -220,75 +174,39 @@ void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash
} }
ep->clearKnownTransactions(); ep->clearKnownTransactions();
if (n) if (n || ep->m_requireTransactions)
{ {
RLPStream ts; RLPStream ts;
EthereumPeer::prep(ts); ep->prep(ts, TransactionsPacket, n).appendRaw(b, n);
ts.appendList(n + 1) << TransactionsPacket; ep->sealAndSend(ts);
ts.appendRaw(b, n).swapOut(b);
seal(b);
ep->send(&b);
} }
ep->m_requireTransactions = false; 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<recursive_mutex> 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 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; // TODO: clean up
EthereumPeer::prep(ts); h256s hs;
hs.push_back(_currentHash);
bytes bs; bytes bs;
unsigned c = 0; for (auto h: hs)
for (auto h: m_chain.treeRoute(m_latestBlockSent, _currentHash, nullptr, false, true))
{
bs += m_chain.block(h); bs += m_chain.block(h);
++c; clog(NetMessageSummary) << "Sending" << hs.size() << "new blocks (current is" << _currentHash << ", was" << m_latestBlockSent << ")";
}
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);
for (auto j: peers()) for (auto j: peers())
{ {
auto p = j->cap<EthereumPeer>(); auto p = j->cap<EthereumPeer>();
RLPStream ts;
p->prep(ts, NewBlockPacket, hs.size()).appendRaw(bs, hs.size());
Guard l(p->x_knownBlocks); Guard l(p->x_knownBlocks);
if (!p->m_knownBlocks.count(_currentHash)) if (!p->m_knownBlocks.count(_currentHash))
p->send(&b); p->sealAndSend(ts);
p->m_knownBlocks.clear(); p->m_knownBlocks.clear();
} }
m_latestBlockSent = _currentHash; m_latestBlockSent = _currentHash;

35
libethereum/EthereumHost.h

@ -70,26 +70,22 @@ public:
void reset(); void reset();
DownloadMan const& downloadMan() const { return m_man; } 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: private:
void noteHavePeerState(EthereumPeer* _who); /// Session is tell us that we may need (re-)syncing with the peer.
/// Session wants to pass us a block that we might not have. void noteNeedsSyncing(EthereumPeer* _who);
/// @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);
/// Called when the peer can no longer provide us with any needed blocks. /// 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. /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
void doWork(); void doWork();
/// Called by peer to add incoming transactions. void maintainTransactions(h256 _currentBlock);
void addIncomingTransaction(bytes const& _bytes) { std::lock_guard<std::recursive_mutex> l(m_incomingLock); m_incomingTransactions.push_back(_bytes); } void maintainBlocks(h256 _currentBlock);
void maintainTransactions(TransactionQueue& _tq, h256 _currentBlock);
void maintainBlocks(BlockQueue& _bq, h256 _currentBlock);
/// Get a bunch of needed blocks. /// Get a bunch of needed blocks.
/// Removes them from our list of needed blocks. /// Removes them from our list of needed blocks.
@ -100,13 +96,12 @@ private:
bool isInitialised() const { return m_latestBlockSent; } 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. /// 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 onStarting() { startWorking(); }
virtual void onStopping() { stopWorking(); } virtual void onStopping() { stopWorking(); }
void readyForSync(); void changeSyncer(EthereumPeer* _ignore);
void updateGrabbing(Grabbing _g);
BlockChain const& m_chain; BlockChain const& m_chain;
TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. 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; u256 m_networkId;
Grabbing m_grabbing = Grabbing::Nothing; // TODO: needs to be thread-safe & switch to just having a peer id. EthereumPeer* m_syncer = nullptr; // TODO: switch to weak_ptr
mutable std::recursive_mutex m_incomingLock;
std::vector<bytes> m_incomingTransactions;
std::vector<bytes> m_incomingBlocks;
DownloadMan m_man; DownloadMan m_man;
h256 m_latestBlockSent; h256 m_latestBlockSent;
h256Set m_transactionsSent; h256Set m_transactionsSent;
std::set<h512> m_banned;
}; };
} }

455
libethereum/EthereumPeer.cpp

@ -27,24 +27,34 @@
#include <libp2p/Session.h> #include <libp2p/Session.h>
#include "BlockChain.h" #include "BlockChain.h"
#include "EthereumHost.h" #include "EthereumHost.h"
#include "TransactionQueue.h"
#include "BlockQueue.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace p2p; using namespace p2p;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] " #define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h): EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i):
Capability(_s, _h), Capability(_s, _h, _i),
m_sub(host()->m_man) m_sub(host()->m_man)
{ {
setGrabbing(Grabbing::State); transition(Asking::State);
sendStatus();
} }
EthereumPeer::~EthereumPeer() EthereumPeer::~EthereumPeer()
{ {
giveUpOnFetch(); abortSync();
}
void EthereumPeer::abortSync()
{
if (isSyncing())
transition(Asking::Nothing, true);
} }
EthereumHost* EthereumPeer::host() const EthereumHost* EthereumPeer::host() const
@ -52,36 +62,207 @@ EthereumHost* EthereumPeer::host() const
return static_cast<EthereumHost*>(Capability::hostCapability()); return static_cast<EthereumHost*>(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; RLPStream s;
prep(s); if (_a == Asking::State)
s.appendList(6) << StatusPacket {
<< host()->protocolVersion() if (m_asking == Asking::Nothing)
<< host()->networkId() {
<< host()->m_chain.details().totalDifficulty setAsking(Asking::State, false);
<< host()->m_chain.currentHash() prep(s, StatusPacket, 5)
<< host()->m_chain.genesisHash(); << host()->protocolVersion()
sealAndSend(s); << 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; m_syncingLatestHash = h256();
prep(s).appendList(1); m_syncingTotalDifficulty = 0;
s << GetTransactionsPacket; m_syncingNeededBlocks.clear();
sealAndSend(s);
} }
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::tryGrabbingHashChain() 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::attemptSync()
{
if (m_asking != Asking::Nothing)
{
clogS(NetAllDetail) << "Can't synced with this peer - outstanding asks.";
return;
}
// if already done this, then ignore. // if already done this, then ignore.
if (m_grabbing != Grabbing::State) if (!needsSyncing())
{ {
clogS(NetAllDetail) << "Already synced with this peer."; clogS(NetAllDetail) << "Already synced with this peer.";
return; return;
@ -95,47 +276,26 @@ void EthereumPeer::tryGrabbingHashChain()
if (td >= m_totalDifficulty) if (td >= m_totalDifficulty)
{ {
clogS(NetAllDetail) << "No. Our chain is better."; clogS(NetAllDetail) << "No. Our chain is better.";
setGrabbing(Grabbing::Nothing); resetNeedsSyncing();
return; // All good - we have the better chain. transition(Asking::Nothing);
} }
else
// Our chain isn't better - grab theirs.
{ {
clogS(NetAllDetail) << "Yes. Their chain is better."; clogS(NetAllDetail) << "Yes. Their chain is better.";
transition(Asking::Hashes);
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);
} }
} }
void EthereumPeer::giveUpOnFetch() bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{ {
clogS(NetNote) << "Finishing fetch..."; switch (_id)
// 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<unsigned>())
{ {
case StatusPacket: case StatusPacket:
{ {
m_protocolVersion = _r[1].toInt<unsigned>(); m_protocolVersion = _r[1].toInt<unsigned>();
m_networkId = _r[2].toInt<u256>(); m_networkId = _r[2].toInt<u256>();
// a bit dirty as we're misusing these to communicate the values to transition, but harmless.
m_totalDifficulty = _r[3].toInt<u256>(); m_totalDifficulty = _r[3].toInt<u256>();
m_latestHash = _r[4].toHash<h256>(); m_latestHash = _r[4].toHash<h256>();
auto genesisHash = _r[5].toHash<h256>(); auto genesisHash = _r[5].toHash<h256>();
@ -148,10 +308,18 @@ bool EthereumPeer::interpret(RLP const& _r)
disable("Invalid protocol version."); disable("Invalid protocol version.");
else if (m_networkId != host()->networkId()) else if (m_networkId != host()->networkId())
disable("Invalid network identifier."); 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."); disable("Blacklisted client version.");
else if (host()->isBanned(session()->id()))
disable("Peer banned for previous bad behaviour.");
else else
startInitialSync(); {
// Grab transactions off them.
RLPStream s;
prep(s, GetTransactionsPacket);
sealAndSend(s);
transition(Asking::Nothing);
}
break; break;
} }
case GetTransactionsPacket: case GetTransactionsPacket:
@ -163,13 +331,14 @@ bool EthereumPeer::interpret(RLP const& _r)
{ {
clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)";
addRating(_r.itemCount() - 1); addRating(_r.itemCount() - 1);
lock_guard<recursive_mutex> l(host()->m_incomingLock); Guard l(x_knownTransactions);
for (unsigned i = 1; i < _r.itemCount(); ++i) for (unsigned i = 1; i < _r.itemCount(); ++i)
{ {
host()->addIncomingTransaction(_r[i].data().toBytes()); auto h = sha3(_r[i].data());
m_knownTransactions.insert(h);
lock_guard<mutex> l(x_knownTransactions); if (!host()->m_tq.import(_r[i].data()))
m_knownTransactions.insert(sha3(_r[i].data())); // if we already had the transaction, then don't bother sending it on.
host()->m_transactionsSent.insert(h);
} }
break; break;
} }
@ -182,7 +351,7 @@ bool EthereumPeer::interpret(RLP const& _r)
unsigned c = min<unsigned>(host()->m_chain.number(later), limit); unsigned c = min<unsigned>(host()->m_chain.number(later), limit);
RLPStream s; RLPStream s;
prep(s).appendList(1 + c).append(BlockHashesPacket); prep(s, BlockHashesPacket, c);
h256 p = host()->m_chain.details(later).parent; h256 p = host()->m_chain.details(later).parent;
for (unsigned i = 0; i < c && p; ++i, p = host()->m_chain.details(p).parent) for (unsigned i = 0; i < c && p; ++i, p = host()->m_chain.details(p).parent)
s << p; s << p;
@ -193,14 +362,14 @@ bool EthereumPeer::interpret(RLP const& _r)
{ {
clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreHashes"); 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."; cwarn << "Peer giving us hashes when we didn't ask for them.";
break; break;
} }
if (_r.itemCount() == 1) if (_r.itemCount() == 1)
{ {
host()->noteHaveChain(this); transition(Asking::Blocks);
return true; return true;
} }
for (unsigned i = 1; i < _r.itemCount(); ++i) for (unsigned i = 1; i < _r.itemCount(); ++i)
@ -208,17 +377,14 @@ bool EthereumPeer::interpret(RLP const& _r)
auto h = _r[i].toHash<h256>(); auto h = _r[i].toHash<h256>();
if (host()->m_chain.isKnown(h)) if (host()->m_chain.isKnown(h))
{ {
host()->noteHaveChain(this); transition(Asking::Blocks);
return true; return true;
} }
else else
m_neededBlocks.push_back(h); m_syncingNeededBlocks.push_back(h);
} }
// run through - ask for more. // run through - ask for more.
RLPStream s; transition(Asking::Hashes);
prep(s).appendList(3);
s << GetBlockHashesPacket << m_neededBlocks.back() << c_maxHashesAsk;
sealAndSend(s);
break; break;
} }
case GetBlocksPacket: case GetBlocksPacket:
@ -237,57 +403,107 @@ bool EthereumPeer::interpret(RLP const& _r)
} }
} }
RLPStream s; RLPStream s;
sealAndSend(prep(s).appendList(n + 1).append(BlocksPacket).appendRaw(rlp, n)); prep(s, BlocksPacket, n).appendRaw(rlp, n);
sealAndSend(s);
break; break;
} }
case BlocksPacket: case BlocksPacket:
{ {
clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); 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) if (_r.itemCount() == 1)
{ {
// Couldn't get any from last batch - probably got to this peer's latest block - just give up. // Got to this peer's latest block - just give up.
if (m_grabbing == Grabbing::Chain || m_grabbing == Grabbing::ChainHelper) transition(Asking::Nothing);
giveUpOnFetch();
break; 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) for (unsigned i = 1; i < _r.itemCount(); ++i)
{ {
auto h = BlockInfo::headerHash(_r[i].data()); auto h = BlockInfo::headerHash(_r[i].data());
m_sub.noteBlock(h); if (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)
{ {
auto h = BlockInfo::headerHash(_r[i].data()); addRating(10);
BlockInfo bi(_r[i].data()); switch (host()->m_bq.import(_r[i].data(), host()->m_chain))
Guard l(x_knownBlocks);
if (!host()->m_chain.details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash))
{
unknownParents++;
clogS(NetAllDetail) << "Unknown parent" << bi.parentHash.abridged() << "of block" << h.abridged();
}
else
{ {
knownParents++; case ImportResult::Success:
clogS(NetAllDetail) << "Known parent" << bi.parentHash.abridged() << "of block" << h.abridged(); 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<u256>());
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; break;
} }
default: default:
@ -295,42 +511,3 @@ bool EthereumPeer::interpret(RLP const& _r)
} }
return true; 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" : "?");
}

80
libethereum/EthereumPeer.h

@ -42,54 +42,98 @@ namespace eth
/** /**
* @brief The EthereumPeer class * @brief The EthereumPeer class
* @todo Document fully. * @todo Document fully.
* @todo make state transitions thread-safe.
*/ */
class EthereumPeer: public p2p::Capability class EthereumPeer: public p2p::Capability
{ {
friend class EthereumHost; friend class EthereumHost;
public: public:
EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h); /// Basic constructor.
EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h, unsigned _i);
/// Basic destructor.
virtual ~EthereumPeer(); virtual ~EthereumPeer();
/// What is our name?
static std::string name() { return "eth"; } 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; EthereumHost* host() const;
private: private:
virtual bool interpret(RLP const& _r); using p2p::Capability::sealAndSend;
void sendStatus(); /// Interpret an incoming message.
void startInitialSync(); 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. /// 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 ensureGettingChain(); void attemptSync();
/// Ensure that we are waiting for a bunch of blocks from our peer.
void continueGettingChain();
void giveUpOnFetch(); /// Abort the sync operation.
void abortSync();
/// Clear all known transactions.
void clearKnownTransactions() { std::lock_guard<std::mutex> l(x_knownTransactions); m_knownTransactions.clear(); } void clearKnownTransactions() { std::lock_guard<std::mutex> 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; unsigned m_protocolVersion;
/// Peer's network id.
u256 m_networkId; u256 m_networkId;
Grabbing m_grabbing; /// What, if anything, we last asked the other peer for.
Asking m_asking = Asking::Nothing;
/// Whether this peer is in the process of syncing or not. Only one peer can be syncing at once.
bool m_isSyncing = false;
h256 m_latestHash; ///< Peer's latest block's hash. /// 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. 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; bool m_requireTransactions;
Mutex x_knownBlocks; Mutex x_knownBlocks;
std::set<h256> m_knownBlocks; h256Set m_knownBlocks; ///< Blocks that the peer already knows about (that don't need to be sent to them).
std::set<h256> m_knownTransactions; Mutex x_knownTransactions;
std::mutex x_knownTransactions; h256Set m_knownTransactions; ///< Transactions that the peer already knows of.
DownloadSub m_sub;
}; };
} }

6
libethereum/Executive.cpp

@ -20,6 +20,7 @@
*/ */
#include <boost/timer.hpp> #include <boost/timer.hpp>
#include <libdevcore/CommonIO.h>
#include <libevm/VM.h> #include <libevm/VM.h>
#include "Executive.h" #include "Executive.h"
#include "State.h" #include "State.h"
@ -217,15 +218,12 @@ u256 Executive::gas() const
return m_vm ? m_vm->gas() : m_endGas; 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()) if (m_t.isCreation() && m_newAddress && m_out.size())
// non-reverted creation - put code in place. // non-reverted creation - put code in place.
m_s.m_cache[m_newAddress].setCode(m_out); 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) << ")"; // 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); m_s.addBalance(m_sender, m_endGas * m_t.gasPrice);

18
libethereum/ExtVM.h

@ -61,7 +61,7 @@ public:
m_s.noteSending(myAddress); m_s.noteSending(myAddress);
if (m_ms) if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1); 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) if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back(); m_ms->internal.pop_back();
return ret; return ret;
@ -72,7 +72,7 @@ public:
{ {
if (m_ms) if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1); 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) if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back(); m_ms->internal.pop_back();
return ret; return ret;
@ -98,20 +98,6 @@ public:
/// @TODO check call site for the parent manifest being discarded. /// @TODO check call site for the parent manifest being discarded.
void revert() { if (m_ms) *m_ms = Manifest(); m_s.m_cache = m_origCache; } 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; } State& state() const { return m_s; }
/// @note not a part of the main API; just for use by tracing/debug stuff. /// @note not a part of the main API; just for use by tracing/debug stuff.

9
libethereum/Interface.h

@ -29,6 +29,7 @@
#include "MessageFilter.h" #include "MessageFilter.h"
#include "Transaction.h" #include "Transaction.h"
#include "AccountDiff.h" #include "AccountDiff.h"
#include "BlockDetails.h"
#include "Miner.h" #include "Miner.h"
namespace dev namespace dev
@ -95,7 +96,13 @@ public:
virtual bool peekWatch(unsigned _watchId) const = 0; virtual bool peekWatch(unsigned _watchId) const = 0;
virtual bool checkWatch(unsigned _watchId) = 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]: // [EXTRA API]:

8
libethereum/Miner.cpp

@ -21,6 +21,8 @@
*/ */
#include "Miner.h" #include "Miner.h"
#include <libdevcore/CommonIO.h>
#include "State.h" #include "State.h"
using namespace std; using namespace std;
using namespace dev; 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() void Miner::doWork()
{ {
// Do some mining. // Do some mining.

2
libethereum/Miner.h

@ -94,7 +94,7 @@ public:
~Miner() { stop(); } ~Miner() { stop(); }
/// Setup its basics. /// 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. /// Start mining.
void start() { startWorking(); } void start() { startWorking(); }

68
libethereum/State.cpp

@ -25,6 +25,7 @@
#include <time.h> #include <time.h>
#include <random> #include <random>
#include <secp256k1/secp256k1.h> #include <secp256k1/secp256k1.h>
#include <libdevcore/CommonIO.h>
#include <libevmface/Instruction.h> #include <libevmface/Instruction.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libethcore/Dagger.h> #include <libethcore/Dagger.h>
@ -40,6 +41,50 @@ using namespace dev::eth;
static const u256 c_blockReward = 1500 * finney; 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<unsigned, PrecompiledAddress> State::c_precompiled =
{
{ 1, { 500, ecrecoverCode }},
{ 2, { 100, sha256Code }},
{ 3, { 100, ripemd160Code }}
};
OverlayDB State::openDB(std::string _path, bool _killExisting) OverlayDB State::openDB(std::string _path, bool _killExisting)
{ {
if (_path.empty()) if (_path.empty())
@ -1060,7 +1105,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
return e.gasUsed(); 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<Address>* 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<Address>* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{ {
if (!_originAddress) if (!_originAddress)
_originAddress = _senderAddress; _originAddress = _senderAddress;
@ -1076,7 +1121,16 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
o_ms->input = _data.toBytes(); 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); VM vm(*_gas);
ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), o_ms, _level); 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) if (o_suicides)
for (auto i: evm.suicides) for (auto i: evm.suicides)
o_suicides->insert(i); o_suicides->insert(i);
if (o_posts)
for (auto i: evm.posts)
o_posts->push_back(i);
if (o_ms) if (o_ms)
o_ms->output = out.toBytes(); o_ms->output = out.toBytes();
} }
@ -1124,7 +1175,7 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
return true; return true;
} }
h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set<Address>* 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<Address>* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{ {
if (!_origin) if (!_origin)
_origin = _sender; _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))); Address newAddress = right160(sha3(rlpList(_sender, transactionsFrom(_sender) - 1)));
while (addressInUse(newAddress))
newAddress = (u160)newAddress + 1;
// Set up new account... // Set up new account...
m_cache[newAddress] = AddressState(0, balance(newAddress) + _endowment, h256(), h256()); 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) if (o_suicides)
for (auto i: evm.suicides) for (auto i: evm.suicides)
o_suicides->insert(i); o_suicides->insert(i);
if (o_posts)
for (auto i: evm.posts)
o_posts->push_back(i);
} }
catch (OutOfGas const& /*_e*/) catch (OutOfGas const& /*_e*/)
{ {

12
libethereum/State.h

@ -69,6 +69,12 @@ struct TransactionReceipt
Manifest changes; Manifest changes;
}; };
struct PrecompiledAddress
{
unsigned gas;
std::function<void(bytesConstRef, bytesRef)> exec;
};
/** /**
* @brief Model of the current state of the ledger. * @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). * 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. // We assume all instrinsic fees are paid up before this point.
/// Execute a contract-creation transaction. /// Execute a contract-creation transaction.
h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set<Address>* 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<Address>* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Execute a call. /// Execute a call.
/// @a _gas points to the amount of gas to use for the call, and will lower it accordingly. /// @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. /// @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<Address>* 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<Address>* 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). /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent(); void resetCurrent();
@ -321,6 +327,8 @@ private:
static std::string c_defaultPath; static std::string c_defaultPath;
static const std::map<unsigned, PrecompiledAddress> c_precompiled;
friend std::ostream& operator<<(std::ostream& _out, State const& _s); friend std::ostream& operator<<(std::ostream& _out, State const& _s);
}; };

11
libevm/ExtVMFace.h

@ -21,6 +21,8 @@
#pragma once #pragma once
#include <list>
#include <functional>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libevmface/Instruction.h> #include <libevmface/Instruction.h>
#include <libethcore/CommonEth.h> #include <libethcore/CommonEth.h>
@ -40,8 +42,6 @@ struct Post
u256 gas; u256 gas;
}; };
using PostList = std::list<Post>;
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, void/*VM*/*, void/*ExtVM*/ const*)>; using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, void/*VM*/*, void/*ExtVM*/ const*)>;
/** /**
@ -86,15 +86,9 @@ public:
/// Make a new message call. /// Make a new message call.
bool call(Address, u256, bytesConstRef, u256*, bytesRef, OnOpFunc const&, Address, Address) { return false; } 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). /// Revert any changes made (by any of the other calls).
void revert() {} 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 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 caller; ///< Address which sent the message (either equal to origin or a contract).
Address origin; ///< Original transactor. Address origin; ///< Original transactor.
@ -105,7 +99,6 @@ public:
BlockInfo previousBlock; ///< The previous block's information. BlockInfo previousBlock; ///< The previous block's information.
BlockInfo currentBlock; ///< The current block's information. BlockInfo currentBlock; ///< The current block's information.
std::set<Address> suicides; ///< Any accounts that have suicided. std::set<Address> suicides; ///< Any accounts that have suicided.
std::list<Post> posts; ///< Any posts that have been made.
}; };
} }

42
libevm/VM.h

@ -39,6 +39,7 @@ struct VMException: virtual Exception {};
struct StepsDone: virtual VMException {}; struct StepsDone: virtual VMException {};
struct BreakPointHit: virtual VMException {}; struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {}; struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {}; struct OutOfGas: virtual VMException {};
class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
@ -171,18 +172,12 @@ template <class Ext> 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])); 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; break;
case Instruction::CALLSTATELESS: case Instruction::CALLCODE:
require(7); require(7);
runGas = c_callGas + m_stack[m_stack.size() - 1]; 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])); 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; 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: case Instruction::CREATE:
{ {
require(3); require(3);
@ -572,12 +567,18 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::JUMP: case Instruction::JUMP:
require(1); require(1);
nextPC = m_stack.back(); nextPC = m_stack.back();
if ((Instruction)_ext.getCode(nextPC) != Instruction::JUMPDEST)
BOOST_THROW_EXCEPTION(BadJumpDestination());
m_stack.pop_back(); m_stack.pop_back();
break; break;
case Instruction::JUMPI: case Instruction::JUMPI:
require(2); require(2);
if (m_stack[m_stack.size() - 2]) if (m_stack[m_stack.size() - 2])
{
nextPC = m_stack.back(); nextPC = m_stack.back();
if ((Instruction)_ext.getCode(nextPC) != Instruction::JUMPDEST)
BOOST_THROW_EXCEPTION(BadJumpDestination());
}
m_stack.pop_back(); m_stack.pop_back();
m_stack.pop_back(); m_stack.pop_back();
break; break;
@ -590,6 +591,8 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::GAS: case Instruction::GAS:
m_stack.push_back(m_gas); m_stack.push_back(m_gas);
break; break;
case Instruction::JUMPDEST:
break;
case Instruction::CREATE: case Instruction::CREATE:
{ {
require(3); require(3);
@ -611,7 +614,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break; break;
} }
case Instruction::CALL: case Instruction::CALL:
case Instruction::CALLSTATELESS: case Instruction::CALLCODE:
{ {
require(7); require(7);
@ -662,29 +665,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
} }
case Instruction::STOP: case Instruction::STOP:
return bytesConstRef(); 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: default:
BOOST_THROW_EXCEPTION(BadInstruction()); BOOST_THROW_EXCEPTION(BadInstruction());
} }

9
libevmface/Instruction.cpp

@ -22,6 +22,7 @@
#include "Instruction.h" #include "Instruction.h"
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -82,6 +83,7 @@ const std::map<std::string, Instruction> dev::eth::c_instructions =
{ "PC", Instruction::PC }, { "PC", Instruction::PC },
{ "MSIZE", Instruction::MSIZE }, { "MSIZE", Instruction::MSIZE },
{ "GAS", Instruction::GAS }, { "GAS", Instruction::GAS },
{ "JUMPDEST", Instruction::JUMPDEST },
{ "PUSH1", Instruction::PUSH1 }, { "PUSH1", Instruction::PUSH1 },
{ "PUSH2", Instruction::PUSH2 }, { "PUSH2", Instruction::PUSH2 },
{ "PUSH3", Instruction::PUSH3 }, { "PUSH3", Instruction::PUSH3 },
@ -148,9 +150,8 @@ const std::map<std::string, Instruction> dev::eth::c_instructions =
{ "SWAP16", Instruction::SWAP16 }, { "SWAP16", Instruction::SWAP16 },
{ "CREATE", Instruction::CREATE }, { "CREATE", Instruction::CREATE },
{ "CALL", Instruction::CALL }, { "CALL", Instruction::CALL },
{ "CALLSTATELESS", Instruction::CALLSTATELESS }, { "CALLCODE", Instruction::CALLCODE },
{ "RETURN", Instruction::RETURN }, { "RETURN", Instruction::RETURN },
{ "POST", Instruction::POST },
{ "SUICIDE", Instruction::SUICIDE } { "SUICIDE", Instruction::SUICIDE }
}; };
@ -209,6 +210,7 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::PC, { "PC", 0, 0, 1 } }, { Instruction::PC, { "PC", 0, 0, 1 } },
{ Instruction::MSIZE, { "MSIZE", 0, 0, 1 } }, { Instruction::MSIZE, { "MSIZE", 0, 0, 1 } },
{ Instruction::GAS, { "GAS", 0, 0, 1 } }, { Instruction::GAS, { "GAS", 0, 0, 1 } },
{ Instruction::JUMPDEST, { "JUMPDEST", 0, 1, 0 } },
{ Instruction::PUSH1, { "PUSH1", 1, 0, 1 } }, { Instruction::PUSH1, { "PUSH1", 1, 0, 1 } },
{ Instruction::PUSH2, { "PUSH2", 2, 0, 1 } }, { Instruction::PUSH2, { "PUSH2", 2, 0, 1 } },
{ Instruction::PUSH3, { "PUSH3", 3, 0, 1 } }, { Instruction::PUSH3, { "PUSH3", 3, 0, 1 } },
@ -275,9 +277,8 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::SWAP16, { "SWAP16", 0, 17, 17 } }, { Instruction::SWAP16, { "SWAP16", 0, 17, 17 } },
{ Instruction::CREATE, { "CREATE", 0, 3, 1 } }, { Instruction::CREATE, { "CREATE", 0, 3, 1 } },
{ Instruction::CALL, { "CALL", 0, 7, 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::RETURN, { "RETURN", 0, 2, 0 } },
{ Instruction::POST, { "POST", 0, 5, 0 } },
{ Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} } { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} }
}; };

4
libevmface/Instruction.h

@ -92,6 +92,7 @@ enum class Instruction: uint8_t
PC, ///< get the program counter PC, ///< get the program counter
MSIZE, ///< get the size of active memory MSIZE, ///< get the size of active memory
GAS, ///< get the amount of available gas GAS, ///< get the amount of available gas
JUMPDEST, ///< set a potential jump destination
PUSH1 = 0x60, ///< place 1 byte item on stack PUSH1 = 0x60, ///< place 1 byte item on stack
PUSH2, ///< place 2 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 CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account CALL, ///< message-call into an account
RETURN, ///< halt execution returning output data RETURN, ///< halt execution returning output data
POST, ///< asynchronous call without output (adds a message to the post queue) CALLCODE,
CALLSTATELESS,
SUICIDE = 0xff ///< halt execution and register account for later deletion SUICIDE = 0xff ///< halt execution and register account for later deletion
}; };

5
liblll/Assembly.cpp

@ -129,7 +129,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i)
_out << " PUSH[tag" << i.data() << "]"; _out << " PUSH[tag" << i.data() << "]";
break; break;
case Tag: case Tag:
_out << " tag" << i.data() << ":"; _out << " tag" << i.data() << ": JUMPDEST";
break; break;
case PushData: case PushData:
_out << " PUSH*[" << hex << (unsigned)i.data() << "]"; _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; _out << _prefix << " PUSH #[$" << h256(i.m_data).abridged() << "]" << endl;
break; break;
case Tag: case Tag:
_out << _prefix << "tag" << i.m_data << ": " << endl; _out << _prefix << "tag" << i.m_data << ": " << endl << _prefix << " JUMPDEST" << endl;
break; break;
case PushData: case PushData:
_out << _prefix << " PUSH [" << hex << (unsigned)i.m_data << "]" << endl; _out << _prefix << " PUSH [" << hex << (unsigned)i.m_data << "]" << endl;
@ -414,6 +414,7 @@ bytes Assembly::assemble() const
} }
case Tag: case Tag:
tagPos[(unsigned)i.m_data] = ret.size(); tagPos[(unsigned)i.m_data] = ret.size();
ret.push_back((byte)Instruction::JUMPDEST);
break; break;
default:; default:;
} }

1
liblll/CodeFragment.cpp

@ -24,6 +24,7 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/spirit/include/support_utree.hpp> #include <boost/spirit/include/support_utree.hpp>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/CommonIO.h>
#include <libevmface/Instruction.h> #include <libevmface/Instruction.h>
#include "CompilerState.h" #include "CompilerState.h"
#include "Parser.h" #include "Parser.h"

17
libp2p/Capability.cpp

@ -21,20 +21,31 @@
#include "Capability.h" #include "Capability.h"
#include <libdevcore/Log.h>
#include "Session.h" #include "Session.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(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) 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; 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) void Capability::sealAndSend(RLPStream& _s)

15
libp2p/Capability.h

@ -34,21 +34,23 @@ class Capability
friend class Session; friend class Session;
public: public:
Capability(Session* _s, HostCapabilityFace* _h): m_session(_s), m_host(_h) {} Capability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset);
virtual ~Capability() {} virtual ~Capability() {}
/// Must return the capability name. // Implement these in the derived class.
static std::string name() { return ""; } /* static std::string name() { return ""; }
static u256 version() { return 0; }
static unsigned messageCount() { return 0; }
*/
Session* session() const { return m_session; } Session* session() const { return m_session; }
HostCapabilityFace* hostCapability() const { return m_host; } HostCapabilityFace* hostCapability() const { return m_host; }
protected: protected:
virtual bool interpret(RLP const&) = 0; virtual bool interpret(unsigned _id, RLP const&) = 0;
void disable(std::string const& _problem); 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 sealAndSend(RLPStream& _s);
void sendDestroy(bytes& _msg); void sendDestroy(bytes& _msg);
void send(bytesConstRef _msg); void send(bytesConstRef _msg);
@ -59,6 +61,7 @@ private:
Session* m_session; Session* m_session;
HostCapabilityFace* m_host; HostCapabilityFace* m_host;
bool m_enabled = true; bool m_enabled = true;
unsigned m_idOffset;
}; };
} }

13
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 // Helper function to determine if an address falls within one of the reserved ranges
// For V4: // For V4:
// Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*" // Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*"
// Not implemented yet for V6 bool p2p::isPrivateAddress(bi::address const& _addressToCheck)
bool p2p::isPrivateAddress(bi::address _addressToCheck)
{ {
if (_addressToCheck.is_v4()) if (_addressToCheck.is_v4())
{ {
@ -41,6 +40,16 @@ bool p2p::isPrivateAddress(bi::address _addressToCheck)
if (bytesToCheck[0] == 192 && bytesToCheck[1] == 168) if (bytesToCheck[0] == 192 && bytesToCheck[1] == 168)
return true; 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; return false;
} }

10
libp2p/Common.h

@ -24,6 +24,8 @@
#pragma once #pragma once
#include <string> #include <string>
#include <set>
#include <vector>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
#include <chrono> #include <chrono>
@ -42,7 +44,7 @@ class RLPStream;
namespace p2p namespace p2p
{ {
bool isPrivateAddress(bi::address _addressToCheck); bool isPrivateAddress(bi::address const& _addressToCheck);
class UPnP; class UPnP;
class Capability; class Capability;
@ -88,13 +90,17 @@ enum DisconnectReason
/// @returns the string form of the given disconnection reason. /// @returns the string form of the given disconnection reason.
std::string reasonOf(DisconnectReason _r); std::string reasonOf(DisconnectReason _r);
typedef std::pair<std::string, u256> CapDesc;
typedef std::set<CapDesc> CapDescSet;
typedef std::vector<CapDesc> CapDescs;
struct PeerInfo struct PeerInfo
{ {
std::string clientVersion; std::string clientVersion;
std::string host; std::string host;
unsigned short port; unsigned short port;
std::chrono::steady_clock::duration lastPing; std::chrono::steady_clock::duration lastPing;
std::set<std::string> caps; std::set<CapDesc> caps;
unsigned socket; unsigned socket;
std::map<std::string, std::string> notes; std::map<std::string, std::string> notes;
}; };

14
libp2p/Host.cpp

@ -36,8 +36,10 @@
#include <thread> #include <thread>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include "Session.h" #include "Session.h"
#include "Common.h"
#include "Capability.h" #include "Capability.h"
#include "UPnP.h" #include "UPnP.h"
using namespace std; using namespace std;
@ -142,18 +144,22 @@ void Host::stop()
unsigned Host::protocolVersion() const unsigned Host::protocolVersion() const
{ {
return 0; return 1;
} }
void Host::registerPeer(std::shared_ptr<Session> _s, vector<string> const& _caps) void Host::registerPeer(std::shared_ptr<Session> _s, CapDescs const& _caps)
{ {
{ {
Guard l(x_peers); Guard l(x_peers);
m_peers[_s->m_id] = _s; m_peers[_s->m_id] = _s;
} }
unsigned o = (unsigned)UserPacket;
for (auto const& i: _caps) for (auto const& i: _caps)
if (haveCapability(i)) if (haveCapability(i))
_s->m_capabilities[i] = shared_ptr<Capability>(m_capabilities[i]->newPeerCapability(_s.get())); {
_s->m_capabilities[i] = shared_ptr<Capability>(m_capabilities[i]->newPeerCapability(_s.get(), o));
o += m_capabilities[i]->messageCount();
}
} }
void Host::disconnectPeers() void Host::disconnectPeers()
@ -457,7 +463,7 @@ void Host::growPeers()
{ {
RLPStream s; RLPStream s;
bytes b; bytes b;
(Session::prep(s).appendList(1) << GetPeersPacket).swapOut(b); Session::prep(s, GetPeersPacket).swapOut(b);
seal(b); seal(b);
for (auto const& i: m_peers) for (auto const& i: m_peers)
if (auto p = i.second.lock()) if (auto p = i.second.lock())

13
libp2p/Host.h

@ -31,6 +31,7 @@
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libdevcore/Worker.h> #include <libdevcore/Worker.h>
#include "HostCapability.h" #include "HostCapability.h"
#include "Common.h"
namespace ba = boost::asio; namespace ba = boost::asio;
namespace bi = boost::asio::ip; namespace bi = boost::asio::ip;
@ -75,11 +76,11 @@ public:
unsigned protocolVersion() const; unsigned protocolVersion() const;
/// Register a peer-capability; all new peer connections will have this capability. /// Register a peer-capability; all new peer connections will have this capability.
template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr<T>(_t); m_capabilities[T::staticName()] = ret; return ret; } template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr<T>(_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; } bool haveCapability(CapDesc const& _name) const { return m_capabilities.count(_name) != 0; }
std::vector<std::string> caps() const { std::vector<std::string> ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; }
template <class T> std::shared_ptr<T> cap() const { try { return std::static_pointer_cast<T>(m_capabilities.at(T::staticName())); } catch (...) { return nullptr; } } template <class T> std::shared_ptr<T> cap() const { try { return std::static_pointer_cast<T>(m_capabilities.at(std::make_pair(T::staticName(), T::staticVersion()))); } catch (...) { return nullptr; } }
/// Connect to a peer explicitly. /// Connect to a peer explicitly.
static std::string pocHost(); static std::string pocHost();
@ -118,7 +119,7 @@ public:
h512 id() const { return m_id; } h512 id() const { return m_id; }
void registerPeer(std::shared_ptr<Session> _s, std::vector<std::string> const& _caps); void registerPeer(std::shared_ptr<Session> _s, CapDescs const& _caps);
private: private:
/// Called when the session has provided us with a new peer we can connect to. /// Called when the session has provided us with a new peer we can connect to.
@ -166,7 +167,7 @@ private:
std::vector<bi::address_v4> m_addresses; std::vector<bi::address_v4> m_addresses;
std::vector<bi::address_v4> m_peerAddresses; std::vector<bi::address_v4> m_peerAddresses;
std::map<std::string, std::shared_ptr<HostCapabilityFace>> m_capabilities; std::map<CapDesc, std::shared_ptr<HostCapabilityFace>> m_capabilities;
bool m_accepting = false; bool m_accepting = false;
}; };

2
libp2p/HostCapability.cpp

@ -38,7 +38,7 @@ std::vector<std::shared_ptr<Session> > HostCapabilityFace::peers() const
std::vector<std::shared_ptr<Session> > ret; std::vector<std::shared_ptr<Session> > ret;
for (auto const& i: m_host->m_peers) for (auto const& i: m_host->m_peers)
if (std::shared_ptr<Session> p = i.second.lock()) if (std::shared_ptr<Session> p = i.second.lock())
if (p->m_capabilities.count(name())) if (p->m_capabilities.count(capDesc()))
ret.push_back(p); ret.push_back(p);
return ret; return ret;
} }

12
libp2p/HostCapability.h

@ -36,6 +36,7 @@ class HostCapabilityFace
friend class Host; friend class Host;
template <class T> friend class HostCapability; template <class T> friend class HostCapability;
friend class Capability; friend class Capability;
friend class Session;
public: public:
HostCapabilityFace() {} HostCapabilityFace() {}
@ -47,7 +48,10 @@ public:
protected: protected:
virtual std::string name() const = 0; 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 onStarting() {}
virtual void onStopping() {} virtual void onStopping() {}
@ -66,10 +70,14 @@ public:
virtual ~HostCapability() {} virtual ~HostCapability() {}
static std::string staticName() { return PeerCap::name(); } static std::string staticName() { return PeerCap::name(); }
static u256 staticVersion() { return PeerCap::version(); }
static unsigned staticMessageCount() { return PeerCap::messageCount(); }
protected: protected:
virtual std::string name() const { return PeerCap::name(); } 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); }
}; };
} }

75
libp2p/Session.cpp

@ -23,6 +23,7 @@
#include <chrono> #include <chrono>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include "Host.h" #include "Host.h"
#include "Capability.h" #include "Capability.h"
@ -30,6 +31,9 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " #define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << m_socket.native_handle() << "] "
Session::Session(Host* _s, bi::tcp::socket _socket, bi::address _peerAddress, unsigned short _peerPort): Session::Session(Host* _s, bi::tcp::socket _socket, bi::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_disconnect = std::chrono::steady_clock::time_point::max();
m_connect = std::chrono::steady_clock::now(); m_connect = std::chrono::steady_clock::now();
m_info = PeerInfo({"?", _peerAddress.to_string(), m_listenPort, std::chrono::steady_clock::duration(0), set<string>(), 0, map<string, string>()}); m_info = PeerInfo({"?", _peerAddress.to_string(), m_listenPort, std::chrono::steady_clock::duration(0), CapDescSet(), 0, map<string, string>()});
} }
Session::~Session() Session::~Session()
@ -72,22 +76,28 @@ bi::tcp::endpoint Session::endpoint() const
bool Session::interpret(RLP const& _r) bool Session::interpret(RLP const& _r)
{ {
clogS(NetRight) << _r; clogS(NetRight) << _r;
switch (_r[0].toInt<unsigned>()) switch ((PacketType)_r[0].toInt<unsigned>())
{ {
case HelloPacket: case HelloPacket:
{ {
m_protocolVersion = _r[1].toInt<unsigned>(); m_protocolVersion = _r[1].toInt<unsigned>();
auto clientVersion = _r[2].toString(); auto clientVersion = _r[2].toString();
auto caps = _r[3].toVector<string>(); auto caps = _r[3].toVector<CapDesc>();
m_listenPort = _r[4].toInt<unsigned short>(); m_listenPort = _r[4].toInt<unsigned short>();
m_id = _r[5].toHash<h512>(); m_id = _r[5].toHash<h512>();
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)) if (m_server->havePeer(m_id))
{ {
// Already connected. // 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); disconnect(DuplicatePeer);
return false; return false;
} }
@ -102,7 +112,7 @@ bool Session::interpret(RLP const& _r)
return false; return false;
} }
try try
{ m_info = PeerInfo({clientVersion, m_socket.remote_endpoint().address().to_string(), m_listenPort, std::chrono::steady_clock::duration(), _r[3].toSet<string>(), (unsigned)m_socket.native_handle(), map<string, string>() }); } { m_info = PeerInfo({clientVersion, m_socket.remote_endpoint().address().to_string(), m_listenPort, std::chrono::steady_clock::duration(), _r[3].toSet<CapDesc>(), (unsigned)m_socket.native_handle(), map<string, string>() }); }
catch (...) catch (...)
{ {
disconnect(BadProtocol); disconnect(BadProtocol);
@ -130,7 +140,7 @@ bool Session::interpret(RLP const& _r)
{ {
clogS(NetTriviaSummary) << "Ping"; clogS(NetTriviaSummary) << "Ping";
RLPStream s; RLPStream s;
sealAndSend(prep(s).appendList(1) << PongPacket); sealAndSend(prep(s, PongPacket));
break; break;
} }
case PongPacket: case PongPacket:
@ -142,12 +152,14 @@ bool Session::interpret(RLP const& _r)
clogS(NetTriviaSummary) << "GetPeers"; clogS(NetTriviaSummary) << "GetPeers";
auto peers = m_server->potentialPeers(); auto peers = m_server->potentialPeers();
RLPStream s; RLPStream s;
prep(s).appendList(peers.size() + 1); prep(s, PeersPacket, peers.size());
s << PeersPacket;
for (auto i: peers) for (auto i: peers)
{ {
clogS(NetTriviaDetail) << "Sending peer " << i.first.abridged() << i.second; 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); sealAndSend(s);
break; break;
@ -156,7 +168,16 @@ bool Session::interpret(RLP const& _r)
clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
for (unsigned i = 1; i < _r.itemCount(); ++i) for (unsigned i = 1; i < _r.itemCount(); ++i)
{ {
bi::address_v4 peerAddress(_r[i][0].toHash<FixedHash<4>>().asArray()); bi::address peerAddress;
if (_r[i][0].size() == 16)
peerAddress = bi::address_v6(_r[i][0].toHash<FixedHash<16>>().asArray());
else if (_r[i][0].size() == 4)
peerAddress = bi::address_v4(_r[i][0].toHash<FixedHash<4>>().asArray());
else
{
disconnect(BadProtocol);
return false;
}
auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt<short>()); auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt<short>());
h512 id = _r[i][2].toHash<h512>(); h512 id = _r[i][2].toHash<h512>();
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); 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; break;
default: default:
{
auto id = _r[0].toInt<unsigned>();
for (auto const& i: m_capabilities) 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 true;
return false; return false;
} }
}
return true; return true;
} }
void Session::ping() void Session::ping()
{ {
RLPStream s; RLPStream s;
sealAndSend(prep(s).appendList(1) << PingPacket); sealAndSend(prep(s, PingPacket));
m_ping = std::chrono::steady_clock::now(); m_ping = std::chrono::steady_clock::now();
} }
void Session::getPeers() void Session::getPeers()
{ {
RLPStream s; 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) RLPStream& Session::prep(RLPStream& _s)
@ -240,7 +269,7 @@ void Session::sendDestroy(bytes& _msg)
if (!checkPacket(bytesConstRef(&_msg))) if (!checkPacket(bytesConstRef(&_msg)))
{ {
cwarn << "INVALID PACKET CONSTRUCTED!"; clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!";
} }
bytes buffer = bytes(std::move(_msg)); bytes buffer = bytes(std::move(_msg));
@ -253,7 +282,7 @@ void Session::send(bytesConstRef _msg)
if (!checkPacket(_msg)) if (!checkPacket(_msg))
{ {
cwarn << "INVALID PACKET CONSTRUCTED!"; clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!";
} }
bytes buffer = bytes(_msg.toBytes()); bytes buffer = bytes(_msg.toBytes());
@ -288,7 +317,7 @@ void Session::write()
// must check queue, as write callback can occur following dropped() // must check queue, as write callback can occur following dropped()
if (ec) if (ec)
{ {
cwarn << "Error sending: " << ec.message(); clogS(NetWarn) << "Error sending: " << ec.message();
dropped(); dropped();
return; return;
} }
@ -323,8 +352,7 @@ void Session::disconnect(int _reason)
if (m_disconnect == chrono::steady_clock::time_point::max()) if (m_disconnect == chrono::steady_clock::time_point::max())
{ {
RLPStream s; RLPStream s;
prep(s); prep(s, DisconnectPacket, 1) << _reason;
s.appendList(2) << DisconnectPacket << _reason;
sealAndSend(s); sealAndSend(s);
m_disconnect = chrono::steady_clock::now(); m_disconnect = chrono::steady_clock::now();
} }
@ -336,8 +364,7 @@ void Session::disconnect(int _reason)
void Session::start() void Session::start()
{ {
RLPStream s; RLPStream s;
prep(s); prep(s, HelloPacket, 5)
s.appendList(6) << HelloPacket
<< m_server->protocolVersion() << m_server->protocolVersion()
<< m_server->m_clientVersion << m_server->m_clientVersion
<< m_server->caps() << 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) if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof)
{ {
// got here with length of 1241... // got here with length of 1241...
cwarn << "Error reading: " << ec.message(); clogS(NetWarn) << "Error reading: " << ec.message();
dropped(); dropped();
} }
else if (ec && length == 0) 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) 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); disconnect(BadProtocol);
return; return;
} }
@ -396,7 +423,7 @@ void Session::doRead()
if (!checkPacket(data)) if (!checkPacket(data))
{ {
cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl;
cwarn << "INVALID MESSAGE RECEIVED"; clogS(NetWarn) << "INVALID MESSAGE RECEIVED";
disconnect(BadProtocol); disconnect(BadProtocol);
return; return;
} }

7
libp2p/Session.h

@ -27,6 +27,7 @@
#include <set> #include <set>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include "Common.h" #include "Common.h"
@ -62,8 +63,9 @@ public:
bi::tcp::endpoint endpoint() const; ///< for other peers to connect to. bi::tcp::endpoint endpoint() const; ///< for other peers to connect to.
template <class PeerCap> template <class PeerCap>
std::shared_ptr<PeerCap> cap() const { try { return std::static_pointer_cast<PeerCap>(m_capabilities.at(PeerCap::name())); } catch (...) { return nullptr; } } std::shared_ptr<PeerCap> cap() const { try { return std::static_pointer_cast<PeerCap>(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); static RLPStream& prep(RLPStream& _s);
void sealAndSend(RLPStream& _s); void sealAndSend(RLPStream& _s);
void sendDestroy(bytes& _msg); void sendDestroy(bytes& _msg);
@ -108,7 +110,8 @@ private:
unsigned m_rating; unsigned m_rating;
std::map<std::string, std::shared_ptr<Capability>> m_capabilities; std::map<CapDesc, std::shared_ptr<Capability>> m_capabilities;
std::set<h512> m_knownPeers;
bool m_willBeDeleted = false; ///< True if we already posted a deleter on the strand. bool m_willBeDeleted = false; ///< True if we already posted a deleter on the strand.
}; };

1
libp2p/UPnP.cpp

@ -31,6 +31,7 @@
#endif #endif
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;

87
libqethereum/QEthereum.cpp

@ -336,6 +336,93 @@ static QString toJson(dev::eth::PastMessages const& _pms)
return QString::fromUtf8(QJsonDocument(jsonArray).toJson()); 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 QString QEthereum::getMessages(QString _json) const
{ {
return m_client ? toJson(m_client->messages(toMessageFilter(_json))) : ""; return m_client ? toJson(m_client->messages(toMessageFilter(_json))) : "";

20
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*/ stateAt(QString/*dev::Address*/ _a, QString/*dev::u256*/ _p) const;
Q_INVOKABLE QString/*dev::u256*/ codeAt(QString/*dev::Address*/ _a) 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/*json*/ getMessages(QString _attribs/*json*/) const;
Q_INVOKABLE QString doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice); 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->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.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.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.transact = function(a, f) { var r = eth.doTransact(JSON.stringify(a)); if (f) f(r); }"); \
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.call = function(a, f) { var ret = eth.doCallJson(JSON.stringify(a)); if (f) f(ret); return ret; }"); \ 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.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("eth.block = function(a) { return JSON.parse(eth.getBlock(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("eth.transaction = function(a) { return JSON.parse(eth.getTransaction(a)); }"); \
frame->evaluateJavaScript("String.prototype.bin = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.toAscii(this) }"); \ frame->evaluateJavaScript("eth.uncle = function(a) { return JSON.parse(eth.getUncle(a)); }"); \
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("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.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)) }"); \ frame->evaluateJavaScript("shh.watch = function(a) { return shh.makeWatch(JSON.stringify(a)) }"); \
} }

5
libwhisper/Common.h

@ -51,10 +51,11 @@ class Whisper;
enum WhisperPacket enum WhisperPacket
{ {
StatusPacket = 0x20, StatusPacket = 0,
MessagesPacket, MessagesPacket,
AddFilterPacket, AddFilterPacket,
RemoveFilterPacket RemoveFilterPacket,
PacketCount
}; };
} }

3
libwhisper/Interface.cpp

@ -29,6 +29,9 @@ using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
using namespace dev::shh; using namespace dev::shh;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] " #define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
bool MessageFilter::matches(Message const& _m) const bool MessageFilter::matches(Message const& _m) const

1
libwhisper/Message.cpp

@ -25,4 +25,3 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
using namespace dev::shh; using namespace dev::shh;
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "

3
libwhisper/WhisperHost.cpp

@ -28,6 +28,9 @@ using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
using namespace dev::shh; using namespace dev::shh;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] " #define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
WhisperHost::WhisperHost() WhisperHost::WhisperHost()

17
libwhisper/WhisperPeer.cpp

@ -28,13 +28,16 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
using namespace dev::shh; using namespace dev::shh;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] " #define clogS(X) dev::LogOutputStream<X, true>(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; RLPStream s;
prep(s); sealAndSend(prep(s, StatusPacket, 1) << host()->protocolVersion());
sealAndSend(s.appendList(2) << StatusPacket << host()->protocolVersion());
} }
WhisperPeer::~WhisperPeer() WhisperPeer::~WhisperPeer()
@ -46,9 +49,9 @@ WhisperHost* WhisperPeer::host() const
return static_cast<WhisperHost*>(Capability::hostCapability()); return static_cast<WhisperHost*>(Capability::hostCapability());
} }
bool WhisperPeer::interpret(RLP const& _r) bool WhisperPeer::interpret(unsigned _id, RLP const& _r)
{ {
switch (_r[0].toInt<unsigned>()) switch (_id)
{ {
case StatusPacket: case StatusPacket:
{ {
@ -95,9 +98,7 @@ void WhisperPeer::sendMessages()
if (n) if (n)
{ {
RLPStream s; RLPStream s;
prep(s); prep(s, MessagesPacket, n).appendRaw(amalg.out(), n);
s.appendList(n + 1) << MessagesPacket;
s.appendRaw(amalg.out(), n);
sealAndSend(s); sealAndSend(s);
} }
else else

6
libwhisper/WhisperPeer.h

@ -49,15 +49,17 @@ class WhisperPeer: public Capability
friend class WhisperHost; friend class WhisperHost;
public: public:
WhisperPeer(Session* _s, HostCapabilityFace* _h); WhisperPeer(Session* _s, HostCapabilityFace* _h, unsigned _i);
virtual ~WhisperPeer(); virtual ~WhisperPeer();
static std::string name() { return "shh"; } static std::string name() { return "shh"; }
static u256 version() { return 1; }
static unsigned messageCount() { return PacketCount; }
WhisperHost* host() const; WhisperHost* host() const;
private: private:
virtual bool interpret(RLP const&); virtual bool interpret(unsigned _id, RLP const&);
void sendMessages(); void sendMessages();

3
test/hexPrefix.cpp

@ -22,8 +22,9 @@
#include <fstream> #include <fstream>
#include "JsonSpiritHeaders.h" #include "JsonSpiritHeaders.h"
#include <libdevcrypto/TrieCommon.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/CommonIO.h>
#include <libdevcrypto/TrieCommon.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
using namespace std; using namespace std;

1
test/rlp.cpp

@ -25,6 +25,7 @@
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <algorithm> #include <algorithm>
#include "JsonSpiritHeaders.h" #include "JsonSpiritHeaders.h"

1
test/trie.cpp

@ -23,6 +23,7 @@
#include <fstream> #include <fstream>
#include <random> #include <random>
#include "JsonSpiritHeaders.h" #include "JsonSpiritHeaders.h"
#include <libdevcore/CommonIO.h>
#include <libdevcrypto/TrieDB.h> #include <libdevcrypto/TrieDB.h>
#include "TrieHash.h" #include "TrieHash.h"
#include "MemTrie.h" #include "MemTrie.h"

7
test/vm.cpp

@ -21,6 +21,7 @@
*/ */
#include "vm.h" #include "vm.h"
#include <libdevcore/CommonIO.h>
#define FILL_TESTS #define FILL_TESTS
@ -44,7 +45,7 @@ h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFun
m_s.noteSending(myAddress); m_s.noteSending(myAddress);
m_ms.internal.resize(m_ms.internal.size() + 1); 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) if (!m_ms.internal.back().from)
m_ms.internal.pop_back(); m_ms.internal.pop_back();
@ -82,7 +83,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data,
{ {
m_s.noteSending(myAddress); m_s.noteSending(myAddress);
m_ms.internal.resize(m_ms.internal.size() + 1); 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) if (!m_ms.internal.back().from)
m_ms.internal.pop_back(); m_ms.internal.pop_back();
if (!m_s.addresses().count(_receiveAddress)) 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); 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) if (!m_ms.internal.back().from)
m_ms.internal.pop_back(); m_ms.internal.pop_back();
if (!ret) if (!ret)

7
third/MainWin.cpp

@ -84,9 +84,10 @@ Main::Main(QWidget *parent) :
ui->setupUi(this); ui->setupUi(this);
cerr << "State root: " << BlockChain::genesis().stateRoot << endl; cerr << "State root: " << BlockChain::genesis().stateRoot << endl;
cerr << "Block Hash: " << sha3(BlockChain::createGenesisBlock()) << endl; auto gb = BlockChain::createGenesisBlock();
cerr << "Block RLP: " << RLP(BlockChain::createGenesisBlock()) << endl; cerr << "Block Hash: " << sha3(gb) << endl;
cerr << "Block Hex: " << toHex(BlockChain::createGenesisBlock()) << endl; cerr << "Block RLP: " << RLP(gb) << endl;
cerr << "Block Hex: " << toHex(gb) << endl;
cerr << "Network protocol version: " << dev::eth::c_protocolVersion << endl; cerr << "Network protocol version: " << dev::eth::c_protocolVersion << endl;
cerr << "Client database version: " << dev::eth::c_databaseVersion << endl; cerr << "Client database version: " << dev::eth::c_databaseVersion << endl;

6
windows/LibEthereum.vcxproj

@ -93,6 +93,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\libethcore\Exceptions.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\libethcore\_libethcore.cpp" /> <ClCompile Include="..\libethcore\_libethcore.cpp" />
<ClCompile Include="..\libethereum\AccountDiff.cpp" /> <ClCompile Include="..\libethereum\AccountDiff.cpp" />
<ClCompile Include="..\libethereum\AddressState.cpp" /> <ClCompile Include="..\libethereum\AddressState.cpp" />

3
windows/LibEthereum.vcxproj.filters

@ -190,6 +190,9 @@
<ClCompile Include="..\libwebthree\WebThree.cpp"> <ClCompile Include="..\libwebthree\WebThree.cpp">
<Filter>libwebthree</Filter> <Filter>libwebthree</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\libethcore\Exceptions.cpp">
<Filter>libethcore</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="stdafx.h"> <ClInclude Include="stdafx.h">

Loading…
Cancel
Save