diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp
index 62515f171..e5504cab2 100644
--- a/alethzero/MainWin.cpp
+++ b/alethzero/MainWin.cpp
@@ -1371,6 +1371,8 @@ void Main::on_transactionQueue_currentItemChanged()
s << "
Log Bloom: " << receipt.bloom() << "
";
else
s << "Log Bloom: Uneventful
";
+ s << "Gas Used: " << receipt.gasUsed() << "
";
+ s << "End State: " << receipt.stateRoot().abridged() << "
";
auto r = receipt.rlp();
s << "Receipt: " << toString(RLP(r)) << "
";
s << "Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "
";
@@ -1564,6 +1566,8 @@ void Main::on_blocks_currentItemChanged()
s << "Log Bloom: " << receipt.bloom() << "
";
else
s << "Log Bloom: Uneventful
";
+ s << "Gas Used: " << receipt.gasUsed() << "
";
+ s << "End State: " << receipt.stateRoot().abridged() << "
";
auto r = receipt.rlp();
s << "Receipt: " << toString(RLP(r)) << "
";
s << "Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "
";
diff --git a/eth/main.cpp b/eth/main.cpp
index 571b80312..260e240e6 100644
--- a/eth/main.cpp
+++ b/eth/main.cpp
@@ -317,8 +317,9 @@ void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, u
innerMean += rate;
}
f.stop();
+ innerMean /= (_trials - 2);
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;
#if ETH_JSONRPC || !ETH_TRUE
diff --git a/ethminer/main.cpp b/ethminer/main.cpp
index 25fdaa941..124d3f779 100644
--- a/ethminer/main.cpp
+++ b/ethminer/main.cpp
@@ -188,8 +188,9 @@ void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, u
innerMean += rate;
}
f.stop();
+ innerMean /= (_trials - 2);
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;
#if ETH_JSONRPC || !ETH_TRUE
diff --git a/exp/main.cpp b/exp/main.cpp
index 973679f3d..4fff5b63a 100644
--- a/exp/main.cpp
+++ b/exp/main.cpp
@@ -72,15 +72,35 @@ public:
KeyManager() { readKeys(); }
~KeyManager() {}
- Secret secret(h128 const& _uuid, std::string const& _pass)
+ Secret secret(h128 const& _uuid, function const& _pass)
{
+ auto rit = m_ready.find(_uuid);
+ if (rit != m_ready.end())
+ return rit->second;
auto it = m_keys.find(_uuid);
if (it == m_keys.end())
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:
+ void writeKeys(std::string const& _keysPath = getDataDir("web3") + "/keys")
+ {
+ (void)_keysPath;
+ }
+
void readKeys(std::string const& _keysPath = getDataDir("web3") + "/keys")
{
fs::path p(_keysPath);
@@ -90,25 +110,44 @@ private:
{
cdebug << "Reading" << it->path();
js::read_string(contentsString(it->path().string()), v);
- js::mObject o = v.get_obj();
- 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"];
+ if (v.type() == js::obj_type)
+ {
+ js::mObject o = v.get_obj();
+ 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
- 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)
{
js::mObject o = _v.get_obj();
- bytes pKey;
+
+ // derive key
+ bytes derivedKey;
if (o["kdf"].get_str() == "pbkdf2")
{
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();
bytes salt = fromHex(params["salt"].get_str());
- pKey = pbkdf2(_pass, salt, iterations).asBytes();
+ derivedKey = pbkdf2(_pass, salt, iterations, params["dklen"].get_int());
}
else
{
@@ -116,16 +155,23 @@ private:
return bytes();
}
- // TODO check MAC
+ bytes cipherText = fromHex(o["ciphertext"].get_str());
+
+ // check MAC
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;
if (o["cipher"].get_str() == "aes-128-cbc")
{
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());
decryptSymNoAuth(key, iv, &cipherText, ret);
}
@@ -138,13 +184,15 @@ private:
return ret;
}
+ mutable std::map m_ready;
std::map m_keys;
};
int main()
{
+ cdebug << toHex(pbkdf2("password", asBytes("salt"), 1, 20));
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
diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp
index 7b9d74155..8afd6082e 100644
--- a/libdevcore/Common.cpp
+++ b/libdevcore/Common.cpp
@@ -28,7 +28,7 @@ using namespace dev;
namespace dev
{
-char const* Version = "0.9.15";
+char const* Version = "0.9.15o";
void HasInvariants::checkInvariants() const
{
@@ -36,9 +36,19 @@ void HasInvariants::checkInvariants() const
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()
{
- cdebug << "Timer" << id << t.elapsed() << "s";
+ auto e = m_t.elapsed();
+ if (!m_ms || e * 1000 > m_ms)
+ clog(TimerChannel) << m_id << e << "s";
}
}
diff --git a/libdevcore/Common.h b/libdevcore/Common.h
index e5d746372..e0872b5d4 100644
--- a/libdevcore/Common.h
+++ b/libdevcore/Common.h
@@ -169,15 +169,17 @@ private:
#define DEV_INVARIANT_CHECK (void)0;
#endif
+/// Simple scope-based timer helper.
class TimerHelper
{
public:
- TimerHelper(char const* _id): id(_id) {}
+ TimerHelper(char const* _id, unsigned _msReportWhenGreater = 0): m_id(_id), m_ms(_msReportWhenGreater) {}
~TimerHelper();
private:
- boost::timer t;
- char const* id;
+ boost::timer m_t;
+ 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)
@@ -188,6 +190,14 @@ private:
#define DEV_TIMED_FUNCTION DEV_TIMED_SCOPE(__PRETTY_FUNCTION__)
#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
{
Trust = 0,
diff --git a/libdevcore/Guards.h b/libdevcore/Guards.h
index d060108ef..2d3eced32 100644
--- a/libdevcore/Guards.h
+++ b/libdevcore/Guards.h
@@ -81,9 +81,9 @@ using SpinGuard = std::lock_guard;
* Mutex m;
* 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
*
* There are several variants of this basic mechanism for different Mutex types and Guards.
@@ -95,7 +95,7 @@ using SpinGuard = std::lock_guard;
* Mutex m;
* int d;
* ...
- * ETH_GUARDED(m)
+ * ETH_(m)
* {
* for (auto d = 50; d > 25; --d)
* foo(d);
@@ -107,19 +107,19 @@ using SpinGuard = std::lock_guard;
* @endcode
*/
-#define ETH_GUARDED(MUTEX) \
+#define DEV_GUARDED(MUTEX) \
for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
-#define ETH_READ_GUARDED(MUTEX) \
+#define DEV_READ_GUARDED(MUTEX) \
for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
-#define ETH_WRITE_GUARDED(MUTEX) \
+#define DEV_WRITE_GUARDED(MUTEX) \
for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
-#define ETH_RECURSIVE_GUARDED(MUTEX) \
+#define DEV_RECURSIVE_GUARDED(MUTEX) \
for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
-#define ETH_UNGUARDED(MUTEX) \
+#define DEV_UNGUARDED(MUTEX) \
for (GenericUnguardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
-#define ETH_READ_UNGUARDED(MUTEX) \
+#define DEV_READ_UNGUARDED(MUTEX) \
for (GenericUnguardSharedBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
-#define ETH_WRITE_UNGUARDED(MUTEX) \
+#define DEV_WRITE_UNGUARDED(MUTEX) \
for (GenericUnguardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
}
diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp
index a68c18958..7d790ccf6 100644
--- a/libdevcore/Worker.cpp
+++ b/libdevcore/Worker.cpp
@@ -29,7 +29,7 @@ using namespace dev;
void Worker::startWorking()
{
- cnote << "startWorking for thread" << m_name;
+// cnote << "startWorking for thread" << m_name;
Guard l(x_work);
if (m_work)
{
@@ -42,65 +42,66 @@ void Worker::startWorking()
m_work.reset(new thread([&]()
{
setThreadName(m_name.c_str());
- cnote << "Thread begins";
+// cnote << "Thread begins";
while (m_state != WorkerState::Killing)
{
WorkerState ex = WorkerState::Starting;
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();
- cnote << "Entering work loop...";
+// cnote << "Entering work loop...";
workLoop();
- cnote << "Finishing up worker thread...";
+// cnote << "Finishing up worker thread...";
doneWorking();
// ex = WorkerState::Stopping;
// m_state.compare_exchange_strong(ex, 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)
m_state.exchange(ex);
- cnote << "Waiting until not Stopped...";
- while (m_state == WorkerState::Stopped)
- this_thread::sleep_for(chrono::milliseconds(20));
+// cnote << "Waiting until not Stopped...";
+ DEV_TIMED_IF(Worker stopping, 100)
+ while (m_state == WorkerState::Stopped)
+ this_thread::sleep_for(chrono::milliseconds(20));
}
}));
- cnote << "Spawning" << m_name;
+// cnote << "Spawning" << m_name;
}
- cnote << "Waiting until Started...";
- while (m_state != WorkerState::Started)
- this_thread::sleep_for(chrono::microseconds(20));
+ DEV_TIMED_IF(Start worker, 100)
+ while (m_state != WorkerState::Started)
+ this_thread::sleep_for(chrono::microseconds(20));
}
void Worker::stopWorking()
{
- cnote << "stopWorking for thread" << m_name;
- ETH_GUARDED(x_work)
+ DEV_GUARDED(x_work)
if (m_work)
{
- cnote << "Stopping" << m_name;
WorkerState ex = WorkerState::Started;
m_state.compare_exchange_strong(ex, WorkerState::Stopping);
- cnote << "Waiting until Stopped...";
- while (m_state != WorkerState::Stopped)
- this_thread::sleep_for(chrono::microseconds(20));
+ DEV_TIMED_IF(Stop worker, 100)
+ while (m_state != WorkerState::Stopped)
+ this_thread::sleep_for(chrono::microseconds(20));
}
}
void Worker::terminate()
{
// cnote << "stopWorking for thread" << m_name;
- ETH_GUARDED(x_work)
+ DEV_GUARDED(x_work)
if (m_work)
{
- cnote << "Terminating" << m_name;
m_state.exchange(WorkerState::Killing);
- m_work->join();
+ DEV_TIMED_IF(Terminate worker, 100)
+ m_work->join();
+
m_work.reset();
}
}
diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp
index 48dd5c384..c3133cca7 100644
--- a/libdevcrypto/Common.cpp
+++ b/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);
}
-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 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;
}
diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h
index 50072c1bf..3b243d319 100644
--- a/libdevcrypto/Common.h
+++ b/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);
/// 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".
/// All of the data of the class can be regenerated from the secret key (m_secret) alone.
diff --git a/libethcore/CommonJS.cpp b/libethcore/CommonJS.cpp
index 167879db3..286641bc3 100644
--- a/libethcore/CommonJS.cpp
+++ b/libethcore/CommonJS.cpp
@@ -26,6 +26,8 @@
namespace dev
{
+const u256 UndefinedU256 = ~(u256)0;
+
Address toAddress(std::string const& _sn)
{
if (_sn.size() == 40)
diff --git a/libethcore/CommonJS.h b/libethcore/CommonJS.h
index 185cd3191..72625122d 100644
--- a/libethcore/CommonJS.h
+++ b/libethcore/CommonJS.h
@@ -48,14 +48,18 @@ inline Address jsToAddress(std::string const& _s) { return jsToFixedWrite(m_writeOptions, &blocksBatch);
m_extrasDB->Write(m_writeOptions, &extrasBatch);
- ETH_WRITE_GUARDED(x_lastBlockHash)
+ DEV_WRITE_GUARDED(x_lastBlockHash)
{
m_lastBlockHash = newLastBlockHash;
m_lastBlockNumber = newLastBlockNumber;
@@ -981,7 +981,7 @@ bool BlockChain::isKnown(h256 const& _hash) const
if (_hash == m_genesisHash)
return true;
- ETH_READ_GUARDED(x_blocks)
+ DEV_READ_GUARDED(x_blocks)
if (!m_blocks.count(_hash))
{
string d;
@@ -989,7 +989,7 @@ bool BlockChain::isKnown(h256 const& _hash) const
if (d.empty())
return false;
}
- ETH_READ_GUARDED(x_details)
+ DEV_READ_GUARDED(x_details)
if (!m_details.count(_hash))
{
string d;
diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp
index 394b0007e..01426527f 100644
--- a/libethereum/Client.cpp
+++ b/libethereum/Client.cpp
@@ -244,13 +244,13 @@ void Client::startedWorking()
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
cdebug << "startedWorking()";
- ETH_WRITE_GUARDED(x_preMine)
+ DEV_WRITE_GUARDED(x_preMine)
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;
- ETH_WRITE_GUARDED(x_postMine)
+ DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_preMine;
}
}
@@ -259,13 +259,13 @@ void Client::doneWorking()
{
// Synchronise the state according to the head of the block chain.
// 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);
- ETH_READ_GUARDED(x_preMine)
+ DEV_READ_GUARDED(x_preMine)
{
- ETH_WRITE_GUARDED(x_working)
+ DEV_WRITE_GUARDED(x_working)
m_working = m_preMine;
- ETH_WRITE_GUARDED(x_postMine)
+ DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_preMine;
}
}
@@ -309,7 +309,7 @@ void Client::killChain()
void Client::clearPending()
{
h256Set changeds;
- ETH_WRITE_GUARDED(x_postMine)
+ DEV_WRITE_GUARDED(x_postMine)
{
if (!m_postMine.pending().size())
return;
@@ -317,7 +317,7 @@ void Client::clearPending()
// appendFromNewPending(m_postMine.logBloom(i), changeds);
changeds.insert(PendingChangedFilter);
m_tq.clear();
- ETH_READ_GUARDED(x_preMine)
+ DEV_READ_GUARDED(x_preMine)
m_postMine = m_preMine;
}
@@ -434,7 +434,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
{
State temp;
// 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.addBalance(_from, _value + _gasPrice * _gas);
Executive e(temp, LastHashes(), 0);
@@ -461,13 +461,13 @@ ProofOfWork::WorkPackage Client::getWork()
bool Client::submitWork(ProofOfWork::Solution const& _solution)
{
bytes newBlock;
- DEV_TIMED(working) ETH_WRITE_GUARDED(x_working)
+ DEV_TIMED(working) DEV_WRITE_GUARDED(x_working)
if (!m_working.completeMine(_solution))
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;
newBlock = m_working.blockData();
}
@@ -499,17 +499,17 @@ void Client::syncTransactionQueue()
h256Set changeds;
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);
if (newPendingReceipts.empty())
return;
- 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;
- ETH_READ_GUARDED(x_postMine)
+ DEV_READ_GUARDED(x_postMine)
for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
changeds.insert(PendingChangedFilter);
@@ -561,7 +561,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
bool preChanged = false;
State newPreMine;
- ETH_READ_GUARDED(x_preMine)
+ DEV_READ_GUARDED(x_preMine)
newPreMine = m_preMine;
// TODO: use m_postMine to avoid re-evaluating our own blocks.
@@ -572,11 +572,11 @@ void Client::onChainChanged(ImportRoute const& _ir)
if (isMining())
cnote << "New block on chain.";
- ETH_WRITE_GUARDED(x_preMine)
+ DEV_WRITE_GUARDED(x_preMine)
m_preMine = newPreMine;
- DEV_TIMED(working) ETH_WRITE_GUARDED(x_working)
+ DEV_TIMED(working) DEV_WRITE_GUARDED(x_working)
m_working = newPreMine;
- ETH_READ_GUARDED(x_postMine)
+ DEV_READ_GUARDED(x_postMine)
for (auto const& t: m_postMine.pending())
{
clog(ClientNote) << "Resubmitting post-mine transaction " << t;
@@ -584,7 +584,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
if (ir != ImportResult::Success)
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;
changeds.insert(PendingChangedFilter);
@@ -609,11 +609,11 @@ void Client::onPostStateChanged()
cnote << "Post state changed: Restarting mining...";
if (isMining() || remoteActive())
{
- DEV_TIMED(working) ETH_WRITE_GUARDED(x_working)
+ DEV_TIMED(working) DEV_WRITE_GUARDED(x_working)
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_miningInfo = m_postMine.info();
}
@@ -694,7 +694,7 @@ void Client::checkWatchGarbage()
{
// watches garbage collection
vector toUninstall;
- ETH_GUARDED(x_filtersWatches)
+ DEV_GUARDED(x_filtersWatches)
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))
{
@@ -733,7 +733,7 @@ eth::State Client::state(h256 _block) const
eth::State Client::state(unsigned _txi) const
{
- ETH_READ_GUARDED(x_postMine)
+ DEV_READ_GUARDED(x_postMine)
return m_postMine.fromPending(_txi);
assert(false);
return State();
diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp
index 34d5eb504..299984a16 100644
--- a/libethereum/EthereumHost.cpp
+++ b/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));
- 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 const& p: s.first)
for (auto const& b: blocks)
{
diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp
index c550bd4f7..85bdf33e9 100644
--- a/libethereum/EthereumPeer.cpp
+++ b/libethereum/EthereumPeer.cpp
@@ -559,7 +559,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
default:;
}
- ETH_GUARDED(x_knownBlocks)
+ DEV_GUARDED(x_knownBlocks)
m_knownBlocks.insert(h);
}
break;
@@ -578,7 +578,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{
addRating(1);
auto h = _r[i].toHash();
- ETH_GUARDED(x_knownBlocks)
+ DEV_GUARDED(x_knownBlocks)
m_knownBlocks.insert(h);
auto status = host()->m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host()->m_chain.isKnown(h))
diff --git a/libethereum/Farm.h b/libethereum/Farm.h
index fda9d64c3..9acb375ad 100644
--- a/libethereum/Farm.h
+++ b/libethereum/Farm.h
@@ -127,7 +127,7 @@ public:
*/
void resetMiningProgress()
{
- ETH_READ_GUARDED(x_minerWork)
+ DEV_READ_GUARDED(x_minerWork)
for (auto const& i: m_miners)
i->resetHashCount();
resetTimer();
diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp
new file mode 100644
index 000000000..e5fb0e09a
--- /dev/null
+++ b/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 .
+*/
+/** @file GasMeter.cpp
+ * @author Christian
+ * @date 2015
+ */
+
+#include "GasMeter.h"
+#include
+
+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::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];
+}
+
+
diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h
new file mode 100644
index 000000000..63dbc1380
--- /dev/null
+++ b/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 .
+*/
+/** @file GasMeter.cpp
+ * @author Christian
+ * @date 2015
+ */
+
+#pragma once
+
+#include
+#include
+
+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;
+}
+
+
+}
+}
diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp
index f4394b146..ed84f014c 100644
--- a/libp2p/Common.cpp
+++ b/libp2p/Common.cpp
@@ -24,8 +24,9 @@ using namespace std;
using namespace dev;
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;
+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::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();
+ tcpPort = _r[2].toInt();
+}
+
namespace dev {
std::ostream& operator<<(std::ostream& _out, dev::p2p::NodeIPEndpoint const& _ep)
diff --git a/libp2p/Common.h b/libp2p/Common.h
index 378064e7d..15ee981bc 100644
--- a/libp2p/Common.h
+++ b/libp2p/Common.h
@@ -36,6 +36,7 @@
#include
#include
#include
+#include
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
@@ -162,10 +163,17 @@ using PeerSessionInfos = std::vector;
*/
struct NodeIPEndpoint
{
+ enum RLPAppend
+ {
+ StreamList,
+ StreamInline
+ };
+
/// Setting true causes isAllowed to return true for all addresses. (Used by test fixtures)
static bool test_allowLocal;
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;
uint16_t udpPort;
@@ -177,11 +185,14 @@ struct NodeIPEndpoint
operator bool() const { return !address.is_unspecified() && udpPort > 0 && tcpPort > 0; }
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
{
- 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 Public const& publicKey() const { return id; }
diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp
index 17ae1a8f1..4560c1564 100644
--- a/libp2p/Host.cpp
+++ b/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
shared_ptr p;
- ETH_RECURSIVE_GUARDED(x_sessions)
+ DEV_RECURSIVE_GUARDED(x_sessions)
{
if (m_peers.count(_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
auto ps = make_shared(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet(), 0, map()}));
- if (protocolVersion < dev::p2p::c_protocolVersion)
+ if (protocolVersion < dev::p2p::c_protocolVersion - 1)
{
ps->disconnect(IncompatibleProtocol);
return;
@@ -257,7 +257,7 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e)
if (Node n = m_nodeTable->node(_n))
{
shared_ptr p;
- ETH_RECURSIVE_GUARDED(x_sessions)
+ DEV_RECURSIVE_GUARDED(x_sessions)
{
if (m_peers.count(_n))
{
@@ -412,7 +412,7 @@ void Host::requirePeer(NodeId const& _n, NodeIPEndpoint const& _endpoint)
{
// create or update m_peers entry
shared_ptr p;
- ETH_RECURSIVE_GUARDED(x_sessions)
+ DEV_RECURSIVE_GUARDED(x_sessions)
if (m_peers.count(_n))
{
p = m_peers[_n];
@@ -579,7 +579,7 @@ void Host::run(boost::system::error_code const&)
// todo: update peerSlotsAvailable()
unsigned pendingCount = 0;
- ETH_GUARDED(x_pendingNodeConns)
+ DEV_GUARDED(x_pendingNodeConns)
pendingCount = m_pendingPeerConns.size();
int openSlots = m_idealPeerCount - peerCount() - pendingCount;
if (openSlots > 0)
@@ -696,15 +696,16 @@ bytes Host::saveNetwork() const
int count = 0;
for (auto const& p: peers)
{
- // Only save peers which have connected within 2 days, with properly-advertised port and public IP address
- // todo: e2e ipv6 support
+ // todo: ipv6
if (!p.endpoint.address.is_v4())
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 << p.endpoint.address.to_v4().to_bytes() << p.endpoint.tcpPort << p.id << p.required
+ network.appendList(11);
+ p.endpoint.streamRLP(network, NodeIPEndpoint::StreamInline);
+ network << p.id << p.required
<< chrono::duration_cast(p.m_lastConnected.time_since_epoch()).count()
<< chrono::duration_cast(p.m_lastAttempted.time_since_epoch()).count()
<< p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating;
@@ -718,12 +719,9 @@ bytes Host::saveNetwork() const
state.sort();
for (auto const& entry: state)
{
- network.appendList(3);
- if (entry.endpoint.address.is_v4())
- network << entry.endpoint.address.to_v4().to_bytes();
- else
- network << entry.endpoint.address.to_v6().to_bytes();
- network << entry.endpoint.tcpPort << entry.id;
+ network.appendList(4);
+ entry.endpoint.streamRLP(network, NodeIPEndpoint::StreamInline);
+ network << entry.id;
count++;
}
}
@@ -739,6 +737,9 @@ bytes Host::saveNetwork() const
void Host::restoreNetwork(bytesConstRef _b)
{
+ if (!_b.size())
+ return;
+
// nodes can only be added if network is added
if (!isStarted())
BOOST_THROW_EXCEPTION(NetworkStartRequired());
@@ -748,7 +749,8 @@ void Host::restoreNetwork(bytesConstRef _b)
RecursiveGuard l(x_sessions);
RLP r(_b);
- if (r.itemCount() > 0 && r[0].isInt() && r[0].toInt() == dev::p2p::c_protocolVersion)
+ unsigned fileVersion = r[0].toInt();
+ if (r.itemCount() > 0 && r[0].isInt() && fileVersion >= dev::p2p::c_protocolVersion - 1)
{
// r[0] = version
// r[1] = key
@@ -756,30 +758,57 @@ void Host::restoreNetwork(bytesConstRef _b)
for (auto i: r[2])
{
- if (i[0].itemCount() != 4)
+ // todo: ipv6
+ if (i[0].itemCount() != 4 && i[0].size() != 4)
continue;
-
- // todo: ipv6, bi::address_v6(i[0].toArray()
- Node n((NodeId)i[2], NodeIPEndpoint(bi::address_v4(i[0].toArray()), i[1].toInt(), i[1].toInt()));
- if (i.itemCount() == 3 && n.endpoint.isAllowed())
- m_nodeTable->addNode(n, NodeTable::NodeRelation::Known);
- else if (i.itemCount() == 10)
+
+ if (i.itemCount() == 4 || i.itemCount() == 11)
{
- n.required = i[3].toInt();
- if (!n.endpoint.isAllowed() && !n.required)
- continue;
- shared_ptr p = make_shared(n);
- p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt()));
- p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt()));
- p->m_failedAttempts = i[6].toInt();
- p->m_lastDisconnect = (DisconnectReason)i[7].toInt();
- p->m_score = (int)i[8].toInt();
- p->m_rating = (int)i[9].toInt();
- m_peers[p->id] = p;
- if (p->required)
- requirePeer(p->id, n.endpoint);
- else
- m_nodeTable->addNode(*p.get(), NodeTable::NodeRelation::Known);
+ Node n((NodeId)i[3], NodeIPEndpoint(i));
+ if (i.itemCount() == 4 && n.endpoint.isAllowed())
+ m_nodeTable->addNode(n);
+ else if (i.itemCount() == 11)
+ {
+ n.required = i[4].toInt();
+ if (!n.endpoint.isAllowed() && !n.required)
+ continue;
+ shared_ptr p = make_shared(n);
+ p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[5].toInt()));
+ p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[6].toInt()));
+ p->m_failedAttempts = i[7].toInt();
+ p->m_lastDisconnect = (DisconnectReason)i[8].toInt();
+ p->m_score = (int)i[9].toInt();
+ p->m_rating = (int)i[10].toInt();
+ 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()), i[1].toInt(), i[1].toInt()));
+ if (i.itemCount() == 3 && n.endpoint.isAllowed())
+ m_nodeTable->addNode(n);
+ else if (i.itemCount() == 10)
+ {
+ n.required = i[3].toInt();
+ if (!n.endpoint.isAllowed() && !n.required)
+ continue;
+ shared_ptr p = make_shared(n);
+ p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt()));
+ p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt()));
+ p->m_failedAttempts = i[6].toInt();
+ p->m_lastDisconnect = (DisconnectReason)i[7].toInt();
+ p->m_score = (int)i[8].toInt();
+ p->m_rating = (int)i[9].toInt();
+ 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)
{
RLP r(_b);
- if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt() == dev::p2p::c_protocolVersion)
+ if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt() >= 3)
return move(KeyPair(move(Secret(r[1].toBytes()))));
else
return move(KeyPair::create());
diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp
index 42f1cad02..c3215da71 100644
--- a/libp2p/NodeTable.cpp
+++ b/libp2p/NodeTable.cpp
@@ -75,12 +75,6 @@ void NodeTable::processEvents()
m_nodeEventHandler->processEvents();
}
-shared_ptr NodeTable::addNode(Public const& _pubk, NodeIPEndpoint const& _ep)
-{
- auto node = Node(_pubk, _ep);
- return addNode(node);
-}
-
shared_ptr NodeTable::addNode(Node const& _node, NodeRelation _relation)
{
if (_relation == Known)
@@ -92,13 +86,8 @@ shared_ptr NodeTable::addNode(Node const& _node, NodeRelation _relati
return ret;
}
- // re-enable tcp checks when NAT hosts are handled by discover
- // 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;
+ if (!_node.endpoint)
return move(shared_ptr());
- }
// ping address to recover nodeid if nodeid is empty
if (!_node.id)
@@ -108,9 +97,7 @@ shared_ptr NodeTable::addNode(Node const& _node, NodeRelation _relati
Guard l(x_pubkDiscoverPings);
m_pubkDiscoverPings[_node.endpoint.address] = std::chrono::steady_clock::now();
}
- PingNode p(_node.endpoint, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort);
- p.sign(m_secret);
- m_socketPointer->send(p);
+ ping(_node.endpoint);
return move(shared_ptr());
}
@@ -123,9 +110,7 @@ shared_ptr NodeTable::addNode(Node const& _node, NodeRelation _relati
shared_ptr ret(new NodeEntry(m_node, _node.id, _node.endpoint));
m_nodes[_node.id] = ret;
clog(NodeTableConnect) << "addNode pending for" << _node.endpoint;
- PingNode p(_node.endpoint, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort);
- p.sign(m_secret);
- m_socketPointer->send(p);
+ ping(_node.endpoint);
return ret;
}
@@ -153,8 +138,10 @@ list NodeTable::snapshot() const
list ret;
Guard l(x_state);
for (auto s: m_state)
- for (auto n: s.nodes)
- ret.push_back(*n.lock());
+ for (auto np: s.nodes)
+ if (auto n = np.lock())
+ if (!!n)
+ ret.push_back(*n);
return move(ret);
}
@@ -295,14 +282,14 @@ vector> NodeTable::nearestNodeEntries(NodeId _target)
vector> ret;
for (auto& nodes: found)
for (auto n: nodes.second)
- if (n->endpoint.isAllowed())
+ if (ret.size() < s_bucketSize && !!n->endpoint && n->endpoint.isAllowed())
ret.push_back(n);
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);
m_socketPointer->send(p);
}
@@ -467,12 +454,17 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
m_pubkDiscoverPings.erase(_from.address());
}
if (!haveNode(nodeid))
- addNode(nodeid, NodeIPEndpoint(_from.address(), _from.port(), _from.port()));
+ addNode(Node(nodeid, NodeIPEndpoint(_from.address(), _from.port(), _from.port())));
}
else
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;
break;
}
@@ -497,17 +489,22 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
}
Neighbours in = Neighbours::fromBytesConstRef(_from, rlpBytes);
- for (auto n: in.nodes)
- addNode(n.node, NodeIPEndpoint(bi::address::from_string(n.ipAddress), n.udpPort, n.udpPort));
+ for (auto n: in.neighbours)
+ addNode(Node(n.node, n.endpoint));
break;
}
case FindNode::type:
{
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> 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)
{
Neighbours out(_from, nearest, offset, nlimit);
@@ -522,17 +519,29 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
case PingNode::type:
{
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))
- dropNode(n);
- return;
+ if (in.version == 3)
+ {
+ 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
- addNode(nodeid, NodeIPEndpoint(_from.address(), _from.port(), in.tcpPort));
+ if (RLPXDatagramFace::secondsSinceEpoch() > in.ts)
+ {
+ 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.sign(m_secret);
m_socketPointer->send(p);
@@ -567,8 +576,8 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec)
bool evictionsRemain = false;
list> drop;
{
- Guard ln(x_nodes);
Guard le(x_evictions);
+ Guard ln(x_nodes);
for (auto& e: m_evictions)
if (chrono::steady_clock::now() - e.first.second > c_reqTimeout)
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
{
_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)
{
RLP r(_bytes);
- if (r.itemCountStrict() == 3)
+ if (r.itemCountStrict() == 4 && r[0].isInt() && r[0].toInt(RLP::Strict) == dev::p2p::c_protocolVersion)
{
- version = 2;
- ipAddress = r[0].toString();
- tcpPort = r[1].toInt(RLP::Strict);
- ts = r[2].toInt(RLP::Strict);
- }
- else if (r.itemCountStrict() == 4)
- {
- version = r[0].toInt(RLP::Strict);
- ipAddress = r[1].toString();
- tcpPort = r[2].toInt(RLP::Strict);
- ts = r[3].toInt(RLP::Strict);
+ version = dev::p2p::c_protocolVersion;
+ source.interpretRLP(r[1]);
+ destination.interpretRLP(r[2]);
+ ts = r[3].toInt(RLP::Strict);
}
else
- BOOST_THROW_EXCEPTION(InvalidRLP());
+ version = r[0].toInt(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();
+}
+
+void compat::Pong::interpretRLP(bytesConstRef _bytes)
+{
+ RLP r(_bytes);
+ echo = (h256)r[0];
+ ts = r[1].toInt();
}
diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h
index 95028db2b..458044997 100644
--- a/libp2p/NodeTable.h
+++ b/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
* interfaces.
*
- *
- * [Integration]
- * @todo TCP endpoints
- * @todo GC uniform 1/32 entires at 112500ms interval
- *
* [Optimization]
* @todo serialize evictions per-bucket
* @todo store evictions in map, unit-test eviction logic
* @todo store root node in table
* @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 cache Ping and FindSelf
*
* [Networking]
- * @todo node-endpoint updates
- * @todo TCP endpoints
* @todo eth/upnp/natpmp/stun/ice/etc for public-discovery
* @todo firewall
*
@@ -131,7 +123,7 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this
using TimePoint = std::chrono::steady_clock::time_point; ///< Steady time point.
using NodeIdTimePoint = std::pair;
using EvictionTimeout = std::pair; ///< First NodeId (NodeIdTimePoint) may be evicted and replaced with second NodeId.
-
+
public:
enum NodeRelation { Unknown = 0, Known };
@@ -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.
void processEvents();
- /// Add node. Node will be pinged and empty shared_ptr is returned if NodeId is uknown.
- std::shared_ptr 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.
std::shared_ptr addNode(Node const& _node, NodeRelation _relation = NodeRelation::Unknown);
@@ -206,7 +195,7 @@ private:
};
/// 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).
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.
std::array 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 m_evictions; ///< Eviction timeouts.
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.
* If the pinged node doesn't respond, then it is removed and the new
* 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(bi::udp::endpoint _ep): RLPXDatagram(_ep) {}
- PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _ts = std::chrono::seconds(60)): RLPXDatagram(_ep), ipAddress(_src), tcpPort(_srcPort), ts(futureFromEpoch(_ts)) {}
+ /// Constructor used for sending PingNode.
+ PingNode(NodeIPEndpoint _src, NodeIPEndpoint _dest): RLPXDatagram(_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(_ep), source(UnspecifiedNodeIPEndpoint), destination(UnspecifiedNodeIPEndpoint) {}
static const uint8_t type = 1;
unsigned version = 0;
- std::string ipAddress;
-// uint16_t udpPort;
- uint16_t tcpPort;
- unsigned ts;
+ NodeIPEndpoint source;
+ NodeIPEndpoint destination;
+ uint32_t ts = 0;
void streamRLP(RLPStream& _s) const override;
void interpretRLP(bytesConstRef _bytes) override;
@@ -332,22 +312,20 @@ struct PingNode: RLPXDatagram
/**
* 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(bi::udp::endpoint _ep): RLPXDatagram(_ep), ts(futureFromEpoch(std::chrono::seconds(60))) {}
+ Pong(bi::udp::endpoint const& _ep): RLPXDatagram(_ep), destination(UnspecifiedNodeIPEndpoint) {}
+ Pong(NodeIPEndpoint const& _dest): RLPXDatagram((bi::udp::endpoint)_dest), destination(_dest), ts(futureFromEpoch(std::chrono::seconds(60))) {}
static const uint8_t type = 2;
+ NodeIPEndpoint destination;
h256 echo; ///< MCD of PingNode
- unsigned ts;
+ uint32_t ts = 0;
- void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << echo << ts; }
- void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); echo = (h256)r[0]; ts = r[1].toInt(); }
+ void streamRLP(RLPStream& _s) const;
+ void interpretRLP(bytesConstRef _bytes);
};
/**
@@ -365,58 +343,63 @@ struct Pong: RLPXDatagram
struct FindNode: RLPXDatagram
{
FindNode(bi::udp::endpoint _ep): RLPXDatagram(_ep) {}
- FindNode(bi::udp::endpoint _ep, NodeId _target, std::chrono::seconds _ts = std::chrono::seconds(60)): RLPXDatagram(_ep), target(_target), ts(futureFromEpoch(_ts)) {}
+ FindNode(bi::udp::endpoint _ep, NodeId _target): RLPXDatagram(_ep), target(_target), ts(futureFromEpoch(std::chrono::seconds(60))) {}
static const uint8_t type = 3;
h512 target;
- unsigned ts;
+ uint32_t ts = 0;
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << ts; }
- void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = r[0].toHash(); ts = r[1].toInt(); }
+ void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = r[0].toHash(); ts = r[1].toInt(); }
};
/**
- * Node Packet: Multiple node packets are sent in response to FindNode.
- *
- * RLP Encoded Items: 2 (first item is list)
- * Minimum Encoded Size: 10 bytes
+ * Node Packet: One or more node packets are sent in response to FindNode.
*/
struct Neighbours: RLPXDatagram
{
- struct Node
+ struct Neighbour
{
- Node() = default;
- Node(RLP const& _r) { interpretRLP(_r); }
- std::string ipAddress;
- uint16_t udpPort;
-// uint16_t tcpPort;
+ Neighbour(Node const& _node): endpoint(_node.endpoint), node(_node.id) {}
+ Neighbour(RLP const& _r): endpoint(_r) { node = h512(_r[3].toBytes()); }
+ NodeIPEndpoint endpoint;
NodeId node;
- void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << udpPort << node; }
- void interpretRLP(RLP const& _r) { ipAddress = _r[0].toString(); udpPort = _r[1].toInt(); node = h512(_r[2].toBytes()); }
+ void streamRLP(RLPStream& _s) const { _s.appendList(4); endpoint.streamRLP(_s, NodeIPEndpoint::StreamInline); _s << node; }
};
- Neighbours(bi::udp::endpoint _ep): RLPXDatagram(_ep), ts(futureFromEpoch(std::chrono::seconds(30))) {}
- Neighbours(bi::udp::endpoint _to, std::vector> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram(_to), ts(futureFromEpoch(std::chrono::seconds(30)))
+ Neighbours(bi::udp::endpoint _ep): RLPXDatagram(_ep), ts(secondsSinceEpoch()) {}
+ Neighbours(bi::udp::endpoint _to, std::vector> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram(_to), ts(futureFromEpoch(std::chrono::seconds(60)))
{
auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size();
for (auto i = _offset; i < limit; 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);
- }
+ neighbours.push_back(Neighbour(*_nearest[i]));
}
static const uint8_t type = 4;
- std::vector nodes;
- unsigned ts = 1;
+ std::vector neighbours;
+ 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 interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n)); ts = r[1].toInt(); }
+ 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]) neighbours.push_back(Neighbour(n)); ts = r[1].toInt(); }
};
+
+namespace compat
+{
+ /**
+ * Pong packet [compatability]: Sent in response to ping
+ */
+ struct Pong: RLPXDatagram
+ {
+ Pong(bi::udp::endpoint const& _ep): RLPXDatagram(_ep) {}
+ Pong(NodeIPEndpoint const& _dest): RLPXDatagram((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 NodeTableNote: public LogChannel { static const char* name(); static const int verbosity = 1; };
diff --git a/libp2p/UDP.h b/libp2p/UDP.h
index 374f986b0..97136ee43 100644
--- a/libp2p/UDP.h
+++ b/libp2p/UDP.h
@@ -61,8 +61,8 @@ protected:
*/
struct RLPXDatagramFace: public UDPDatagram
{
- static uint64_t futureFromEpoch(std::chrono::milliseconds _ms) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _ms).time_since_epoch()).count(); }
- static uint64_t futureFromEpoch(std::chrono::seconds _sec) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); }
+ static uint32_t futureFromEpoch(std::chrono::seconds _sec) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); }
+ static uint32_t secondsSinceEpoch() { return std::chrono::duration_cast((std::chrono::system_clock::now()).time_since_epoch()).count(); }
static Public authenticate(bytesConstRef _sig, bytesConstRef _rlp);
virtual uint8_t packetType() = 0;
diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp
index 713059d38..0a170f8e1 100644
--- a/libsolidity/ASTPrinter.cpp
+++ b/libsolidity/ASTPrinter.cpp
@@ -30,8 +30,11 @@ namespace dev
namespace solidity
{
-ASTPrinter::ASTPrinter(ASTNode const& _ast, string const& _source):
- m_indentation(0), m_source(_source), m_ast(&_ast)
+ASTPrinter::ASTPrinter(
+ 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)
{
+ if (m_gasCosts.count(&_node))
+ *m_ostream << getIndentation() << " Gas costs: " << m_gasCosts.at(&_node) << endl;
if (!m_source.empty())
{
SourceLocation const& location(_node.getLocation());
diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h
index 9494bf8cb..dabf6470a 100644
--- a/libsolidity/ASTPrinter.h
+++ b/libsolidity/ASTPrinter.h
@@ -24,6 +24,7 @@
#include
#include
+#include
namespace dev
{
@@ -38,7 +39,11 @@ class ASTPrinter: public ASTConstVisitor
public:
/// 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.
- 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.
void print(std::ostream& _stream);
@@ -128,6 +133,7 @@ private:
int m_indentation;
std::string m_source;
ASTNode const* m_ast;
+ StructuralGasEstimator::ASTGasConsumption m_gasCosts;
std::ostream* m_ostream;
};
diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h
index ec22bd443..fbda50791 100644
--- a/libsolidity/ASTVisitor.h
+++ b/libsolidity/ASTVisitor.h
@@ -23,6 +23,8 @@
#pragma once
#include
+#include
+#include
#include
namespace dev
@@ -218,5 +220,47 @@ protected:
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 _onNode,
+ std::function _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 m_parents;
+ std::function m_onNode;
+ std::function m_onEdge;
+};
+
}
}
diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp
index 554d06fd7..b3fedc45d 100644
--- a/libsolidity/CompilerStack.cpp
+++ b/libsolidity/CompilerStack.cpp
@@ -257,6 +257,18 @@ bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimiz
return stack.compile(_sourceCode, _optimize);
}
+tuple 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)
{
m_parseSuccessful = false;
diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h
index 7d9198622..2ad791f22 100644
--- a/libsolidity/CompilerStack.h
+++ b/libsolidity/CompilerStack.h
@@ -31,6 +31,7 @@
#include
#include
#include
+#include
namespace dev
{
@@ -131,6 +132,11 @@ public:
/// scanning the source code - this is useful for printing exception information.
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 positionFromSourceLocation(SourceLocation const& _sourceLocation) const;
+
private:
/**
* Information pertaining to one source unit, filled gradually during parsing and compilation.
diff --git a/libsolidity/StructuralGasEstimator.cpp b/libsolidity/StructuralGasEstimator.cpp
new file mode 100644
index 000000000..ececd7116
--- /dev/null
+++ b/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 .
+*/
+/**
+ * @author Christian
+ * @date 2015
+ * Gas consumption estimator working alongside the AST.
+ */
+
+#include "StructuralGasEstimator.h"
+#include