Browse Source

Merge remote-tracking branch 'ethereum/develop' into sol_overloadingFunctions

Conflicts:
	libsolidity/AST.cpp
	libsolidity/AST.h
cl-refactor
chriseth 10 years ago
parent
commit
9a9af0561f
  1. 6
      alethzero/Main.ui
  2. 21
      alethzero/MainWin.cpp
  3. 1
      alethzero/MainWin.h
  4. 1
      cmake/scripts/jsonrpcstub.cmake
  5. 3
      eth/Farm.h
  6. 2
      eth/farm.json
  7. 44
      eth/main.cpp
  8. 1
      exp/CMakeLists.txt
  9. 2
      libdevcore/Common.cpp
  10. 5
      libdevcore/CommonIO.h
  11. 78
      libdevcore/Log.cpp
  12. 22
      libdevcore/Log.h
  13. 15
      libdevcore/RLP.cpp
  14. 7
      libdevcore/RLP.h
  15. 72
      libdevcore/Worker.cpp
  16. 25
      libdevcore/Worker.h
  17. 7
      libdevcrypto/OverlayDB.cpp
  18. 1
      libdevcrypto/OverlayDB.h
  19. 24
      libethash-cl/ethash_cl_miner.cpp
  20. 4
      libethash-cl/ethash_cl_miner.h
  21. 2
      libethcore/BlockInfo.cpp
  22. 2
      libethcore/Common.cpp
  23. 16
      libethcore/Ethash.cpp
  24. 3
      libethcore/Ethash.h
  25. 17
      libethcore/EthashAux.cpp
  26. 4
      libethcore/EthashAux.h
  27. 6
      libethcore/Miner.h
  28. 82
      libethereum/BlockChain.cpp
  29. 11
      libethereum/BlockChain.h
  30. 49
      libethereum/BlockQueue.cpp
  31. 12
      libethereum/BlockQueue.h
  32. 122
      libethereum/Client.cpp
  33. 26
      libethereum/Client.h
  34. 12
      libethereum/CommonNet.h
  35. 18
      libethereum/EthereumHost.cpp
  36. 104
      libethereum/EthereumPeer.cpp
  37. 1
      libethereum/EthereumPeer.h
  38. 5
      libethereum/Farm.h
  39. 106
      libethereum/State.cpp
  40. 9
      libethereum/State.h
  41. 8
      libethereum/TransactionQueue.cpp
  42. 12
      libethereum/TransactionQueue.h
  43. 120
      libevmcore/Assembly.cpp
  44. 20
      libevmcore/Assembly.h
  45. 1
      libevmcore/CMakeLists.txt
  46. 1
      liblll/CMakeLists.txt
  47. 9
      libp2p/Capability.cpp
  48. 1
      libp2p/Common.h
  49. 10
      libp2p/NodeTable.cpp
  50. 6
      libp2p/RLPxHandshake.cpp
  51. 51
      libp2p/Session.cpp
  52. 56
      libsolidity/AST.cpp
  53. 5
      libsolidity/AST.h
  54. 1
      libsolidity/Compiler.cpp
  55. 5
      libsolidity/Compiler.h
  56. 6
      libsolidity/CompilerContext.h
  57. 18
      libsolidity/CompilerStack.cpp
  58. 7
      libsolidity/CompilerStack.h
  59. 2
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  60. 2
      libweb3jsonrpc/WebThreeStubServerBase.h
  61. 6
      libweb3jsonrpc/abstractwebthreestubserver.h
  62. 2
      libweb3jsonrpc/spec.json
  63. 5
      libwhisper/Interface.cpp
  64. 7
      libwhisper/WhisperHost.cpp
  65. 7
      libwhisper/WhisperPeer.cpp
  66. 2
      mix/ClientModel.cpp
  67. 4
      mix/CodeModel.cpp
  68. 1
      mix/MixClient.cpp
  69. 7
      mix/qml/LogsPane.qml
  70. 7
      mix/qml/StatusPane.qml
  71. 2
      mix/qml/TransactionLog.qml
  72. 2
      mix/qml/WebPreview.qml
  73. 10
      mix/test/qml/TestMain.qml
  74. 5
      mix/test/qml/js/TestDebugger.js
  75. 10
      mix/test/qml/js/TestMiner.js
  76. 4
      mix/test/qml/js/TestProject.js
  77. 1
      neth/main.cpp
  78. 26
      solc/CommandLineInterface.cpp
  79. 15
      solc/docker_emscripten/Dockerfile
  80. 37
      test/SolidityNameAndTypeResolution.cpp
  81. 8
      test/TestHelper.cpp
  82. 10
      test/blockchain.cpp
  83. 4
      test/stateOriginal.cpp
  84. 3
      test/webthreestubclient.h

6
alethzero/Main.ui

@ -176,6 +176,7 @@
<addaction name="clearPending"/>
<addaction name="killBlockchain"/>
<addaction name="inject"/>
<addaction name="injectBlock"/>
<addaction name="forceMining"/>
<addaction name="separator"/>
<addaction name="usePrivate"/>
@ -1685,6 +1686,11 @@ font-size: 14pt</string>
<string>Retry Unknown Parent Blocks</string>
</property>
</action>
<action name="injectBlock">
<property name="text">
<string>In&amp;ject Block</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

21
alethzero/MainWin.cpp

@ -164,7 +164,7 @@ Main::Main(QWidget *parent) :
statusBar()->addPermanentWidget(ui->chainStatus);
statusBar()->addPermanentWidget(ui->blockCount);
ui->blockCount->setText(QString("PV%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(QString::fromStdString(ProofOfWork::name())).arg(ProofOfWork::revision()).arg(dev::Version));
ui->blockCount->setText(QString("PV%1.%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(eth::c_minorProtocolVersion).arg(c_databaseVersion).arg(QString::fromStdString(ProofOfWork::name())).arg(ProofOfWork::revision()).arg(dev::Version));
connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved()));
@ -1448,6 +1448,25 @@ void Main::on_inject_triggered()
}
}
void Main::on_injectBlock_triggered()
{
QString s = QInputDialog::getText(this, "Inject Block", "Enter block dump in hex");
try
{
bytes b = fromHex(s.toStdString(), WhenError::Throw);
ethereum()->injectBlock(b);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "block rejected";
}
}
void Main::on_blocks_currentItemChanged()
{
ui->info->clear();

1
alethzero/MainWin.h

@ -159,6 +159,7 @@ private slots:
void on_killBlockchain_triggered();
void on_clearPending_triggered();
void on_inject_triggered();
void on_injectBlock_triggered();
void on_forceMining_triggered();
void on_usePrivate_triggered();
void on_turboMining_triggered();

1
cmake/scripts/jsonrpcstub.cmake

@ -42,4 +42,3 @@ else()
replace_if_different("${SERVER_TMPFILE}" "${SERVER_OUTFILE}")
replace_if_different("${CLIENT_TMPFILE}" "${CLIENT_OUTFILE}")
endif()

3
eth/Farm.h

@ -22,11 +22,12 @@ class Farm : public jsonrpc::Client
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool eth_submitWork(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
bool eth_submitWork(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
p.append(param3);
Json::Value result = this->CallMethod("eth_submitWork",p);
if (result.isBool())
return result.asBool();

2
eth/farm.json

@ -1,4 +1,4 @@
[
{ "name": "eth_getWork", "params": [], "order": [], "returns": []},
{ "name": "eth_submitWork", "params": ["", ""], "order": [], "returns": true}
{ "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true}
]

44
eth/main.cpp

@ -121,7 +121,7 @@ void help()
<< " --json-rpc-port <n> Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl
#endif
<< " -K,--kill First kill the blockchain." << endl
<< " -R,--rebuild First rebuild the blockchain from the existing database." << endl
<< " -R,--rebuild Rebuild the blockchain from the existing database." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -S,--session-secret <secretkeyhex> Set the secret key for use with send command, for this session only." << endl
<< "Client transacting:" << endl
@ -134,6 +134,7 @@ void help()
<< " -f,--force-mining Mine even when there are no transactions to mine (default: off)" << endl
<< " -C,--cpu When mining, use the CPU." << endl
<< " -G,--opencl When mining use the GPU via OpenCL." << endl
<< " --opencl-platform <n> When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl
<< " --opencl-device <n> When mining using -G/--opencl use OpenCL device n (default: 0)." << endl
<< "Client networking:" << endl
<< " --client-name <name> Add a name to your client's version string (default: blank)." << endl
@ -338,6 +339,9 @@ void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, u
exit(0);
}
struct HappyChannel: public LogChannel { static const char* name() { return ":-D"; } static const int verbosity = 1; };
struct SadChannel: public LogChannel { static const char* name() { return ":-("; } static const int verbosity = 1; };
void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
{
(void)_m;
@ -346,9 +350,7 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
#if ETH_JSONRPC || !ETH_TRUE
jsonrpc::HttpClient client(_remote);
Farm rpc(client);
GenericFarm<Ethash> f;
if (_m == MinerType::CPU)
f.startCPU();
else if (_m == MinerType::GPU)
@ -356,6 +358,7 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
ProofOfWork::WorkPackage current;
while (true)
try
{
bool completed = false;
ProofOfWork::Solution solution;
@ -366,18 +369,35 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
});
for (unsigned i = 0; !completed; ++i)
{
if (current)
cnote << "Mining on PoWhash" << current.headerHash.abridged() << ": " << f.miningProgress();
else
cnote << "Getting work package...";
Json::Value v = rpc.eth_getWork();
h256 hh(v[0].asString());
if (hh != current.headerHash)
{
current.headerHash = hh;
current.seedHash = h256(v[1].asString());
current.boundary = h256(v[2].asString());
current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight);
cnote << "Got work package:" << current.headerHash.abridged() << " < " << current.boundary;
f.setWork(current);
}
this_thread::sleep_for(chrono::milliseconds(_recheckPeriod));
}
rpc.eth_submitWork("0x" + toString(solution.nonce), "0x" + toString(solution.mixHash));
cnote << "Solution found; submitting [" << solution.nonce << "," << current.headerHash.abridged() << "," << solution.mixHash.abridged() << "] to" << _remote << "...";
bool ok = rpc.eth_submitWork("0x" + toString(solution.nonce), "0x" + toString(current.headerHash), "0x" + toString(solution.mixHash));
if (ok)
clog(HappyChannel) << "Submitted and accepted.";
else
clog(SadChannel) << "Not accepted.";
current.reset();
}
catch (jsonrpc::JsonRpcException&)
{
for (auto i = 3; --i; this_thread::sleep_for(chrono::seconds(1)))
cerr << "JSON-RPC problem. Probably couldn't connect. Retrying in " << i << "... \r";
cerr << endl;
}
#endif
exit(0);
@ -394,6 +414,7 @@ int main(int argc, char** argv)
/// Mining options
MinerType minerType = MinerType::CPU;
unsigned openclPlatform = 0;
unsigned openclDevice = 0;
/// File name for import/export.
@ -511,6 +532,15 @@ int main(int argc, char** argv)
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--opencl-platform" && i + 1 < argc)
try {
openclPlatform= stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--opencl-device" && i + 1 < argc)
try {
openclDevice = stol(argv[++i]);
@ -598,7 +628,7 @@ int main(int argc, char** argv)
}
else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill")
killChain = WithExisting::Kill;
else if (arg == "-B" || arg == "--rebuild")
else if (arg == "-R" || arg == "--rebuild")
killChain = WithExisting::Verify;
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
{
@ -809,6 +839,7 @@ int main(int argc, char** argv)
if (sessionSecret)
sigKey = KeyPair(sessionSecret);
ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform);
ProofOfWork::GPUMiner::setDefaultDevice(openclDevice);
// Two codepaths is necessary since named block require database, but numbered
@ -926,6 +957,7 @@ int main(int argc, char** argv)
{
c->setGasPricer(gasPricer);
c->setForceMining(forceMining);
c->setTurboMining(minerType == MinerType::GPU);
c->setAddress(coinbase);
}

1
exp/CMakeLists.txt

@ -3,6 +3,7 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${LEVELDB_INCLUDE_DIRS})

2
libdevcore/Common.cpp

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

5
libdevcore/CommonIO.h

@ -45,7 +45,8 @@ namespace dev
/// Retrieve and returns the contents of the given file. If the file doesn't exist or isn't readable, returns an empty bytes.
bytes contents(std::string const& _file);
std::string contentsString(std::string const& _file);
/// Retrieve and returns the allocated contents of the given file. If the file doesn't exist or isn't readable, returns nullptr. Don't forget to delete [] when finished.
/// Retrieve and returns the allocated contents of the given file; if @_dest is given, don't allocate, use it directly.
/// If the file doesn't exist or isn't readable, returns bytesRef(). Don't forget to delete [] the returned value's data when finished.
bytesRef contentsNew(std::string const& _file, bytesRef _dest = bytesRef());
/// Write the given binary data into the given file, replacing the file if it pre-exists.
@ -76,7 +77,7 @@ template <class T, class U> inline std::ostream& operator<<(std::ostream& _out,
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::multimap<T, U> const& _e);
template <class _S, class _T> _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p);
template <class T> inline std::string toString(std::chrono::time_point<T> const& _e, std::string _format = "")
template <class T> inline std::string toString(std::chrono::time_point<T> const& _e, std::string _format = "%F %T")
{
unsigned long milliSecondsSinceEpoch = std::chrono::duration_cast<std::chrono::milliseconds>(_e.time_since_epoch()).count();
auto const durationSinceEpoch = std::chrono::milliseconds(milliSecondsSinceEpoch);

78
libdevcore/Log.cpp

@ -23,6 +23,7 @@
#include <string>
#include <iostream>
#include <thread>
#include "Guards.h"
using namespace std;
using namespace dev;
@ -31,13 +32,87 @@ using namespace dev;
int dev::g_logVerbosity = 5;
map<type_info const*, bool> dev::g_logOverride;
ThreadLocalLogName dev::t_logThreadName("main");
/// Associate a name with each thread for nice logging.
struct ThreadLocalLogName
{
ThreadLocalLogName(std::string const& _name) { m_name.reset(new string(_name)); }
boost::thread_specific_ptr<std::string> m_name;
};
/// Associate a name with each thread for nice logging.
struct ThreadLocalLogContext
{
ThreadLocalLogContext() = default;
void push(std::string const& _name)
{
if (!m_contexts.get())
m_contexts.reset(new vector<string>);
m_contexts->push_back(_name);
}
void pop()
{
m_contexts->pop_back();
}
string join(string const& _prior)
{
string ret;
if (m_contexts.get())
for (auto const& i: *m_contexts)
ret += _prior + i;
return ret;
}
boost::thread_specific_ptr<std::vector<std::string>> m_contexts;
};
ThreadLocalLogContext g_logThreadContext;
ThreadLocalLogName g_logThreadName("main");
void dev::ThreadContext::push(string const& _n)
{
g_logThreadContext.push(_n);
}
void dev::ThreadContext::pop()
{
g_logThreadContext.pop();
}
string dev::ThreadContext::join(string const& _prior)
{
return g_logThreadContext.join(_prior);
}
// foward declare without all of Windows.h
#ifdef _WIN32
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);
#endif
string dev::getThreadName()
{
#ifdef __linux__
char buffer[128];
pthread_getname_np(pthread_self(), buffer, 127);
buffer[127] = 0;
return buffer;
#else
return g_logThreadName.m_name.get() ? *g_logThreadName.m_name.get() : "<unknown>";
#endif
}
void dev::setThreadName(string const& _n)
{
#ifdef __linux__
pthread_setname_np(pthread_self(), _n.c_str());
#else
g_logThreadName.m_name.reset(new std::string(_n));
#endif
}
void dev::simpleDebugOut(std::string const& _s, char const*)
{
static Mutex s_lock;
@ -55,4 +130,3 @@ void dev::simpleDebugOut(std::string const& _s, char const*)
}
std::function<void(std::string const&, char const*)> dev::g_logPost = simpleDebugOut;

22
libdevcore/Log.h

@ -53,18 +53,24 @@ extern std::function<void(std::string const&, char const*)> g_logPost;
/// or equal to the currently output verbosity (g_logVerbosity).
extern std::map<std::type_info const*, bool> g_logOverride;
/// Associate a name with each thread for nice logging.
struct ThreadLocalLogName
#define ETH_THREAD_CONTEXT(name) for (std::pair<dev::ThreadContext, bool> __eth_thread_context(name, true); p.second; p.second = false)
class ThreadContext
{
ThreadLocalLogName(std::string _name) { m_name.reset(new std::string(_name)); };
boost::thread_specific_ptr<std::string> m_name;
public:
ThreadContext(std::string const& _info) { push(_info); }
~ThreadContext() { pop(); }
static void push(std::string const& _n);
static void pop();
static std::string join(std::string const& _prior);
};
/// The current thread's name.
extern ThreadLocalLogName t_logThreadName;
/// Set the current thread's log name.
void setThreadName(std::string const& _n);
/// Set the current thread's log name.
inline void setThreadName(char const* _n) { t_logThreadName.m_name.reset(new std::string(_n)); }
std::string getThreadName();
/// The default logging channels. Each has an associated verbosity and three-letter prefix (name() ).
/// Channels should inherit from LogChannel and define name() and verbosity.
@ -92,7 +98,7 @@ public:
char buf[24];
if (strftime(buf, 24, "%X", localtime(&rawTime)) == 0)
buf[0] = '\0'; // empty if case strftime fails
m_sstr << Id::name() << " [ " << buf << " | " << (t_logThreadName.m_name.get() ? *t_logThreadName.m_name.get() : std::string("<unknown>")) << (_term ? " ] " : "");
m_sstr << Id::name() << " [ " << buf << " | " << getThreadName() << ThreadContext::join(" | ") << (_term ? " ] " : "");
}
}

15
libdevcore/RLP.cpp

@ -111,10 +111,24 @@ unsigned RLP::actualSize() const
return 0;
}
void RLP::requireGood() const
{
if (isNull())
BOOST_THROW_EXCEPTION(BadRLP());
byte n = m_data[0];
if (n != c_rlpDataImmLenStart + 1)
return;
if (m_data.size() < 2)
BOOST_THROW_EXCEPTION(BadRLP());
if (m_data[1] < c_rlpDataImmLenStart)
BOOST_THROW_EXCEPTION(BadRLP());
}
bool RLP::isInt() const
{
if (isNull())
return false;
requireGood();
byte n = m_data[0];
if (n < c_rlpDataImmLenStart)
return !!n;
@ -141,6 +155,7 @@ unsigned RLP::length() const
{
if (isNull())
return 0;
requireGood();
unsigned ret = 0;
byte n = m_data[0];
if (n < c_rlpDataImmLenStart)

7
libdevcore/RLP.h

@ -253,6 +253,7 @@ public:
/// Converts to int of type given; if isString(), decodes as big-endian bytestream. @returns 0 if not an int or string.
template <class _T = unsigned> _T toInt(int _flags = Strict) const
{
requireGood();
if ((!isInt() && !(_flags & AllowNonCanon)) || isList() || isNull())
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
@ -273,6 +274,7 @@ public:
template <class _N> _N toHash(int _flags = Strict) const
{
requireGood();
if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)) || (length() < _N::size && (_flags & FailIfTooSmall)))
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
@ -290,7 +292,7 @@ public:
RLPs toList() const;
/// @returns the data payload. Valid for all types.
bytesConstRef payload() const { return m_data.cropped(payloadOffset()); }
bytesConstRef payload() const { auto l = length(); if (l > m_data.size()) throw BadRLP(); return m_data.cropped(payloadOffset(), l); }
/// @returns the theoretical size of this item.
/// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work.
@ -300,6 +302,9 @@ private:
/// Disable construction from rvalue
explicit RLP(bytes const&&) {}
/// Throws if is non-canonical data (i.e. single byte done in two bytes that could be done in one).
void requireGood() const;
/// Single-byte data payload.
bool isSingleByte() const { return !isNull() && m_data[0] < c_rlpDataImmLenStart; }

72
libdevcore/Worker.cpp

@ -27,52 +27,76 @@
using namespace std;
using namespace dev;
void Worker::startWorking(IfRunning _ir)
void Worker::startWorking()
{
// cnote << "startWorking for thread" << m_name;
Guard l(x_work);
if (m_work && m_work->joinable())
try {
if (_ir == IfRunning::Detach)
m_work->detach();
else if (_ir == IfRunning::Join)
m_work->join();
else
return;
} catch (...) {}
cnote << "Spawning" << m_name;
m_stop = false;
m_state = WorkerState::Starting;
if (!m_work)
{
m_work.reset(new thread([&]()
{
setThreadName(m_name.c_str());
while (m_state != WorkerState::Killing)
{
WorkerState ex = WorkerState::Starting;
m_state.compare_exchange_strong(ex, WorkerState::Started);
startedWorking();
cnote << "Entering work loop...";
workLoop();
m_work->detach();
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);
if (ex == WorkerState::Killing)
m_state.exchange(ex);
while (m_state == WorkerState::Stopped)
this_thread::sleep_for(chrono::milliseconds(20));
}
}));
cnote << "Spawning" << m_name;
}
while (m_state != WorkerState::Started)
this_thread::sleep_for(chrono::microseconds(20));
}
void Worker::stopWorking()
{
// cnote << "stopWorking for thread" << m_name;
Guard l(x_work);
if (!m_work || !m_work->joinable())
return;
ETH_GUARDED(x_work)
if (m_work)
{
cnote << "Stopping" << m_name;
m_stop = true;
try {
m_work->join();
WorkerState ex = WorkerState::Started;
m_state.compare_exchange_strong(ex, WorkerState::Stopping);
while (m_state != WorkerState::Stopped)
this_thread::sleep_for(chrono::microseconds(20));
}
}
catch (...) {}
void Worker::terminate()
{
// cnote << "stopWorking for thread" << m_name;
ETH_GUARDED(x_work)
if (m_work)
{
cnote << "Terminating" << m_name;
m_state.exchange(WorkerState::Killing);
m_work->join();
m_work.reset();
cnote << "Stopped" << m_name;
}
}
void Worker::workLoop()
{
while (!m_stop)
while (m_state == WorkerState::Started)
{
if (m_idleWaitMs)
this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs));

25
libdevcore/Worker.h

@ -36,6 +36,15 @@ enum class IfRunning
Detach
};
enum class WorkerState
{
Starting,
Started,
Stopping,
Stopped,
Killing
};
class Worker
{
protected:
@ -47,19 +56,19 @@ protected:
/// Move-assignment.
Worker& operator=(Worker&& _m) { std::swap(m_name, _m.m_name); return *this; }
virtual ~Worker() { stopWorking(); }
virtual ~Worker() { terminate(); }
/// Allows changing worker name if work is stopped.
void setName(std::string _n) { if (!isWorking()) m_name = _n; }
/// Starts worker thread; causes startedWorking() to be called.
void startWorking(IfRunning _ir = IfRunning::Fail);
void startWorking();
/// Stop worker thread; causes call to stopWorking().
void stopWorking();
/// Returns if worker thread is present.
bool isWorking() const { Guard l(x_work); return !!m_work; }
bool isWorking() const { Guard l(x_work); return m_state == WorkerState::Started; }
/// Called after thread is started from startWorking().
virtual void startedWorking() {}
@ -69,21 +78,25 @@ protected:
/// Overrides doWork(); should call shouldStop() often and exit when true.
virtual void workLoop();
bool shouldStop() const { return m_stop; }
bool shouldStop() const { return m_state != WorkerState::Started; }
/// Called when is to be stopped, just prior to thread being joined.
virtual void doneWorking() {}
/// Blocks caller into worker thread has finished.
void join() const { Guard l(x_work); try { if (m_work) m_work->join(); } catch (...) {} }
// void join() const { Guard l(x_work); try { if (m_work) m_work->join(); } catch (...) {} }
private:
/// Stop and never start again.
void terminate();
std::string m_name;
unsigned m_idleWaitMs = 0;
mutable Mutex x_work; ///< Lock for the network existance.
std::unique_ptr<std::thread> m_work; ///< The network thread.
bool m_stop = false;
std::atomic<WorkerState> m_state = {WorkerState::Starting};
};
}

7
libdevcrypto/OverlayDB.cpp

@ -34,13 +34,6 @@ OverlayDB::~OverlayDB()
cnote << "Closing state DB";
}
void OverlayDB::setDB(ldb::DB* _db, bool _clearOverlay)
{
m_db = std::shared_ptr<ldb::DB>(_db);
if (_clearOverlay)
m_over.clear();
}
void OverlayDB::commit()
{
if (m_db)

1
libdevcrypto/OverlayDB.h

@ -42,7 +42,6 @@ public:
~OverlayDB();
ldb::DB* db() const { return m_db.get(); }
void setDB(ldb::DB* _db, bool _clearOverlay = true);
void commit();
void rollback();

24
libethash-cl/ethash_cl_miner.cpp

@ -57,7 +57,7 @@ ethash_cl_miner::ethash_cl_miner()
{
}
std::string ethash_cl_miner::platform_info()
std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId)
{
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
@ -67,21 +67,22 @@ std::string ethash_cl_miner::platform_info()
return std::string();
}
// get GPU device of the default platform
// get GPU device of the selected platform
std::vector<cl::Device> devices;
platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
debugf("No OpenCL devices found.\n");
return std::string();
}
// use default device
unsigned device_num = 0;
// use selected default device
unsigned device_num = std::min<unsigned>(_deviceId, devices.size() - 1);
cl::Device& device = devices[device_num];
std::string device_version = device.getInfo<CL_DEVICE_VERSION>();
return "{ \"platform\": \"" + platforms[0].getInfo<CL_PLATFORM_NAME>() + "\", \"device\": \"" + device.getInfo<CL_DEVICE_NAME>() + "\", \"version\": \"" + device_version + "\" }";
return "{ \"platform\": \"" + platforms[platform_num].getInfo<CL_PLATFORM_NAME>() + "\", \"device\": \"" + device.getInfo<CL_DEVICE_NAME>() + "\", \"version\": \"" + device_version + "\" }";
}
void ethash_cl_miner::finish()
@ -92,7 +93,7 @@ void ethash_cl_miner::finish()
}
}
bool ethash_cl_miner::init(ethash_params const& params, std::function<void(void*)> _fillDAG, unsigned workgroup_size, unsigned _deviceId)
bool ethash_cl_miner::init(ethash_params const& params, std::function<void(void*)> _fillDAG, unsigned workgroup_size, unsigned _platformId, unsigned _deviceId)
{
// store params
m_params = params;
@ -106,12 +107,15 @@ bool ethash_cl_miner::init(ethash_params const& params, std::function<void(void*
return false;
}
// use default platform
fprintf(stderr, "Using platform: %s\n", platforms[0].getInfo<CL_PLATFORM_NAME>().c_str());
// use selected platform
_platformId = std::min<unsigned>(_platformId, platforms.size() - 1);
fprintf(stderr, "Using platform: %s\n", platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str());
// get GPU device of the default platform
std::vector<cl::Device> devices;
platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
debugf("No OpenCL devices found.\n");

4
libethash-cl/ethash_cl_miner.h

@ -31,8 +31,8 @@ public:
public:
ethash_cl_miner();
bool init(ethash_params const& params, std::function<void(void*)> _fillDAG, unsigned workgroup_size = 64, unsigned _deviceId = 0);
static std::string platform_info();
bool init(ethash_params const& params, std::function<void(void*)> _fillDAG, unsigned workgroup_size = 64, unsigned _platformId = 0, unsigned _deviceId = 0);
static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0);
void finish();
void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count);

2
libethcore/BlockInfo.cpp

@ -77,7 +77,7 @@ h256 const& BlockInfo::hash() const
h256 const& BlockInfo::boundary() const
{
if (!m_boundary)
if (!m_boundary && difficulty)
m_boundary = (h256)(u256)((bigint(1) << 256) / difficulty);
return m_boundary;
}

2
libethcore/Common.cpp

@ -34,7 +34,7 @@ namespace eth
{
const unsigned c_protocolVersion = 60;
const unsigned c_minorProtocolVersion = 0;
const unsigned c_minorProtocolVersion = 1;
const unsigned c_databaseBaseVersion = 9;
#if ETH_FATDB
const unsigned c_databaseVersionModifier = 1;

16
libethcore/Ethash.cpp

@ -237,7 +237,7 @@ protected:
return true;
}
}
return false;
return m_owner->shouldStop();
}
virtual bool searched(uint64_t _startNonce, uint32_t _count) override
@ -246,7 +246,7 @@ protected:
// std::cerr << "Searched " << _count << " from " << _startNonce << std::endl;
m_owner->accumulateHashes(_count);
m_last = _startNonce + _count;
if (m_abort)
if (m_abort || m_owner->shouldStop())
{
m_aborted = true;
return true;
@ -262,10 +262,12 @@ private:
Ethash::GPUMiner* m_owner = nullptr;
};
unsigned Ethash::GPUMiner::s_platformId = 0;
unsigned Ethash::GPUMiner::s_deviceId = 0;
Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci):
Miner(_ci),
Worker("gpuminer"),
m_hook(new EthashCLHook(this))
{
}
@ -295,6 +297,7 @@ void Ethash::GPUMiner::kickOff()
void Ethash::GPUMiner::workLoop()
{
// take local copy of work since it may end up being overwritten by kickOff/pause.
try {
WorkPackage w = work();
if (!m_miner || m_minerSeed != w.seedHash)
{
@ -305,12 +308,17 @@ void Ethash::GPUMiner::workLoop()
auto p = EthashAux::params(m_minerSeed);
auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); };
m_miner->init(p, cb, 32, s_deviceId);
m_miner->init(p, cb, 32, s_platformId, s_deviceId);
}
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
m_miner->search(w.headerHash.data(), upper64OfBoundary, *m_hook);
}
catch (...)
{
cwarn << "Error GPU mining. GPU memory fragmentation?";
}
}
void Ethash::GPUMiner::pause()
{
@ -320,7 +328,7 @@ void Ethash::GPUMiner::pause()
std::string Ethash::GPUMiner::platformInfo()
{
return ethash_cl_miner::platform_info();
return ethash_cl_miner::platform_info(s_platformId, s_deviceId);
}
#endif

3
libethcore/Ethash.h

@ -86,6 +86,7 @@ public:
static unsigned instances() { return std::thread::hardware_concurrency(); }
static std::string platformInfo();
static void setDefaultPlatform(unsigned) {}
static void setDefaultDevice(unsigned) {}
protected:
@ -113,6 +114,7 @@ public:
static unsigned instances() { return 1; }
static std::string platformInfo();
static void setDefaultPlatform(unsigned _id) { s_platformId = _id; }
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; }
protected:
@ -129,6 +131,7 @@ public:
ethash_cl_miner* m_miner = nullptr;
h256 m_minerSeed; ///< Last seed in m_miner
static unsigned s_platformId;
static unsigned s_deviceId;
};
#else

17
libethcore/EthashAux.cpp

@ -75,11 +75,11 @@ h256 EthashAux::seedHash(unsigned _number)
n = get()->m_seedHashes.size() - 1;
}
get()->m_seedHashes.resize(epoch + 1);
cdebug << "Searching for seedHash of epoch " << epoch;
// cdebug << "Searching for seedHash of epoch " << epoch;
for (; n <= epoch; ++n, ret = sha3(ret))
{
get()->m_seedHashes[n] = ret;
cdebug << "Epoch" << n << "is" << ret.abridged();
// cdebug << "Epoch" << n << "is" << ret.abridged();
}
}
return get()->m_seedHashes[epoch];
@ -95,7 +95,7 @@ ethash_params EthashAux::params(h256 const& _seedHash)
}
catch (...)
{
cdebug << "Searching for seedHash " << _seedHash.abridged();
// cdebug << "Searching for seedHash " << _seedHash.abridged();
for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {}
if (epoch == 2048)
{
@ -138,12 +138,12 @@ EthashAux::LightAllocation::~LightAllocation()
}
EthashAux::FullType EthashAux::full(BlockInfo const& _header, bytesRef _dest)
EthashAux::FullType EthashAux::full(BlockInfo const& _header, bytesRef _dest, bool _createIfMissing)
{
return full(_header.seedHash(), _dest);
return full(_header.seedHash(), _dest, _createIfMissing);
}
EthashAux::FullType EthashAux::full(h256 const& _seedHash, bytesRef _dest)
EthashAux::FullType EthashAux::full(h256 const& _seedHash, bytesRef _dest, bool _createIfMissing)
{
RecursiveGuard l(get()->x_this);
FullType ret = get()->m_fulls[_seedHash].lock();
@ -180,6 +180,8 @@ EthashAux::FullType EthashAux::full(h256 const& _seedHash, bytesRef _dest)
bytesRef r = contentsNew(memoFile, _dest);
if (!r)
{
if (!_createIfMissing)
return FullType();
// file didn't exist.
if (_dest)
// buffer was passed in - no insertion into cache nor need to allocate
@ -221,8 +223,7 @@ Ethash::Result EthashAux::LightAllocation::compute(h256 const& _seedHash, h256 c
Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce)
{
// TODO: should be EthashAux::get()->haveFull(_seedHash)
if (auto dag = EthashAux::get()->full(_seedHash))
if (auto dag = EthashAux::get()->full(_seedHash, bytesRef(), false))
return dag->compute(_seedHash, _headerHash, _nonce);
return EthashAux::get()->light(_seedHash)->compute(_seedHash, _headerHash, _nonce);
}

4
libethcore/EthashAux.h

@ -60,8 +60,8 @@ public:
static ethash_params params(unsigned _n);
static LightType light(BlockInfo const& _header);
static LightType light(h256 const& _seedHash);
static FullType full(BlockInfo const& _header, bytesRef _dest = bytesRef());
static FullType full(h256 const& _header, bytesRef _dest = bytesRef());
static FullType full(BlockInfo const& _header, bytesRef _dest = bytesRef(), bool _createIfMissing = true);
static FullType full(h256 const& _header, bytesRef _dest = bytesRef(), bool _createIfMissing = true);
static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); }
static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce);

6
libethcore/Miner.h

@ -24,7 +24,9 @@
#include <thread>
#include <list>
#include <atomic>
#include <boost/timer.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libdevcore/Worker.h>
#include <libethcore/Common.h>
@ -105,8 +107,12 @@ public:
}
if (!!_work)
{
boost::timer t;
pause();
cdebug << "pause took" << t.elapsed();
t.restart();
kickOff();
cdebug << "kickOff took" << t.elapsed();
}
else if (!_work && !!old)
pause();

82
libethereum/BlockChain.cpp

@ -167,6 +167,7 @@ void BlockChain::open(std::string const& _path, WithExisting _we)
std::string l;
m_extrasDB->Get(m_readOptions, ldb::Slice("best"), &l);
m_lastBlockHash = l.empty() ? m_genesisHash : *(h256*)l.data();
m_lastBlockNumber = number(m_lastBlockHash);
cnote << "Opened blockchain DB. Latest: " << currentHash();
}
@ -177,6 +178,7 @@ void BlockChain::close()
delete m_extrasDB;
delete m_blocksDB;
m_lastBlockHash = m_genesisHash;
m_lastBlockNumber = 0;
m_details.clear();
m_blocks.clear();
}
@ -185,26 +187,27 @@ void BlockChain::close()
void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned, unsigned)> const& _progress)
{
std::string path = _path.empty() ? Defaults::get()->m_dbPath : _path;
#if ETH_PROFILING_GPERF
ProfilerStart("BlockChain_rebuild.log");
#endif
// unsigned originalNumber = (unsigned)BlockInfo(oldBlock(m_lastBlockHash)).number;
unsigned originalNumber = number();
unsigned originalNumber = m_lastBlockNumber;
// Keep extras DB around, but under a temp name
delete m_extrasDB;
m_extrasDB = nullptr;
IGNORE_EXCEPTIONS(boost::filesystem::remove_all(_path + "/details.old"));
boost::filesystem::rename(_path + "/details", _path + "/details.old");
IGNORE_EXCEPTIONS(boost::filesystem::remove_all(path + "/details.old"));
boost::filesystem::rename(path + "/details", path + "/details.old");
ldb::DB* oldExtrasDB;
ldb::Options o;
o.create_if_missing = true;
ldb::DB::Open(o, _path + "/details.old", &oldExtrasDB);
ldb::DB::Open(o, _path + "/details", &m_extrasDB);
ldb::DB::Open(o, path + "/details.old", &oldExtrasDB);
ldb::DB::Open(o, path + "/details", &m_extrasDB);
// Open a fresh state DB
State s(State::openDB(_path, WithExisting::Kill), BaseState::CanonGenesis);
State s(State::openDB(path, WithExisting::Kill), BaseState::CanonGenesis);
// Clear all memos ready for replay.
m_details.clear();
@ -215,8 +218,13 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
m_blocksBlooms.clear();
m_lastLastHashes.clear();
m_lastBlockHash = genesisHash();
m_lastBlockNumber = 0;
m_details[m_lastBlockHash].totalDifficulty = c_genesisDifficulty;
h256 lastHash = genesisHash();
m_extrasDB->Put(m_writeOptions, toSlice(m_lastBlockHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[m_lastBlockHash].rlp()));
h256 lastHash = m_lastBlockHash;
boost::timer t;
for (unsigned d = 1; d < originalNumber; ++d)
{
@ -238,7 +246,7 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
return;
}
lastHash = bi.hash();
import(b, s.db(), ImportRequirements::Default);
import(b, s.db(), 0);
}
catch (...)
{
@ -255,7 +263,7 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
#endif
delete oldExtrasDB;
boost::filesystem::remove_all(_path + "/details.old");
boost::filesystem::remove_all(path + "/details.old");
}
template <class T, class V>
@ -308,7 +316,7 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
fresh += r.first;
dead += r.second;
}
catch (UnknownParent)
catch (dev::eth::UnknownParent)
{
cwarn << "ODD: Import queue contains block with unknown parent." << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the right order.
@ -396,20 +404,22 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
if (!pd)
{
auto pdata = pd.rlp();
cwarn << "Odd: details is returning false despite block known:" << RLP(pdata);
clog(BlockChainDebug) << "Details is returning false despite block known:" << RLP(pdata);
auto parentBlock = block(bi.parentHash);
cwarn << "Block:" << RLP(parentBlock);
clog(BlockChainDebug) << "Block:" << RLP(parentBlock);
clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
exit(-1);
}
// Check it's not crazy
if (bi.timestamp > (u256)time(0))
{
clog(BlockChainNote) << bi.hash() << ": Future time " << bi.timestamp << " (now at " << time(0) << ")";
clog(BlockChainChat) << bi.hash() << ": Future time " << bi.timestamp << " (now at " << time(0) << ")";
// Block has a timestamp in the future. This is no good.
BOOST_THROW_EXCEPTION(FutureTime());
}
clog(BlockChainNote) << "Attempting import of " << bi.hash().abridged() << "...";
clog(BlockChainChat) << "Attempting import of " << bi.hash().abridged() << "...";
#if ETH_TIMED_IMPORTS
preliminaryChecks = t.elapsed();
@ -527,12 +537,6 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
{
unsigned commonIndex;
tie(route, common, commonIndex) = treeRoute(last, bi.hash());
{
WriteGuard l(x_lastBlockHash);
m_lastBlockHash = bi.hash();
}
m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&(bi.hash()), 32));
// Most of the time these two will be equal - only when we're doing a chain revert will they not be
if (common != last)
@ -593,6 +597,14 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp()));
}
// FINALLY! change our best hash.
{
WriteGuard l(x_lastBlockHash);
m_lastBlockHash = bi.hash();
m_lastBlockNumber = (unsigned)bi.number;
m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&(bi.hash()), 32));
}
clog(BlockChainNote) << " Imported and best" << td << " (#" << bi.number << "). Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(route);
noteCanonChanged();
@ -605,7 +617,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
}
else
{
clog(BlockChainNote) << " Imported but not best (oTD:" << details(last).totalDifficulty << " > TD:" << td << ")";
clog(BlockChainChat) << " Imported but not best (oTD:" << details(last).totalDifficulty << " > TD:" << td << ")";
}
#if ETH_TIMED_IMPORTS
@ -618,6 +630,14 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
cnote << "checkBest:" << checkBest;
#endif
if (isKnown(bi.hash()) && !details(bi.hash()))
{
clog(BlockChainDebug) << "Known block just inserted has no details.";
clog(BlockChainDebug) << "Block:" << bi;
clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
exit(-1);
}
h256s fresh;
h256s dead;
bool isOld = true;
@ -944,14 +964,26 @@ bool BlockChain::isKnown(h256 const& _hash) const
{
if (_hash == m_genesisHash)
return true;
BlockInfo bi;
{
ReadGuard l(x_blocks);
if (m_blocks.count(_hash))
return true;
auto it = m_blocks.find(_hash);
if (it != m_blocks.end())
bi = BlockInfo(it->second, CheckNothing, _hash);
}
if (!bi)
{
string d;
m_blocksDB->Get(m_readOptions, toSlice(_hash), &d);
return !!d.size();
if (!d.size())
return false;
bi = BlockInfo(bytesConstRef(&d), CheckNothing, _hash);
}
return bi.number <= m_lastBlockNumber; // TODO: m_lastBlockNumber
}
bytes BlockChain::block(h256 const& _hash) const

11
libethereum/BlockChain.h

@ -56,9 +56,10 @@ struct AlreadyHaveBlock: virtual Exception {};
struct UnknownParent: virtual Exception {};
struct FutureTime: virtual Exception {};
struct BlockChainChat: public LogChannel { static const char* name() { return "-B-"; } static const int verbosity = 7; };
struct BlockChainNote: public LogChannel { static const char* name() { return "=B="; } static const int verbosity = 4; };
struct BlockChainChat: public LogChannel { static const char* name() { return "-B-"; } static const int verbosity = 5; };
struct BlockChainNote: public LogChannel { static const char* name() { return "=B="; } static const int verbosity = 3; };
struct BlockChainWarn: public LogChannel { static const char* name() { return "=B="; } static const int verbosity = 1; };
struct BlockChainDebug: public LogChannel { static const char* name() { return "#B#"; } static const int verbosity = 0; };
// TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::map<Address, Account> const& genesisState();
@ -98,7 +99,8 @@ public:
/// To be called from main loop every 100ms or so.
void process();
/// Sync the chain with any incoming blocks. All blocks should, if processed in order
/// Sync the chain with any incoming blocks. All blocks should, if processed in order.
/// @returns fresh blocks, dead blocks and true iff there are additional blocks to be processed waiting.
std::tuple<h256s, h256s, bool> sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max);
/// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB.
@ -183,7 +185,7 @@ public:
/// Get a number for the given hash (or the most recent mined if none given). Thread-safe.
unsigned number(h256 const& _hash) const { return details(_hash).number; }
unsigned number() const { return number(currentHash()); }
unsigned number() const { return m_lastBlockNumber; }
/// Get a given block (RLP format). Thread-safe.
h256 currentHash() const { ReadGuard l(x_lastBlockHash); return m_lastBlockHash; }
@ -313,6 +315,7 @@ private:
/// Hash of the last (valid) block on the longest chain.
mutable boost::shared_mutex x_lastBlockHash;
h256 m_lastBlockHash;
unsigned m_lastBlockNumber = 0;
/// Genesis block info.
h256 m_genesisHash;

49
libethereum/BlockQueue.cpp

@ -74,7 +74,11 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (bi.timestamp > (u256)time(0)/* && !_isOurs*/)
{
m_future.insert(make_pair((unsigned)bi.timestamp, _block.toBytes()));
cblockq << "OK - queued for future.";
char buf[24];
time_t bit = (unsigned)bi.timestamp;
if (strftime(buf, 24, "%X", localtime(&bit)) == 0)
buf[0] = '\0'; // empty if case strftime fails
cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf;
return ImportResult::FutureTime;
}
else
@ -132,12 +136,32 @@ bool BlockQueue::doneDrain(h256s const& _bad)
void BlockQueue::tick(BlockChain const& _bc)
{
vector<bytes> todo;
{
UpgradableGuard l(m_lock);
if (m_future.empty())
return;
cblockq << "Checking past-future blocks...";
unsigned t = time(0);
for (auto i = m_future.begin(); i != m_future.end() && i->first < t; ++i)
import(&(i->second), _bc);
if (t <= m_future.begin()->first)
return;
WriteGuard l(m_lock);
m_future.erase(m_future.begin(), m_future.upper_bound(t));
cblockq << "Past-future blocks ready.";
{
UpgradeGuard l2(l);
auto end = m_future.lower_bound(t);
for (auto i = m_future.begin(); i != end; ++i)
todo.push_back(move(i->second));
m_future.erase(m_future.begin(), end);
}
}
cblockq << "Importing" << todo.size() << "past-future blocks.";
for (auto const& b: todo)
import(&b, _bc);
}
template <class T> T advanced(T _t, unsigned _n)
@ -146,6 +170,21 @@ template <class T> T advanced(T _t, unsigned _n)
return _t;
}
QueueStatus BlockQueue::blockStatus(h256 const& _h) const
{
ReadGuard l(m_lock);
return
m_readySet.count(_h) ?
QueueStatus::Ready :
m_drainingSet.count(_h) ?
QueueStatus::Importing :
m_unknownSet.count(_h) ?
QueueStatus::UnknownParent :
m_knownBad.count(_h) ?
QueueStatus::Bad :
QueueStatus::Unknown;
}
void BlockQueue::drain(std::vector<bytes>& o_out, unsigned _max)
{
WriteGuard l(m_lock);

12
libethereum/BlockQueue.h

@ -46,6 +46,15 @@ struct BlockQueueStatus
size_t bad;
};
enum class QueueStatus
{
Ready,
Importing,
UnknownParent,
Bad,
Unknown
};
/**
* @brief A queue of blocks. Sits between network or other I/O and the BlockChain.
* Sorts them ready for blockchain insertion (with the BlockChain::sync() method).
@ -86,6 +95,9 @@ public:
/// Get some infomration on the current status.
BlockQueueStatus status() const { ReadGuard l(m_lock); return BlockQueueStatus{m_ready.size(), m_future.size(), m_unknown.size(), m_knownBad.size()}; }
/// Get some infomration on the given block's status regarding us.
QueueStatus blockStatus(h256 const& _h) const;
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
private:

122
libethereum/Client.cpp

@ -117,6 +117,13 @@ void BasicGasPricer::update(BlockChain const& _bc)
}
}
std::ostream& dev::eth::operator<<(std::ostream& _out, ActivityReport const& _r)
{
_out << "Since " << toString(_r.since) << " (" << std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - _r.since).count();
_out << "): " << _r.ticks << "ticks";
return _out;
}
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Worker("eth"),
m_vc(_dbPath),
@ -197,15 +204,16 @@ void Client::startedWorking()
// Synchronise the state according to the head of the block chain.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
cdebug << "startedWorking()";
WriteGuard l(x_stateDB);
cdebug << m_bc.number() << m_bc.currentHash();
cdebug << "Pre:" << m_preMine.info();
cdebug << "Post:" << m_postMine.info();
cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce);
ETH_WRITE_GUARDED(x_preMine)
m_preMine.sync(m_bc);
ETH_WRITE_GUARDED(x_postMine)
ETH_READ_GUARDED(x_preMine)
m_postMine = m_preMine;
cdebug << "Pre:" << m_preMine.info();
@ -217,13 +225,18 @@ 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.
WriteGuard l(x_stateDB);
ETH_WRITE_GUARDED(x_preMine)
m_preMine.sync(m_bc);
ETH_WRITE_GUARDED(x_postMine)
ETH_READ_GUARDED(x_preMine)
m_postMine = m_preMine;
}
void Client::killChain()
{
WriteGuard l(x_postMine);
WriteGuard l2(x_preMine);
bool wasMining = isMining();
if (wasMining)
stopMining();
@ -235,8 +248,8 @@ void Client::killChain()
m_preMine = State();
m_postMine = State();
// ETH_WRITE_GUARDED(x_stateDB) // no point doing this yet since we can't control where else it's open yet.
{
WriteGuard l(x_stateDB);
m_stateDB = OverlayDB();
m_stateDB = State::openDB(Defaults::dbPath(), WithExisting::Kill);
}
@ -258,14 +271,15 @@ void Client::killChain()
void Client::clearPending()
{
h256Set changeds;
ETH_WRITE_GUARDED(x_postMine)
{
WriteGuard l(x_stateDB);
if (!m_postMine.pending().size())
return;
// for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
// appendFromNewPending(m_postMine.logBloom(i), changeds);
changeds.insert(PendingChangedFilter);
m_tq.clear();
ETH_READ_GUARDED(x_preMine)
m_postMine = m_preMine;
}
@ -338,11 +352,15 @@ void Client::setForceMining(bool _enable)
MiningProgress Client::miningProgress() const
{
if (m_farm.isMining())
return m_farm.miningProgress();
return MiningProgress();
}
uint64_t Client::hashrate() const
{
if (m_farm.isMining())
return m_farm.miningProgress().rate();
return 0;
}
@ -364,29 +382,6 @@ std::list<MineInfo> Client::miningHistory()
return ret;
}
/*void Client::setupState(State& _s)
{
{
ReadGuard l(x_stateDB);
cwork << "SETUP MINE";
_s = m_postMine;
}
if (m_paranoia)
{
if (_s.amIJustParanoid(m_bc))
{
cnote << "I'm just paranoid. Block is fine.";
_s.commitToMine(m_bc);
}
else
{
cwarn << "I'm not just paranoid. Cannot mine. Please file a bug report.";
}
}
else
_s.commitToMine(m_bc);
}*/
ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice, Address const& _from)
{
ExecutionResult ret;
@ -394,11 +389,9 @@ 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));
{
ReadGuard l(x_stateDB);
ETH_READ_GUARDED(x_postMine)
temp = m_postMine;
temp.addBalance(_from, _value + _gasPrice * _gas);
}
Executive e(temp, LastHashes(), 0);
if (!e.call(_dest, _dest, _from, _value, _gasPrice, &_data, _gas, _from))
e.go();
@ -413,6 +406,10 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
ProofOfWork::WorkPackage Client::getWork()
{
// lock the work so a later submission isn't invalidated by processing a transaction elsewhere.
// this will be reset as soon as a new block arrives, allowing more transactions to be processed.
m_lastGetWork = chrono::system_clock::now();
m_remoteWorking = true;
return ProofOfWork::package(m_miningInfo);
}
@ -420,10 +417,11 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution)
{
bytes newBlock;
{
WriteGuard l(x_stateDB);
WriteGuard l(x_postMine);
if (!m_postMine.completeMine<ProofOfWork>(_solution))
return false;
newBlock = m_postMine.blockData();
// OPTIMISE: very inefficient to not utilise the existing OverlayDB in m_postMine that contains all trie changes.
}
m_bq.import(&newBlock, m_bc, true);
/*
@ -439,13 +437,9 @@ void Client::syncBlockQueue()
cwork << "BQ ==> CHAIN ==> STATE";
{
WriteGuard l(x_stateDB);
OverlayDB db = m_stateDB;
ETH_WRITE_UNGUARDED(x_stateDB)
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100);
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, 100);
if (ir.first.empty())
return;
m_stateDB = db;
}
onChainChanged(ir);
}
@ -458,16 +452,18 @@ void Client::syncTransactionQueue()
h256Set changeds;
TransactionReceipts newPendingReceipts;
ETH_WRITE_GUARDED(x_stateDB)
newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp);
ETH_WRITE_GUARDED(x_postMine)
tie(newPendingReceipts, m_syncTransactionQueue) = m_postMine.sync(m_bc, m_tq, *m_gp);
if (newPendingReceipts.size())
{
if (newPendingReceipts.empty())
return;
ETH_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);
// TODO: Tell farm about new transaction (i.e. restartProofOfWork mining).
// Tell farm about new transaction (i.e. restartProofOfWork mining).
onPostStateChanged();
// Tell watches about the new transactions.
@ -477,7 +473,6 @@ void Client::syncTransactionQueue()
if (auto h = m_host.lock())
h->noteNewTransactions();
}
}
void Client::onChainChanged(ImportRoute const& _ir)
{
@ -488,7 +483,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
for (auto const& t: m_bc.transactions(h))
{
clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None);
m_tq.import(t);
m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
}
}
@ -514,34 +509,43 @@ void Client::onChainChanged(ImportRoute const& _ir)
// RESTART MINING
// LOCKS REALLY NEEDED?
ETH_WRITE_GUARDED(x_stateDB)
if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address())
bool preChanged = false;
ETH_WRITE_GUARDED(x_preMine)
preChanged = m_preMine.sync(m_bc);
if (preChanged || m_postMine.address() != m_preMine.address())
{
if (isMining())
cnote << "New block on chain.";
ETH_WRITE_GUARDED(x_postMine)
ETH_READ_GUARDED(x_preMine)
m_postMine = m_preMine;
changeds.insert(PendingChangedFilter);
ETH_WRITE_UNGUARDED(x_stateDB)
onPostStateChanged();
}
noteChanged(changeds);
}
bool Client::remoteActive() const
{
return chrono::system_clock::now() - m_lastGetWork < chrono::seconds(30);
}
void Client::onPostStateChanged()
{
cnote << "Post state changed: Restarting mining...";
if (isMining())
if (isMining() || remoteActive())
{
{
WriteGuard l(x_stateDB);
WriteGuard l(x_postMine);
m_postMine.commitToMine(m_bc);
m_miningInfo = m_postMine.info();
}
m_farm.setWork(m_miningInfo);
}
m_remoteWorking = false;
}
void Client::startMining()
@ -578,15 +582,16 @@ void Client::doWork()
// TODO: Use condition variable rather than this rubbish.
bool t = true;
if (m_syncTransactionQueue.compare_exchange_strong(t, false))
syncTransactionQueue();
t = true;
if (m_syncBlockQueue.compare_exchange_strong(t, false))
syncBlockQueue();
t = true;
if (m_syncTransactionQueue.compare_exchange_strong(t, false) && !m_remoteWorking)
syncTransactionQueue();
tick();
if (!m_syncBlockQueue && !m_syncTransactionQueue)
this_thread::sleep_for(chrono::milliseconds(20));
}
@ -594,9 +599,12 @@ void Client::tick()
{
if (chrono::system_clock::now() - m_lastTick > chrono::seconds(1))
{
m_report.ticks++;
checkWatchGarbage();
m_bq.tick(m_bc);
m_lastTick = chrono::system_clock::now();
if (m_report.ticks == 15)
cnote << activityReport();
}
}
@ -606,15 +614,13 @@ void Client::checkWatchGarbage()
{
// watches garbage collection
vector<unsigned> toUninstall;
{
Guard l(x_filtersWatches);
ETH_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))
{
toUninstall.push_back(key);
cnote << "GC: Uninstall" << key << "(" << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - m_watches[key].lastPoll).count() << "s old)";
}
}
for (auto i: toUninstall)
uninstallWatch(i);
@ -627,7 +633,6 @@ void Client::checkWatchGarbage()
State Client::asOf(h256 const& _block) const
{
ReadGuard l(x_stateDB);
return State(m_stateDB, bc(), _block);
}
@ -638,19 +643,16 @@ void Client::prepareForTransaction()
State Client::state(unsigned _txi, h256 _block) const
{
ReadGuard l(x_stateDB);
return State(m_stateDB, m_bc, _block).fromPending(_txi);
}
eth::State Client::state(h256 _block) const
{
ReadGuard l(x_stateDB);
return State(m_stateDB, m_bc, _block);
}
eth::State Client::state(unsigned _txi) const
{
ReadGuard l(x_stateDB);
return m_postMine.fromPending(_txi);
}

26
libethereum/Client.h

@ -97,6 +97,14 @@ struct ClientChat: public LogChannel { static const char* name() { return "=C=";
struct ClientTrace: public LogChannel { static const char* name() { return "-C-"; } static const int verbosity = 7; };
struct ClientDetail: public LogChannel { static const char* name() { return " C "; } static const int verbosity = 14; };
struct ActivityReport
{
unsigned ticks = 0;
std::chrono::system_clock::time_point since = std::chrono::system_clock::now();
};
std::ostream& operator<<(std::ostream& _out, ActivityReport const& _r);
/**
* @brief Main API hub for interfacing with Ethereum.
*/
@ -144,7 +152,7 @@ public:
dev::eth::State state(unsigned _txi) const;
/// Get the object representing the current state of Ethereum.
dev::eth::State postState() const { ReadGuard l(x_stateDB); return m_postMine; }
dev::eth::State postState() const { ReadGuard l(x_postMine); return m_postMine; }
/// Get the object representing the current canonical blockchain.
CanonBlockChain const& blockChain() const { return m_bc; }
/// Get some information on the block queue.
@ -152,7 +160,7 @@ public:
// Mining stuff:
void setAddress(Address _us) { WriteGuard l(x_stateDB); m_preMine.setAddress(_us); }
void setAddress(Address _us) { WriteGuard l(x_preMine); m_preMine.setAddress(_us); }
/// Check block validity prior to mining.
bool miningParanoia() const { return m_paranoia; }
@ -204,6 +212,8 @@ public:
void killChain();
/// Retries all blocks with unknown parents.
void retryUnkonwn() { m_bq.retryAllUnknown(); }
/// Get a report of activity.
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
protected:
/// InterfaceStub methods
@ -214,8 +224,8 @@ protected:
/// Works properly with LatestBlock and PendingBlock.
using ClientBase::asOf;
virtual State asOf(h256 const& _block) const override;
virtual State preMine() const override { ReadGuard l(x_stateDB); return m_preMine; }
virtual State postMine() const override { ReadGuard l(x_stateDB); return m_postMine; }
virtual State preMine() const override { ReadGuard l(x_preMine); return m_preMine; }
virtual State postMine() const override { ReadGuard l(x_postMine); return m_postMine; }
virtual void prepareForTransaction() override;
/// Collate the changed filters for the bloom filter of the given pending transaction.
@ -271,11 +281,15 @@ private:
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
std::shared_ptr<GasPricer> m_gp; ///< The gas pricer.
mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine.
OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
mutable SharedMutex x_preMine; ///< Lock on the OverlayDB and other attributes of m_preMine.
State m_preMine; ///< The present state of the client.
mutable SharedMutex x_postMine; ///< Lock on the OverlayDB and other attributes of m_postMine.
State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
BlockInfo m_miningInfo; ///< The header we're attempting to mine on (derived from m_postMine).
bool remoteActive() const; ///< Is there an active and valid remote worker?
bool m_remoteWorking = false; ///< Has the remote worker recently been reset?
std::chrono::system_clock::time_point m_lastGetWork = std::chrono::system_clock::time_point::min(); ///< Is there an active and valid remote worker?
std::weak_ptr<EthereumHost> m_host; ///< Our Ethereum Host. Don't do anything if we can't lock.
@ -293,6 +307,8 @@ private:
mutable std::chrono::system_clock::time_point m_lastTick = std::chrono::system_clock::now();
///< When did we last tick()?
ActivityReport m_report;
// TODO!!!!!! REPLACE WITH A PROPER X-THREAD ASIO SIGNAL SYSTEM (could just be condition variables)
std::atomic<bool> m_syncTransactionQueue = {false};
std::atomic<bool> m_syncBlockQueue = {false};

12
libethereum/CommonNet.h

@ -37,13 +37,13 @@ namespace eth
{
#if ETH_DEBUG
static const unsigned c_maxHashes = 64; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 64; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 32; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 32; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
#else
static const unsigned c_maxHashes = 256; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 256; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
#endif

18
libethereum/EthereumHost.cpp

@ -189,7 +189,7 @@ void EthereumHost::maintainTransactions()
for (auto const& i: ts)
{
bool unsent = !m_transactionsSent.count(i.first);
for (auto const& p: randomSelection(25, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }))
for (auto const& p: randomSelection(100, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }))
peerTransactions[p].push_back(i.first);
}
for (auto const& t: ts)
@ -242,19 +242,29 @@ std::vector<std::shared_ptr<EthereumPeer>> EthereumHost::randomSelection(unsigne
void EthereumHost::maintainBlocks(h256 _currentHash)
{
// Send any new blocks.
if (m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty)
auto detailsFrom = m_chain.details(m_latestBlockSent);
auto detailsTo = m_chain.details(_currentHash);
if (detailsFrom.totalDifficulty < detailsTo.totalDifficulty)
{
if (diff(detailsFrom.number, detailsTo.number) < 20)
{
// don't be sending more than 20 "new" blocks. if there are any more we were probably waaaay behind.
clog(NetMessageSummary) << "Sending a new block (current is" << _currentHash << ", was" << m_latestBlockSent << ")";
for (auto const& p: randomSelection(25, [&](EthereumPeer* p){return !p->m_knownBlocks.count(_currentHash); }))
h256s blocks = get<0>(m_chain.treeRoute(m_latestBlockSent, _currentHash, false, false, true));
for (auto const& p: randomSelection(100, [&](EthereumPeer* p){return !p->m_knownBlocks.count(_currentHash); }))
for (auto const& b: blocks)
if (!p->m_knownBlocks.count(b))
{
RLPStream ts;
p->prep(ts, NewBlockPacket, 2).appendRaw(m_chain.block(), 1).append(m_chain.details().totalDifficulty);
p->prep(ts, NewBlockPacket, 2).appendRaw(m_chain.block(b), 1).append(m_chain.details(b).totalDifficulty);
Guard l(p->x_knownBlocks);
p->sealAndSend(ts);
p->m_knownBlocks.clear();
}
}
m_latestBlockSent = _currentHash;
}
}

104
libethereum/EthereumPeer.cpp

@ -34,11 +34,6 @@ using namespace dev;
using namespace dev::eth;
using namespace p2p;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i):
Capability(_s, _h, _i),
m_sub(host()->m_man)
@ -48,7 +43,7 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i):
EthereumPeer::~EthereumPeer()
{
clogS(NetMessageSummary) << "Aborting Sync :-(";
clog(NetMessageSummary) << "Aborting Sync :-(";
abortSync();
}
@ -81,7 +76,7 @@ string toString(Asking _a)
void EthereumPeer::transition(Asking _a, bool _force)
{
clogS(NetMessageSummary) << "Transition!" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
clog(NetMessageSummary) << "Transition!" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
if (m_asking == Asking::State && _a != Asking::State)
m_requireTransactions = true;
@ -108,7 +103,7 @@ void EthereumPeer::transition(Asking _a, bool _force)
if (m_asking == Asking::State || m_asking == Asking::Nothing)
{
if (isSyncing())
clogS(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
m_syncingLatestHash = m_latestHash;
m_syncingTotalDifficulty = m_totalDifficulty;
@ -123,10 +118,10 @@ void EthereumPeer::transition(Asking _a, bool _force)
else if (m_asking == Asking::Hashes)
{
if (!isSyncing())
clogS(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
setAsking(_a, true);
prep(s, GetBlockHashesPacket, 2) << m_syncingNeededBlocks.back() << c_maxHashesAsk;
prep(s, GetBlockHashesPacket, 2) << m_syncingLastReceivedHash << c_maxHashesAsk;
sealAndSend(s);
return;
}
@ -136,13 +131,13 @@ void EthereumPeer::transition(Asking _a, bool _force)
if (m_asking == Asking::Hashes)
{
if (!isSyncing())
clogS(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
if (shouldGrabBlocks())
{
clog(NetNote) << "Difficulty of hashchain HIGHER. Grabbing" << m_syncingNeededBlocks.size() << "blocks [latest now" << m_syncingLatestHash.abridged() << ", was" << host()->m_latestBlockSent.abridged() << "]";
host()->m_man.resetToChain(m_syncingNeededBlocks);
host()->m_latestBlockSent = m_syncingLatestHash;
// host()->m_latestBlockSent = m_syncingLatestHash;
}
else
{
@ -174,7 +169,7 @@ void EthereumPeer::transition(Asking _a, bool _force)
{
if (m_asking == Asking::Blocks)
{
clogS(NetNote) << "Finishing blocks fetch...";
clog(NetNote) << "Finishing blocks fetch...";
// a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry.
if (isSyncing())
@ -187,7 +182,7 @@ void EthereumPeer::transition(Asking _a, bool _force)
}
else if (m_asking == Asking::Hashes)
{
clogS(NetNote) << "Finishing hashes fetch...";
clog(NetNote) << "Finishing hashes fetch...";
setAsking(Asking::Nothing, false);
}
@ -202,7 +197,7 @@ void EthereumPeer::transition(Asking _a, bool _force)
return;
}
clogS(NetWarn) << "Invalid state transition:" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
clog(NetWarn) << "Invalid state transition:" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
}
void EthereumPeer::setAsking(Asking _a, bool _isSyncing)
@ -270,14 +265,14 @@ void EthereumPeer::attemptSync()
{
if (m_asking != Asking::Nothing)
{
clogS(NetAllDetail) << "Can't synced with this peer - outstanding asks.";
clog(NetAllDetail) << "Can't synced with this peer - outstanding asks.";
return;
}
// if already done this, then ignore.
if (!needsSyncing())
{
clogS(NetAllDetail) << "Already synced with this peer.";
clog(NetAllDetail) << "Already synced with this peer.";
return;
}
@ -285,16 +280,16 @@ void EthereumPeer::attemptSync()
unsigned n = host()->m_chain.number();
u256 td = host()->m_chain.details().totalDifficulty;
clogS(NetAllDetail) << "Attempt chain-grab? Latest:" << c.abridged() << ", number:" << n << ", TD:" << td << " versus " << m_totalDifficulty;
clog(NetAllDetail) << "Attempt chain-grab? Latest:" << c.abridged() << ", number:" << n << ", TD:" << td << " versus " << m_totalDifficulty;
if (td >= m_totalDifficulty)
{
clogS(NetAllDetail) << "No. Our chain is better.";
clog(NetAllDetail) << "No. Our chain is better.";
resetNeedsSyncing();
transition(Asking::Nothing);
}
else
{
clogS(NetAllDetail) << "Yes. Their chain is better.";
clog(NetAllDetail) << "Yes. Their chain is better.";
transition(Asking::Hashes);
}
}
@ -315,7 +310,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
m_latestHash = _r[3].toHash<h256>();
auto genesisHash = _r[4].toHash<h256>();
clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged();
clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged();
if (genesisHash != host()->m_chain.genesisHash())
disable("Invalid genesis hash");
@ -334,7 +329,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case GetTransactionsPacket: break; // DEPRECATED.
case TransactionsPacket:
{
clogS(NetAllDetail) << "Transactions (" << dec << _r.itemCount() << "entries)";
clog(NetAllDetail) << "Transactions (" << dec << _r.itemCount() << "entries)";
Guard l(x_knownTransactions);
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
@ -363,7 +358,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{
h256 later = _r[0].toHash<h256>();
unsigned limit = _r[1].toInt<unsigned>();
clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")";
clog(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")";
unsigned c = min<unsigned>(host()->m_chain.number(later), limit);
@ -378,7 +373,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case BlockHashesPacket:
{
clogS(NetMessageSummary) << "BlockHashes (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreHashes");
clog(NetMessageSummary) << "BlockHashes (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreHashes");
if (m_asking != Asking::Hashes)
{
@ -390,37 +385,67 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
transition(Asking::Blocks);
return true;
}
unsigned knowns = 0;
unsigned unknowns = 0;
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
addRating(1);
auto h = _r[i].toHash<h256>();
if (host()->m_chain.isKnown(h))
auto status = host()->m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host()->m_chain.isKnown(h))
{
clog(NetMessageSummary) << "block hash ready:" << h << ". Start blocks download...";
transition(Asking::Blocks);
return true;
}
else
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
transition(Asking::Nothing);
return true;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
m_syncingNeededBlocks.push_back(h);
}
else
knowns++;
m_syncingLastReceivedHash = h;
}
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLastReceivedHash.abridged();
// run through - ask for more.
transition(Asking::Hashes);
break;
}
case GetBlocksPacket:
{
clogS(NetMessageSummary) << "GetBlocks (" << dec << _r.itemCount() << "entries)";
unsigned count = _r.itemCount();
clog(NetMessageSummary) << "GetBlocks (" << dec << count << "entries)";
if (!count)
{
clog(NetImpolite) << "Zero-entry GetBlocks: Not replying.";
addRating(-10);
break;
}
// return the requested blocks.
bytes rlp;
unsigned n = 0;
for (unsigned i = 0; i < _r.itemCount() && i <= c_maxBlocks; ++i)
for (unsigned i = 0; i < min(count, c_maxBlocks); ++i)
{
auto b = host()->m_chain.block(_r[i].toHash<h256>());
if (b.size())
auto h = _r[i].toHash<h256>();
if (host()->m_chain.isKnown(h))
{
rlp += b;
rlp += host()->m_chain.block(_r[i].toHash<h256>());
++n;
}
}
if (count > 20 && n == 0)
clog(NetWarn) << "all" << count << "unknown blocks requested; peer on different chain?";
else
clog(NetMessageSummary) << n << "blocks known and returned;" << (min(count, c_maxBlocks) - n) << "blocks unknown;" << (count > c_maxBlocks ? count - c_maxBlocks : 0) << "blocks ignored";
addRating(0);
RLPStream s;
prep(s, BlocksPacket, n).appendRaw(rlp, n);
@ -429,10 +454,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case BlocksPacket:
{
clogS(NetMessageSummary) << "Blocks (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreBlocks");
clog(NetMessageSummary) << "Blocks (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreBlocks");
if (m_asking != Asking::Blocks)
clogS(NetWarn) << "Unexpected Blocks received!";
clog(NetWarn) << "Unexpected Blocks received!";
if (_r.itemCount() == 0)
{
@ -476,6 +501,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case ImportResult::UnknownParent:
unknown++;
break;
default:;
}
}
else
@ -485,7 +512,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
}
clogS(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
if (m_asking == Asking::Blocks)
{
@ -499,7 +526,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case NewBlockPacket:
{
auto h = BlockInfo::headerHash(_r[0].data());
clogS(NetMessageSummary) << "NewBlock: " << h.abridged();
clog(NetMessageSummary) << "NewBlock: " << h.abridged();
if (_r.itemCount() != 2)
disable("NewBlock without 2 data fields.");
@ -524,9 +551,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
break;
case ImportResult::UnknownParent:
clogS(NetMessageSummary) << "Received block with no known parent. Resyncing...";
clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
setNeedsSyncing(h, _r[1].toInt<u256>());
break;
default:;
}
Guard l(x_knownBlocks);
@ -540,11 +568,11 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
catch (Exception const& _e)
{
clogS(NetWarn) << "Peer causing an Exception:" << _e.what() << _r;
clog(NetWarn) << "Peer causing an Exception:" << _e.what() << _r;
}
catch (std::exception const& _e)
{
clogS(NetWarn) << "Peer causing an exception:" << _e.what() << _r;
clog(NetWarn) << "Peer causing an exception:" << _e.what() << _r;
}
return true;

1
libethereum/EthereumPeer.h

@ -129,6 +129,7 @@ private:
/// This is built as we ask for hashes. Once no more hashes are given, we present this to the
/// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks.
h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer.
h256 m_syncingLastReceivedHash; ///< Hash more recently received from peer.
h256 m_syncingLatestHash; ///< Peer's latest block's hash, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Peer's latest block's total difficulty, as of the current sync.

5
libethereum/Farm.h

@ -155,13 +155,16 @@ private:
{
if (m_onSolutionFound && m_onSolutionFound(_s))
{
WriteGuard ul(x_minerWork);
if (x_minerWork.try_lock())
{
for (std::shared_ptr<Miner> const& m: m_miners)
if (m.get() != _m)
m->setWork();
m_work.reset();
x_minerWork.unlock();
return true;
}
}
return false;
}

106
libethereum/State.cpp

@ -276,6 +276,7 @@ bool State::sync(BlockChain const& _bc)
bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi, ImportRequirements::value _ir)
{
(void)_ir;
bool ret = false;
// BLOCK
BlockInfo bi = _bi ? _bi : _bc.info(_block);
@ -315,6 +316,24 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi, Impor
// No change since last sync.
// Carry on as we were.
}
else
{
// New blocks available, or we've switched to a different branch. All change.
// Find most recent state dump and replay what's left.
// (Most recent state dump might end up being genesis.)
if (m_db.lookup(bi.stateRoot).empty())
{
cwarn << "Unable to sync to" << bi.hash().abridged() << "; state root" << bi.stateRoot.abridged() << "not found in database.";
cwarn << "Database corrupt: contains block without stateRoot:" << bi;
cwarn << "Bailing.";
exit(-1);
}
m_previousBlock = bi;
resetCurrent();
ret = true;
}
#if ALLOW_REBUILD
else
{
// New blocks available, or we've switched to a different branch. All change.
@ -352,6 +371,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi, Impor
resetCurrent();
ret = true;
}
#endif
return ret;
}
@ -441,40 +461,18 @@ void State::resetCurrent()
paranoia("begin resetCurrent", true);
}
bool State::cull(TransactionQueue& _tq) const
{
bool ret = false;
auto ts = _tq.transactions();
for (auto const& i: ts)
{
if (!m_transactionSet.count(i.first))
{
try
{
if (i.second.nonce() < transactionsFrom(i.second.sender()))
{
_tq.drop(i.first);
ret = true;
}
}
catch (...)
{
_tq.drop(i.first);
ret = true;
}
}
}
return ret;
}
TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, bool* o_transactionQueueChanged)
pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned msTimeout)
{
// TRANSACTIONS
TransactionReceipts ret;
pair<TransactionReceipts, bool> ret;
ret.second = false;
auto ts = _tq.transactions();
LastHashes lh;
auto deadline = chrono::steady_clock::now() + chrono::milliseconds(msTimeout);
for (int goodTxs = 1; goodTxs; )
{
goodTxs = 0;
@ -489,51 +487,67 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga
if (lh.empty())
lh = _bc.lastHashes();
execute(lh, i.second);
ret.push_back(m_receipts.back());
ret.first.push_back(m_receipts.back());
_tq.noteGood(i);
++goodTxs;
// cnote << "TX took:" << t.elapsed() * 1000;
}
else if (i.second.gasPrice() < _gp.ask(*this) * 9 / 10)
{
// less than 90% of our ask price for gas. drop.
cnote << i.first.abridged() << "Dropping El Cheapo transaction (<90% of ask price)";
_tq.drop(i.first);
}
}
#if ETH_DEBUG
catch (InvalidNonce const& in)
{
bigint const* req = boost::get_error_info<errinfo_required>(in);
bigint const* got = boost::get_error_info<errinfo_got>(in);
bigint const& req = *boost::get_error_info<errinfo_required>(in);
bigint const& got = *boost::get_error_info<errinfo_got>(in);
if (*req > *got)
if (req > got)
{
// too old
cnote << i.first.abridged() << "Dropping old transaction (nonce too low)";
_tq.drop(i.first);
}
else if (got > req + 5)
{
// too new
cnote << i.first.abridged() << "Dropping new transaction (> 5 nonces ahead)";
_tq.drop(i.first);
if (o_transactionQueueChanged)
*o_transactionQueueChanged = true;
}
else
_tq.setFuture(i);
}
catch (BlockGasLimitReached const& e)
{
bigint const& got = *boost::get_error_info<errinfo_got>(e);
if (got > m_currentBlock.gasLimit)
{
cnote << i.first.abridged() << "Dropping over-gassy transaction (gas > block's gas limit)";
_tq.drop(i.first);
}
else
_tq.setFuture(i);
}
#endif
catch (Exception const& _e)
{
// Something else went wrong - drop it.
cnote << i.first.abridged() << "Dropping invalid transaction:" << diagnostic_information(_e);
_tq.drop(i.first);
if (o_transactionQueueChanged)
*o_transactionQueueChanged = true;
cnote << "Dropping invalid transaction:";
cnote << diagnostic_information(_e);
}
catch (std::exception const&)
{
// Something else went wrong - drop it.
_tq.drop(i.first);
if (o_transactionQueueChanged)
*o_transactionQueueChanged = true;
cnote << "Transaction caused low-level exception :(";
cnote << i.first.abridged() << "Transaction caused low-level exception :(";
}
}
if (chrono::steady_clock::now() > deadline)
{
ret.second = true;
break;
}
}
return ret;
}
@ -649,7 +663,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
uncle.verifyParent(uncleParent);
nonces.insert(uncle.nonce);
tdIncrease += uncle.difficulty;
// tdIncrease += uncle.difficulty;
rewarded.push_back(uncle);
}
@ -690,13 +704,15 @@ void State::cleanup(bool _fullCommit)
paranoia("immediately before database commit", true);
// Commit the new trie to disk.
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot.abridged() << "=" << rootHash().abridged() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
m_db.commit();
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot.abridged() << "=" << rootHash().abridged() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
paranoia("immediately after database commit", true);
m_previousBlock = m_currentBlock;
m_currentBlock.populateFromParent(m_previousBlock);
cdebug << "finalising enactment. current -> previous, hash is" << m_previousBlock.hash().abridged();
clog(StateTrace) << "finalising enactment. current -> previous, hash is" << m_previousBlock.hash().abridged();
}
else
m_db.rollback();

9
libethereum/State.h

@ -200,14 +200,9 @@ public:
/// Only valid after mine() returns true.
bytes const& blockData() const { return m_currentBytes; }
// TODO: Cleaner interface.
/// Sync our transactions, killing those from the queue that we have and assimilating those that we don't.
/// @returns a list of receipts one for each transaction placed from the queue into the state.
/// @a o_transactionQueueChanged boolean pointer, the value of which will be set to true if the transaction queue
/// changed and the pointer is non-null
TransactionReceipts sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, bool* o_transactionQueueChanged = nullptr);
/// Like sync but only operate on _tq, killing the invalid/old ones.
bool cull(TransactionQueue& _tq) const;
/// @returns a list of receipts one for each transaction placed from the queue into the state and bool, true iff there are more transactions to be processed.
std::pair<TransactionReceipts, bool> sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned _msTimeout = 100);
/// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly.

8
libethereum/TransactionQueue.cpp

@ -28,7 +28,7 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb)
ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb, IfDropped _ik)
{
// Check if we already know this transaction.
h256 h = sha3(_transactionRLP);
@ -39,6 +39,9 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallb
if (m_known.count(h))
return ImportResult::AlreadyKnown;
if (m_dropped.count(h) && _ik == IfDropped::Ignore)
return ImportResult::AlreadyInChain;
try
{
// Check validity of _transactionRLP as a transaction. To do this we just deserialise and attempt to determine the sender.
@ -88,7 +91,7 @@ void TransactionQueue::noteGood(std::pair<h256, Transaction> const& _t)
m_unknown.erase(r.first, r.second);
}
void TransactionQueue::drop(h256 _txHash)
void TransactionQueue::drop(h256 const& _txHash)
{
UpgradableGuard l(m_lock);
@ -96,6 +99,7 @@ void TransactionQueue::drop(h256 _txHash)
return;
UpgradeGuard ul(l);
m_dropped.insert(_txHash);
m_known.erase(_txHash);
if (m_current.count(_txHash))

12
libethereum/TransactionQueue.h

@ -22,7 +22,6 @@
#pragma once
#include <functional>
#include <boost/thread.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
@ -39,6 +38,8 @@ class BlockChain;
struct TransactionQueueChannel: public LogChannel { static const char* name() { return "->Q"; } static const int verbosity = 4; };
#define ctxq dev::LogOutputStream<dev::eth::TransactionQueueChannel, true>()
enum class IfDropped { Ignore, Retry };
/**
* @brief A queue of Transactions, each stored as RLP.
* @threadsafe
@ -48,10 +49,10 @@ class TransactionQueue
public:
using ImportCallback = std::function<void(ImportResult)>;
ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback()) { return import(&_tx, _cb); }
ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback());
ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback(), IfDropped _ik = IfDropped::Ignore) { return import(&_tx, _cb, _ik); }
ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback(), IfDropped _ik = IfDropped::Ignore);
void drop(h256 _txHash);
void drop(h256 const& _txHash);
std::map<h256, Transaction> transactions() const { ReadGuard l(m_lock); return m_current; }
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_unknown.size()); }
@ -63,11 +64,12 @@ public:
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
private:
mutable boost::shared_mutex m_lock; ///< General lock.
mutable SharedMutex m_lock; ///< General lock.
std::set<h256> m_known; ///< Hashes of transactions in both sets.
std::map<h256, Transaction> m_current; ///< Map of SHA3(tx) to tx.
std::multimap<Address, std::pair<h256, Transaction>> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX.
std::map<h256, std::function<void(ImportResult)>> m_callbacks; ///< Called once.
std::set<h256> m_dropped; ///< Transactions that have previously been dropped.
Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast.
};

120
libevmcore/Assembly.cpp

@ -24,7 +24,7 @@
#include <libdevcore/Log.h>
#include <libevmcore/CommonSubexpressionEliminator.h>
#include <libevmcore/ControlFlowGraph.h>
#include <json/json.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -65,6 +65,13 @@ void Assembly::append(Assembly const& _a, int _deposit)
}
}
string Assembly::out() const
{
stringstream ret;
stream(ret);
return ret.str();
}
unsigned Assembly::bytesRequired() const
{
for (unsigned br = 1;; ++br)
@ -101,7 +108,7 @@ string Assembly::getLocationFromSources(StringMap const& _sourceCodes, SourceLoc
return move(cut);
}
ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const
ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const
{
_out << _prefix << ".code:" << endl;
for (AssemblyItem const& i: m_items)
@ -157,6 +164,115 @@ ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const&
return _out;
}
Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) const
{
Json::Value value;
value["name"] = _name;
value["begin"] = _begin;
value["end"] = _end;
if (!_value.empty())
value["value"] = _value;
if (!_jumpType.empty())
value["jumpType"] = _jumpType;
return value;
}
string toStringInHex(u256 _value)
{
std::stringstream hexStr;
hexStr << hex << _value;
return hexStr.str();
}
Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes) const
{
Json::Value root;
Json::Value collection(Json::arrayValue);
for (AssemblyItem const& i: m_items)
{
switch (i.type())
{
case Operation:
collection.append(
createJsonValue(instructionInfo(i.instruction()).name, i.getLocation().start, i.getLocation().end, i.getJumpTypeAsString()));
break;
case Push:
collection.append(
createJsonValue("PUSH", i.getLocation().start, i.getLocation().end, toStringInHex(i.data()), i.getJumpTypeAsString()));
break;
case PushString:
collection.append(
createJsonValue("PUSH tag", i.getLocation().start, i.getLocation().end, m_strings.at((h256)i.data())));
break;
case PushTag:
collection.append(
createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
break;
case PushSub:
collection.append(
createJsonValue("PUSH [$]", i.getLocation().start, i.getLocation().end, dev::toString(h256(i.data()))));
break;
case PushSubSize:
collection.append(
createJsonValue("PUSH #[$]", i.getLocation().start, i.getLocation().end, dev::toString(h256(i.data()))));
break;
case PushProgramSize:
collection.append(
createJsonValue("PUSHSIZE", i.getLocation().start, i.getLocation().end));
break;
case Tag:
{
collection.append(
createJsonValue("tag", i.getLocation().start, i.getLocation().end, string(i.data())));
collection.append(
createJsonValue("JUMDEST", i.getLocation().start, i.getLocation().end));
}
break;
case PushData:
{
Json::Value pushData;
pushData["name"] = "PUSH hex";
collection.append(createJsonValue("PUSH hex", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
}
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
}
root[".code"] = collection;
if (!m_data.empty() || !m_subs.empty())
{
Json::Value data;
for (auto const& i: m_data)
if (u256(i.first) >= m_subs.size())
data[toStringInHex((u256)i.first)] = toHex(i.second);
for (size_t i = 0; i < m_subs.size(); ++i)
{
std::stringstream hexStr;
hexStr << hex << i;
data[hexStr.str()] = m_subs[i].stream(_out, "", _sourceCodes, true);
}
root[".data"] = data;
_out << root;
}
return root;
}
Json::Value Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes, bool _inJsonFormat) const
{
if (_inJsonFormat)
return streamAsmJson(_out, _sourceCodes);
else
{
streamAsm(_out, _prefix, _sourceCodes);
return Json::Value();
}
}
AssemblyItem const& Assembly::append(AssemblyItem const& _i)
{
m_deposit += _i.deposit();

20
libevmcore/Assembly.h

@ -29,7 +29,12 @@
#include <libevmcore/Instruction.h>
#include <libevmcore/AssemblyItem.h>
#include "Exceptions.h"
#include <json/json.h>
namespace Json
{
class Value;
}
namespace dev
{
namespace eth
@ -76,7 +81,7 @@ public:
void popTo(int _deposit) { while (m_deposit > _deposit) append(Instruction::POP); }
void injectStart(AssemblyItem const& _i);
std::string out() const { std::stringstream ret; stream(ret); return ret.str(); }
std::string out() const;
int deposit() const { return m_deposit; }
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
@ -86,13 +91,24 @@ public:
bytes assemble() const;
Assembly& optimise(bool _enable);
std::ostream& stream(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const;
Json::Value stream(
std::ostream& _out,
std::string const& _prefix = "",
const StringMap &_sourceCodes = StringMap(),
bool _inJsonFormat = false
) const;
protected:
std::string getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const;
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
unsigned bytesRequired() const;
private:
Json::Value streamAsmJson(std::ostream& _out, const StringMap &_sourceCodes) const;
std::ostream& streamAsm(std::ostream& _out, std::string const& _prefix, StringMap const& _sourceCodes) const;
Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()) const;
protected:
unsigned m_usedTags = 0;
AssemblyItems m_items;
mutable std::map<h256, bytes> m_data;

1
libevmcore/CMakeLists.txt

@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})

1
liblll/CMakeLists.txt

@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})

9
libp2p/Capability.cpp

@ -27,19 +27,14 @@ using namespace std;
using namespace dev;
using namespace dev::p2p;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
Capability::Capability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset): m_session(_s), m_host(_h), m_idOffset(_idOffset)
{
clogS(NetConnect) << "New session for capability" << m_host->name() << "; idOffset:" << m_idOffset;
clog(NetConnect) << "New session for capability" << m_host->name() << "; idOffset:" << m_idOffset;
}
void Capability::disable(std::string const& _problem)
{
clogS(NetWarn) << "DISABLE: Disabling capability '" << m_host->name() << "'. Reason:" << _problem;
clog(NetWarn) << "DISABLE: Disabling capability '" << m_host->name() << "'. Reason:" << _problem;
m_enabled = false;
}

1
libp2p/Common.h

@ -77,6 +77,7 @@ struct InvalidHostIPAddress: virtual dev::Exception {};
struct NetWarn: public LogChannel { static const char* name() { return "!N!"; } static const int verbosity = 0; };
struct NetNote: public LogChannel { static const char* name() { return "*N*"; } static const int verbosity = 1; };
struct NetImpolite: public LogChannel { static const char* name() { return "#!*"; } static const int verbosity = 1; };
struct NetMessageSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 2; };
struct NetConnect: public LogChannel { static const char* name() { return "+N+"; } static const int verbosity = 10; };
struct NetMessageDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 5; };

10
libp2p/NodeTable.cpp

@ -382,7 +382,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
// h256 + Signature + type + RLP (smallest possible packet is empty neighbours packet which is 3 bytes)
if (_packet.size() < h256::size + Signature::size + 1 + 3)
{
clog(NodeTableWarn) << "Invalid Message size from " << _from.address().to_string() << ":" << _from.port();
clog(NodeTableTriviaSummary) << "Invalid message size from " << _from.address().to_string() << ":" << _from.port();
return;
}
@ -390,7 +390,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
h256 hashSigned(sha3(hashedBytes));
if (!_packet.cropped(0, h256::size).contentsEqual(hashSigned.asBytes()))
{
clog(NodeTableWarn) << "Invalid Message hash from " << _from.address().to_string() << ":" << _from.port();
clog(NodeTableTriviaSummary) << "Invalid message hash from " << _from.address().to_string() << ":" << _from.port();
return;
}
@ -402,7 +402,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
Public nodeid(dev::recover(*(Signature const*)sigBytes.data(), sha3(signedBytes)));
if (!nodeid)
{
clog(NodeTableWarn) << "Invalid Message signature from " << _from.address().to_string() << ":" << _from.port();
clog(NodeTableTriviaSummary) << "Invalid message signature from " << _from.address().to_string() << ":" << _from.port();
return;
}
@ -469,7 +469,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
if (!expected)
{
clog(NetConnect) << "Dropping unsolicited Neighbours packet from " << _from.address();
clog(NetConnect) << "Dropping unsolicited neighbours packet from " << _from.address();
break;
}
@ -517,7 +517,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
}
default:
clog(NodeTableWarn) << "Invalid Message, " << hex << packetType << ", received from " << _from.address().to_string() << ":" << dec << _from.port();
clog(NodeTableWarn) << "Invalid message, " << hex << packetType << ", received from " << _from.address().to_string() << ":" << dec << _from.port();
return;
}

6
libp2p/RLPxHandshake.cpp

@ -249,7 +249,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
bytesRef frame(&m_handshakeInBuffer);
if (!m_io->authAndDecryptFrame(frame))
{
clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed";
clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed";
m_nextState = Error;
transition();
return;
@ -258,13 +258,13 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
PacketType packetType = (PacketType)(frame[0] == 0x80 ? 0x0 : frame[0]);
if (packetType != 0)
{
clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type";
clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type";
m_nextState = Error;
transition();
return;
}
clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session.";
clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session.";
RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall);
m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint());
}

51
libp2p/Session.cpp

@ -33,11 +33,6 @@ using namespace std;
using namespace dev;
using namespace dev::p2p;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << m_socket.native_handle() << "] "
Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr<Peer> const& _n, PeerSessionInfo _info):
m_server(_s),
m_io(_io),
@ -53,7 +48,8 @@ Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr<Peer> const& _n, Pe
Session::~Session()
{
clogS(NetMessageSummary) << "Closing Peer Session :-(";
ThreadContext tc(info().id.abridged() + " | " + info().clientVersion);
clog(NetMessageSummary) << "Closing peer session :-(";
m_peer->m_lastConnected = m_peer->m_lastAttempted - chrono::seconds(1);
// Read-chain finished for one reason or another.
@ -121,6 +117,8 @@ void Session::ensureNodesRequested()
void Session::serviceNodesRequest()
{
ThreadContext tc(info().id.abridged() + "/" + info().clientVersion);
if (!m_theyRequestedNodes)
return;
@ -139,7 +137,7 @@ void Session::serviceNodesRequest()
auto rs = randomSelection(peers, 10);
for (auto const& i: rs)
{
clogS(NetTriviaDetail) << "Sending peer " << i.id.abridged() << i.endpoint;
clog(NetTriviaDetail) << "Sending peer " << i.id.abridged() << i.endpoint;
if (i.endpoint.address.is_v4())
s.appendList(3) << bytesConstRef(i.endpoint.address.to_v4().to_bytes().data(), 4) << i.endpoint.tcpPort << i.id;
else// if (i.second.address().is_v6()) - assumed
@ -154,7 +152,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
{
m_lastReceived = chrono::steady_clock::now();
clogS(NetRight) << _t << _r;
clog(NetRight) << _t << _r;
try // Generic try-catch block designed to capture RLP format errors - TODO: give decent diagnostics, make a bit more specific over what is caught.
{
switch (_t)
@ -168,28 +166,28 @@ bool Session::interpret(PacketType _t, RLP const& _r)
else
{
reason = reasonOf(r);
clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")";
clog(NetMessageSummary) << "Disconnect (reason: " << reason << ")";
drop(DisconnectRequested);
}
break;
}
case PingPacket:
{
clogS(NetTriviaSummary) << "Ping";
clog(NetTriviaSummary) << "Ping";
RLPStream s;
sealAndSend(prep(s, PongPacket));
break;
}
case PongPacket:
m_info.lastPing = std::chrono::steady_clock::now() - m_ping;
clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast<chrono::milliseconds>(m_info.lastPing).count() << " ms";
clog(NetTriviaSummary) << "Latency: " << chrono::duration_cast<chrono::milliseconds>(m_info.lastPing).count() << " ms";
break;
case GetPeersPacket:
// Disabled for interop testing.
// GetPeers/PeersPacket will be modified to only exchange new nodes which it's peers are interested in.
break;
clogS(NetTriviaSummary) << "GetPeers";
clog(NetTriviaSummary) << "GetPeers";
m_theyRequestedNodes = true;
serviceNodesRequest();
break;
@ -198,7 +196,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
// GetPeers/PeersPacket will be modified to only exchange new nodes which it's peers are interested in.
break;
clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
clog(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
m_weRequestedNodes = false;
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
@ -216,7 +214,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt<short>());
NodeId id = _r[i][2].toHash<NodeId>();
clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")";
clog(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")";
if (!isPublicAddress(peerAddress))
goto CONTINUE; // Private address. Ignore.
@ -239,7 +237,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
// OK passed all our checks. Assume it's good.
addRating(1000);
m_server->addNode(id, NodeIPEndpoint(ep.address(), ep.port(), ep.port()));
clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")";
clog(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")";
CONTINUE:;
LAMEPEER:;
}
@ -258,7 +256,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
}
catch (std::exception const& _e)
{
clogS(NetWarn) << "Peer causing an exception:" << _e.what() << _r;
clog(NetWarn) << "Peer causing an exception:" << _e.what() << _r;
disconnect(BadProtocol);
return true;
}
@ -298,11 +296,11 @@ bool Session::checkPacket(bytesConstRef _msg)
void Session::send(bytes&& _msg)
{
clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1));
clog(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1));
bytesConstRef msg(&_msg);
if (!checkPacket(msg))
clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!";
clog(NetWarn) << "INVALID PACKET CONSTRUCTED!";
if (!m_socket.is_open())
return;
@ -325,10 +323,11 @@ void Session::write()
auto self(shared_from_this());
ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/)
{
ThreadContext tc(info().id.abridged() + " | " + info().clientVersion);
// must check queue, as write callback can occur following dropped()
if (ec)
{
clogS(NetWarn) << "Error sending: " << ec.message();
clog(NetWarn) << "Error sending: " << ec.message();
drop(TCPError);
return;
}
@ -350,7 +349,7 @@ void Session::drop(DisconnectReason _reason)
if (m_socket.is_open())
try
{
clogS(NetConnect) << "Closing " << m_socket.remote_endpoint() << "(" << reasonOf(_reason) << ")";
clog(NetConnect) << "Closing " << m_socket.remote_endpoint() << "(" << reasonOf(_reason) << ")";
boost::system::error_code ec;
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
m_socket.close();
@ -368,7 +367,7 @@ void Session::drop(DisconnectReason _reason)
void Session::disconnect(DisconnectReason _reason)
{
clogS(NetConnect) << "Disconnecting (our reason:" << reasonOf(_reason) << ")";
clog(NetConnect) << "Disconnecting (our reason:" << reasonOf(_reason) << ")";
StructuredLogger::p2pDisconnected(
m_info.id.abridged(),
m_peer->endpoint, // TODO: may not be 100% accurate
@ -398,9 +397,10 @@ void Session::doRead()
auto self(shared_from_this());
ba::async_read(m_socket, boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length)
{
ThreadContext tc(info().id.abridged() + " | " + info().clientVersion);
if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof)
{
clogS(NetWarn) << "Error reading: " << ec.message();
clog(NetWarn) << "Error reading: " << ec.message();
drop(TCPError);
}
else if (ec && length == 0)
@ -433,9 +433,10 @@ void Session::doRead()
auto tlen = frameSize + ((16 - (frameSize % 16)) % 16) + h128::size;
ba::async_read(m_socket, boost::asio::buffer(m_data, tlen), [this, self, headerRLP, frameSize, tlen](boost::system::error_code ec, std::size_t length)
{
ThreadContext tc(info().id.abridged() + " | " + info().clientVersion);
if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof)
{
clogS(NetWarn) << "Error reading: " << ec.message();
clog(NetWarn) << "Error reading: " << ec.message();
drop(TCPError);
}
else if (ec && length == 0)
@ -453,7 +454,7 @@ void Session::doRead()
if (!checkPacket(frame))
{
cerr << "Received " << frame.size() << ": " << toHex(frame) << endl;
clogS(NetWarn) << "INVALID MESSAGE RECEIVED";
clog(NetWarn) << "INVALID MESSAGE RECEIVED";
disconnect(BadProtocol);
return;
}
@ -462,7 +463,7 @@ void Session::doRead()
auto packetType = (PacketType)RLP(frame.cropped(0, 1)).toInt<unsigned>();
RLP r(frame.cropped(1));
if (!interpret(packetType, r))
clogS(NetWarn) << "Couldn't interpret packet." << RLP(r);
clog(NetWarn) << "Couldn't interpret packet." << RLP(r);
}
doRead();
}

56
libsolidity/AST.cpp

@ -55,6 +55,7 @@ void ContractDefinition::checkTypeRequirements()
checkDuplicateFunctions();
checkIllegalOverrides();
checkAbstractFunctions();
checkAbstractConstructors();
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
@ -181,6 +182,45 @@ void ContractDefinition::checkAbstractFunctions()
}
}
void ContractDefinition::checkAbstractConstructors()
{
set<ContractDefinition const*> argumentsNeeded;
// check that we get arguments for all base constructors that need it.
// If not mark the contract as abstract (not fully implemented)
vector<ContractDefinition const*> const& bases = getLinearizedBaseContracts();
for (ContractDefinition const* contract: bases)
if (FunctionDefinition const* constructor = contract->getConstructor())
if (contract != this && !constructor->getParameters().empty())
argumentsNeeded.insert(contract);
for (ContractDefinition const* contract: bases)
{
if (FunctionDefinition const* constructor = contract->getConstructor())
for (auto const& modifier: constructor->getModifiers())
{
auto baseContract = dynamic_cast<ContractDefinition const*>(
&modifier->getName()->getReferencedDeclaration()
);
if (baseContract)
argumentsNeeded.erase(baseContract);
}
for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
{
auto baseContract = dynamic_cast<ContractDefinition const*>(
&base->getName()->getReferencedDeclaration()
);
solAssert(baseContract, "");
if (!base->getArguments().empty())
argumentsNeeded.erase(baseContract);
}
}
if (!argumentsNeeded.empty())
setFullyImplemented(false);
}
void ContractDefinition::checkIllegalOverrides() const
{
// TODO unify this at a later point. for this we need to put the constness and the access specifier
@ -354,7 +394,7 @@ void InheritanceSpecifier::checkTypeRequirements()
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->getReferencedDeclaration());
solAssert(base, "Base contract not available.");
TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
@ -421,8 +461,8 @@ void FunctionDefinition::checkTypeRequirements()
}
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
modifier->checkTypeRequirements(isConstructor() ?
dynamic_cast<ContractDefinition const&>(*getScope()).getBaseContracts() :
vector<ASTPointer<InheritanceSpecifier>>());
dynamic_cast<ContractDefinition const&>(*getScope()).getLinearizedBaseContracts() :
vector<ContractDefinition const*>());
if (m_body)
m_body->checkTypeRequirements();
}
@ -500,7 +540,7 @@ void ModifierDefinition::checkTypeRequirements()
m_body->checkTypeRequirements();
}
void ModifierInvocation::checkTypeRequirements(vector<ASTPointer<InheritanceSpecifier>> const& _bases)
void ModifierInvocation::checkTypeRequirements(vector<ContractDefinition const*> const& _bases)
{
TypePointers argumentTypes;
for (ASTPointer<Expression> const& argument: m_arguments)
@ -517,10 +557,10 @@ void ModifierInvocation::checkTypeRequirements(vector<ASTPointer<InheritanceSpec
parameters = &modifier->getParameters();
else
// check parameters for Base constructors
for (auto const& base: _bases)
if (declaration == &base->getName()->getReferencedDeclaration())
for (ContractDefinition const* base: _bases)
if (declaration == base)
{
if (auto referencedConstructor = dynamic_cast<ContractDefinition const&>(*declaration).getConstructor())
if (auto referencedConstructor = base->getConstructor())
parameters = &referencedConstructor->getParameters();
else
parameters = &emptyParameterList;
@ -777,7 +817,7 @@ void NewExpression::checkTypeRequirements(TypePointers const*)
if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
if (!m_contract->isFullyImplemented())
BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an instance of an abstract contract."));
BOOST_THROW_EXCEPTION(createTypeError("Trying to create an instance of an abstract contract."));
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},

5
libsolidity/AST.h

@ -287,6 +287,7 @@ private:
void checkDuplicateFunctions() const;
void checkIllegalOverrides() const;
void checkAbstractFunctions();
void checkAbstractConstructors();
/// Checks that different functions with external visibility end up having different
/// external argument types (i.e. different signature).
void checkExternalTypeClashes() const;
@ -382,7 +383,7 @@ class EnumValue: public Declaration
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
TypePointer getType(ContractDefinition const* = nullptr) const;
virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
};
/**
@ -572,7 +573,7 @@ public:
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
/// @param _bases is the list of base contracts for base constructor calls. For modifiers an empty vector should be passed.
void checkTypeRequirements(std::vector<ASTPointer<InheritanceSpecifier>> const& _bases);
void checkTypeRequirements(std::vector<ContractDefinition const*> const& _bases);
private:
ASTPointer<Identifier> m_modifierName;

1
libsolidity/Compiler.cpp

@ -136,6 +136,7 @@ void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
FunctionType constructorType(_constructor);
if (!constructorType.getParameterTypes().empty())
{
solAssert(m_baseArguments.count(&_constructor), "");
std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor];
solAssert(arguments, "");
for (unsigned i = 0; i < arguments->size(); ++i)

5
libsolidity/Compiler.h

@ -42,9 +42,10 @@ public:
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
/// @arg _sourceCodes is the map of input files to source code strings
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const
/// @arg _inJsonFromat shows whether the out should be in Json format
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
{
m_context.streamAssembly(_stream, _sourceCodes);
m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat);
}
/// @returns Assembly items of the normal compiler context
eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); }

6
libsolidity/CompilerContext.h

@ -122,7 +122,11 @@ public:
eth::Assembly const& getAssembly() const { return m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.stream(_stream, "", _sourceCodes); }
/// @arg _inJsonFormat shows whether the out should be in Json format
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
{
m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
}
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }

18
libsolidity/CompilerStack.cpp

@ -157,14 +157,16 @@ bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
return getBytecode();
}
eth::AssemblyItems const& CompilerStack::getAssemblyItems(string const& _contractName) const
eth::AssemblyItems const* CompilerStack::getAssemblyItems(string const& _contractName) const
{
return getContract(_contractName).compiler->getAssemblyItems();
Contract const& contract = getContract(_contractName);
return contract.compiler ? &getContract(_contractName).compiler->getAssemblyItems() : nullptr;
}
eth::AssemblyItems const& CompilerStack::getRuntimeAssemblyItems(string const& _contractName) const
eth::AssemblyItems const* CompilerStack::getRuntimeAssemblyItems(string const& _contractName) const
{
return getContract(_contractName).compiler->getRuntimeAssemblyItems();
Contract const& contract = getContract(_contractName);
return contract.compiler ? &getContract(_contractName).compiler->getRuntimeAssemblyItems() : nullptr;
}
bytes const& CompilerStack::getBytecode(string const& _contractName) const
@ -182,9 +184,13 @@ dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const
return dev::sha3(getRuntimeBytecode(_contractName));
}
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes) const
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const
{
getContract(_contractName).compiler->streamAssembly(_outStream, _sourceCodes);
Contract const& contract = getContract(_contractName);
if (contract.compiler)
contract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat);
else
_outStream << "Contract not fully implemented" << endl;
}
string const& CompilerStack::getInterface(string const& _contractName) const

7
libsolidity/CompilerStack.h

@ -94,16 +94,17 @@ public:
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
/// @returns normal contract assembly items
eth::AssemblyItems const& getAssemblyItems(std::string const& _contractName = "") const;
eth::AssemblyItems const* getAssemblyItems(std::string const& _contractName = "") const;
/// @returns runtime contract assembly items
eth::AssemblyItems const& getRuntimeAssemblyItems(std::string const& _contractName = "") const;
eth::AssemblyItems const* getRuntimeAssemblyItems(std::string const& _contractName = "") const;
/// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
dev::h256 getContractCodeHash(std::string const& _contractName = "") const;
/// Streams a verbose version of the assembly to @a _outStream.
/// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFromat shows whether the out should be in Json format
/// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const;
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const;
/// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile.

2
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -775,7 +775,7 @@ Json::Value WebThreeStubServerBase::eth_getWork()
return ret;
}
bool WebThreeStubServerBase::eth_submitWork(string const& _nonce, string const& _mixHash)
bool WebThreeStubServerBase::eth_submitWork(string const& _nonce, string const&, string const& _mixHash)
{
try
{

2
libweb3jsonrpc/WebThreeStubServerBase.h

@ -113,7 +113,7 @@ public:
virtual Json::Value eth_getFilterLogs(std::string const& _filterId);
virtual Json::Value eth_getLogs(Json::Value const& _json);
virtual Json::Value eth_getWork();
virtual bool eth_submitWork(std::string const& _nonce, std::string const& _mixHash);
virtual bool eth_submitWork(std::string const& _nonce, std::string const&, std::string const& _mixHash);
virtual std::string eth_register(std::string const& _address);
virtual bool eth_unregister(std::string const& _accountId);
virtual Json::Value eth_fetchQueuedTransactions(std::string const& _accountId);

6
libweb3jsonrpc/abstractwebthreestubserver.h

@ -53,7 +53,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
this->bindAndAddMethod(jsonrpc::Procedure("eth_getFilterLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getFilterLogsI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_getLogsI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_getWorkI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_register", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_registerI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_unregister", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_unregisterI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_fetchQueuedTransactions", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_fetchQueuedTransactionsI);
@ -250,7 +250,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
}
inline virtual void eth_submitWorkI(const Json::Value &request, Json::Value &response)
{
response = this->eth_submitWork(request[0u].asString(), request[1u].asString());
response = this->eth_submitWork(request[0u].asString(), request[1u].asString(), request[2u].asString());
}
inline virtual void eth_registerI(const Json::Value &request, Json::Value &response)
{
@ -350,7 +350,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
virtual Json::Value eth_getFilterLogs(const std::string& param1) = 0;
virtual Json::Value eth_getLogs(const Json::Value& param1) = 0;
virtual Json::Value eth_getWork() = 0;
virtual bool eth_submitWork(const std::string& param1, const std::string& param2) = 0;
virtual bool eth_submitWork(const std::string& param1, const std::string& param2, const std::string& param3) = 0;
virtual std::string eth_register(const std::string& param1) = 0;
virtual bool eth_unregister(const std::string& param1) = 0;
virtual Json::Value eth_fetchQueuedTransactions(const std::string& param1) = 0;

2
libweb3jsonrpc/spec.json

@ -42,7 +42,7 @@
{ "name": "eth_getFilterLogs", "params": [""], "order": [], "returns": []},
{ "name": "eth_getLogs", "params": [{}], "order": [], "returns": []},
{ "name": "eth_getWork", "params": [], "order": [], "returns": []},
{ "name": "eth_submitWork", "params": ["", ""], "order": [], "returns": true},
{ "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true},
{ "name": "eth_register", "params": [""], "order": [], "returns": ""},
{ "name": "eth_unregister", "params": [""], "order": [], "returns": true},
{ "name": "eth_fetchQueuedTransactions", "params": [""], "order": [], "returns": []},

5
libwhisper/Interface.cpp

@ -29,11 +29,6 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
Interface::~Interface()
{
}

7
libwhisper/WhisperHost.cpp

@ -29,12 +29,7 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
WhisperHost::WhisperHost()
WhisperHost::WhisperHost(): Worker("shh")
{
}

7
libwhisper/WhisperPeer.cpp

@ -29,11 +29,6 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
#if defined(clogS)
#undef clogS
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
WhisperPeer::WhisperPeer(Session* _s, HostCapabilityFace* _h, unsigned _i): Capability(_s, _h, _i)
{
RLPStream s;
@ -57,7 +52,7 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r)
{
auto protocolVersion = _r[0].toInt<unsigned>();
clogS(NetMessageSummary) << "Status: " << protocolVersion;
clog(NetMessageSummary) << "Status: " << protocolVersion;
if (protocolVersion != version())
disable("Invalid protocol version.");

2
mix/ClientModel.cpp

@ -498,6 +498,8 @@ QVariant ClientModel::formatStorageValue(SolidityType const& _type, map<u256, u2
bytes slotBytes = toBigEndian(slotValue);
auto start = slotBytes.end() - _type.size - offset;
bytes val(32 - _type.size); //prepend with zeroes
if (_type.type == SolidityType::SignedInteger && (*start & 0x80)) //extend sign
std::fill(val.begin(), val.end(), 0xff);
val.insert(val.end(), start, start + _type.size);
values.append(decoder.decode(_type, val));
offset += _type.size;

4
mix/CodeModel.cpp

@ -135,8 +135,8 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
CollectDeclarationsVisitor visitor(&m_functions, &m_locals);
m_storage = collectStorage(contractDefinition);
contractDefinition.accept(visitor);
m_assemblyItems = _compiler.getRuntimeAssemblyItems(name);
m_constructorAssemblyItems = _compiler.getAssemblyItems(name);
m_assemblyItems = *_compiler.getRuntimeAssemblyItems(name);
m_constructorAssemblyItems = *_compiler.getAssemblyItems(name);
}
QString CompiledContract::codeHex() const

1
mix/MixClient.cpp

@ -117,6 +117,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256();
State execState = _state;
execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation
Executive execution(execState, lastHashes, 0);
execution.initialize(&rlp);
execution.execute();

7
mix/qml/LogsPane.qml

@ -6,12 +6,15 @@ import org.ethereum.qml.SortFilterProxyModel 1.0
Rectangle
{
property variant statusPane
property variant currentStatus
property int contentXPos: logStyle.generic.layout.dateWidth + logStyle.generic.layout.typeWidth - 70
function clear()
{
logsModel.clear();
statusPane.clear();
currentStatus = undefined;
}
function push(_level, _type, _content)
@ -22,7 +25,7 @@ Rectangle
onVisibleChanged:
{
if (visible && (logsModel.count === 0 || (logsModel.get(0).date !== currentStatus.date && logsModel.get(0).content !== currentStatus.content)))
if (currentStatus && visible && (logsModel.count === 0 || logsModel.get(0).content !== currentStatus.content || logsModel.get(0).date !== currentStatus.date))
logsModel.insert(0, { "type": currentStatus.type, "date": currentStatus.date, "content": currentStatus.content, "level": currentStatus.level });
else if (!visible)
{
@ -533,7 +536,7 @@ Rectangle
enabled: logsModel.count > 0
tooltip: qsTr("Clear")
onTriggered: {
logsModel.clear();
logsPane.clear()
}
}
}

7
mix/qml/StatusPane.qml

@ -55,6 +55,12 @@ Rectangle {
currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "error" }
}
function clear()
{
status.state = "";
status.text = "";
}
StatusPaneStyle {
id: statusPaneStyle
}
@ -359,6 +365,7 @@ Rectangle {
LogsPane
{
id: logPane;
statusPane: statusHeader
onContentXPosChanged:
{
parent.move();

2
mix/qml/TransactionLog.qml

@ -151,7 +151,7 @@ Item {
Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) {
var item = logTable.model.get(currentRow);
appContext.toClipboard(item.returned);
clipboard.text = item.returned;
}
}
}

2
mix/qml/WebPreview.qml

@ -317,7 +317,7 @@ Item {
experimental.settings.localContentCanAccessRemoteUrls: true
onJavaScriptConsoleMessage: {
console.log(sourceID + ":" + lineNumber + ": " + message);
webPreview.javaScriptMessage(level, sourceID, lineNumber, message);
webPreview.javaScriptMessage(level, sourceID, lineNumber - 1, message);
}
onLoadingChanged: {
if (!loading) {

10
mix/test/qml/TestMain.qml

@ -58,11 +58,20 @@ TestCase
ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
}
function waitForMining()
{
while (mainApplication.clientModel.mining)
ts.waitForSignal(mainApplication.clientModel, "miningComplete()", 5000);
wait(1); //allow events to propagate 2 times for transaction log to be updated
wait(1);
}
function waitForExecution()
{
while (mainApplication.clientModel.running)
ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000);
wait(1); //allow events to propagate 2 times for transaction log to be updated
wait(1);
}
function editHtml(c)
@ -89,6 +98,7 @@ TestCase
function test_dbg_vm() { TestDebugger.test_vmDebugging(); }
function test_miner_getDefaultiner() { TestMiner.test_getDefaultMiner(); }
function test_miner_selectMiner() { TestMiner.test_selectMiner(); }
function test_miner_mine() { TestMiner.test_mine(); }
function test_project_contractRename() { TestProject.test_contractRename(); }
}

5
mix/test/qml/js/TestDebugger.js

@ -91,17 +91,20 @@ function test_arrayParametersAndStorage()
" {\r" +
" m = x;\r" +
" s = 5;\r" +
" signed = 6534;\r" +
" }\r" +
" \r" +
" function setMV(uint72[5] x) external\r" +
" {\r" +
" mv = x;\r" +
" s = 42;\r" +
" signed = -534;\r" +
" }\r" +
" \r" +
" uint256[] m;\r" +
" uint72[5] mv;\r" +
" uint256 s;\r" +
" int48 signed;\r" +
" }\r");
mainApplication.projectModel.stateListModel.editState(0);
@ -127,11 +130,13 @@ function test_arrayParametersAndStorage()
mainApplication.mainContent.rightPane.debugSlider.value = mainApplication.mainContent.rightPane.debugSlider.maximumValue;
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "m", ["4","5","6","2","10"]);
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "s", "5");
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "signed", "6534");
//debug setMV
mainApplication.clientModel.debugRecord(4);
mainApplication.mainContent.rightPane.debugSlider.value = mainApplication.mainContent.rightPane.debugSlider.maximumValue - 1;
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "mv", ["13","35","1","4","0"]);
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "s", "42");
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "signed", "-534");
tryCompare(mainApplication.mainContent.rightPane.solCallStack.listModel, 0, "setMV");
}

10
mix/test/qml/js/TestMiner.js

@ -18,3 +18,13 @@ function test_selectMiner()
compare(state.miner.secret, account.secret);
}
function test_mine()
{
newProject();
mainApplication.mainContent.startQuickDebugging();
waitForExecution();
mainApplication.clientModel.mine();
waitForMining();
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "contract", " - Block - ");
}

4
mix/test/qml/js/TestProject.js

@ -1,11 +1,12 @@
function test_contractRename()
{
newProject();
waitForExecution();
tryCompare(mainApplication.mainContent.projectNavigator.sections.itemAt(0).model.get(0), "name", "Contract");
editContract("contract Renamed {}");
mainApplication.mainContent.startQuickDebugging();
waitForExecution();
wait(1000);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(2), "contract", "Renamed");
tryCompare(mainApplication.mainContent.projectNavigator.sections.itemAt(0).model.get(0), "name", "Renamed");
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.editTransaction(2);
@ -14,5 +15,4 @@ function test_contractRename()
tryCompare(transactionDialog, "functionId", "Renamed");
transactionDialog.close();
mainApplication.projectModel.stateDialog.close();
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(2), "contract", "Renamed");
}

1
neth/main.cpp

@ -626,6 +626,7 @@ int main(int argc, char** argv)
logwin = newwin(height * 2 / 5 - 2, width * 2 / 3, qheight, 0);
nc::nc_window_streambuf outbuf(logwin, std::cout);
nc::nc_window_streambuf eoutbuf(logwin, std::cerr);
consolewin = newwin(qheight, width / 4, 0, 0);
nc::nc_window_streambuf coutbuf(consolewin, ccout);

26
solc/CommandLineInterface.cpp

@ -55,6 +55,7 @@ namespace solidity
static string const g_argAbiStr = "json-abi";
static string const g_argSolAbiStr = "sol-abi";
static string const g_argAsmStr = "asm";
static string const g_argAsmJsonStr = "asm-json";
static string const g_argAstStr = "ast";
static string const g_argAstJson = "ast-json";
static string const g_argBinaryStr = "binary";
@ -80,10 +81,15 @@ static bool needStdout(po::variables_map const& _args)
{
return
argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argSolAbiStr) ||
argToStdout(_args, g_argNatspecUserStr) || argToStdout(_args, g_argAstJson) ||
argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) ||
argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr);
argToStdout(_args, g_argAbiStr) ||
argToStdout(_args, g_argSolAbiStr) ||
argToStdout(_args, g_argNatspecUserStr) ||
argToStdout(_args, g_argAstJson) ||
argToStdout(_args, g_argNatspecDevStr) ||
argToStdout(_args, g_argAsmStr) ||
argToStdout(_args, g_argAsmJsonStr) ||
argToStdout(_args, g_argOpcodesStr) ||
argToStdout(_args, g_argBinaryStr);
}
static inline bool outputToFile(OutputType type)
@ -220,6 +226,8 @@ bool CommandLineInterface::parseArguments(int argc, char** argv)
"Request to output the AST of the contract in JSON format.")
(g_argAsmStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the EVM assembly of the contract.")
(g_argAsmJsonStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the EVM assembly of the contract in JSON format.")
(g_argOpcodesStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the Opcodes of the contract.")
(g_argBinaryStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
@ -411,19 +419,19 @@ void CommandLineInterface::actOnInput()
cout << endl << "======= " << contract << " =======" << endl;
// do we need EVM assembly?
if (m_args.count(g_argAsmStr))
if (m_args.count(g_argAsmStr) || m_args.count(g_argAsmJsonStr))
{
auto choice = m_args[g_argAsmStr].as<OutputType>();
auto choice = m_args.count(g_argAsmStr) ? m_args[g_argAsmStr].as<OutputType>() : m_args[g_argAsmJsonStr].as<OutputType>();
if (outputToStdout(choice))
{
cout << "EVM assembly:" << endl;
m_compiler->streamAssembly(cout, contract, m_sourceCodes);
m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
}
if (outputToFile(choice))
{
ofstream outFile(contract + ".evm");
m_compiler->streamAssembly(outFile, contract, m_sourceCodes);
ofstream outFile(contract + (m_args.count(g_argAsmJsonStr) ? "_evm.json" : ".evm"));
m_compiler->streamAssembly(outFile, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
outFile.close();
}
}

15
solc/docker_emscripten/Dockerfile

@ -43,7 +43,14 @@ RUN sed -i 's/$(archiver\[1\])/\/home\/user\/emsdk_portable\/emscripten\/master\
RUN sed -i 's/$(ranlib\[1\])/\/home\/user\/emsdk_portable\/emscripten\/master\/emranlib/g' ./tools/build/src/tools/gcc.jam
RUN ./b2 link=static variant=release threading=single runtime-link=static thread system regex
# Build soljs
# Json-CPP
WORKDIR /home/user
RUN git clone https://github.com/open-source-parsers/jsoncpp.git
WORKDIR /home/user/jsoncpp
RUN emcmake cmake -DJSONCPP_LIB_BUILD_STATIC=ON -DJSONCPP_LIB_BUILD_SHARED=OFF -DJSONCPP_WITH_TESTS=OFF -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF -G "Unix Makefiles" .
RUN emmake make
## Build soljs
WORKDIR /home/user
ADD https://api.github.com/repos/ethereum/cpp-ethereum/git/refs/heads/develop unused.txt
RUN git clone --depth=1 https://github.com/ethereum/cpp-ethereum
@ -55,8 +62,10 @@ RUN git remote add -f solidityjs https://github.com/chriseth/cpp-ethereum
# TODO this should be a proper merge but somehow causes problems
# NOTE that we only get the latest commit of that branch
RUN git cherry-pick solidityjs/solidity-js
RUN emcmake cmake -DETH_STATIC=1 -DONLY_SOLIDITY=1 -DHEADLESS=1 -DCMAKE_CXX_COMPILER=/home/user/emsdk_portable/emscripten/master/em++ -DCMAKE_C_COMPILER=/home/user/emsdk_portable/emscripten/master/emcc
RUN emcmake cmake -DETH_STATIC=1 -DSOLIDITY=ON -DGUI=0 -DCMAKE_CXX_COMPILER=/home/user/emsdk_portable/emscripten/master/em++ -DCMAKE_C_COMPILER=/home/user/emsdk_portable/emscripten/master/emcc
RUN emmake make -j 6 soljs
ENTRYPOINT cat soljs/soljs.js
WORKDIR /home/user/cpp-ethereum/soljs
# somehow it does not work to pipe out both files
#ENTRYPOINT tar -c soljs.js soljs.js.mem | base64

37
test/SolidityNameAndTypeResolution.cpp

@ -407,6 +407,39 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_optional)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract BaseBase { function BaseBase(uint j); }
contract base is BaseBase { function foo(); }
contract derived is base {
function derived(uint i) BaseBase(i){}
function foo() {}
}
)";
ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(text), "Parsing and name resolving failed");
}
BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract BaseBase { function BaseBase(uint j); }
contract base is BaseBase { function foo(); }
contract derived is base {
function derived(uint i) {}
function foo() {}
}
)";
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name resolving failed");
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->getNodes();
BOOST_CHECK_EQUAL(nodes.size(), 3);
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
BOOST_CHECK(derived);
BOOST_CHECK(!derived->isFullyImplemented());
}
BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
{
ASTPointer<SourceUnit> sourceUnit;
@ -665,7 +698,7 @@ BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments)
contract A { function A(uint a) { } }
contract B is A { }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed");
}
BOOST_AUTO_TEST_CASE(base_constructor_arguments_override)
@ -674,7 +707,7 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments_override)
contract A { function A(uint a) { } }
contract B is A { }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed");
}
BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion)

8
test/TestHelper.cpp

@ -123,7 +123,7 @@ json_spirit::mObject& ImportTest::makeAllFieldsHex(json_spirit::mObject& _o)
{
static const set<string> hashes {"bloom" , "coinbase", "hash", "mixHash", "parentHash", "receiptTrie",
"stateRoot", "transactionsTrie", "uncleHash", "currentCoinbase",
"previousHash", "to", "address", "caller", "origin", "secretKey"};
"previousHash", "to", "address", "caller", "origin", "secretKey", "data"};
for (auto& i: _o)
{
@ -140,7 +140,7 @@ json_spirit::mObject& ImportTest::makeAllFieldsHex(json_spirit::mObject& _o)
str = value.get_str();
else continue;
_o[key] = (str.substr(0, 2) == "0x") ? str : "0x" + toHex(toCompactBigEndian(toInt(str)));
_o[key] = (str.substr(0, 2) == "0x") ? str : "0x" + toHex(toCompactBigEndian(toInt(str), 1));
}
return _o;
}
@ -363,8 +363,8 @@ json_spirit::mObject fillJsonWithState(State _state)
for (auto const& a: _state.addresses())
{
json_spirit::mObject o;
o["balance"] = "0x" + toHex(toCompactBigEndian(_state.balance(a.first)));
o["nonce"] = "0x" + toHex(toCompactBigEndian(_state.transactionsFrom(a.first)));
o["balance"] = "0x" + toHex(toCompactBigEndian(_state.balance(a.first), 1));
o["nonce"] = "0x" + toHex(toCompactBigEndian(_state.transactionsFrom(a.first), 1));
{
json_spirit::mObject store;
for (auto const& s: _state.storage(a.first))

10
test/blockchain.cpp

@ -625,11 +625,11 @@ void writeBlockHeaderToJson(mObject& _o, BlockInfo const& _bi)
_o["transactionsTrie"] = toString(_bi.transactionsRoot);
_o["receiptTrie"] = toString(_bi.receiptsRoot);
_o["bloom"] = toString(_bi.logBloom);
_o["difficulty"] = "0x" + toHex(toCompactBigEndian(_bi.difficulty));
_o["number"] = "0x" + toHex(toCompactBigEndian(_bi.number));
_o["gasLimit"] = "0x" + toHex(toCompactBigEndian(_bi.gasLimit));
_o["gasUsed"] = "0x" + toHex(toCompactBigEndian(_bi.gasUsed));
_o["timestamp"] = "0x" + toHex(toCompactBigEndian(_bi.timestamp));
_o["difficulty"] = "0x" + toHex(toCompactBigEndian(_bi.difficulty), 1);
_o["number"] = "0x" + toHex(toCompactBigEndian(_bi.number), 1);
_o["gasLimit"] = "0x" + toHex(toCompactBigEndian(_bi.gasLimit), 1);
_o["gasUsed"] = "0x" + toHex(toCompactBigEndian(_bi.gasUsed), 1);
_o["timestamp"] = "0x" + toHex(toCompactBigEndian(_bi.timestamp), 1);
_o["extraData"] ="0x" + toHex(_bi.extraData);
_o["mixHash"] = toString(_bi.mixHash);
_o["nonce"] = toString(_bi.nonce);

4
test/stateOriginal.cpp

@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(Complex)
CanonBlockChain bc;
cout << bc;
State s(stateDB, BaseState::Empty, myMiner.address());
State s(stateDB, BaseState::CanonGenesis, myMiner.address());
cout << s;
// Sync up - this won't do much until we use the last state.
@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(Complex)
cout << s;
// Inject a transaction to transfer funds from miner to me.
Transaction t(1000, 10000, 10000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret());
Transaction t(1000, 10000, 100000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret());
assert(t.sender() == myMiner.address());
s.execute(bc.lastHashes(), t);

3
test/webthreestubclient.h

@ -434,11 +434,12 @@ class WebThreeStubClient : public jsonrpc::Client
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool eth_submitWork(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
bool eth_submitWork(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
p.append(param3);
Json::Value result = this->CallMethod("eth_submitWork",p);
if (result.isBool())
return result.asBool();

Loading…
Cancel
Save