Browse Source

Merge branch 'develop' into ethash_reloaded

cl-refactor
Lefteris Karapetsas 10 years ago
parent
commit
23163f6e99
  1. 4
      alethzero/MainWin.cpp
  2. 3
      eth/main.cpp
  3. 3
      ethminer/main.cpp
  4. 76
      exp/main.cpp
  5. 14
      libdevcore/Common.cpp
  6. 16
      libdevcore/Common.h
  7. 20
      libdevcore/Guards.h
  8. 45
      libdevcore/Worker.cpp
  9. 6
      libdevcrypto/Common.cpp
  10. 2
      libdevcrypto/Common.h
  11. 2
      libethcore/CommonJS.cpp
  12. 8
      libethcore/CommonJS.h
  13. 10
      libethereum/BlockChain.cpp
  14. 56
      libethereum/Client.cpp
  15. 2
      libethereum/EthereumHost.cpp
  16. 4
      libethereum/EthereumPeer.cpp
  17. 2
      libethereum/Farm.h
  18. 104
      libevmasm/GasMeter.cpp
  19. 67
      libevmasm/GasMeter.h
  20. 28
      libp2p/Common.cpp
  21. 13
      libp2p/Common.h
  22. 107
      libp2p/Host.cpp
  23. 125
      libp2p/NodeTable.cpp
  24. 115
      libp2p/NodeTable.h
  25. 4
      libp2p/UDP.h
  26. 9
      libsolidity/ASTPrinter.cpp
  27. 8
      libsolidity/ASTPrinter.h
  28. 44
      libsolidity/ASTVisitor.h
  29. 12
      libsolidity/CompilerStack.cpp
  30. 6
      libsolidity/CompilerStack.h
  31. 110
      libsolidity/StructuralGasEstimator.cpp
  32. 62
      libsolidity/StructuralGasEstimator.h
  33. 4
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  34. 18
      solc/CommandLineInterface.cpp
  35. 98
      test/GasMeter.cpp
  36. 4060
      test/libethereum/BlockTestsFiller/bcWalletTestFiller.json
  37. 90
      test/libethereum/StateTestsFiller/stCallCreateCallCodeTestFiller.json
  38. 42
      test/libethereum/StateTestsFiller/stSpecialTestFiller.json
  39. 1289
      test/libethereum/StateTestsFiller/stWalletTestFiller.json
  40. 5
      test/libethereum/blockchain.cpp
  41. 4
      test/libethereum/state.cpp
  42. 38
      test/libp2p/net.cpp
  43. 8
      test/libp2p/peer.cpp

4
alethzero/MainWin.cpp

@ -1371,6 +1371,8 @@ void Main::on_transactionQueue_currentItemChanged()
s << "<div>Log Bloom: " << receipt.bloom() << "</div>"; s << "<div>Log Bloom: " << receipt.bloom() << "</div>";
else else
s << "<div>Log Bloom: <b><i>Uneventful</i></b></div>"; s << "<div>Log Bloom: <b><i>Uneventful</i></b></div>";
s << "<div>Gas Used: <b>" << receipt.gasUsed() << "</b></div>";
s << "<div>End State: <b>" << receipt.stateRoot().abridged() << "</b></div>";
auto r = receipt.rlp(); auto r = receipt.rlp();
s << "<div>Receipt: " << toString(RLP(r)) << "</div>"; s << "<div>Receipt: " << toString(RLP(r)) << "</div>";
s << "<div>Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "</span></div>"; s << "<div>Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "</span></div>";
@ -1564,6 +1566,8 @@ void Main::on_blocks_currentItemChanged()
s << "<div>Log Bloom: " << receipt.bloom() << "</div>"; s << "<div>Log Bloom: " << receipt.bloom() << "</div>";
else else
s << "<div>Log Bloom: <b><i>Uneventful</i></b></div>"; s << "<div>Log Bloom: <b><i>Uneventful</i></b></div>";
s << "<div>Gas Used: <b>" << receipt.gasUsed() << "</b></div>";
s << "<div>End State: <b>" << receipt.stateRoot().abridged() << "</b></div>";
auto r = receipt.rlp(); auto r = receipt.rlp();
s << "<div>Receipt: " << toString(RLP(r)) << "</div>"; s << "<div>Receipt: " << toString(RLP(r)) << "</div>";
s << "<div>Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "</span></div>"; s << "<div>Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "</span></div>";

3
eth/main.cpp

@ -317,8 +317,9 @@ void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, u
innerMean += rate; innerMean += rate;
} }
f.stop(); f.stop();
innerMean /= (_trials - 2);
cout << "min/mean/max: " << results.begin()->second.rate() << "/" << (mean / _trials) << "/" << results.rbegin()->second.rate() << " H/s" << endl; cout << "min/mean/max: " << results.begin()->second.rate() << "/" << (mean / _trials) << "/" << results.rbegin()->second.rate() << " H/s" << endl;
cout << "inner mean: " << (innerMean / (_trials - 2)) << " H/s" << endl; cout << "inner mean: " << innerMean << " H/s" << endl;
(void)_phoneHome; (void)_phoneHome;
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE

3
ethminer/main.cpp

@ -188,8 +188,9 @@ void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, u
innerMean += rate; innerMean += rate;
} }
f.stop(); f.stop();
innerMean /= (_trials - 2);
cout << "min/mean/max: " << results.begin()->second.rate() << "/" << (mean / _trials) << "/" << results.rbegin()->second.rate() << " H/s" << endl; cout << "min/mean/max: " << results.begin()->second.rate() << "/" << (mean / _trials) << "/" << results.rbegin()->second.rate() << " H/s" << endl;
cout << "inner mean: " << (innerMean / (_trials - 2)) << " H/s" << endl; cout << "inner mean: " << innerMean << " H/s" << endl;
(void)_phoneHome; (void)_phoneHome;
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE

76
exp/main.cpp

@ -72,15 +72,35 @@ public:
KeyManager() { readKeys(); } KeyManager() { readKeys(); }
~KeyManager() {} ~KeyManager() {}
Secret secret(h128 const& _uuid, std::string const& _pass) Secret secret(h128 const& _uuid, function<std::string()> const& _pass)
{ {
auto rit = m_ready.find(_uuid);
if (rit != m_ready.end())
return rit->second;
auto it = m_keys.find(_uuid); auto it = m_keys.find(_uuid);
if (it == m_keys.end()) if (it == m_keys.end())
return Secret(); return Secret();
return Secret(decrypt(it->second, _pass)); Secret ret(decrypt(it->second, _pass()));
if (ret)
m_ready[_uuid] = ret;
return ret;
}
h128 create(std::string const& _pass)
{
auto s = Secret::random();
h128 r(sha3(s));
m_ready[r] = s;
m_keys[r] = encrypt(s.asBytes(), _pass);
return r;
} }
private: private:
void writeKeys(std::string const& _keysPath = getDataDir("web3") + "/keys")
{
(void)_keysPath;
}
void readKeys(std::string const& _keysPath = getDataDir("web3") + "/keys") void readKeys(std::string const& _keysPath = getDataDir("web3") + "/keys")
{ {
fs::path p(_keysPath); fs::path p(_keysPath);
@ -90,25 +110,44 @@ private:
{ {
cdebug << "Reading" << it->path(); cdebug << "Reading" << it->path();
js::read_string(contentsString(it->path().string()), v); js::read_string(contentsString(it->path().string()), v);
js::mObject o = v.get_obj(); if (v.type() == js::obj_type)
int version = o.count("Version") ? stoi(o["Version"].get_str()) : o.count("version") ? o["version"].get_int() : 0; {
if (version == 2) js::mObject o = v.get_obj();
m_keys[fromUUID(o["id"].get_str())] = o["crypto"]; int version = o.count("Version") ? stoi(o["Version"].get_str()) : o.count("version") ? o["version"].get_int() : 0;
if (version == 2)
m_keys[fromUUID(o["id"].get_str())] = o["crypto"];
else
cwarn << "Cannot read key version" << version;
}
else else
cwarn << "Cannot read key version" << version; cwarn << "Invalid JSON in key file" << it->path().string();
} }
} }
static js::mValue encrypt(bytes const& _v, std::string const& _pass)
{
(void)_v;
(void)_pass;
return js::mValue();
}
static bytes decrypt(js::mValue const& _v, std::string const& _pass) static bytes decrypt(js::mValue const& _v, std::string const& _pass)
{ {
js::mObject o = _v.get_obj(); js::mObject o = _v.get_obj();
bytes pKey;
// derive key
bytes derivedKey;
if (o["kdf"].get_str() == "pbkdf2") if (o["kdf"].get_str() == "pbkdf2")
{ {
auto params = o["kdfparams"].get_obj(); auto params = o["kdfparams"].get_obj();
if (params["prf"].get_str() != "hmac-sha256")
{
cwarn << "Unknown PRF for PBKDF2" << params["prf"].get_str() << "not supported.";
return bytes();
}
unsigned iterations = params["c"].get_int(); unsigned iterations = params["c"].get_int();
bytes salt = fromHex(params["salt"].get_str()); bytes salt = fromHex(params["salt"].get_str());
pKey = pbkdf2(_pass, salt, iterations).asBytes(); derivedKey = pbkdf2(_pass, salt, iterations, params["dklen"].get_int());
} }
else else
{ {
@ -116,16 +155,23 @@ private:
return bytes(); return bytes();
} }
// TODO check MAC bytes cipherText = fromHex(o["ciphertext"].get_str());
// check MAC
h256 mac(o["mac"].get_str()); h256 mac(o["mac"].get_str());
(void)mac; h256 macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText);
if (mac != macExp)
{
cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac);
return bytes();
}
bytes cipherText = fromHex(o["ciphertext"].get_str()); // decrypt
bytes ret; bytes ret;
if (o["cipher"].get_str() == "aes-128-cbc") if (o["cipher"].get_str() == "aes-128-cbc")
{ {
auto params = o["cipherparams"].get_obj(); auto params = o["cipherparams"].get_obj();
h128 key(sha3(h128(pKey, h128::AlignRight)), h128::AlignRight); h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight);
h128 iv(params["iv"].get_str()); h128 iv(params["iv"].get_str());
decryptSymNoAuth(key, iv, &cipherText, ret); decryptSymNoAuth(key, iv, &cipherText, ret);
} }
@ -138,13 +184,15 @@ private:
return ret; return ret;
} }
mutable std::map<h128, Secret> m_ready;
std::map<h128, js::mValue> m_keys; std::map<h128, js::mValue> m_keys;
}; };
int main() int main()
{ {
cdebug << toHex(pbkdf2("password", asBytes("salt"), 1, 20));
KeyManager keyman; KeyManager keyman;
cdebug << "Secret key for 0498f19a-59db-4d54-ac95-33901b4f1870 is " << keyman.secret(fromUUID("0498f19a-59db-4d54-ac95-33901b4f1870"), "foo"); cdebug << "Secret key for 0498f19a-59db-4d54-ac95-33901b4f1870 is " << keyman.secret(fromUUID("0498f19a-59db-4d54-ac95-33901b4f1870"), [](){ return "foo"; });
} }
#elif 0 #elif 0

14
libdevcore/Common.cpp

@ -28,7 +28,7 @@ using namespace dev;
namespace dev namespace dev
{ {
char const* Version = "0.9.15"; char const* Version = "0.9.15o";
void HasInvariants::checkInvariants() const void HasInvariants::checkInvariants() const
{ {
@ -36,9 +36,19 @@ void HasInvariants::checkInvariants() const
BOOST_THROW_EXCEPTION(FailedInvariant()); BOOST_THROW_EXCEPTION(FailedInvariant());
} }
struct TimerChannel: public LogChannel { static const char* name(); static const int verbosity = 0; };
#ifdef _WIN32
const char* TimerChannel::name() { return EthRed " ! "; }
#else
const char* TimerChannel::name() { return EthRed ""; }
#endif
TimerHelper::~TimerHelper() TimerHelper::~TimerHelper()
{ {
cdebug << "Timer" << id << t.elapsed() << "s"; auto e = m_t.elapsed();
if (!m_ms || e * 1000 > m_ms)
clog(TimerChannel) << m_id << e << "s";
} }
} }

16
libdevcore/Common.h

@ -169,15 +169,17 @@ private:
#define DEV_INVARIANT_CHECK (void)0; #define DEV_INVARIANT_CHECK (void)0;
#endif #endif
/// Simple scope-based timer helper.
class TimerHelper class TimerHelper
{ {
public: public:
TimerHelper(char const* _id): id(_id) {} TimerHelper(char const* _id, unsigned _msReportWhenGreater = 0): m_id(_id), m_ms(_msReportWhenGreater) {}
~TimerHelper(); ~TimerHelper();
private: private:
boost::timer t; boost::timer m_t;
char const* id; char const* m_id;
unsigned m_ms;
}; };
#define DEV_TIMED(S) for (::std::pair<::dev::TimerHelper, bool> __eth_t(#S, true); __eth_t.second; __eth_t.second = false) #define DEV_TIMED(S) for (::std::pair<::dev::TimerHelper, bool> __eth_t(#S, true); __eth_t.second; __eth_t.second = false)
@ -188,6 +190,14 @@ private:
#define DEV_TIMED_FUNCTION DEV_TIMED_SCOPE(__PRETTY_FUNCTION__) #define DEV_TIMED_FUNCTION DEV_TIMED_SCOPE(__PRETTY_FUNCTION__)
#endif #endif
#define DEV_TIMED_IF(S, MS) for (::std::pair<::dev::TimerHelper, bool> __eth_t(::dev::TimerHelper(#S, MS), true); __eth_t.second; __eth_t.second = false)
#define DEV_TIMED_SCOPE_IF(S) ::dev::TimerHelper __eth_t(S, MS)
#if WIN32
#define DEV_TIMED_FUNCTION_IF(MS) DEV_TIMED_SCOPE_IF(__FUNCSIG__, MS)
#else
#define DEV_TIMED_FUNCTION_IF(MS) DEV_TIMED_SCOPE_IF(__PRETTY_FUNCTION__, MS)
#endif
enum class WithExisting: int enum class WithExisting: int
{ {
Trust = 0, Trust = 0,

20
libdevcore/Guards.h

@ -81,9 +81,9 @@ using SpinGuard = std::lock_guard<SpinLock>;
* Mutex m; * Mutex m;
* unsigned d; * unsigned d;
* ... * ...
* ETH_GUARDED(m) d = 1; * ETH_(m) d = 1;
* ... * ...
* ETH_GUARDED(m) { for (auto d = 10; d > 0; --d) foo(d); d = 0; } * ETH_(m) { for (auto d = 10; d > 0; --d) foo(d); d = 0; }
* @endcode * @endcode
* *
* There are several variants of this basic mechanism for different Mutex types and Guards. * There are several variants of this basic mechanism for different Mutex types and Guards.
@ -95,7 +95,7 @@ using SpinGuard = std::lock_guard<SpinLock>;
* Mutex m; * Mutex m;
* int d; * int d;
* ... * ...
* ETH_GUARDED(m) * ETH_(m)
* { * {
* for (auto d = 50; d > 25; --d) * for (auto d = 50; d > 25; --d)
* foo(d); * foo(d);
@ -107,19 +107,19 @@ using SpinGuard = std::lock_guard<SpinLock>;
* @endcode * @endcode
*/ */
#define ETH_GUARDED(MUTEX) \ #define DEV_GUARDED(MUTEX) \
for (GenericGuardBool<Guard, Mutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) for (GenericGuardBool<Guard, Mutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_READ_GUARDED(MUTEX) \ #define DEV_READ_GUARDED(MUTEX) \
for (GenericGuardBool<ReadGuard, SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) for (GenericGuardBool<ReadGuard, SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_WRITE_GUARDED(MUTEX) \ #define DEV_WRITE_GUARDED(MUTEX) \
for (GenericGuardBool<WriteGuard, SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) for (GenericGuardBool<WriteGuard, SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_RECURSIVE_GUARDED(MUTEX) \ #define DEV_RECURSIVE_GUARDED(MUTEX) \
for (GenericGuardBool<RecursiveGuard, RecursiveMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) for (GenericGuardBool<RecursiveGuard, RecursiveMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_UNGUARDED(MUTEX) \ #define DEV_UNGUARDED(MUTEX) \
for (GenericUnguardBool<Mutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) for (GenericUnguardBool<Mutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_READ_UNGUARDED(MUTEX) \ #define DEV_READ_UNGUARDED(MUTEX) \
for (GenericUnguardSharedBool<SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) for (GenericUnguardSharedBool<SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_WRITE_UNGUARDED(MUTEX) \ #define DEV_WRITE_UNGUARDED(MUTEX) \
for (GenericUnguardBool<SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) for (GenericUnguardBool<SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
} }

45
libdevcore/Worker.cpp

@ -29,7 +29,7 @@ using namespace dev;
void Worker::startWorking() void Worker::startWorking()
{ {
cnote << "startWorking for thread" << m_name; // cnote << "startWorking for thread" << m_name;
Guard l(x_work); Guard l(x_work);
if (m_work) if (m_work)
{ {
@ -42,65 +42,66 @@ void Worker::startWorking()
m_work.reset(new thread([&]() m_work.reset(new thread([&]()
{ {
setThreadName(m_name.c_str()); setThreadName(m_name.c_str());
cnote << "Thread begins"; // cnote << "Thread begins";
while (m_state != WorkerState::Killing) while (m_state != WorkerState::Killing)
{ {
WorkerState ex = WorkerState::Starting; WorkerState ex = WorkerState::Starting;
bool ok = m_state.compare_exchange_strong(ex, WorkerState::Started); bool ok = m_state.compare_exchange_strong(ex, WorkerState::Started);
cnote << "Trying to set Started: Thread was" << (unsigned)ex << "; " << ok; // cnote << "Trying to set Started: Thread was" << (unsigned)ex << "; " << ok;
(void)ok;
startedWorking(); startedWorking();
cnote << "Entering work loop..."; // cnote << "Entering work loop...";
workLoop(); workLoop();
cnote << "Finishing up worker thread..."; // cnote << "Finishing up worker thread...";
doneWorking(); doneWorking();
// ex = WorkerState::Stopping; // ex = WorkerState::Stopping;
// m_state.compare_exchange_strong(ex, WorkerState::Stopped); // m_state.compare_exchange_strong(ex, WorkerState::Stopped);
ex = m_state.exchange(WorkerState::Stopped); ex = m_state.exchange(WorkerState::Stopped);
cnote << "State: Stopped: Thread was" << (unsigned)ex; // cnote << "State: Stopped: Thread was" << (unsigned)ex;
if (ex == WorkerState::Killing || ex == WorkerState::Starting) if (ex == WorkerState::Killing || ex == WorkerState::Starting)
m_state.exchange(ex); m_state.exchange(ex);
cnote << "Waiting until not Stopped..."; // cnote << "Waiting until not Stopped...";
while (m_state == WorkerState::Stopped) DEV_TIMED_IF(Worker stopping, 100)
this_thread::sleep_for(chrono::milliseconds(20)); while (m_state == WorkerState::Stopped)
this_thread::sleep_for(chrono::milliseconds(20));
} }
})); }));
cnote << "Spawning" << m_name; // cnote << "Spawning" << m_name;
} }
cnote << "Waiting until Started..."; DEV_TIMED_IF(Start worker, 100)
while (m_state != WorkerState::Started) while (m_state != WorkerState::Started)
this_thread::sleep_for(chrono::microseconds(20)); this_thread::sleep_for(chrono::microseconds(20));
} }
void Worker::stopWorking() void Worker::stopWorking()
{ {
cnote << "stopWorking for thread" << m_name; DEV_GUARDED(x_work)
ETH_GUARDED(x_work)
if (m_work) if (m_work)
{ {
cnote << "Stopping" << m_name;
WorkerState ex = WorkerState::Started; WorkerState ex = WorkerState::Started;
m_state.compare_exchange_strong(ex, WorkerState::Stopping); m_state.compare_exchange_strong(ex, WorkerState::Stopping);
cnote << "Waiting until Stopped..."; DEV_TIMED_IF(Stop worker, 100)
while (m_state != WorkerState::Stopped) while (m_state != WorkerState::Stopped)
this_thread::sleep_for(chrono::microseconds(20)); this_thread::sleep_for(chrono::microseconds(20));
} }
} }
void Worker::terminate() void Worker::terminate()
{ {
// cnote << "stopWorking for thread" << m_name; // cnote << "stopWorking for thread" << m_name;
ETH_GUARDED(x_work) DEV_GUARDED(x_work)
if (m_work) if (m_work)
{ {
cnote << "Terminating" << m_name;
m_state.exchange(WorkerState::Killing); m_state.exchange(WorkerState::Killing);
m_work->join(); DEV_TIMED_IF(Terminate worker, 100)
m_work->join();
m_work.reset(); m_work.reset();
} }
} }

6
libdevcrypto/Common.cpp

@ -175,11 +175,11 @@ bool dev::verify(Public const& _p, Signature const& _s, h256 const& _hash)
return s_secp256k1.verify(_p, _s, _hash.ref(), true); return s_secp256k1.verify(_p, _s, _hash.ref(), true);
} }
h256 dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations) bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen)
{ {
h256 ret; bytes ret(_dkLen);
PKCS5_PBKDF2_HMAC<SHA256> pbkdf; PKCS5_PBKDF2_HMAC<SHA256> pbkdf;
pbkdf.DeriveKey(ret.data(), ret.size, 0, (byte*)_pass.data(), _pass.size(), _salt.data(), _salt.size(), _iterations); pbkdf.DeriveKey(ret.data(), ret.size(), 0, (byte*)_pass.data(), _pass.size(), _salt.data(), _salt.size(), _iterations);
return ret; return ret;
} }

2
libdevcrypto/Common.h

@ -121,7 +121,7 @@ Signature sign(Secret const& _k, h256 const& _hash);
bool verify(Public const& _k, Signature const& _s, h256 const& _hash); bool verify(Public const& _k, Signature const& _s, h256 const& _hash);
/// Derive key via PBKDF2. /// Derive key via PBKDF2.
h256 pbkdf2(std::string const& _pass, bytes const& _salt, unsigned _iterations); bytes pbkdf2(std::string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen = 32);
/// Simple class that represents a "key pair". /// Simple class that represents a "key pair".
/// All of the data of the class can be regenerated from the secret key (m_secret) alone. /// All of the data of the class can be regenerated from the secret key (m_secret) alone.

2
libethcore/CommonJS.cpp

@ -26,6 +26,8 @@
namespace dev namespace dev
{ {
const u256 UndefinedU256 = ~(u256)0;
Address toAddress(std::string const& _sn) Address toAddress(std::string const& _sn)
{ {
if (_sn.size() == 40) if (_sn.size() == 40)

8
libethcore/CommonJS.h

@ -48,14 +48,18 @@ inline Address jsToAddress(std::string const& _s) { return jsToFixed<sizeof(dev:
/// Convert u256 into user-readable string. Returns int/hex value of 64 bits int, hex of 160 bits FixedHash. As a fallback try to handle input as h256. /// Convert u256 into user-readable string. Returns int/hex value of 64 bits int, hex of 160 bits FixedHash. As a fallback try to handle input as h256.
std::string prettyU256(u256 _n, bool _abridged = true); std::string prettyU256(u256 _n, bool _abridged = true);
extern const u256 UndefinedU256;
} }
// ethcore // ethcore
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
struct TransactionSkeleton struct TransactionSkeleton
{ {
bool creation = false; bool creation = false;
@ -63,8 +67,8 @@ struct TransactionSkeleton
Address to; Address to;
u256 value; u256 value;
bytes data; bytes data;
u256 gas; u256 gas = UndefinedU256;
u256 gasPrice; u256 gasPrice = UndefinedU256;
}; };
/// Convert to a block number, a bit like jsToInt, except that it correctly recognises "pending" and "latest". /// Convert to a block number, a bit like jsToInt, except that it correctly recognises "pending" and "latest".

10
libethereum/BlockChain.cpp

@ -486,7 +486,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
// This is safe in practice since the caches don't get flushed nearly often enough to be // This is safe in practice since the caches don't get flushed nearly often enough to be
// done here. // done here.
details(bi.parentHash); details(bi.parentHash);
ETH_WRITE_GUARDED(x_details) DEV_WRITE_GUARDED(x_details)
m_details[bi.parentHash].children.push_back(bi.hash()); m_details[bi.parentHash].children.push_back(bi.hash());
#if ETH_TIMED_IMPORTS || !ETH_TRUE #if ETH_TIMED_IMPORTS || !ETH_TRUE
@ -495,7 +495,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
#endif #endif
blocksBatch.Put(toSlice(bi.hash()), (ldb::Slice)ref(_block)); blocksBatch.Put(toSlice(bi.hash()), (ldb::Slice)ref(_block));
ETH_READ_GUARDED(x_details) DEV_READ_GUARDED(x_details)
extrasBatch.Put(toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); extrasBatch.Put(toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp()));
extrasBatch.Put(toSlice(bi.hash(), ExtraDetails), (ldb::Slice)dev::ref(BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}).rlp())); extrasBatch.Put(toSlice(bi.hash(), ExtraDetails), (ldb::Slice)dev::ref(BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}).rlp()));
@ -623,7 +623,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
m_blocksDB->Write(m_writeOptions, &blocksBatch); m_blocksDB->Write(m_writeOptions, &blocksBatch);
m_extrasDB->Write(m_writeOptions, &extrasBatch); m_extrasDB->Write(m_writeOptions, &extrasBatch);
ETH_WRITE_GUARDED(x_lastBlockHash) DEV_WRITE_GUARDED(x_lastBlockHash)
{ {
m_lastBlockHash = newLastBlockHash; m_lastBlockHash = newLastBlockHash;
m_lastBlockNumber = newLastBlockNumber; m_lastBlockNumber = newLastBlockNumber;
@ -981,7 +981,7 @@ bool BlockChain::isKnown(h256 const& _hash) const
if (_hash == m_genesisHash) if (_hash == m_genesisHash)
return true; return true;
ETH_READ_GUARDED(x_blocks) DEV_READ_GUARDED(x_blocks)
if (!m_blocks.count(_hash)) if (!m_blocks.count(_hash))
{ {
string d; string d;
@ -989,7 +989,7 @@ bool BlockChain::isKnown(h256 const& _hash) const
if (d.empty()) if (d.empty())
return false; return false;
} }
ETH_READ_GUARDED(x_details) DEV_READ_GUARDED(x_details)
if (!m_details.count(_hash)) if (!m_details.count(_hash))
{ {
string d; string d;

56
libethereum/Client.cpp

@ -244,13 +244,13 @@ void Client::startedWorking()
// TODO: currently it contains keys for *all* blocks. Make it remove old ones. // TODO: currently it contains keys for *all* blocks. Make it remove old ones.
cdebug << "startedWorking()"; cdebug << "startedWorking()";
ETH_WRITE_GUARDED(x_preMine) DEV_WRITE_GUARDED(x_preMine)
m_preMine.sync(m_bc); m_preMine.sync(m_bc);
ETH_READ_GUARDED(x_preMine) DEV_READ_GUARDED(x_preMine)
{ {
ETH_WRITE_GUARDED(x_working) DEV_WRITE_GUARDED(x_working)
m_working = m_preMine; m_working = m_preMine;
ETH_WRITE_GUARDED(x_postMine) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_preMine; m_postMine = m_preMine;
} }
} }
@ -259,13 +259,13 @@ void Client::doneWorking()
{ {
// Synchronise the state according to the head of the block chain. // Synchronise the state according to the head of the block chain.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones. // TODO: currently it contains keys for *all* blocks. Make it remove old ones.
ETH_WRITE_GUARDED(x_preMine) DEV_WRITE_GUARDED(x_preMine)
m_preMine.sync(m_bc); m_preMine.sync(m_bc);
ETH_READ_GUARDED(x_preMine) DEV_READ_GUARDED(x_preMine)
{ {
ETH_WRITE_GUARDED(x_working) DEV_WRITE_GUARDED(x_working)
m_working = m_preMine; m_working = m_preMine;
ETH_WRITE_GUARDED(x_postMine) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_preMine; m_postMine = m_preMine;
} }
} }
@ -309,7 +309,7 @@ void Client::killChain()
void Client::clearPending() void Client::clearPending()
{ {
h256Set changeds; h256Set changeds;
ETH_WRITE_GUARDED(x_postMine) DEV_WRITE_GUARDED(x_postMine)
{ {
if (!m_postMine.pending().size()) if (!m_postMine.pending().size())
return; return;
@ -317,7 +317,7 @@ void Client::clearPending()
// appendFromNewPending(m_postMine.logBloom(i), changeds); // appendFromNewPending(m_postMine.logBloom(i), changeds);
changeds.insert(PendingChangedFilter); changeds.insert(PendingChangedFilter);
m_tq.clear(); m_tq.clear();
ETH_READ_GUARDED(x_preMine) DEV_READ_GUARDED(x_preMine)
m_postMine = m_preMine; m_postMine = m_preMine;
} }
@ -434,7 +434,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
{ {
State temp; State temp;
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
ETH_READ_GUARDED(x_postMine) DEV_READ_GUARDED(x_postMine)
temp = m_postMine; temp = m_postMine;
temp.addBalance(_from, _value + _gasPrice * _gas); temp.addBalance(_from, _value + _gasPrice * _gas);
Executive e(temp, LastHashes(), 0); Executive e(temp, LastHashes(), 0);
@ -461,13 +461,13 @@ ProofOfWork::WorkPackage Client::getWork()
bool Client::submitWork(ProofOfWork::Solution const& _solution) bool Client::submitWork(ProofOfWork::Solution const& _solution)
{ {
bytes newBlock; bytes newBlock;
DEV_TIMED(working) ETH_WRITE_GUARDED(x_working) DEV_TIMED(working) DEV_WRITE_GUARDED(x_working)
if (!m_working.completeMine<ProofOfWork>(_solution)) if (!m_working.completeMine<ProofOfWork>(_solution))
return false; return false;
ETH_READ_GUARDED(x_working) DEV_READ_GUARDED(x_working)
{ {
DEV_TIMED(post) ETH_WRITE_GUARDED(x_postMine) DEV_TIMED(post) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working; m_postMine = m_working;
newBlock = m_working.blockData(); newBlock = m_working.blockData();
} }
@ -499,17 +499,17 @@ void Client::syncTransactionQueue()
h256Set changeds; h256Set changeds;
TransactionReceipts newPendingReceipts; TransactionReceipts newPendingReceipts;
DEV_TIMED(working) ETH_WRITE_GUARDED(x_working) DEV_TIMED(working) DEV_WRITE_GUARDED(x_working)
tie(newPendingReceipts, m_syncTransactionQueue) = m_working.sync(m_bc, m_tq, *m_gp); tie(newPendingReceipts, m_syncTransactionQueue) = m_working.sync(m_bc, m_tq, *m_gp);
if (newPendingReceipts.empty()) if (newPendingReceipts.empty())
return; return;
ETH_READ_GUARDED(x_working) DEV_READ_GUARDED(x_working)
DEV_TIMED(post) ETH_WRITE_GUARDED(x_postMine) DEV_TIMED(post) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working; m_postMine = m_working;
ETH_READ_GUARDED(x_postMine) DEV_READ_GUARDED(x_postMine)
for (size_t i = 0; i < newPendingReceipts.size(); i++) for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3()); appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
changeds.insert(PendingChangedFilter); changeds.insert(PendingChangedFilter);
@ -561,7 +561,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
bool preChanged = false; bool preChanged = false;
State newPreMine; State newPreMine;
ETH_READ_GUARDED(x_preMine) DEV_READ_GUARDED(x_preMine)
newPreMine = m_preMine; newPreMine = m_preMine;
// TODO: use m_postMine to avoid re-evaluating our own blocks. // TODO: use m_postMine to avoid re-evaluating our own blocks.
@ -572,11 +572,11 @@ void Client::onChainChanged(ImportRoute const& _ir)
if (isMining()) if (isMining())
cnote << "New block on chain."; cnote << "New block on chain.";
ETH_WRITE_GUARDED(x_preMine) DEV_WRITE_GUARDED(x_preMine)
m_preMine = newPreMine; m_preMine = newPreMine;
DEV_TIMED(working) ETH_WRITE_GUARDED(x_working) DEV_TIMED(working) DEV_WRITE_GUARDED(x_working)
m_working = newPreMine; m_working = newPreMine;
ETH_READ_GUARDED(x_postMine) DEV_READ_GUARDED(x_postMine)
for (auto const& t: m_postMine.pending()) for (auto const& t: m_postMine.pending())
{ {
clog(ClientNote) << "Resubmitting post-mine transaction " << t; clog(ClientNote) << "Resubmitting post-mine transaction " << t;
@ -584,7 +584,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
if (ir != ImportResult::Success) if (ir != ImportResult::Success)
onTransactionQueueReady(); onTransactionQueueReady();
} }
ETH_READ_GUARDED(x_working) DEV_TIMED(post) ETH_WRITE_GUARDED(x_postMine) DEV_READ_GUARDED(x_working) DEV_TIMED(post) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working; m_postMine = m_working;
changeds.insert(PendingChangedFilter); changeds.insert(PendingChangedFilter);
@ -609,11 +609,11 @@ void Client::onPostStateChanged()
cnote << "Post state changed: Restarting mining..."; cnote << "Post state changed: Restarting mining...";
if (isMining() || remoteActive()) if (isMining() || remoteActive())
{ {
DEV_TIMED(working) ETH_WRITE_GUARDED(x_working) DEV_TIMED(working) DEV_WRITE_GUARDED(x_working)
m_working.commitToMine(m_bc); m_working.commitToMine(m_bc);
ETH_READ_GUARDED(x_working) DEV_READ_GUARDED(x_working)
{ {
DEV_TIMED(post) ETH_WRITE_GUARDED(x_postMine) DEV_TIMED(post) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working; m_postMine = m_working;
m_miningInfo = m_postMine.info(); m_miningInfo = m_postMine.info();
} }
@ -694,7 +694,7 @@ void Client::checkWatchGarbage()
{ {
// watches garbage collection // watches garbage collection
vector<unsigned> toUninstall; vector<unsigned> toUninstall;
ETH_GUARDED(x_filtersWatches) DEV_GUARDED(x_filtersWatches)
for (auto key: keysOf(m_watches)) for (auto key: keysOf(m_watches))
if (m_watches[key].lastPoll != chrono::system_clock::time_point::max() && chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20)) if (m_watches[key].lastPoll != chrono::system_clock::time_point::max() && chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20))
{ {
@ -733,7 +733,7 @@ eth::State Client::state(h256 _block) const
eth::State Client::state(unsigned _txi) const eth::State Client::state(unsigned _txi) const
{ {
ETH_READ_GUARDED(x_postMine) DEV_READ_GUARDED(x_postMine)
return m_postMine.fromPending(_txi); return m_postMine.fromPending(_txi);
assert(false); assert(false);
return State(); return State();

2
libethereum/EthereumHost.cpp

@ -253,7 +253,7 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
h256s blocks = get<0>(m_chain.treeRoute(m_latestBlockSent, _currentHash, false, false, true)); h256s blocks = get<0>(m_chain.treeRoute(m_latestBlockSent, _currentHash, false, false, true));
auto s = randomSelection(25, [&](EthereumPeer* p){ ETH_GUARDED(p->x_knownBlocks) return !p->m_knownBlocks.count(_currentHash); return false; }); auto s = randomSelection(25, [&](EthereumPeer* p){ DEV_GUARDED(p->x_knownBlocks) return !p->m_knownBlocks.count(_currentHash); return false; });
for (shared_ptr<EthereumPeer> const& p: s.first) for (shared_ptr<EthereumPeer> const& p: s.first)
for (auto const& b: blocks) for (auto const& b: blocks)
{ {

4
libethereum/EthereumPeer.cpp

@ -559,7 +559,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
default:; default:;
} }
ETH_GUARDED(x_knownBlocks) DEV_GUARDED(x_knownBlocks)
m_knownBlocks.insert(h); m_knownBlocks.insert(h);
} }
break; break;
@ -578,7 +578,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{ {
addRating(1); addRating(1);
auto h = _r[i].toHash<h256>(); auto h = _r[i].toHash<h256>();
ETH_GUARDED(x_knownBlocks) DEV_GUARDED(x_knownBlocks)
m_knownBlocks.insert(h); m_knownBlocks.insert(h);
auto status = host()->m_bq.blockStatus(h); auto status = host()->m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host()->m_chain.isKnown(h)) if (status == QueueStatus::Importing || status == QueueStatus::Ready || host()->m_chain.isKnown(h))

2
libethereum/Farm.h

@ -127,7 +127,7 @@ public:
*/ */
void resetMiningProgress() void resetMiningProgress()
{ {
ETH_READ_GUARDED(x_minerWork) DEV_READ_GUARDED(x_minerWork)
for (auto const& i: m_miners) for (auto const& i: m_miners)
i->resetHashCount(); i->resetHashCount();
resetTimer(); resetTimer();

104
libevmasm/GasMeter.cpp

@ -0,0 +1,104 @@
/*
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 GasMeter.cpp
* @author Christian <c@ethdev.com>
* @date 2015
*/
#include "GasMeter.h"
#include <libevmcore/Params.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption const& _other)
{
isInfinite = isInfinite || _other.isInfinite;
if (isInfinite)
return *this;
bigint v = bigint(value) + _other.value;
if (v > std::numeric_limits<u256>::max())
isInfinite = true;
else
value = u256(v);
return *this;
}
GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item)
{
switch (_item.type()) {
case Push:
case PushTag:
return runGas(Instruction::PUSH1);
case Tag:
return runGas(Instruction::JUMPDEST);
case Operation:
{
GasConsumption gas = runGas(_item.instruction());
switch (_item.instruction())
{
case Instruction::SSTORE:
// @todo logic can be improved
gas += c_sstoreSetGas;
break;
case Instruction::SLOAD:
gas += c_sloadGas;
break;
case Instruction::MSTORE:
case Instruction::MSTORE8:
case Instruction::MLOAD:
case Instruction::RETURN:
case Instruction::SHA3:
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::EXTCODECOPY:
case Instruction::LOG0:
case Instruction::LOG1:
case Instruction::LOG2:
case Instruction::LOG3:
case Instruction::LOG4:
case Instruction::CALL:
case Instruction::CALLCODE:
case Instruction::CREATE:
case Instruction::EXP:
// @todo logic can be improved
gas = GasConsumption::infinite();
break;
default:
break;
}
return gas;
break;
}
default:
break;
}
return GasConsumption::infinite();
}
GasMeter::GasConsumption GasMeter::runGas(Instruction _instruction)
{
if (_instruction == Instruction::JUMPDEST)
return GasConsumption(1);
int tier = instructionInfo(_instruction).gasPriceTier;
return tier == InvalidTier ? GasConsumption::infinite() : c_tierStepGas[tier];
}

67
libevmasm/GasMeter.h

@ -0,0 +1,67 @@
/*
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 GasMeter.cpp
* @author Christian <c@ethdev.com>
* @date 2015
*/
#pragma once
#include <ostream>
#include <libevmasm/AssemblyItem.h>
namespace dev
{
namespace eth
{
/**
* Class that helps computing the maximum gas consumption for instructions.
*/
class GasMeter
{
public:
struct GasConsumption
{
GasConsumption(u256 _value = 0, bool _infinite = false): value(_value), isInfinite(_infinite) {}
static GasConsumption infinite() { return GasConsumption(0, true); }
GasConsumption& operator+=(GasConsumption const& _otherS);
std::ostream& operator<<(std::ostream& _str) const;
u256 value;
bool isInfinite;
};
/// Returns an upper bound on the gas consumed by the given instruction.
GasConsumption estimateMax(AssemblyItem const& _item);
private:
static GasConsumption runGas(Instruction _instruction);
};
inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption const& _consumption)
{
if (_consumption.isInfinite)
return _str << "inf";
else
return _str << _consumption.value;
}
}
}

28
libp2p/Common.cpp

@ -24,8 +24,9 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
const unsigned dev::p2p::c_protocolVersion = 3; const unsigned dev::p2p::c_protocolVersion = 4;
const unsigned dev::p2p::c_defaultIPPort = 30303; const unsigned dev::p2p::c_defaultIPPort = 30303;
static_assert(dev::p2p::c_protocolVersion == 4, "Replace v3 compatbility with v4 compatibility before updating network version.");
const dev::p2p::NodeIPEndpoint dev::p2p::UnspecifiedNodeIPEndpoint = NodeIPEndpoint(bi::address(), 0, 0); const dev::p2p::NodeIPEndpoint dev::p2p::UnspecifiedNodeIPEndpoint = NodeIPEndpoint(bi::address(), 0, 0);
const dev::p2p::Node dev::p2p::UnspecifiedNode = dev::p2p::Node(NodeId(), UnspecifiedNodeIPEndpoint); const dev::p2p::Node dev::p2p::UnspecifiedNode = dev::p2p::Node(NodeId(), UnspecifiedNodeIPEndpoint);
@ -144,6 +145,31 @@ std::string p2p::reasonOf(DisconnectReason _r)
} }
} }
void NodeIPEndpoint::streamRLP(RLPStream& _s, RLPAppend _append) const
{
if (_append == StreamList)
_s.appendList(3);
if (address.is_v4())
_s << bytesConstRef(&address.to_v4().to_bytes()[0], 4);
else if (address.is_v6())
_s << bytesConstRef(&address.to_v6().to_bytes()[0], 16);
else
_s << bytes();
_s << udpPort << tcpPort;
}
void NodeIPEndpoint::interpretRLP(RLP const& _r)
{
if (_r[0].size() == 4)
address = bi::address_v4(*(bi::address_v4::bytes_type*)_r[0].toBytes().data());
else if (_r[0].size() == 16)
address = bi::address_v6(*(bi::address_v6::bytes_type*)_r[0].toBytes().data());
else
address = bi::address();
udpPort = _r[1].toInt<uint16_t>();
tcpPort = _r[2].toInt<uint16_t>();
}
namespace dev { namespace dev {
std::ostream& operator<<(std::ostream& _out, dev::p2p::NodeIPEndpoint const& _ep) std::ostream& operator<<(std::ostream& _out, dev::p2p::NodeIPEndpoint const& _ep)

13
libp2p/Common.h

@ -36,6 +36,7 @@
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libdevcore/RLP.h>
namespace ba = boost::asio; namespace ba = boost::asio;
namespace bi = boost::asio::ip; namespace bi = boost::asio::ip;
@ -162,10 +163,17 @@ using PeerSessionInfos = std::vector<PeerSessionInfo>;
*/ */
struct NodeIPEndpoint struct NodeIPEndpoint
{ {
enum RLPAppend
{
StreamList,
StreamInline
};
/// Setting true causes isAllowed to return true for all addresses. (Used by test fixtures) /// Setting true causes isAllowed to return true for all addresses. (Used by test fixtures)
static bool test_allowLocal; static bool test_allowLocal;
NodeIPEndpoint(bi::address _addr, uint16_t _udp, uint16_t _tcp): address(_addr), udpPort(_udp), tcpPort(_tcp) {} NodeIPEndpoint(bi::address _addr, uint16_t _udp, uint16_t _tcp): address(_addr), udpPort(_udp), tcpPort(_tcp) {}
NodeIPEndpoint(RLP const& _r) { interpretRLP(_r); }
bi::address address; bi::address address;
uint16_t udpPort; uint16_t udpPort;
@ -177,11 +185,14 @@ struct NodeIPEndpoint
operator bool() const { return !address.is_unspecified() && udpPort > 0 && tcpPort > 0; } operator bool() const { return !address.is_unspecified() && udpPort > 0 && tcpPort > 0; }
bool isAllowed() const { return NodeIPEndpoint::test_allowLocal ? !address.is_unspecified() : isPublicAddress(address); } bool isAllowed() const { return NodeIPEndpoint::test_allowLocal ? !address.is_unspecified() : isPublicAddress(address); }
void streamRLP(RLPStream& _s, RLPAppend _append = StreamList) const;
void interpretRLP(RLP const& _r);
}; };
struct Node struct Node
{ {
Node(Public _pubk, NodeIPEndpoint _ip, bool _required = false): id(_pubk), endpoint(_ip), required(_required) {} Node(Public _pubk, NodeIPEndpoint const& _ip, bool _required = false): id(_pubk), endpoint(_ip), required(_required) {}
virtual NodeId const& address() const { return id; } virtual NodeId const& address() const { return id; }
virtual Public const& publicKey() const { return id; } virtual Public const& publicKey() const { return id; }

107
libp2p/Host.cpp

@ -176,7 +176,7 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
{ {
// session maybe ingress or egress so m_peers and node table entries may not exist // session maybe ingress or egress so m_peers and node table entries may not exist
shared_ptr<Peer> p; shared_ptr<Peer> p;
ETH_RECURSIVE_GUARDED(x_sessions) DEV_RECURSIVE_GUARDED(x_sessions)
{ {
if (m_peers.count(_id)) if (m_peers.count(_id))
p = m_peers[_id]; p = m_peers[_id];
@ -208,7 +208,7 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
// create session so disconnects are managed // create session so disconnects are managed
auto ps = make_shared<Session>(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet<CapDesc>(), 0, map<string, string>()})); auto ps = make_shared<Session>(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet<CapDesc>(), 0, map<string, string>()}));
if (protocolVersion < dev::p2p::c_protocolVersion) if (protocolVersion < dev::p2p::c_protocolVersion - 1)
{ {
ps->disconnect(IncompatibleProtocol); ps->disconnect(IncompatibleProtocol);
return; return;
@ -257,7 +257,7 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e)
if (Node n = m_nodeTable->node(_n)) if (Node n = m_nodeTable->node(_n))
{ {
shared_ptr<Peer> p; shared_ptr<Peer> p;
ETH_RECURSIVE_GUARDED(x_sessions) DEV_RECURSIVE_GUARDED(x_sessions)
{ {
if (m_peers.count(_n)) if (m_peers.count(_n))
{ {
@ -412,7 +412,7 @@ void Host::requirePeer(NodeId const& _n, NodeIPEndpoint const& _endpoint)
{ {
// create or update m_peers entry // create or update m_peers entry
shared_ptr<Peer> p; shared_ptr<Peer> p;
ETH_RECURSIVE_GUARDED(x_sessions) DEV_RECURSIVE_GUARDED(x_sessions)
if (m_peers.count(_n)) if (m_peers.count(_n))
{ {
p = m_peers[_n]; p = m_peers[_n];
@ -579,7 +579,7 @@ void Host::run(boost::system::error_code const&)
// todo: update peerSlotsAvailable() // todo: update peerSlotsAvailable()
unsigned pendingCount = 0; unsigned pendingCount = 0;
ETH_GUARDED(x_pendingNodeConns) DEV_GUARDED(x_pendingNodeConns)
pendingCount = m_pendingPeerConns.size(); pendingCount = m_pendingPeerConns.size();
int openSlots = m_idealPeerCount - peerCount() - pendingCount; int openSlots = m_idealPeerCount - peerCount() - pendingCount;
if (openSlots > 0) if (openSlots > 0)
@ -696,15 +696,16 @@ bytes Host::saveNetwork() const
int count = 0; int count = 0;
for (auto const& p: peers) for (auto const& p: peers)
{ {
// Only save peers which have connected within 2 days, with properly-advertised port and public IP address // todo: ipv6
// todo: e2e ipv6 support
if (!p.endpoint.address.is_v4()) if (!p.endpoint.address.is_v4())
continue; continue;
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && p.endpoint.tcpPort > 0 && p.id != id() && (p.required || p.endpoint.isAllowed())) // Only save peers which have connected within 2 days, with properly-advertised port and public IP address
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && !!p.endpoint && p.id != id() && (p.required || p.endpoint.isAllowed()))
{ {
network.appendList(10); network.appendList(11);
network << p.endpoint.address.to_v4().to_bytes() << p.endpoint.tcpPort << p.id << p.required p.endpoint.streamRLP(network, NodeIPEndpoint::StreamInline);
network << p.id << p.required
<< chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count() << chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count()
<< chrono::duration_cast<chrono::seconds>(p.m_lastAttempted.time_since_epoch()).count() << chrono::duration_cast<chrono::seconds>(p.m_lastAttempted.time_since_epoch()).count()
<< p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating; << p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating;
@ -718,12 +719,9 @@ bytes Host::saveNetwork() const
state.sort(); state.sort();
for (auto const& entry: state) for (auto const& entry: state)
{ {
network.appendList(3); network.appendList(4);
if (entry.endpoint.address.is_v4()) entry.endpoint.streamRLP(network, NodeIPEndpoint::StreamInline);
network << entry.endpoint.address.to_v4().to_bytes(); network << entry.id;
else
network << entry.endpoint.address.to_v6().to_bytes();
network << entry.endpoint.tcpPort << entry.id;
count++; count++;
} }
} }
@ -739,6 +737,9 @@ bytes Host::saveNetwork() const
void Host::restoreNetwork(bytesConstRef _b) void Host::restoreNetwork(bytesConstRef _b)
{ {
if (!_b.size())
return;
// nodes can only be added if network is added // nodes can only be added if network is added
if (!isStarted()) if (!isStarted())
BOOST_THROW_EXCEPTION(NetworkStartRequired()); BOOST_THROW_EXCEPTION(NetworkStartRequired());
@ -748,7 +749,8 @@ void Host::restoreNetwork(bytesConstRef _b)
RecursiveGuard l(x_sessions); RecursiveGuard l(x_sessions);
RLP r(_b); RLP r(_b);
if (r.itemCount() > 0 && r[0].isInt() && r[0].toInt<unsigned>() == dev::p2p::c_protocolVersion) unsigned fileVersion = r[0].toInt<unsigned>();
if (r.itemCount() > 0 && r[0].isInt() && fileVersion >= dev::p2p::c_protocolVersion - 1)
{ {
// r[0] = version // r[0] = version
// r[1] = key // r[1] = key
@ -756,30 +758,57 @@ void Host::restoreNetwork(bytesConstRef _b)
for (auto i: r[2]) for (auto i: r[2])
{ {
if (i[0].itemCount() != 4) // todo: ipv6
if (i[0].itemCount() != 4 && i[0].size() != 4)
continue; continue;
// todo: ipv6, bi::address_v6(i[0].toArray<byte, 16>() if (i.itemCount() == 4 || i.itemCount() == 11)
Node n((NodeId)i[2], NodeIPEndpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<uint16_t>(), i[1].toInt<uint16_t>()));
if (i.itemCount() == 3 && n.endpoint.isAllowed())
m_nodeTable->addNode(n, NodeTable::NodeRelation::Known);
else if (i.itemCount() == 10)
{ {
n.required = i[3].toInt<bool>(); Node n((NodeId)i[3], NodeIPEndpoint(i));
if (!n.endpoint.isAllowed() && !n.required) if (i.itemCount() == 4 && n.endpoint.isAllowed())
continue; m_nodeTable->addNode(n);
shared_ptr<Peer> p = make_shared<Peer>(n); else if (i.itemCount() == 11)
p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt<unsigned>())); {
p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>())); n.required = i[4].toInt<bool>();
p->m_failedAttempts = i[6].toInt<unsigned>(); if (!n.endpoint.isAllowed() && !n.required)
p->m_lastDisconnect = (DisconnectReason)i[7].toInt<unsigned>(); continue;
p->m_score = (int)i[8].toInt<unsigned>(); shared_ptr<Peer> p = make_shared<Peer>(n);
p->m_rating = (int)i[9].toInt<unsigned>(); p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>()));
m_peers[p->id] = p; p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[6].toInt<unsigned>()));
if (p->required) p->m_failedAttempts = i[7].toInt<unsigned>();
requirePeer(p->id, n.endpoint); p->m_lastDisconnect = (DisconnectReason)i[8].toInt<unsigned>();
else p->m_score = (int)i[9].toInt<unsigned>();
m_nodeTable->addNode(*p.get(), NodeTable::NodeRelation::Known); p->m_rating = (int)i[10].toInt<unsigned>();
m_peers[p->id] = p;
if (p->required)
requirePeer(p->id, n.endpoint);
else
m_nodeTable->addNode(*p.get(), NodeTable::NodeRelation::Known);
}
}
else if (i.itemCount() == 3 || i.itemCount() == 10)
{
Node n((NodeId)i[2], NodeIPEndpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<uint16_t>(), i[1].toInt<uint16_t>()));
if (i.itemCount() == 3 && n.endpoint.isAllowed())
m_nodeTable->addNode(n);
else if (i.itemCount() == 10)
{
n.required = i[3].toInt<bool>();
if (!n.endpoint.isAllowed() && !n.required)
continue;
shared_ptr<Peer> p = make_shared<Peer>(n);
p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt<unsigned>()));
p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>()));
p->m_failedAttempts = i[6].toInt<unsigned>();
p->m_lastDisconnect = (DisconnectReason)i[7].toInt<unsigned>();
p->m_score = (int)i[8].toInt<unsigned>();
p->m_rating = (int)i[9].toInt<unsigned>();
m_peers[p->id] = p;
if (p->required)
requirePeer(p->id, n.endpoint);
else
m_nodeTable->addNode(*p.get(), NodeTable::NodeRelation::Known);
}
} }
} }
} }
@ -788,7 +817,7 @@ void Host::restoreNetwork(bytesConstRef _b)
KeyPair Host::networkAlias(bytesConstRef _b) KeyPair Host::networkAlias(bytesConstRef _b)
{ {
RLP r(_b); RLP r(_b);
if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt<unsigned>() == dev::p2p::c_protocolVersion) if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt<unsigned>() >= 3)
return move(KeyPair(move(Secret(r[1].toBytes())))); return move(KeyPair(move(Secret(r[1].toBytes()))));
else else
return move(KeyPair::create()); return move(KeyPair::create());

125
libp2p/NodeTable.cpp

@ -75,12 +75,6 @@ void NodeTable::processEvents()
m_nodeEventHandler->processEvents(); m_nodeEventHandler->processEvents();
} }
shared_ptr<NodeEntry> NodeTable::addNode(Public const& _pubk, NodeIPEndpoint const& _ep)
{
auto node = Node(_pubk, _ep);
return addNode(node);
}
shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relation) shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relation)
{ {
if (_relation == Known) if (_relation == Known)
@ -92,13 +86,8 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relati
return ret; return ret;
} }
// re-enable tcp checks when NAT hosts are handled by discover if (!_node.endpoint)
// we handle when tcp endpoint is 0 below
if (_node.endpoint.address.to_string() == "0.0.0.0")
{
clog(NodeTableWarn) << "addNode Failed. Invalid UDP address" << LogTag::Url << "0.0.0.0" << "for" << _node.id;
return move(shared_ptr<NodeEntry>()); return move(shared_ptr<NodeEntry>());
}
// ping address to recover nodeid if nodeid is empty // ping address to recover nodeid if nodeid is empty
if (!_node.id) if (!_node.id)
@ -108,9 +97,7 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relati
Guard l(x_pubkDiscoverPings); Guard l(x_pubkDiscoverPings);
m_pubkDiscoverPings[_node.endpoint.address] = std::chrono::steady_clock::now(); m_pubkDiscoverPings[_node.endpoint.address] = std::chrono::steady_clock::now();
} }
PingNode p(_node.endpoint, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort); ping(_node.endpoint);
p.sign(m_secret);
m_socketPointer->send(p);
return move(shared_ptr<NodeEntry>()); return move(shared_ptr<NodeEntry>());
} }
@ -123,9 +110,7 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relati
shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, _node.endpoint)); shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, _node.endpoint));
m_nodes[_node.id] = ret; m_nodes[_node.id] = ret;
clog(NodeTableConnect) << "addNode pending for" << _node.endpoint; clog(NodeTableConnect) << "addNode pending for" << _node.endpoint;
PingNode p(_node.endpoint, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort); ping(_node.endpoint);
p.sign(m_secret);
m_socketPointer->send(p);
return ret; return ret;
} }
@ -153,8 +138,10 @@ list<NodeEntry> NodeTable::snapshot() const
list<NodeEntry> ret; list<NodeEntry> ret;
Guard l(x_state); Guard l(x_state);
for (auto s: m_state) for (auto s: m_state)
for (auto n: s.nodes) for (auto np: s.nodes)
ret.push_back(*n.lock()); if (auto n = np.lock())
if (!!n)
ret.push_back(*n);
return move(ret); return move(ret);
} }
@ -295,14 +282,14 @@ vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeId _target)
vector<shared_ptr<NodeEntry>> ret; vector<shared_ptr<NodeEntry>> ret;
for (auto& nodes: found) for (auto& nodes: found)
for (auto n: nodes.second) for (auto n: nodes.second)
if (n->endpoint.isAllowed()) if (ret.size() < s_bucketSize && !!n->endpoint && n->endpoint.isAllowed())
ret.push_back(n); ret.push_back(n);
return move(ret); return move(ret);
} }
void NodeTable::ping(bi::udp::endpoint _to) const void NodeTable::ping(NodeIPEndpoint _to) const
{ {
PingNode p(_to, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort); PingNode p(m_node.endpoint, _to);
p.sign(m_secret); p.sign(m_secret);
m_socketPointer->send(p); m_socketPointer->send(p);
} }
@ -467,12 +454,17 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
m_pubkDiscoverPings.erase(_from.address()); m_pubkDiscoverPings.erase(_from.address());
} }
if (!haveNode(nodeid)) if (!haveNode(nodeid))
addNode(nodeid, NodeIPEndpoint(_from.address(), _from.port(), _from.port())); addNode(Node(nodeid, NodeIPEndpoint(_from.address(), _from.port(), _from.port())));
} }
else else
return; // unsolicited pong; don't note node as active return; // unsolicited pong; don't note node as active
} }
// update our endpoint address and UDP port
if ((!m_node.endpoint || !m_node.endpoint.isAllowed()) && isPublicAddress(in.destination.address))
m_node.endpoint.address = in.destination.address;
m_node.endpoint.udpPort = in.destination.udpPort;
clog(NodeTableConnect) << "PONG from " << nodeid << _from; clog(NodeTableConnect) << "PONG from " << nodeid << _from;
break; break;
} }
@ -497,17 +489,22 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
} }
Neighbours in = Neighbours::fromBytesConstRef(_from, rlpBytes); Neighbours in = Neighbours::fromBytesConstRef(_from, rlpBytes);
for (auto n: in.nodes) for (auto n: in.neighbours)
addNode(n.node, NodeIPEndpoint(bi::address::from_string(n.ipAddress), n.udpPort, n.udpPort)); addNode(Node(n.node, n.endpoint));
break; break;
} }
case FindNode::type: case FindNode::type:
{ {
FindNode in = FindNode::fromBytesConstRef(_from, rlpBytes); FindNode in = FindNode::fromBytesConstRef(_from, rlpBytes);
if (RLPXDatagramFace::secondsSinceEpoch() > in.ts)
{
clog(NodeTableTriviaSummary) << "Received expired FindNode from " << _from.address().to_string() << ":" << _from.port();
return;
}
vector<shared_ptr<NodeEntry>> nearest = nearestNodeEntries(in.target); vector<shared_ptr<NodeEntry>> nearest = nearestNodeEntries(in.target);
static unsigned const nlimit = (m_socketPointer->maxDatagramSize - 111) / 87; static unsigned const nlimit = (m_socketPointer->maxDatagramSize - 109) / 90;
for (unsigned offset = 0; offset < nearest.size(); offset += nlimit) for (unsigned offset = 0; offset < nearest.size(); offset += nlimit)
{ {
Neighbours out(_from, nearest, offset, nlimit); Neighbours out(_from, nearest, offset, nlimit);
@ -522,17 +519,29 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
case PingNode::type: case PingNode::type:
{ {
PingNode in = PingNode::fromBytesConstRef(_from, rlpBytes); PingNode in = PingNode::fromBytesConstRef(_from, rlpBytes);
if (in.version != dev::p2p::c_protocolVersion) if (in.version < dev::p2p::c_protocolVersion)
{ {
if (auto n = nodeEntry(nodeid)) if (in.version == 3)
dropNode(n); {
return; compat::Pong p(in.source);
p.echo = sha3(rlpBytes);
p.sign(m_secret);
m_socketPointer->send(p);
}
else
return;
} }
// TODO: Feedback if _from.address() != in.ipAddress if (RLPXDatagramFace::secondsSinceEpoch() > in.ts)
addNode(nodeid, NodeIPEndpoint(_from.address(), _from.port(), in.tcpPort)); {
clog(NodeTableTriviaSummary) << "Received expired PingNode from " << _from.address().to_string() << ":" << _from.port();
return;
}
Pong p(_from); in.source.address = _from.address();
in.source.udpPort = _from.port();
addNode(Node(nodeid, in.source));
Pong p(in.source);
p.echo = sha3(rlpBytes); p.echo = sha3(rlpBytes);
p.sign(m_secret); p.sign(m_secret);
m_socketPointer->send(p); m_socketPointer->send(p);
@ -567,8 +576,8 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec)
bool evictionsRemain = false; bool evictionsRemain = false;
list<shared_ptr<NodeEntry>> drop; list<shared_ptr<NodeEntry>> drop;
{ {
Guard ln(x_nodes);
Guard le(x_evictions); Guard le(x_evictions);
Guard ln(x_nodes);
for (auto& e: m_evictions) for (auto& e: m_evictions)
if (chrono::steady_clock::now() - e.first.second > c_reqTimeout) if (chrono::steady_clock::now() - e.first.second > c_reqTimeout)
if (m_nodes.count(e.second)) if (m_nodes.count(e.second))
@ -608,26 +617,44 @@ void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec)
void PingNode::streamRLP(RLPStream& _s) const void PingNode::streamRLP(RLPStream& _s) const
{ {
_s.appendList(4); _s.appendList(4);
_s << dev::p2p::c_protocolVersion << ipAddress << tcpPort << ts; _s << dev::p2p::c_protocolVersion;
source.streamRLP(_s);
destination.streamRLP(_s);
_s << ts;
} }
void PingNode::interpretRLP(bytesConstRef _bytes) void PingNode::interpretRLP(bytesConstRef _bytes)
{ {
RLP r(_bytes); RLP r(_bytes);
if (r.itemCountStrict() == 3) if (r.itemCountStrict() == 4 && r[0].isInt() && r[0].toInt<unsigned>(RLP::Strict) == dev::p2p::c_protocolVersion)
{ {
version = 2; version = dev::p2p::c_protocolVersion;
ipAddress = r[0].toString(); source.interpretRLP(r[1]);
tcpPort = r[1].toInt<unsigned>(RLP::Strict); destination.interpretRLP(r[2]);
ts = r[2].toInt<unsigned>(RLP::Strict); ts = r[3].toInt<uint32_t>(RLP::Strict);
}
else if (r.itemCountStrict() == 4)
{
version = r[0].toInt<unsigned>(RLP::Strict);
ipAddress = r[1].toString();
tcpPort = r[2].toInt<unsigned>(RLP::Strict);
ts = r[3].toInt<unsigned>(RLP::Strict);
} }
else else
BOOST_THROW_EXCEPTION(InvalidRLP()); version = r[0].toInt<unsigned>(RLP::Strict);
}
void Pong::streamRLP(RLPStream& _s) const
{
_s.appendList(3);
destination.streamRLP(_s);
_s << echo << ts;
}
void Pong::interpretRLP(bytesConstRef _bytes)
{
RLP r(_bytes);
destination.interpretRLP(r[0]);
echo = (h256)r[1];
ts = r[2].toInt<uint32_t>();
}
void compat::Pong::interpretRLP(bytesConstRef _bytes)
{
RLP r(_bytes);
echo = (h256)r[0];
ts = r[1].toInt<uint32_t>();
} }

115
libp2p/NodeTable.h

@ -100,23 +100,15 @@ inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable)
* NodeTable accepts a port for UDP and will listen to the port on all available * NodeTable accepts a port for UDP and will listen to the port on all available
* interfaces. * interfaces.
* *
*
* [Integration]
* @todo TCP endpoints
* @todo GC uniform 1/32 entires at 112500ms interval
*
* [Optimization] * [Optimization]
* @todo serialize evictions per-bucket * @todo serialize evictions per-bucket
* @todo store evictions in map, unit-test eviction logic * @todo store evictions in map, unit-test eviction logic
* @todo store root node in table * @todo store root node in table
* @todo encapsulate discover into NetworkAlgorithm (task) * @todo encapsulate discover into NetworkAlgorithm (task)
* @todo Pong to include ip:port where ping was received
* @todo expiration and sha3(id) 'to' for messages which are replies (prevents replay) * @todo expiration and sha3(id) 'to' for messages which are replies (prevents replay)
* @todo cache Ping and FindSelf * @todo cache Ping and FindSelf
* *
* [Networking] * [Networking]
* @todo node-endpoint updates
* @todo TCP endpoints
* @todo eth/upnp/natpmp/stun/ice/etc for public-discovery * @todo eth/upnp/natpmp/stun/ice/etc for public-discovery
* @todo firewall * @todo firewall
* *
@ -148,9 +140,6 @@ public:
/// Called by implementation which provided handler to process NodeEntryAdded/NodeEntryDropped events. Events are coalesced by type whereby old events are ignored. /// Called by implementation which provided handler to process NodeEntryAdded/NodeEntryDropped events. Events are coalesced by type whereby old events are ignored.
void processEvents(); void processEvents();
/// Add node. Node will be pinged and empty shared_ptr is returned if NodeId is uknown.
std::shared_ptr<NodeEntry> addNode(Public const& _pubk, NodeIPEndpoint const& _ep);
/// Add node. Node will be pinged and empty shared_ptr is returned if node has never been seen or NodeId is empty. /// Add node. Node will be pinged and empty shared_ptr is returned if node has never been seen or NodeId is empty.
std::shared_ptr<NodeEntry> addNode(Node const& _node, NodeRelation _relation = NodeRelation::Unknown); std::shared_ptr<NodeEntry> addNode(Node const& _node, NodeRelation _relation = NodeRelation::Unknown);
@ -206,7 +195,7 @@ private:
}; };
/// Used to ping endpoint. /// Used to ping endpoint.
void ping(bi::udp::endpoint _to) const; void ping(NodeIPEndpoint _to) const;
/// Used ping known node. Used by node table when refreshing buckets and as part of eviction process (see evict). /// Used ping known node. Used by node table when refreshing buckets and as part of eviction process (see evict).
void ping(NodeEntry* _n) const; void ping(NodeEntry* _n) const;
@ -265,7 +254,7 @@ private:
mutable Mutex x_state; ///< LOCK x_state first if both x_nodes and x_state locks are required. mutable Mutex x_state; ///< LOCK x_state first if both x_nodes and x_state locks are required.
std::array<NodeBucket, s_bins> m_state; ///< State of p2p node network. std::array<NodeBucket, s_bins> m_state; ///< State of p2p node network.
Mutex x_evictions; ///< LOCK x_nodes first if both x_nodes and x_evictions locks are required. Mutex x_evictions; ///< LOCK x_evictions first if both x_nodes and x_evictions locks are required.
std::deque<EvictionTimeout> m_evictions; ///< Eviction timeouts. std::deque<EvictionTimeout> m_evictions; ///< Eviction timeouts.
Mutex x_pubkDiscoverPings; ///< LOCK x_nodes first if both x_nodes and x_pubkDiscoverPings locks are required. Mutex x_pubkDiscoverPings; ///< LOCK x_nodes first if both x_nodes and x_pubkDiscoverPings locks are required.
@ -301,30 +290,21 @@ struct InvalidRLP: public Exception {};
* a given bucket which is full, the least-responsive node is pinged. * a given bucket which is full, the least-responsive node is pinged.
* If the pinged node doesn't respond, then it is removed and the new * If the pinged node doesn't respond, then it is removed and the new
* node is inserted. * node is inserted.
*
* RLP Encoded Items: 3
* Minimum Encoded Size: 18 bytes
* Maximum Encoded Size: bytes // todo after u128 addresses
*
* signature: Signature of message.
* ipAddress: Our IP address.
* port: Our port.
*
* @todo uint128_t for ip address (<->integer ipv4/6, asio-address, asio-endpoint)
*
*/ */
struct PingNode: RLPXDatagram<PingNode> struct PingNode: RLPXDatagram<PingNode>
{ {
PingNode(bi::udp::endpoint _ep): RLPXDatagram<PingNode>(_ep) {} /// Constructor used for sending PingNode.
PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _ts = std::chrono::seconds(60)): RLPXDatagram<PingNode>(_ep), ipAddress(_src), tcpPort(_srcPort), ts(futureFromEpoch(_ts)) {} PingNode(NodeIPEndpoint _src, NodeIPEndpoint _dest): RLPXDatagram<PingNode>(_dest), source(_src), destination(_dest), ts(futureFromEpoch(std::chrono::seconds(60))) {}
/// Constructor used to create empty PingNode for parsing inbound packets.
PingNode(bi::udp::endpoint _ep): RLPXDatagram<PingNode>(_ep), source(UnspecifiedNodeIPEndpoint), destination(UnspecifiedNodeIPEndpoint) {}
static const uint8_t type = 1; static const uint8_t type = 1;
unsigned version = 0; unsigned version = 0;
std::string ipAddress; NodeIPEndpoint source;
// uint16_t udpPort; NodeIPEndpoint destination;
uint16_t tcpPort; uint32_t ts = 0;
unsigned ts;
void streamRLP(RLPStream& _s) const override; void streamRLP(RLPStream& _s) const override;
void interpretRLP(bytesConstRef _bytes) override; void interpretRLP(bytesConstRef _bytes) override;
@ -332,22 +312,20 @@ struct PingNode: RLPXDatagram<PingNode>
/** /**
* Pong packet: Sent in response to ping * Pong packet: Sent in response to ping
*
* RLP Encoded Items: 2
* Minimum Encoded Size: 33 bytes
* Maximum Encoded Size: 33 bytes
*/ */
struct Pong: RLPXDatagram<Pong> struct Pong: RLPXDatagram<Pong>
{ {
Pong(bi::udp::endpoint _ep): RLPXDatagram<Pong>(_ep), ts(futureFromEpoch(std::chrono::seconds(60))) {} Pong(bi::udp::endpoint const& _ep): RLPXDatagram<Pong>(_ep), destination(UnspecifiedNodeIPEndpoint) {}
Pong(NodeIPEndpoint const& _dest): RLPXDatagram<Pong>((bi::udp::endpoint)_dest), destination(_dest), ts(futureFromEpoch(std::chrono::seconds(60))) {}
static const uint8_t type = 2; static const uint8_t type = 2;
NodeIPEndpoint destination;
h256 echo; ///< MCD of PingNode h256 echo; ///< MCD of PingNode
unsigned ts; uint32_t ts = 0;
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << echo << ts; } void streamRLP(RLPStream& _s) const;
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); echo = (h256)r[0]; ts = r[1].toInt<unsigned>(); } void interpretRLP(bytesConstRef _bytes);
}; };
/** /**
@ -365,59 +343,64 @@ struct Pong: RLPXDatagram<Pong>
struct FindNode: RLPXDatagram<FindNode> struct FindNode: RLPXDatagram<FindNode>
{ {
FindNode(bi::udp::endpoint _ep): RLPXDatagram<FindNode>(_ep) {} FindNode(bi::udp::endpoint _ep): RLPXDatagram<FindNode>(_ep) {}
FindNode(bi::udp::endpoint _ep, NodeId _target, std::chrono::seconds _ts = std::chrono::seconds(60)): RLPXDatagram<FindNode>(_ep), target(_target), ts(futureFromEpoch(_ts)) {} FindNode(bi::udp::endpoint _ep, NodeId _target): RLPXDatagram<FindNode>(_ep), target(_target), ts(futureFromEpoch(std::chrono::seconds(60))) {}
static const uint8_t type = 3; static const uint8_t type = 3;
h512 target; h512 target;
unsigned ts; uint32_t ts = 0;
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << ts; } void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << ts; }
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = r[0].toHash<h512>(); ts = r[1].toInt<unsigned>(); } void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = r[0].toHash<h512>(); ts = r[1].toInt<uint32_t>(); }
}; };
/** /**
* Node Packet: Multiple node packets are sent in response to FindNode. * Node Packet: One or more node packets are sent in response to FindNode.
*
* RLP Encoded Items: 2 (first item is list)
* Minimum Encoded Size: 10 bytes
*/ */
struct Neighbours: RLPXDatagram<Neighbours> struct Neighbours: RLPXDatagram<Neighbours>
{ {
struct Node struct Neighbour
{ {
Node() = default; Neighbour(Node const& _node): endpoint(_node.endpoint), node(_node.id) {}
Node(RLP const& _r) { interpretRLP(_r); } Neighbour(RLP const& _r): endpoint(_r) { node = h512(_r[3].toBytes()); }
std::string ipAddress; NodeIPEndpoint endpoint;
uint16_t udpPort;
// uint16_t tcpPort;
NodeId node; NodeId node;
void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << udpPort << node; } void streamRLP(RLPStream& _s) const { _s.appendList(4); endpoint.streamRLP(_s, NodeIPEndpoint::StreamInline); _s << node; }
void interpretRLP(RLP const& _r) { ipAddress = _r[0].toString(); udpPort = _r[1].toInt<uint16_t>(); node = h512(_r[2].toBytes()); }
}; };
Neighbours(bi::udp::endpoint _ep): RLPXDatagram<Neighbours>(_ep), ts(futureFromEpoch(std::chrono::seconds(30))) {} Neighbours(bi::udp::endpoint _ep): RLPXDatagram<Neighbours>(_ep), ts(secondsSinceEpoch()) {}
Neighbours(bi::udp::endpoint _to, std::vector<std::shared_ptr<NodeEntry>> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram<Neighbours>(_to), ts(futureFromEpoch(std::chrono::seconds(30))) Neighbours(bi::udp::endpoint _to, std::vector<std::shared_ptr<NodeEntry>> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram<Neighbours>(_to), ts(futureFromEpoch(std::chrono::seconds(60)))
{ {
auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size(); auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size();
for (auto i = _offset; i < limit; i++) for (auto i = _offset; i < limit; i++)
{ neighbours.push_back(Neighbour(*_nearest[i]));
Node node;
node.ipAddress = _nearest[i]->endpoint.address.to_string();
node.udpPort = _nearest[i]->endpoint.udpPort;
node.node = _nearest[i]->publicKey();
nodes.push_back(node);
}
} }
static const uint8_t type = 4; static const uint8_t type = 4;
std::vector<Node> nodes; std::vector<Neighbour> neighbours;
unsigned ts = 1; uint32_t ts = 0;
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << ts; } void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(neighbours.size()); for (auto& n: neighbours) n.streamRLP(_s); _s << ts; }
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n)); ts = r[1].toInt<unsigned>(); } void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) neighbours.push_back(Neighbour(n)); ts = r[1].toInt<uint32_t>(); }
}; };
namespace compat
{
/**
* Pong packet [compatability]: Sent in response to ping
*/
struct Pong: RLPXDatagram<Pong>
{
Pong(bi::udp::endpoint const& _ep): RLPXDatagram<Pong>(_ep) {}
Pong(NodeIPEndpoint const& _dest): RLPXDatagram<Pong>((bi::udp::endpoint)_dest), ts(futureFromEpoch(std::chrono::seconds(60))) {}
static const uint8_t type = 2;
h256 echo;
uint32_t ts = 0;
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << echo << ts; }
void interpretRLP(bytesConstRef _bytes);
};
}
struct NodeTableWarn: public LogChannel { static const char* name(); static const int verbosity = 0; }; struct NodeTableWarn: public LogChannel { static const char* name(); static const int verbosity = 0; };
struct NodeTableNote: public LogChannel { static const char* name(); static const int verbosity = 1; }; struct NodeTableNote: public LogChannel { static const char* name(); static const int verbosity = 1; };
struct NodeTableMessageSummary: public LogChannel { static const char* name(); static const int verbosity = 2; }; struct NodeTableMessageSummary: public LogChannel { static const char* name(); static const int verbosity = 2; };

4
libp2p/UDP.h

@ -61,8 +61,8 @@ protected:
*/ */
struct RLPXDatagramFace: public UDPDatagram struct RLPXDatagramFace: public UDPDatagram
{ {
static uint64_t futureFromEpoch(std::chrono::milliseconds _ms) { return std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now() + _ms).time_since_epoch()).count(); } static uint32_t futureFromEpoch(std::chrono::seconds _sec) { return std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); }
static uint64_t futureFromEpoch(std::chrono::seconds _sec) { return std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); } static uint32_t secondsSinceEpoch() { return std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now()).time_since_epoch()).count(); }
static Public authenticate(bytesConstRef _sig, bytesConstRef _rlp); static Public authenticate(bytesConstRef _sig, bytesConstRef _rlp);
virtual uint8_t packetType() = 0; virtual uint8_t packetType() = 0;

9
libsolidity/ASTPrinter.cpp

@ -30,8 +30,11 @@ namespace dev
namespace solidity namespace solidity
{ {
ASTPrinter::ASTPrinter(ASTNode const& _ast, string const& _source): ASTPrinter::ASTPrinter(
m_indentation(0), m_source(_source), m_ast(&_ast) ASTNode const& _ast,
string const& _source,
StructuralGasEstimator::ASTGasConsumption const& _gasCosts
): m_indentation(0), m_source(_source), m_ast(&_ast), m_gasCosts(_gasCosts)
{ {
} }
@ -503,6 +506,8 @@ void ASTPrinter::endVisit(Literal const&)
void ASTPrinter::printSourcePart(ASTNode const& _node) void ASTPrinter::printSourcePart(ASTNode const& _node)
{ {
if (m_gasCosts.count(&_node))
*m_ostream << getIndentation() << " Gas costs: " << m_gasCosts.at(&_node) << endl;
if (!m_source.empty()) if (!m_source.empty())
{ {
SourceLocation const& location(_node.getLocation()); SourceLocation const& location(_node.getLocation());

8
libsolidity/ASTPrinter.h

@ -24,6 +24,7 @@
#include <ostream> #include <ostream>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
#include <libsolidity/StructuralGasEstimator.h>
namespace dev namespace dev
{ {
@ -38,7 +39,11 @@ class ASTPrinter: public ASTConstVisitor
public: public:
/// Create a printer for the given abstract syntax tree. If the source is specified, /// Create a printer for the given abstract syntax tree. If the source is specified,
/// the corresponding parts of the source are printed with each node. /// the corresponding parts of the source are printed with each node.
ASTPrinter(ASTNode const& _ast, std::string const& _source = std::string()); ASTPrinter(
ASTNode const& _ast,
std::string const& _source = std::string(),
StructuralGasEstimator::ASTGasConsumption const& _gasCosts = {}
);
/// Output the string representation of the AST to _stream. /// Output the string representation of the AST to _stream.
void print(std::ostream& _stream); void print(std::ostream& _stream);
@ -128,6 +133,7 @@ private:
int m_indentation; int m_indentation;
std::string m_source; std::string m_source;
ASTNode const* m_ast; ASTNode const* m_ast;
StructuralGasEstimator::ASTGasConsumption m_gasCosts;
std::ostream* m_ostream; std::ostream* m_ostream;
}; };

44
libsolidity/ASTVisitor.h

@ -23,6 +23,8 @@
#pragma once #pragma once
#include <string> #include <string>
#include <functional>
#include <vector>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
namespace dev namespace dev
@ -218,5 +220,47 @@ protected:
virtual void endVisitNode(ASTNode const&) { } virtual void endVisitNode(ASTNode const&) { }
}; };
/**
* Utility class that visits the AST in depth-first order and calls a function on each node and each edge.
* Child nodes are only visited if the node callback of the parent returns true.
* The node callback of a parent is called before any edge or node callback involving the children.
* The edge callbacks of all children are called before the edge callback of the parent.
* This way, the node callback can be used as an initializing callback and the edge callbacks can be
* used to compute a "reduce" function.
*/
class ASTReduce: public ASTConstVisitor
{
public:
/**
* Constructs a new ASTReduce object with the given callback functions.
* @param _onNode called for each node, before its child edges and nodes, should return true to descend deeper
* @param _onEdge called for each edge with (parent, child)
*/
ASTReduce(
std::function<bool(ASTNode const&)> _onNode,
std::function<void(ASTNode const&, ASTNode const&)> _onEdge
): m_onNode(_onNode), m_onEdge(_onEdge)
{
}
protected:
bool visitNode(ASTNode const& _node) override
{
m_parents.push_back(&_node);
return m_onNode(_node);
}
void endVisitNode(ASTNode const& _node) override
{
m_parents.pop_back();
if (!m_parents.empty())
m_onEdge(*m_parents.back(), _node);
}
private:
std::vector<ASTNode const*> m_parents;
std::function<bool(ASTNode const&)> m_onNode;
std::function<void(ASTNode const&, ASTNode const&)> m_onEdge;
};
} }
} }

12
libsolidity/CompilerStack.cpp

@ -257,6 +257,18 @@ bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimiz
return stack.compile(_sourceCode, _optimize); return stack.compile(_sourceCode, _optimize);
} }
tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocation const& _sourceLocation) const
{
int startLine;
int startColumn;
int endLine;
int endColumn;
tie(startLine, startColumn) = getScanner(*_sourceLocation.sourceName).translatePositionToLineColumn(_sourceLocation.start);
tie(endLine, endColumn) = getScanner(*_sourceLocation.sourceName).translatePositionToLineColumn(_sourceLocation.end);
return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn);
}
void CompilerStack::reset(bool _keepSources) void CompilerStack::reset(bool _keepSources)
{ {
m_parseSuccessful = false; m_parseSuccessful = false;

6
libsolidity/CompilerStack.h

@ -31,6 +31,7 @@
#include <json/json.h> #include <json/json.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h> #include <libdevcore/FixedHash.h>
#include <libevmasm/SourceLocation.h>
namespace dev namespace dev
{ {
@ -131,6 +132,11 @@ public:
/// scanning the source code - this is useful for printing exception information. /// scanning the source code - this is useful for printing exception information.
static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false); static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false);
/// Helper function for logs printing. Do only use in error cases, it's quite expensive.
/// line and columns are numbered starting from 1 with following order:
/// start line, start column, end line, end column
std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const;
private: private:
/** /**
* Information pertaining to one source unit, filled gradually during parsing and compilation. * Information pertaining to one source unit, filled gradually during parsing and compilation.

110
libsolidity/StructuralGasEstimator.cpp

@ -0,0 +1,110 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2015
* Gas consumption estimator working alongside the AST.
*/
#include "StructuralGasEstimator.h"
#include <map>
#include <functional>
#include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::solidity;
StructuralGasEstimator::ASTGasConsumptionSelfAccumulated StructuralGasEstimator::performEstimation(
AssemblyItems const& _items,
vector<ASTNode const*> const& _ast
)
{
solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
map<SourceLocation, GasMeter::GasConsumption> particularCosts;
GasMeter meter;
for (auto const& item: _items)
particularCosts[item.getLocation()] += meter.estimateMax(item);
ASTGasConsumptionSelfAccumulated gasCosts;
auto onNode = [&](ASTNode const& _node)
{
gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.getLocation()];
return true;
};
auto onEdge = [&](ASTNode const& _parent, ASTNode const& _child)
{
gasCosts[&_parent][1] += gasCosts[&_child][1];
};
ASTReduce folder(onNode, onEdge);
for (ASTNode const* ast: _ast)
ast->accept(folder);
return gasCosts;
}
map<ASTNode const*, GasMeter::GasConsumption> StructuralGasEstimator::breakToStatementLevel(
ASTGasConsumptionSelfAccumulated const& _gasCosts,
vector<ASTNode const*> const& _roots
)
{
solAssert(std::count(_roots.begin(), _roots.end(), nullptr) == 0, "");
// first pass: statementDepth[node] is the distance from the deepend statement to node
// in direction of the tree root (or undefined if not possible)
map<ASTNode const*, int> statementDepth;
auto onNodeFirstPass = [&](ASTNode const& _node)
{
if (dynamic_cast<Statement const*>(&_node))
statementDepth[&_node] = 0;
return true;
};
auto onEdgeFirstPass = [&](ASTNode const& _parent, ASTNode const& _child)
{
if (statementDepth.count(&_child))
statementDepth[&_parent] = max(statementDepth[&_parent], statementDepth[&_child] + 1);
};
ASTReduce firstPass(onNodeFirstPass, onEdgeFirstPass);
for (ASTNode const* node: _roots)
node->accept(firstPass);
// we use the location of a node if
// - its statement depth is 0 or
// - its statement depth is undefined but the parent's statement depth is at least 1
map<ASTNode const*, GasMeter::GasConsumption> gasCosts;
auto onNodeSecondPass = [&](ASTNode const& _node)
{
return statementDepth.count(&_node);
};
auto onEdgeSecondPass = [&](ASTNode const& _parent, ASTNode const& _child)
{
bool useNode = false;
if (statementDepth.count(&_child))
useNode = statementDepth[&_child] == 0;
else
useNode = statementDepth.count(&_parent) && statementDepth.at(&_parent) > 0;
if (useNode)
gasCosts[&_child] = _gasCosts.at(&_child)[1];
};
ASTReduce secondPass(onNodeSecondPass, onEdgeSecondPass);
for (ASTNode const* node: _roots)
node->accept(secondPass);
// gasCosts should only contain non-overlapping locations
return gasCosts;
}

62
libsolidity/StructuralGasEstimator.h

@ -0,0 +1,62 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2015
* Gas consumption estimator working alongside the AST.
*/
#pragma once
#include <vector>
#include <map>
#include <array>
#include <libsolidity/ASTForward.h>
#include <libevmasm/GasMeter.h>
#include <libevmasm/Assembly.h>
namespace dev
{
namespace solidity
{
class StructuralGasEstimator
{
public:
using ASTGasConsumption = std::map<ASTNode const*, eth::GasMeter::GasConsumption>;
using ASTGasConsumptionSelfAccumulated =
std::map<ASTNode const*, std::array<eth::GasMeter::GasConsumption, 2>>;
/// Estimates the gas consumption for every assembly item in the given assembly and stores
/// it by source location.
/// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs.
ASTGasConsumptionSelfAccumulated performEstimation(
eth::AssemblyItems const& _items,
std::vector<ASTNode const*> const& _ast
);
/// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that
/// the following source locations are part of the mapping:
/// 1. source locations of statements that do not contain other statements
/// 2. maximal source locations that do not overlap locations coming from the first rule
ASTGasConsumption breakToStatementLevel(
ASTGasConsumptionSelfAccumulated const& _gasCosts,
std::vector<ASTNode const*> const& _roots
);
};
}
}

4
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -502,9 +502,9 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json)
t.from = m_accounts->getDefaultTransactAccount(); t.from = m_accounts->getDefaultTransactAccount();
if (t.creation) if (t.creation)
ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));; ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));;
if (!t.gasPrice) if (t.gasPrice == UndefinedU256)
t.gasPrice = 10 * dev::eth::szabo; // TODO: should be determined by user somehow. t.gasPrice = 10 * dev::eth::szabo; // TODO: should be determined by user somehow.
if (!t.gas) if (t.gas == UndefinedU256)
t.gas = min<u256>(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice); t.gas = min<u256>(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice);
if (m_accounts->isRealAccount(t.from)) if (m_accounts->isRealAccount(t.from))

18
solc/CommandLineInterface.cpp

@ -42,6 +42,7 @@
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
#include <libsolidity/StructuralGasEstimator.h>
using namespace std; using namespace std;
namespace po = boost::program_options; namespace po = boost::program_options;
@ -464,6 +465,17 @@ void CommandLineInterface::handleAst(string const& _argStr)
// do we need AST output? // do we need AST output?
if (m_args.count(_argStr)) if (m_args.count(_argStr))
{ {
StructuralGasEstimator gasEstimator;
vector<ASTNode const*> asts;
for (auto const& sourceCode: m_sourceCodes)
asts.push_back(&m_compiler->getAST(sourceCode.first));
map<ASTNode const*, eth::GasMeter::GasConsumption> gasCosts;
if (m_compiler->getRuntimeAssemblyItems())
gasCosts = gasEstimator.breakToStatementLevel(
gasEstimator.performEstimation(*m_compiler->getRuntimeAssemblyItems(), asts),
asts
);
auto choice = m_args[_argStr].as<OutputType>(); auto choice = m_args[_argStr].as<OutputType>();
if (outputToStdout(choice)) if (outputToStdout(choice))
{ {
@ -473,7 +485,11 @@ void CommandLineInterface::handleAst(string const& _argStr)
cout << endl << "======= " << sourceCode.first << " =======" << endl; cout << endl << "======= " << sourceCode.first << " =======" << endl;
if (_argStr == g_argAstStr) if (_argStr == g_argAstStr)
{ {
ASTPrinter printer(m_compiler->getAST(sourceCode.first), sourceCode.second); ASTPrinter printer(
m_compiler->getAST(sourceCode.first),
sourceCode.second,
gasCosts
);
printer.print(cout); printer.print(cout);
} }
else else

98
test/GasMeter.cpp

@ -0,0 +1,98 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2015
* Unit tests for the gas estimator.
*/
#include <test/libsolidity/solidityExecutionFramework.h>
#include <libsolidity/AST.h>
#include <libsolidity/StructuralGasEstimator.h>
#include <libsolidity/SourceReferenceFormatter.h>
using namespace std;
using namespace dev::eth;
using namespace dev::solidity;
namespace dev
{
namespace solidity
{
namespace test
{
class GasMeterTestFramework: public ExecutionFramework
{
public:
GasMeterTestFramework() { }
void compile(string const& _sourceCode)
{
m_compiler.setSource(_sourceCode);
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(), "Compiling contract failed");
StructuralGasEstimator estimator;
AssemblyItems const* items = m_compiler.getRuntimeAssemblyItems("");
ASTNode const& sourceUnit = m_compiler.getAST();
BOOST_REQUIRE(items != nullptr);
m_gasCosts = estimator.breakToStatementLevel(
estimator.performEstimation(*items, vector<ASTNode const*>({&sourceUnit})),
{&sourceUnit}
);
}
protected:
dev::solidity::CompilerStack m_compiler;
map<ASTNode const*, eth::GasMeter::GasConsumption> m_gasCosts;
};
BOOST_FIXTURE_TEST_SUITE(GasMeterTests, GasMeterTestFramework)
BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs)
{
char const* sourceCode = R"(
contract test {
bytes x;
function f(uint a) returns (uint b) {
x.length = a;
for (; a < 200; ++a) {
x[a] = 9;
b = a * a;
}
return f(a - 1);
}
}
)";
compile(sourceCode);
for (auto first = m_gasCosts.cbegin(); first != m_gasCosts.cend(); ++first)
{
auto second = first;
for (++second; second != m_gasCosts.cend(); ++second)
if (first->first->getLocation().intersects(second->first->getLocation()))
{
BOOST_CHECK_MESSAGE(false, "Source locations should not overlap!");
SourceReferenceFormatter::printSourceLocation(cout, first->first->getLocation(), m_compiler.getScanner());
SourceReferenceFormatter::printSourceLocation(cout, second->first->getLocation(), m_compiler.getScanner());
}
}
}
BOOST_AUTO_TEST_SUITE_END()
}
}
}

4060
test/libethereum/BlockTestsFiller/bcWalletTestFiller.json

File diff suppressed because one or more lines are too long

90
test/libethereum/StateTestsFiller/stCallCreateCallCodeTestFiller.json

@ -618,6 +618,96 @@
} }
}, },
"createNameRegistratorPreStore1NotEnoughGas": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "100000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : "0",
"code" : "{(MSTORE 0 0x6001600155601080600c6000396000f3006000355415600957005b6020356000 ) (MSTORE8 32 0x35) (MSTORE8 33 0x55) (CREATE 23 0 34) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "0x0129ef",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"createNameRegistratorPerTxs": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "10000000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "0xb44e",
"to" : "",
"value" : "100000",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : "0x6001600155601080600c6000396000f3006000355415600957005b60203560003555"
}
},
"createNameRegistratorPerTxsNotEnoughGas": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "10000000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "0xb44d",
"to" : "",
"value" : "100000",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : "0x6001600155601080600c6000396000f3006000355415600957005b60203560003555"
}
},
"createNameRegistratorendowmentTooHigh": { "createNameRegistratorendowmentTooHigh": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",

42
test/libethereum/StateTestsFiller/stSpecialTestFiller.json

@ -1,5 +1,5 @@
{ {
"makeMoney" : { "makeMoney" : {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -8,7 +8,7 @@
"currentTimestamp" : 1, "currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
}, },
"expect" : { "expect" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000" "balance" : "1000000000000000000"
}, },
@ -60,7 +60,7 @@
"currentTimestamp" : 1, "currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
}, },
"expect" : { "expect" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000" "balance" : "1000"
} }
@ -84,5 +84,41 @@
"to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", "to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"value" : "501" "value" : "501"
} }
},
"gasPrice0" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0xa033",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
} }
} }

1289
test/libethereum/StateTestsFiller/stWalletTestFiller.json

File diff suppressed because one or more lines are too long

5
test/libethereum/blockchain.cpp

@ -704,6 +704,11 @@ BOOST_AUTO_TEST_CASE(bcGasPricerTest)
dev::test::executeTests("bcGasPricerTest", "/BlockTests",dev::test::getFolder(__FILE__) + "/BlockTestsFiller", dev::test::doBlockchainTests); dev::test::executeTests("bcGasPricerTest", "/BlockTests",dev::test::getFolder(__FILE__) + "/BlockTestsFiller", dev::test::doBlockchainTests);
} }
BOOST_AUTO_TEST_CASE(bcWalletTest)
{
dev::test::executeTests("bcWalletTest", "/BlockTests",dev::test::getFolder(__FILE__) + "/BlockTestsFiller", dev::test::doBlockchainTests);
}
BOOST_AUTO_TEST_CASE(userDefinedFile) BOOST_AUTO_TEST_CASE(userDefinedFile)
{ {
dev::test::userDefinedTest("--singletest", dev::test::doBlockchainTests); dev::test::userDefinedTest("--singletest", dev::test::doBlockchainTests);

4
test/libethereum/state.cpp

@ -188,6 +188,10 @@ BOOST_AUTO_TEST_CASE(stMemoryTest)
dev::test::executeTests("stMemoryTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); dev::test::executeTests("stMemoryTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);
} }
BOOST_AUTO_TEST_CASE(stWalletTest)
{
dev::test::executeTests("stWalletTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stCreateTest) BOOST_AUTO_TEST_CASE(stCreateTest)
{ {

38
test/libp2p/net.cpp

@ -82,7 +82,7 @@ struct TestNodeTable: public NodeTable
bi::address ourIp = bi::address::from_string("127.0.0.1"); bi::address ourIp = bi::address::from_string("127.0.0.1");
for (auto& n: _testNodes) for (auto& n: _testNodes)
{ {
ping(bi::udp::endpoint(ourIp, n.second)); ping(NodeIPEndpoint(ourIp, n.second, n.second));
this_thread::sleep_for(chrono::milliseconds(2)); this_thread::sleep_for(chrono::milliseconds(2));
} }
} }
@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE(v2PingNodePacket)
PingNode p((bi::udp::endpoint())); PingNode p((bi::udp::endpoint()));
BOOST_REQUIRE_NO_THROW(p = PingNode::fromBytesConstRef(bi::udp::endpoint(), bytesConstRef(&s.out()))); BOOST_REQUIRE_NO_THROW(p = PingNode::fromBytesConstRef(bi::udp::endpoint(), bytesConstRef(&s.out())));
BOOST_REQUIRE(p.version == 2); BOOST_REQUIRE(p.version == 0);
} }
BOOST_AUTO_TEST_CASE(neighboursPacketLength) BOOST_AUTO_TEST_CASE(neighboursPacketLength)
@ -235,8 +235,8 @@ BOOST_AUTO_TEST_CASE(neighboursPacketLength)
std::vector<std::pair<KeyPair,unsigned>> testNodes(TestNodeTable::createTestNodes(16)); std::vector<std::pair<KeyPair,unsigned>> testNodes(TestNodeTable::createTestNodes(16));
bi::udp::endpoint to(boost::asio::ip::address::from_string("127.0.0.1"), 30000); bi::udp::endpoint to(boost::asio::ip::address::from_string("127.0.0.1"), 30000);
// hash(32), signature(65), overhead: packet(2), type(1), nodeList(2), ts(9), // hash(32), signature(65), overhead: packetSz(3), type(1), nodeListSz(3), ts(5),
static unsigned const nlimit = (1280 - 111) / 87; static unsigned const nlimit = (1280 - 109) / 90; // neighbour: 2 + 65 + 3 + 3 + 17
for (unsigned offset = 0; offset < testNodes.size(); offset += nlimit) for (unsigned offset = 0; offset < testNodes.size(); offset += nlimit)
{ {
Neighbours out(to); Neighbours out(to);
@ -244,11 +244,9 @@ BOOST_AUTO_TEST_CASE(neighboursPacketLength)
auto limit = nlimit ? std::min(testNodes.size(), (size_t)(offset + nlimit)) : testNodes.size(); auto limit = nlimit ? std::min(testNodes.size(), (size_t)(offset + nlimit)) : testNodes.size();
for (auto i = offset; i < limit; i++) for (auto i = offset; i < limit; i++)
{ {
Neighbours::Node node; Node n(testNodes[i].first.pub(), NodeIPEndpoint(boost::asio::ip::address::from_string("200.200.200.200"), testNodes[i].second, testNodes[i].second));
node.ipAddress = boost::asio::ip::address::from_string("200.200.200.200").to_string(); Neighbours::Neighbour neighbour(n);
node.udpPort = testNodes[i].second; out.neighbours.push_back(neighbour);
node.node = testNodes[i].first.pub();
out.nodes.push_back(node);
} }
out.sign(k.sec()); out.sign(k.sec());
@ -256,7 +254,7 @@ BOOST_AUTO_TEST_CASE(neighboursPacketLength)
} }
} }
BOOST_AUTO_TEST_CASE(test_neighbours_packet) BOOST_AUTO_TEST_CASE(neighboursPacket)
{ {
KeyPair k = KeyPair::create(); KeyPair k = KeyPair::create();
std::vector<std::pair<KeyPair,unsigned>> testNodes(TestNodeTable::createTestNodes(16)); std::vector<std::pair<KeyPair,unsigned>> testNodes(TestNodeTable::createTestNodes(16));
@ -265,11 +263,9 @@ BOOST_AUTO_TEST_CASE(test_neighbours_packet)
Neighbours out(to); Neighbours out(to);
for (auto n: testNodes) for (auto n: testNodes)
{ {
Neighbours::Node node; Node node(n.first.pub(), NodeIPEndpoint(boost::asio::ip::address::from_string("200.200.200.200"), n.second, n.second));
node.ipAddress = boost::asio::ip::address::from_string("127.0.0.1").to_string(); Neighbours::Neighbour neighbour(node);
node.udpPort = n.second; out.neighbours.push_back(neighbour);
node.node = n.first.pub();
out.nodes.push_back(node);
} }
out.sign(k.sec()); out.sign(k.sec());
@ -277,9 +273,9 @@ BOOST_AUTO_TEST_CASE(test_neighbours_packet)
bytesConstRef rlpBytes(packet.cropped(h256::size + Signature::size + 1)); bytesConstRef rlpBytes(packet.cropped(h256::size + Signature::size + 1));
Neighbours in = Neighbours::fromBytesConstRef(to, rlpBytes); Neighbours in = Neighbours::fromBytesConstRef(to, rlpBytes);
int count = 0; int count = 0;
for (auto n: in.nodes) for (auto n: in.neighbours)
{ {
BOOST_REQUIRE_EQUAL(testNodes[count].second, n.udpPort); BOOST_REQUIRE_EQUAL(testNodes[count].second, n.endpoint.udpPort);
BOOST_REQUIRE_EQUAL(testNodes[count].first.pub(), n.node); BOOST_REQUIRE_EQUAL(testNodes[count].first.pub(), n.node);
BOOST_REQUIRE_EQUAL(sha3(testNodes[count].first.pub()), sha3(n.node)); BOOST_REQUIRE_EQUAL(sha3(testNodes[count].first.pub()), sha3(n.node));
count++; count++;
@ -293,12 +289,6 @@ BOOST_AUTO_TEST_CASE(test_findnode_neighbours)
// into the same list of nearest nodes. // into the same list of nearest nodes.
} }
BOOST_AUTO_TEST_CASE(test_windows_template)
{
bi::udp::endpoint ep;
PingNode p(ep);
}
BOOST_AUTO_TEST_CASE(kademlia) BOOST_AUTO_TEST_CASE(kademlia)
{ {
// Not yet a 'real' test. // Not yet a 'real' test.
@ -332,7 +322,7 @@ BOOST_AUTO_TEST_CASE(kademlia)
} }
BOOST_AUTO_TEST_CASE(test_udp_once) BOOST_AUTO_TEST_CASE(udpOnce)
{ {
UDPDatagram d(bi::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300), bytes({65,65,65,65})); UDPDatagram d(bi::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300), bytes({65,65,65,65}));
TestUDPSocket a; a.m_socket->connect(); a.start(); TestUDPSocket a; a.m_socket->connect(); a.start();

8
test/libp2p/peer.cpp

@ -51,6 +51,8 @@ BOOST_AUTO_TEST_CASE(host)
auto node2 = host2.id(); auto node2 = host2.id();
host2.start(); host2.start();
while (!host2.isStarted())
this_thread::sleep_for(chrono::milliseconds(20));
host1.addNode(node2, NodeIPEndpoint(bi::address::from_string("127.0.0.1"), host2prefs.listenPort, host2prefs.listenPort)); host1.addNode(node2, NodeIPEndpoint(bi::address::from_string("127.0.0.1"), host2prefs.listenPort, host2prefs.listenPort));
this_thread::sleep_for(chrono::seconds(3)); this_thread::sleep_for(chrono::seconds(3));
@ -72,7 +74,7 @@ BOOST_AUTO_TEST_CASE(networkConfig)
BOOST_REQUIRE(save.id() == restore.id()); BOOST_REQUIRE(save.id() == restore.id());
} }
BOOST_AUTO_TEST_CASE(save_nodes) BOOST_AUTO_TEST_CASE(saveNodes)
{ {
std::list<Host*> hosts; std::list<Host*> hosts;
for (auto i:{0,1,2,3,4,5}) for (auto i:{0,1,2,3,4,5})
@ -111,8 +113,8 @@ BOOST_AUTO_TEST_CASE(save_nodes)
for (auto i: r[2]) for (auto i: r[2])
{ {
BOOST_REQUIRE(i.itemCount() == 3 || i.itemCount() == 10); BOOST_REQUIRE(i.itemCount() == 4 || i.itemCount() == 11);
BOOST_REQUIRE(i[0].itemCount() == 4 || i[0].itemCount() == 16); BOOST_REQUIRE(i[0].size() == 4 || i[0].size() == 16);
} }
} }

Loading…
Cancel
Save