diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4adac7060..7e3b6a53c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -394,6 +394,7 @@ endif ()
if (JSCONSOLE)
add_subdirectory(libjsengine)
add_subdirectory(libjsconsole)
+ add_subdirectory(ethconsole)
endif ()
add_subdirectory(secp256k1)
diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp
index 7a26f56f2..594133ba4 100644
--- a/alethzero/Transact.cpp
+++ b/alethzero/Transact.cpp
@@ -139,7 +139,7 @@ void Transact::updateDestination()
void Transact::updateFee()
{
- ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str()));
+// ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str()));
auto totalReq = total();
ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str()));
@@ -435,9 +435,18 @@ Address Transact::fromAccount()
return *it;
}
+void Transact::updateNonce()
+{
+ u256 n = ethereum()->countAt(fromAccount(), PendingBlock);
+ ui->nonce->setMaximum((unsigned)n);
+ ui->nonce->setMinimum(0);
+ ui->nonce->setValue((unsigned)n);
+}
+
void Transact::on_send_clicked()
{
// Secret s = findSecret(value() + fee());
+ u256 nonce = ui->autoNonce->isChecked() ? ethereum()->countAt(fromAccount(), PendingBlock) : ui->nonce->value();
auto a = fromAccount();
auto b = ethereum()->balanceAt(a, PendingBlock);
@@ -455,7 +464,7 @@ void Transact::on_send_clicked()
{
// If execution is a contract creation, add Natspec to
// a local Natspec LEVELDB
- ethereum()->submitTransaction(s, value(), m_data, ui->gas->value(), gasPrice());
+ ethereum()->submitTransaction(s, value(), m_data, ui->gas->value(), gasPrice(), nonce);
#if ETH_SOLIDITY
string src = ui->data->toPlainText().toStdString();
if (sourceIsSolidity(src))
@@ -474,7 +483,7 @@ void Transact::on_send_clicked()
}
else
// TODO: cache like m_data.
- ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText().toStdString()).first, m_data, ui->gas->value(), gasPrice());
+ ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText().toStdString()).first, m_data, ui->gas->value(), gasPrice(), nonce);
close();
}
diff --git a/alethzero/Transact.h b/alethzero/Transact.h
index b8b5134a4..e2c1a6662 100644
--- a/alethzero/Transact.h
+++ b/alethzero/Transact.h
@@ -45,7 +45,7 @@ public:
void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB);
private slots:
- void on_from_currentIndexChanged(int) { rejigData(); rejigData(); }
+ void on_from_currentIndexChanged(int) { updateNonce(); rejigData(); }
void on_destination_currentTextChanged(QString);
void on_value_valueChanged(int) { updateFee(); rejigData(); }
void on_gas_valueChanged(int) { updateFee(); rejigData(); }
@@ -61,6 +61,7 @@ private slots:
private:
dev::eth::Client* ethereum() const { return m_ethereum; }
void rejigData();
+ void updateNonce();
dev::Address fromAccount();
void updateDestination();
diff --git a/alethzero/Transact.ui b/alethzero/Transact.ui
index 87a8ebd99..fe1fc4c3e 100644
--- a/alethzero/Transact.ui
+++ b/alethzero/Transact.ui
@@ -14,35 +14,6 @@
Transact
- -
-
-
-
- 0
- 0
-
-
-
- D&ata
-
-
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
-
-
- data
-
-
-
- -
-
-
- &Optimise
-
-
- true
-
-
-
-
@@ -92,6 +63,9 @@
+ -
+
+
-
@@ -105,22 +79,6 @@
- -
-
-
-
-
-
- 430000000
-
-
- 0
-
-
-
- -
-
-
-
@@ -166,6 +124,54 @@
+ -
+
+
+
+ 0
+ 0
+
+
+
+ D&ata
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+ data
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ 430000000
+
+
+ 0
+
+
+
+ -
+
+
+ false
+
+
+ true
+
+
+
+
+
+
-
@@ -205,53 +211,68 @@
- -
-
-
- false
-
-
- true
+
-
+
+
+ &From
-
-
+
+ from
- -
-
+
-
+
- -
-
-
-
- 0
- 0
-
-
+
-
+
-
+ &Optimise
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+ true
- -
-
+
-
+
+
+ false
+
+
+
+ -
+
- &From
+ Auto Nonce
-
- from
+
+ true
+
+
+ false
- -
-
-
-
+
+
+ autoNonce
+ toggled(bool)
+ nonce
+ setDisabled(bool)
+
+
+ 374
+ 196
+
+
+ 451
+ 190
+
+
+
+
diff --git a/eth/main.cpp b/eth/main.cpp
index c5a15e5fd..235183ef6 100644
--- a/eth/main.cpp
+++ b/eth/main.cpp
@@ -41,7 +41,7 @@
#include
#if ETH_JSCONSOLE || !ETH_TRUE
-#include
+#include
#endif
#if ETH_READLINE || !ETH_TRUE
#include
@@ -141,6 +141,7 @@ void help()
<< " --master Give the master password for the key store." << endl
<< " --password Give a password for a private key." << endl
<< " --sentinel Set the sentinel for reporting bad blocks or chain issues." << endl
+ << " --prime Specify n as the 6 digit prime number to start Frontier." << endl
<< endl
<< "Client transacting:" << endl
/*<< " -B,--block-fees Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl
@@ -187,6 +188,7 @@ void help()
<< " --from Export only from block n; n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl
<< " --to Export only to block n (inclusive); n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl
<< " --only Equivalent to --export-from n --export-to n." << endl
+ << " --dont-check Avoids checking some of the aspects of blocks. Faster importing, but only do if you know the data is valid." << endl
<< endl
<< "General Options:" << endl
<< " -d,--db-path Load database from path (default: " << getDataDir() << ")" << endl
@@ -240,6 +242,16 @@ string pretty(h160 _a, dev::eth::State const& _st)
bool g_exit = false;
+inline bool isPrime(unsigned _number)
+{
+ if (((!(_number & 1)) && _number != 2 ) || (_number < 2) || (_number % 3 == 0 && _number != 3))
+ return false;
+ for(unsigned k = 1; 36 * k * k - 12 * k < _number; ++k)
+ if ((_number % (6 * k + 1) == 0) || (_number % (6 * k - 1) == 0))
+ return false;
+ return true;
+}
+
void sighandler(int)
{
g_exit = true;
@@ -280,9 +292,12 @@ int main(int argc, char** argv)
/// Operating mode.
OperationMode mode = OperationMode::Node;
string dbPath;
+ unsigned prime = 0;
+ bool yesIReallyKnowWhatImDoing = false;
/// File name for import/export.
string filename;
+ bool safeImport = false;
/// Hashes/numbers for export range.
string exportFrom = "1";
@@ -395,11 +410,25 @@ int main(int argc, char** argv)
mode = OperationMode::Import;
filename = argv[++i];
}
+ else if (arg == "--dont-check")
+ safeImport = true;
else if ((arg == "-E" || arg == "--export") && i + 1 < argc)
{
mode = OperationMode::Export;
filename = argv[++i];
}
+ else if (arg == "--prime" && i + 1 < argc)
+ try
+ {
+ prime = stoi(argv[++i]);
+ }
+ catch (...)
+ {
+ cerr << "Bad " << arg << " option: " << argv[i] << endl;
+ return -1;
+ }
+ else if (arg == "--yes-i-really-know-what-im-doing")
+ yesIReallyKnowWhatImDoing = true;
else if (arg == "--sentinel" && i + 1 < argc)
sentinel = argv[++i];
else if (arg == "--mine-on-wrong-chain")
@@ -753,13 +782,18 @@ int main(int argc, char** argv)
unsigned futureTime = 0;
unsigned unknownParent = 0;
unsigned bad = 0;
+ chrono::steady_clock::time_point t = chrono::steady_clock::now();
+ double last = 0;
+ unsigned lastImported = 0;
+ unsigned imported = 0;
while (in.peek() != -1)
{
bytes block(8);
in.read((char*)block.data(), 8);
block.resize(RLP(block, RLP::LaisezFaire).actualSize());
in.read((char*)block.data() + 8, block.size() - 8);
- switch (web3.ethereum()->injectBlock(block))
+
+ switch (web3.ethereum()->queueBlock(block, safeImport))
{
case ImportResult::Success: good++; break;
case ImportResult::AlreadyKnown: alreadyHave++; break;
@@ -768,11 +802,52 @@ int main(int argc, char** argv)
case ImportResult::FutureTimeKnown: futureTime++; break;
default: bad++; break;
}
+
+ // sync chain with queue
+ tuple r = web3.ethereum()->syncQueue(10);
+ imported += get<2>(r);
+
+ double e = chrono::duration_cast(chrono::steady_clock::now() - t).count() / 1000.0;
+ if ((unsigned)e >= last + 10)
+ {
+ auto i = imported - lastImported;
+ auto d = e - last;
+ cout << i << " more imported at " << (round(i * 10 / d) / 10) << " blocks/s. " << imported << " imported in " << e << " seconds at " << (round(imported * 10 / e) / 10) << " blocks/s (#" << web3.ethereum()->number() << ")" << endl;
+ last = (unsigned)e;
+ lastImported = imported;
+ }
}
- cout << (good + bad + futureTime + unknownParent + alreadyHave) << " total: " << good << " ok, " << alreadyHave << " got, " << futureTime << " future, " << unknownParent << " unknown parent, " << bad << " malformed." << endl;
+
+ while (web3.ethereum()->blockQueue().items().first + web3.ethereum()->blockQueue().items().second > 0)
+ {
+ sleep(1);
+ web3.ethereum()->syncQueue(100000);
+ }
+ double e = chrono::duration_cast(chrono::steady_clock::now() - t).count() / 1000.0;
+ cout << imported << " imported in " << e << " seconds at " << (round(imported * 10 / e) / 10) << " blocks/s (#" << web3.ethereum()->number() << ")" << endl;
return 0;
}
+ if (c_network == eth::Network::Frontier && !yesIReallyKnowWhatImDoing)
+ {
+ auto pd = contents(getDataDir() + "primes");
+ unordered_set primes = RLP(pd).toUnorderedSet();
+ while (true)
+ {
+ if (!prime)
+ try
+ {
+ prime = stoi(getPassword("To enter the Frontier, enter a 6 digit prime that you have not entered before: "));
+ }
+ catch (...) {}
+ if (isPrime(prime) && !primes.count(prime))
+ break;
+ prime = 0;
+ }
+ primes.insert(prime);
+ writeFile(getDataDir() + "primes", rlp(primes));
+ }
+
if (keyManager.exists())
{
if (masterPassword.empty() || !keyManager.load(masterPassword))
@@ -1742,12 +1817,24 @@ int main(int argc, char** argv)
if (useConsole)
{
#if ETH_JSCONSOLE
- JSConsole console(web3, make_shared([&](){return web3.ethereum();}, getAccountPassword, keyManager));
+ JSLocalConsole console;
+
+ jsonrpcServer = shared_ptr(new dev::WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager, *gasPricer));
+ jsonrpcServer->setMiningBenefactorChanger([&](Address const& a) { beneficiary = a; });
+ jsonrpcServer->StartListening();
+ if (jsonAdmin.empty())
+ jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}});
+ else
+ jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}});
+ cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl;
+
while (!g_exit)
{
console.readExpression();
stopMiningAfterXBlocks(c, n, mining);
}
+
+ jsonrpcServer->StopListening();
#endif
}
else
diff --git a/ethconsole/CMakeLists.txt b/ethconsole/CMakeLists.txt
new file mode 100644
index 000000000..08fa7ca83
--- /dev/null
+++ b/ethconsole/CMakeLists.txt
@@ -0,0 +1,31 @@
+cmake_policy(SET CMP0015 NEW)
+set(CMAKE_AUTOMOC OFF)
+
+aux_source_directory(. SRC_LIST)
+
+include_directories(BEFORE ..)
+include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
+include_directories(${CURL_INCLUDE_DIRS})
+include_directories(${V8_INCLUDE_DIRS})
+
+set(EXECUTABLE ethconsole)
+
+file(GLOB HEADERS "*.h")
+
+add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
+
+target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
+target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
+target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
+
+if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
+ eth_copy_dlls(${EXECUTABLE} CURL_DLLS)
+endif()
+target_link_libraries(${EXECUTABLE} jsconsole)
+
+if (APPLE)
+ install(TARGETS ${EXECUTABLE} DESTINATION bin)
+else()
+ eth_install_executable(${EXECUTABLE})
+endif()
+
diff --git a/ethconsole/main.cpp b/ethconsole/main.cpp
new file mode 100644
index 000000000..5df3444eb
--- /dev/null
+++ b/ethconsole/main.cpp
@@ -0,0 +1,41 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file main.cpp
+ * @author Marek
+ * @date 2014
+ */
+
+#include
+#include
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+
+int main(int argc, char** argv)
+{
+ string remote;
+ if (argc == 1)
+ remote = "http://localhost:8545";
+ else if (argc == 2)
+ remote = argv[1];
+
+ JSRemoteConsole console(remote);
+ while (true)
+ console.readExpression();
+
+ return 0;
+}
diff --git a/ethvm/main.cpp b/ethvm/main.cpp
index 08a1b4508..4ca733ed0 100644
--- a/ethvm/main.cpp
+++ b/ethvm/main.cpp
@@ -165,7 +165,7 @@ int main(int argc, char** argv)
executive.initialize(t);
executive.create(sender, value, gasPrice, gas, &data, origin);
- boost::timer timer;
+ Timer timer;
executive.go(onOp);
double execTime = timer.elapsed();
executive.finalize();
diff --git a/exp/main.cpp b/exp/main.cpp
index f0574fa7c..1db9b4267 100644
--- a/exp/main.cpp
+++ b/exp/main.cpp
@@ -88,7 +88,7 @@ int main()
data.push_back(rlp(i));
h256 ret;
- DEV_TIMED(triedb)
+ DEV_TIMED("triedb")
{
MemoryDB mdb;
GenericTrieDB t(&mdb);
@@ -99,7 +99,7 @@ int main()
ret = t.root();
}
cdebug << ret;
- DEV_TIMED(hash256)
+ DEV_TIMED("hash256")
ret = orderedTrieRoot(data);
cdebug << ret;
}
diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp
index 17ccae6b1..e33936102 100644
--- a/libdevcore/Common.cpp
+++ b/libdevcore/Common.cpp
@@ -48,9 +48,9 @@ const char* TimerChannel::name() { return EthRed " ⚡ "; }
TimerHelper::~TimerHelper()
{
- auto e = m_t.elapsed();
- if (!m_ms || e * 1000 > m_ms)
- clog(TimerChannel) << m_id << e << "s";
+ auto e = std::chrono::high_resolution_clock::now() - m_t;
+ if (!m_ms || e > chrono::milliseconds(m_ms))
+ clog(TimerChannel) << m_id << chrono::duration_cast(e).count() << "ms";
}
}
diff --git a/libdevcore/Common.h b/libdevcore/Common.h
index c6ed25223..f1d35bbc7 100644
--- a/libdevcore/Common.h
+++ b/libdevcore/Common.h
@@ -40,7 +40,7 @@
#include
#include
#include
-#include
+#include
#include
#pragma warning(push)
#pragma GCC diagnostic push
@@ -193,16 +193,29 @@ private:
class TimerHelper
{
public:
- TimerHelper(char const* _id, unsigned _msReportWhenGreater = 0): m_id(_id), m_ms(_msReportWhenGreater) {}
+ TimerHelper(std::string const& _id, unsigned _msReportWhenGreater = 0): m_t(std::chrono::high_resolution_clock::now()), m_id(_id), m_ms(_msReportWhenGreater) {}
~TimerHelper();
private:
- boost::timer m_t;
- char const* m_id;
+ std::chrono::high_resolution_clock::time_point m_t;
+ std::string m_id;
unsigned m_ms;
};
-#define DEV_TIMED(S) for (::std::pair<::dev::TimerHelper, bool> __eth_t(#S, true); __eth_t.second; __eth_t.second = false)
+class Timer
+{
+public:
+ Timer() { restart(); }
+
+ std::chrono::high_resolution_clock::duration duration() const { return std::chrono::high_resolution_clock::now() - m_t; }
+ double elapsed() const { return std::chrono::duration_cast(duration()).count() / 1000000.0; }
+ void restart() { m_t = std::chrono::high_resolution_clock::now(); }
+
+private:
+ std::chrono::high_resolution_clock::time_point m_t;
+};
+
+#define DEV_TIMED(S) for (::std::pair<::dev::TimerHelper, bool> __eth_t(S, true); __eth_t.second; __eth_t.second = false)
#define DEV_TIMED_SCOPE(S) ::dev::TimerHelper __eth_t(S)
#if WIN32
#define DEV_TIMED_FUNCTION DEV_TIMED_SCOPE(__FUNCSIG__)
@@ -210,7 +223,7 @@ private:
#define DEV_TIMED_FUNCTION DEV_TIMED_SCOPE(__PRETTY_FUNCTION__)
#endif
-#define DEV_TIMED_ABOVE(S, MS) for (::std::pair<::dev::TimerHelper, bool> __eth_t(::dev::TimerHelper(#S, MS), true); __eth_t.second; __eth_t.second = false)
+#define DEV_TIMED_ABOVE(S, MS) for (::std::pair<::dev::TimerHelper, bool> __eth_t(::dev::TimerHelper(S, MS), true); __eth_t.second; __eth_t.second = false)
#define DEV_TIMED_SCOPE_ABOVE(S, MS) ::dev::TimerHelper __eth_t(S, MS)
#if WIN32
#define DEV_TIMED_FUNCTION_ABOVE(MS) DEV_TIMED_SCOPE_ABOVE(__FUNCSIG__, MS)
diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h
index 922a6ec30..c7c551c2b 100644
--- a/libdevcore/FixedHash.h
+++ b/libdevcore/FixedHash.h
@@ -113,6 +113,9 @@ public:
/// @returns an abridged version of the hash as a user-readable hex string.
std::string abridged() const { return toHex(ref().cropped(0, 4)) + "\342\200\246"; }
+ /// @returns a version of the hash as a user-readable hex string that leaves out the middle part.
+ std::string abridgedMiddle() const { return toHex(ref().cropped(0, 4)) + "\342\200\246" + toHex(ref().cropped(N - 4)); }
+
/// @returns the hash as a user-readable hex string.
std::string hex() const { return toHex(ref()); }
diff --git a/libdevcore/RLP.h b/libdevcore/RLP.h
index 6ae9c165b..dee438be4 100644
--- a/libdevcore/RLP.h
+++ b/libdevcore/RLP.h
@@ -204,9 +204,7 @@ public:
{
ret.reserve(itemCount());
for (auto const& i: *this)
- {
ret.push_back((T)i);
- }
}
return ret;
}
@@ -216,15 +214,21 @@ public:
{
std::set ret;
if (isList())
- {
for (auto const& i: *this)
- {
ret.insert((T)i);
- }
- }
return ret;
}
-
+
+ template
+ std::unordered_set toUnorderedSet() const
+ {
+ std::unordered_set ret;
+ if (isList())
+ for (auto const& i: *this)
+ ret.insert((T)i);
+ return ret;
+ }
+
template
std::pair toPair() const
{
diff --git a/libdevcore/TrieCommon.cpp b/libdevcore/TrieCommon.cpp
index ff44906b1..5bf6070ee 100644
--- a/libdevcore/TrieCommon.cpp
+++ b/libdevcore/TrieCommon.cpp
@@ -60,7 +60,7 @@ std::string hexPrefixEncode(bytes const& _hexVector, bool _leaf, int _begin, int
std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, int _endNibble, unsigned _offset)
{
unsigned begin = _beginNibble + _offset;
- unsigned end = (_endNibble < 0 ? (_data.size() * 2 - _offset) + 1 + _endNibble : _endNibble) + _offset;
+ unsigned end = (_endNibble < 0 ? ((int)(_data.size() * 2 - _offset) + 1) + _endNibble : _endNibble) + _offset;
bool odd = (end - begin) & 1;
std::string ret(1, ((_leaf ? 2 : 0) | (odd ? 1 : 0)) * 16);
diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp
index ab19b2f74..d47955753 100644
--- a/libdevcore/Worker.cpp
+++ b/libdevcore/Worker.cpp
@@ -65,14 +65,14 @@ void Worker::startWorking()
m_state.exchange(ex);
// cnote << "Waiting until not Stopped...";
- DEV_TIMED_ABOVE(Worker stopping, 100)
+ DEV_TIMED_ABOVE("Worker stopping", 100)
while (m_state == WorkerState::Stopped)
this_thread::sleep_for(chrono::milliseconds(20));
}
}));
// cnote << "Spawning" << m_name;
}
- DEV_TIMED_ABOVE(Start worker, 100)
+ DEV_TIMED_ABOVE("Start worker", 100)
while (m_state == WorkerState::Starting)
this_thread::sleep_for(chrono::microseconds(20));
}
@@ -85,7 +85,7 @@ void Worker::stopWorking()
WorkerState ex = WorkerState::Started;
m_state.compare_exchange_strong(ex, WorkerState::Stopping);
- DEV_TIMED_ABOVE(Stop worker, 100)
+ DEV_TIMED_ABOVE("Stop worker", 100)
while (m_state != WorkerState::Stopped)
this_thread::sleep_for(chrono::microseconds(20));
}
@@ -99,7 +99,7 @@ void Worker::terminate()
{
m_state.exchange(WorkerState::Killing);
- DEV_TIMED_ABOVE(Terminate worker, 100)
+ DEV_TIMED_ABOVE("Terminate worker", 100)
m_work->join();
m_work.reset();
diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp
index 315f29685..79024e2ad 100644
--- a/libethash-cl/ethash_cl_miner.cpp
+++ b/libethash-cl/ethash_cl_miner.cpp
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -64,7 +65,7 @@ static void addDefinition(string& _source, char const* _id, unsigned _value)
ethash_cl_miner::search_hook::~search_hook() {}
ethash_cl_miner::ethash_cl_miner()
-: m_opencl_1_1()
+: m_openclOnePointOne()
{
}
@@ -252,7 +253,7 @@ void ethash_cl_miner::finish()
bool ethash_cl_miner::init(
uint8_t const* _dag,
uint64_t _dagSize,
- unsigned workgroup_size,
+ unsigned _workgroupSize,
unsigned _platformId,
unsigned _deviceId
)
@@ -291,23 +292,23 @@ bool ethash_cl_miner::init(
return false;
}
if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0)
- m_opencl_1_1 = true;
+ m_openclOnePointOne = true;
// create context
m_context = cl::Context(vector(&device, &device + 1));
m_queue = cl::CommandQueue(m_context, device);
// use requested workgroup size, but we require multiple of 8
- m_workgroup_size = ((workgroup_size + 7) / 8) * 8;
+ m_workgroupSize = ((_workgroupSize + 7) / 8) * 8;
// patch source code
// note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled
// into a byte array by bin2h.cmake. There is no need to load the file by hand in runtime
string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
- addDefinition(code, "GROUP_SIZE", m_workgroup_size);
+ addDefinition(code, "GROUP_SIZE", m_workgroupSize);
addDefinition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
addDefinition(code, "ACCESSES", ETHASH_ACCESSES);
- addDefinition(code, "MAX_OUTPUTS", c_max_search_results);
+ addDefinition(code, "MAX_OUTPUTS", c_maxSearchResults);
//debugf("%s", code.c_str());
// create miner OpenCL program
@@ -330,7 +331,7 @@ bool ethash_cl_miner::init(
// create buffer for dag
try
{
- m_dagChunksNum = 1;
+ m_dagChunksCount = 1;
m_dagChunks.push_back(cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize));
ETHCL_LOG("Created one big buffer for the DAG");
}
@@ -346,8 +347,8 @@ bool ethash_cl_miner::init(
<< result << ". Trying to allocate 4 chunks."
);
// The OpenCL kernel has a hard coded number of 4 chunks at the moment
- m_dagChunksNum = 4;
- for (unsigned i = 0; i < m_dagChunksNum; i++)
+ m_dagChunksCount = 4;
+ for (unsigned i = 0; i < m_dagChunksCount; i++)
{
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
ETHCL_LOG("Creating buffer for chunk " << i);
@@ -359,24 +360,24 @@ bool ethash_cl_miner::init(
}
}
- if (m_dagChunksNum == 1)
+ if (m_dagChunksCount == 1)
{
ETHCL_LOG("Loading single big chunk kernels");
- m_hash_kernel = cl::Kernel(program, "ethash_hash");
- m_search_kernel = cl::Kernel(program, "ethash_search");
+ m_hashKernel = cl::Kernel(program, "ethash_hash");
+ m_searchKernel = cl::Kernel(program, "ethash_search");
}
else
{
ETHCL_LOG("Loading chunk kernels");
- m_hash_kernel = cl::Kernel(program, "ethash_hash_chunks");
- m_search_kernel = cl::Kernel(program, "ethash_search_chunks");
+ m_hashKernel = cl::Kernel(program, "ethash_hash_chunks");
+ m_searchKernel = cl::Kernel(program, "ethash_search_chunks");
}
// create buffer for header
ETHCL_LOG("Creating buffer for header.");
m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32);
- if (m_dagChunksNum == 1)
+ if (m_dagChunksCount == 1)
{
ETHCL_LOG("Mapping one big chunk.");
m_queue.enqueueWriteBuffer(m_dagChunks[0], CL_TRUE, 0, _dagSize, _dag);
@@ -385,12 +386,12 @@ bool ethash_cl_miner::init(
{
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
void* dag_ptr[4];
- for (unsigned i = 0; i < m_dagChunksNum; i++)
+ for (unsigned i = 0; i < m_dagChunksCount; i++)
{
ETHCL_LOG("Mapping chunk " << i);
- dag_ptr[i] = m_queue.enqueueMapBuffer(m_dagChunks[i], true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
+ dag_ptr[i] = m_queue.enqueueMapBuffer(m_dagChunks[i], true, m_openclOnePointOne ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
}
- for (unsigned i = 0; i < m_dagChunksNum; i++)
+ for (unsigned i = 0; i < m_dagChunksCount; i++)
{
memcpy(dag_ptr[i], (char *)_dag + i*((_dagSize >> 9) << 7), (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
m_queue.enqueueUnmapMemObject(m_dagChunks[i], dag_ptr[i]);
@@ -398,11 +399,11 @@ bool ethash_cl_miner::init(
}
// create mining buffers
- for (unsigned i = 0; i != c_num_buffers; ++i)
+ for (unsigned i = 0; i != c_bufferCount; ++i)
{
ETHCL_LOG("Creating mining buffer " << i);
- m_hash_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY | (!m_opencl_1_1 ? CL_MEM_HOST_READ_ONLY : 0), 32 * c_hash_batch_size);
- m_search_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_max_search_results + 1) * sizeof(uint32_t));
+ m_hashBuffer[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY | (!m_openclOnePointOne ? CL_MEM_HOST_READ_ONLY : 0), 32 * c_hashBatchSize);
+ m_searchBuffer[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_maxSearchResults + 1) * sizeof(uint32_t));
}
}
catch (cl::Error const& err)
@@ -413,7 +414,7 @@ bool ethash_cl_miner::init(
return true;
}
-void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook)
+void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook, unsigned _msPerBatch)
{
try
{
@@ -429,8 +430,8 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
// update header constant buffer
m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header);
- for (unsigned i = 0; i != c_num_buffers; ++i)
- m_queue.enqueueWriteBuffer(m_search_buf[i], false, 0, 4, &c_zero);
+ for (unsigned i = 0; i != c_bufferCount; ++i)
+ m_queue.enqueueWriteBuffer(m_searchBuffer[i], false, 0, 4, &c_zero);
#if CL_VERSION_1_2 && 0
cl::Event pre_return_event;
@@ -441,53 +442,59 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_queue.finish();
unsigned argPos = 2;
- m_search_kernel.setArg(1, m_header);
- for (unsigned i = 0; i < m_dagChunksNum; ++i, ++argPos)
- m_search_kernel.setArg(argPos, m_dagChunks[i]);
+ m_searchKernel.setArg(1, m_header);
+ for (unsigned i = 0; i < m_dagChunksCount; ++i, ++argPos)
+ m_searchKernel.setArg(argPos, m_dagChunks[i]);
// pass these to stop the compiler unrolling the loops
- m_search_kernel.setArg(argPos + 1, target);
- m_search_kernel.setArg(argPos + 2, ~0u);
+ m_searchKernel.setArg(argPos + 1, target);
+ m_searchKernel.setArg(argPos + 2, ~0u);
unsigned buf = 0;
random_device engine;
uint64_t start_nonce = uniform_int_distribution()(engine);
- for (;; start_nonce += c_search_batch_size)
+ for (;; start_nonce += m_batchSize)
{
// supply output buffer to kernel
- m_search_kernel.setArg(0, m_search_buf[buf]);
- if (m_dagChunksNum == 1)
- m_search_kernel.setArg(3, start_nonce);
+ m_searchKernel.setArg(0, m_searchBuffer[buf]);
+ if (m_dagChunksCount == 1)
+ m_searchKernel.setArg(3, start_nonce);
else
- m_search_kernel.setArg(6, start_nonce);
+ m_searchKernel.setArg(6, start_nonce);
// execute it!
- m_queue.enqueueNDRangeKernel(m_search_kernel, cl::NullRange, c_search_batch_size, m_workgroup_size);
+ boost::timer t;
+ m_queue.enqueueNDRangeKernel(m_searchKernel, cl::NullRange, m_batchSize, m_workgroupSize);
+ unsigned ms = t.elapsed() * 1000;
+ if (ms > _msPerBatch * 1.1)
+ m_batchSize = max(128, m_batchSize * 9 / 10);
+ else if (ms < _msPerBatch * 0.9)
+ m_batchSize = m_batchSize * 10 / 9;
pending.push({ start_nonce, buf });
- buf = (buf + 1) % c_num_buffers;
+ buf = (buf + 1) % c_bufferCount;
// read results
- if (pending.size() == c_num_buffers)
+ if (pending.size() == c_bufferCount)
{
pending_batch const& batch = pending.front();
// could use pinned host pointer instead
- uint32_t* results = (uint32_t*)m_queue.enqueueMapBuffer(m_search_buf[batch.buf], true, CL_MAP_READ, 0, (1 + c_max_search_results) * sizeof(uint32_t));
- unsigned num_found = min(results[0], c_max_search_results);
+ uint32_t* results = (uint32_t*)m_queue.enqueueMapBuffer(m_searchBuffer[batch.buf], true, CL_MAP_READ, 0, (1 + c_maxSearchResults) * sizeof(uint32_t));
+ unsigned num_found = min(results[0], c_maxSearchResults);
- uint64_t nonces[c_max_search_results];
+ uint64_t nonces[c_maxSearchResults];
for (unsigned i = 0; i != num_found; ++i)
nonces[i] = batch.start_nonce + results[i + 1];
- m_queue.enqueueUnmapMemObject(m_search_buf[batch.buf], results);
+ m_queue.enqueueUnmapMemObject(m_searchBuffer[batch.buf], results);
bool exit = num_found && hook.found(nonces, num_found);
- exit |= hook.searched(batch.start_nonce, c_search_batch_size); // always report searched before exit
+ exit |= hook.searched(batch.start_nonce, m_batchSize); // always report searched before exit
if (exit)
break;
// reset search buffer if we're still going
if (num_found)
- m_queue.enqueueWriteBuffer(m_search_buf[batch.buf], true, 0, 4, &c_zero);
+ m_queue.enqueueWriteBuffer(m_searchBuffer[batch.buf], true, 0, 4, &c_zero);
pending.pop();
}
diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h
index f36082a5a..996453c00 100644
--- a/libethash-cl/ethash_cl_miner.h
+++ b/libethash-cl/ethash_cl_miner.h
@@ -19,6 +19,9 @@
class ethash_cl_miner
{
+private:
+ enum { c_maxSearchResults = 63, c_bufferCount = 2, c_hashBatchSize = 1024, c_searchBatchSize = 1024 * 16 };
+
public:
struct search_hook
{
@@ -29,7 +32,6 @@ public:
virtual bool searched(uint64_t start_nonce, uint32_t count) = 0;
};
-public:
ethash_cl_miner();
~ethash_cl_miner();
@@ -50,33 +52,32 @@ public:
bool init(
uint8_t const* _dag,
uint64_t _dagSize,
- unsigned workgroup_size = 64,
+ unsigned _workgroupSize = 64,
unsigned _platformId = 0,
unsigned _deviceId = 0
);
void finish();
- void search(uint8_t const* header, uint64_t target, search_hook& hook);
+ void search(uint8_t const* _header, uint64_t _target, search_hook& _hook, unsigned _msPerBatch = 100);
- void hash_chunk(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count);
- void search_chunk(uint8_t const* header, uint64_t target, search_hook& hook);
+ void hash_chunk(uint8_t* _ret, uint8_t const* _header, uint64_t _nonce, unsigned _count);
+ void search_chunk(uint8_t const*_header, uint64_t _target, search_hook& _hook);
private:
static std::vector getDevices(std::vector const& _platforms, unsigned _platformId);
-
- enum { c_max_search_results = 63, c_num_buffers = 2, c_hash_batch_size = 1024, c_search_batch_size = 1024*256 };
cl::Context m_context;
cl::CommandQueue m_queue;
- cl::Kernel m_hash_kernel;
- cl::Kernel m_search_kernel;
- unsigned int m_dagChunksNum;
+ cl::Kernel m_hashKernel;
+ cl::Kernel m_searchKernel;
+ unsigned int m_dagChunksCount;
std::vector m_dagChunks;
cl::Buffer m_header;
- cl::Buffer m_hash_buf[c_num_buffers];
- cl::Buffer m_search_buf[c_num_buffers];
- unsigned m_workgroup_size;
- bool m_opencl_1_1;
+ cl::Buffer m_hashBuffer[c_bufferCount];
+ cl::Buffer m_searchBuffer[c_bufferCount];
+ unsigned m_workgroupSize;
+ unsigned m_batchSize = c_searchBatchSize;
+ bool m_openclOnePointOne;
/// Allow CPU to appear as an OpenCL device or not. Default is false
static bool s_allowCPU;
diff --git a/libethcore/Common.h b/libethcore/Common.h
index 19ca600b9..25a6a8e1d 100644
--- a/libethcore/Common.h
+++ b/libethcore/Common.h
@@ -97,6 +97,12 @@ enum class RelativeBlock: BlockNumber
Pending = PendingBlock
};
+struct ImportRoute
+{
+ h256s deadBlocks;
+ h256s liveBlocks;
+};
+
enum class ImportResult
{
Success = 0,
diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp
index b277e3c1c..70908ee44 100644
--- a/libethcore/Ethash.cpp
+++ b/libethcore/Ethash.cpp
@@ -225,26 +225,58 @@ std::string Ethash::CPUMiner::platformInfo()
#if ETH_ETHASHCL || !ETH_TRUE
+using UniqueGuard = std::unique_lock;
+
+template
+class Notified
+{
+public:
+ Notified() {}
+ Notified(N const& _v): m_value(_v) {}
+ Notified(Notified const&) = delete;
+ Notified& operator=(N const& _v) { UniqueGuard l(m_mutex); m_value = _v; m_cv.notify_all(); return *this; }
+
+ operator N() const { UniqueGuard l(m_mutex); return m_value; }
+
+ void wait() const { UniqueGuard l(m_mutex); m_cv.wait(l); }
+ void wait(N const& _v) const { UniqueGuard l(m_mutex); m_cv.wait(l, [&](){return m_value == _v;}); }
+ template void wait(F const& _f) const { UniqueGuard l(m_mutex); m_cv.wait(l, _f); }
+
+private:
+ mutable Mutex m_mutex;
+ mutable std::condition_variable m_cv;
+ N m_value;
+};
+
class EthashCLHook: public ethash_cl_miner::search_hook
{
public:
EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {}
+ EthashCLHook(EthashCLHook const&) = delete;
void abort()
{
- Guard l(x_all);
- if (m_aborted)
- return;
+ {
+ UniqueGuard l(x_all);
+ if (m_aborted)
+ return;
// cdebug << "Attempting to abort";
- m_abort = true;
- for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout)
- std::this_thread::sleep_for(chrono::milliseconds(30));
+
+ m_abort = true;
+ }
+ // m_abort is true so now searched()/found() will return true to abort the search.
+ // we hang around on this thread waiting for them to point out that they have aborted since
+ // otherwise we may end up deleting this object prior to searched()/found() being called.
+ m_aborted.wait(true);
+// for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout)
+// std::this_thread::sleep_for(chrono::milliseconds(30));
// if (!m_aborted)
// cwarn << "Couldn't abort. Abandoning OpenCL process.";
}
void reset()
{
+ UniqueGuard l(x_all);
m_aborted = m_abort = false;
}
@@ -253,27 +285,19 @@ protected:
{
// dev::operator <<(std::cerr << "Found nonces: ", vector(_nonces, _nonces + _count)) << std::endl;
for (uint32_t i = 0; i < _count; ++i)
- {
if (m_owner->report(_nonces[i]))
- {
- m_aborted = true;
- return true;
- }
- }
+ return (m_aborted = true);
return m_owner->shouldStop();
}
virtual bool searched(uint64_t _startNonce, uint32_t _count) override
{
- Guard l(x_all);
+ UniqueGuard l(x_all);
// std::cerr << "Searched " << _count << " from " << _startNonce << std::endl;
m_owner->accumulateHashes(_count);
m_last = _startNonce + _count;
if (m_abort || m_owner->shouldStop())
- {
- m_aborted = true;
- return true;
- }
+ return (m_aborted = true);
return false;
}
@@ -281,7 +305,7 @@ private:
Mutex x_all;
uint64_t m_last;
bool m_abort = false;
- bool m_aborted = true;
+ Notified m_aborted = {true};
Ethash::GPUMiner* m_owner = nullptr;
};
diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp
index 14a44a812..efb6066f7 100644
--- a/libethcore/EthashAux.cpp
+++ b/libethcore/EthashAux.cpp
@@ -119,9 +119,11 @@ void EthashAux::killCache(h256 const& _s)
EthashAux::LightType EthashAux::light(h256 const& _seedHash)
{
- ReadGuard l(get()->x_lights);
- LightType ret = get()->m_lights[_seedHash];
- return ret ? ret : (get()->m_lights[_seedHash] = make_shared(_seedHash));
+ UpgradableGuard l(get()->x_lights);
+ if (get()->m_lights.count(_seedHash))
+ return get()->m_lights.at(_seedHash);
+ UpgradeGuard l2(l);
+ return (get()->m_lights[_seedHash] = make_shared(_seedHash));
}
EthashAux::LightAllocation::LightAllocation(h256 const& _seedHash)
diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h
index b411ea416..0ac9df5b2 100644
--- a/libethcore/Exceptions.h
+++ b/libethcore/Exceptions.h
@@ -51,6 +51,7 @@ DEV_SIMPLE_EXCEPTION(FeeTooSmall);
DEV_SIMPLE_EXCEPTION(TooMuchGasUsed);
DEV_SIMPLE_EXCEPTION(ExtraDataTooBig);
DEV_SIMPLE_EXCEPTION(InvalidSignature);
+DEV_SIMPLE_EXCEPTION(InvalidTransactionFormat);
DEV_SIMPLE_EXCEPTION(InvalidBlockFormat);
DEV_SIMPLE_EXCEPTION(InvalidUnclesHash);
DEV_SIMPLE_EXCEPTION(TooManyUncles);
diff --git a/libethcore/Miner.h b/libethcore/Miner.h
index 11b9ae140..415da9878 100644
--- a/libethcore/Miner.h
+++ b/libethcore/Miner.h
@@ -107,9 +107,9 @@ public:
}
if (!!_work)
{
- DEV_TIMED_ABOVE(pause, 250)
+ DEV_TIMED_ABOVE("pause", 250)
pause();
- DEV_TIMED_ABOVE(kickOff, 250)
+ DEV_TIMED_ABOVE("kickOff", 250)
kickOff();
}
else if (!_work && !!old)
diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp
index 640fd2df4..7abf9316e 100644
--- a/libethereum/BlockChain.cpp
+++ b/libethereum/BlockChain.cpp
@@ -149,6 +149,7 @@ void BlockChain::open(std::string const& _path, WithExisting _we)
ldb::Options o;
o.create_if_missing = true;
+ o.max_open_files = 256;
ldb::DB::Open(o, path + "/blocks", &m_blocksDB);
ldb::DB::Open(o, path + "/details", &m_extrasDB);
if (!m_blocksDB || !m_extrasDB)
@@ -239,7 +240,7 @@ void BlockChain::rebuild(std::string const& _path, std::functionPut(m_writeOptions, toSlice(m_lastBlockHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[m_lastBlockHash].rlp()));
h256 lastHash = m_lastBlockHash;
- boost::timer t;
+ Timer t;
for (unsigned d = 1; d < originalNumber; ++d)
{
if (!(d % 1000))
@@ -323,7 +324,7 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB c
{
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r;
- DEV_TIMED_ABOVE(Block import, 500)
+ DEV_TIMED_ABOVE("Block import", 500)
r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.liveBlocks;
dead += r.deadBlocks;
@@ -397,6 +398,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
catch (Exception& ex)
{
// clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex);
+ ex << errinfo_phase(2);
ex << errinfo_now(time(0));
ex << errinfo_block(_block);
throw;
@@ -411,13 +413,13 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
//@tidy This is a behemoth of a method - could do to be split into a few smaller ones.
#if ETH_TIMED_IMPORTS
- boost::timer total;
+ Timer total;
double preliminaryChecks;
double enactment;
double collation;
double writing;
double checkBest;
- boost::timer t;
+ Timer t;
#endif
// Check block doesn't already exist first!
@@ -469,6 +471,9 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
h256 newLastBlockHash = currentHash();
unsigned newLastBlockNumber = number();
+ BlockLogBlooms blb;
+ BlockReceipts br;
+
u256 td;
#if ETH_CATCH
try
@@ -479,8 +484,6 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
State s(_db);
auto tdIncrease = s.enactOn(_block, *this, _ir);
- BlockLogBlooms blb;
- BlockReceipts br;
for (unsigned i = 0; i < s.pending().size(); ++i)
{
blb.blooms.push_back(s.receipt(i).bloom());
@@ -674,15 +677,17 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
#if ETH_TIMED_IMPORTS
checkBest = t.elapsed();
- if (total.elapsed() > 1.0)
+ if (total.elapsed() > 0.5)
{
- cnote << "SLOW IMPORT:" << _block.info.hash();
+ cnote << "SLOW IMPORT:" << _block.info.hash() << " #" << _block.info.number;
cnote << " Import took:" << total.elapsed();
cnote << " preliminaryChecks:" << preliminaryChecks;
cnote << " enactment:" << enactment;
cnote << " collation:" << collation;
cnote << " writing:" << writing;
cnote << " checkBest:" << checkBest;
+ cnote << " " << _block.transactions.size() << " transactions";
+ cnote << " " << _block.info.gasUsed << " gas used";
}
#endif
@@ -1080,6 +1085,7 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function;
using TransactionHashes = h256s;
using UncleHashes = h256s;
-struct ImportRoute
-{
- h256s deadBlocks;
- h256s liveBlocks;
-};
-
enum {
ExtraDetails = 0,
ExtraBlockHash,
diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp
index d4cc23aaf..b45620e3c 100644
--- a/libethereum/Client.cpp
+++ b/libethereum/Client.cpp
@@ -87,6 +87,18 @@ void VersionChecker::setOk()
}
}
+ImportResult Client::queueBlock(bytes const& _block, bool _isSafe)
+{
+ if (m_bq.status().verified + m_bq.status().verifying + m_bq.status().unverified > 30000)
+ this_thread::sleep_for(std::chrono::milliseconds(500));
+ return m_bq.import(&_block, bc(), _isSafe);
+}
+
+tuple Client::syncQueue(unsigned _max)
+{
+ return m_bc.sync(m_bq, m_stateDB, _max);
+}
+
void Client::onBadBlock(Exception& _ex) const
{
// BAD BLOCK!!!
@@ -159,7 +171,9 @@ void Client::onBadBlock(Exception& _ex) const
DEV_HINT_ERRINFO(max);
DEV_HINT_ERRINFO(name);
DEV_HINT_ERRINFO(field);
+ DEV_HINT_ERRINFO(transaction);
DEV_HINT_ERRINFO(data);
+ DEV_HINT_ERRINFO(phase);
DEV_HINT_ERRINFO_HASH(nonce);
DEV_HINT_ERRINFO(difficulty);
DEV_HINT_ERRINFO(target);
@@ -367,7 +381,7 @@ 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()";
+ clog(ClientTrace) << "startedWorking()";
DEV_WRITE_GUARDED(x_preMine)
m_preMine.sync(m_bc);
@@ -563,7 +577,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
try
{
State temp;
-// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
+// clog(ClientTrace) << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
DEV_READ_GUARDED(x_postMine)
temp = m_postMine;
temp.addBalance(_from, _value + _gasPrice * _gas);
@@ -628,12 +642,12 @@ void Client::syncBlockQueue()
cwork << "BQ ==> CHAIN ==> STATE";
ImportRoute ir;
unsigned count;
- boost::timer t;
+ Timer t;
tie(ir, m_syncBlockQueue, count) = m_bc.sync(m_bq, m_stateDB, m_syncAmount);
double elapsed = t.elapsed();
if (count)
- cnote << count << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (count / elapsed) << "blocks/s)";
+ clog(ClientNote) << count << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (count / elapsed) << "blocks/s)";
if (elapsed > c_targetDuration * 1.1 && count > c_syncMin)
m_syncAmount = max(c_syncMin, count * 9 / 10);
@@ -666,7 +680,6 @@ void Client::syncTransactionQueue()
for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
-
// Tell farm about new transaction (i.e. restartProofOfWork mining).
onPostStateChanged();
@@ -732,7 +745,7 @@ void Client::restartMining()
if (preChanged || m_postMine.address() != m_preMine.address())
{
if (isMining())
- cnote << "New block on chain.";
+ clog(ClientTrace) << "New block on chain.";
DEV_WRITE_GUARDED(x_preMine)
m_preMine = newPreMine;
@@ -774,7 +787,7 @@ bool Client::remoteActive() const
void Client::onPostStateChanged()
{
- cnote << "Post state changed.";
+ clog(ClientTrace) << "Post state changed.";
rejigMining();
m_remoteWorking = false;
}
@@ -789,7 +802,7 @@ void Client::rejigMining()
{
if ((wouldMine() || remoteActive()) && !isMajorSyncing() && (!isChainBad() || mineOnBadChain()) /*&& (forceMining() || transactionsWaiting())*/)
{
- cnote << "Rejigging mining...";
+ clog(ClientTrace) << "Rejigging mining...";
DEV_WRITE_GUARDED(x_working)
m_working.commitToMine(m_bc);
DEV_READ_GUARDED(x_working)
@@ -886,7 +899,7 @@ void Client::checkWatchGarbage()
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::system_clock::now() - m_watches[key].lastPoll).count() << "s old)";
+ clog(ClientTrace) << "GC: Uninstall" << key << "(" << chrono::duration_cast(chrono::system_clock::now() - m_watches[key].lastPoll).count() << "s old)";
}
for (auto i: toUninstall)
uninstallWatch(i);
diff --git a/libethereum/Client.h b/libethereum/Client.h
index 596c382bd..aa118054f 100644
--- a/libethereum/Client.h
+++ b/libethereum/Client.h
@@ -138,6 +138,9 @@ public:
/// Blocks until all pending transactions have been processed.
virtual void flushTransactions() override;
+ /// Queues a block for import.
+ ImportResult queueBlock(bytes const& _block, bool _isSafe = false);
+
using Interface::call; // to remove warning about hiding virtual function
/// Makes the given call. Nothing is recorded into the state. This cheats by creating a null address and endowing it with a lot of ETH.
ExecutionResult call(Address _dest, bytes const& _data = bytes(), u256 _gas = 125000, u256 _value = 0, u256 _gasPrice = 1 * ether, Address const& _from = Address());
@@ -160,6 +163,11 @@ public:
SyncStatus syncStatus() const;
/// Get the block queue.
BlockQueue const& blockQueue() const { return m_bq; }
+ /// Get the block queue.
+ OverlayDB const& stateDB() const { return m_stateDB; }
+
+ /// Freeze worker thread and sync some of the block queue.
+ std::tuple syncQueue(unsigned _max = 1);
// Mining stuff:
diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp
index 5f10267a6..4f50234e6 100644
--- a/libethereum/ClientBase.cpp
+++ b/libethereum/ClientBase.cpp
@@ -56,6 +56,19 @@ void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, b
cnote << "New transaction " << t;
}
+Address ClientBase::submitTransaction(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce)
+{
+ prepareForTransaction();
+
+ Transaction t(_value, _gasPrice, _gas, _data, _nonce, _secret);
+ m_tq.import(t.rlp());
+
+ StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
+ cnote << "New transaction " << t;
+
+ return right160(sha3(rlpList(t.sender(), t.nonce())));
+}
+
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
auto a = toAddress(_secret);
diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h
index e46bfe24d..aac10ae60 100644
--- a/libethereum/ClientBase.h
+++ b/libethereum/ClientBase.h
@@ -81,6 +81,7 @@ public:
/// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through).
+ virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, u256 _nonce);
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override;
using Interface::submitTransaction;
diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp
index afe12639a..9980f4339 100644
--- a/libethereum/EthereumHost.cpp
+++ b/libethereum/EthereumHost.cpp
@@ -166,20 +166,21 @@ tuple>, vector>, vector
vector> allowed;
vector> sessions;
- auto const& ps = peerSessions();
- allowed.reserve(ps.size());
- for (auto const& j: ps)
+ size_t peerCount = 0;
+ foreachPeer([&](std::shared_ptr _p)
{
- auto pp = j.first->cap();
- if (_allow(pp.get()))
+ if (_allow(_p.get()))
{
- allowed.push_back(move(pp));
- sessions.push_back(move(j.first));
+ allowed.push_back(_p);
+ sessions.push_back(_p->session());
}
- }
+ ++peerCount;
+ return true;
+ });
- chosen.reserve((ps.size() * _percent + 99) / 100);
- for (unsigned i = (ps.size() * _percent + 99) / 100; i-- && allowed.size();)
+ size_t chosenSize = (peerCount * _percent + 99) / 100;
+ chosen.reserve(chosenSize);
+ for (unsigned i = chosenSize; i && allowed.size(); i--)
{
unsigned n = rand() % allowed.size();
chosen.push_back(std::move(allowed[n]));
diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp
index 9bf0cc74f..02e263ab1 100644
--- a/libethereum/Executive.cpp
+++ b/libethereum/Executive.cpp
@@ -316,7 +316,7 @@ bool Executive::go(OnOpFunc const& _onOp)
if (m_ext)
{
#if ETH_TIMED_EXECUTIONS
- boost::timer t;
+ Timer t;
#endif
try
{
diff --git a/libethereum/State.cpp b/libethereum/State.cpp
index 37978bedb..ead72f496 100644
--- a/libethereum/State.cpp
+++ b/libethereum/State.cpp
@@ -139,7 +139,7 @@ PopulationStatistics State::populateFromChain(BlockChain const& _bc, h256 const&
// 2. Enact the block's transactions onto this state.
m_ourAddress = bi.coinbaseAddress;
- boost::timer t;
+ Timer t;
auto vb = BlockChain::verifyBlock(b);
ret.verify = t.elapsed();
t.restart();
@@ -401,7 +401,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi, Impor
u256 State::enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportRequirements::value _ir)
{
#if ETH_TIMED_ENACTMENTS
- boost::timer t;
+ Timer t;
double populateVerify;
double populateGrand;
double syncReset;
@@ -507,7 +507,7 @@ pair State::sync(BlockChain const& _bc, TransactionQu
{
if (i.second.gasPrice() >= _gp.ask(*this))
{
- // boost::timer t;
+ // Timer t;
if (lh.empty())
lh = _bc.lastHashes();
execute(lh, i.second);
@@ -531,8 +531,20 @@ pair State::sync(BlockChain const& _bc, TransactionQu
if (req > got)
{
// too old
- cnote << i.first << "Dropping old transaction (nonce too low)";
- _tq.drop(i.first);
+ for (Transaction const& t: m_transactions)
+ if (t.from() == i.second.from())
+ {
+ if (t.nonce() < i.second.nonce())
+ {
+ cnote << i.first << "Dropping old transaction (nonce too low)";
+ _tq.drop(i.first);
+ }
+ else if (t.nonce() == i.second.nonce() && t.gasPrice() <= i.second.gasPrice())
+ {
+ cnote << i.first << "Dropping old transaction (gas price lower)";
+ _tq.drop(i.first);
+ }
+ }
}
else if (got > req + _tq.waiting(i.second.sender()))
{
@@ -634,7 +646,7 @@ u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportR
// cnote << m_state;
LastHashes lh;
- DEV_TIMED_ABOVE(lastHashes, 500)
+ DEV_TIMED_ABOVE("lastHashes", 500)
lh = _bc.lastHashes((unsigned)m_previousBlock.number);
RLP rlp(_block.block);
@@ -643,7 +655,7 @@ u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportR
// All ok with the block generally. Play back the transactions now...
unsigned i = 0;
- DEV_TIMED_ABOVE(txEcec, 500)
+ DEV_TIMED_ABOVE("txExec", 500)
for (auto const& tr: _block.transactions)
{
try
@@ -664,7 +676,7 @@ u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportR
}
h256 receiptsRoot;
- DEV_TIMED_ABOVE(receiptsRoot, 500)
+ DEV_TIMED_ABOVE("receiptsRoot", 500)
receiptsRoot = orderedTrieRoot(receipts);
if (receiptsRoot != m_currentBlock.receiptsRoot)
@@ -698,12 +710,12 @@ u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportR
vector rewarded;
h256Hash excluded;
- DEV_TIMED_ABOVE(allKin, 500)
+ DEV_TIMED_ABOVE("allKin", 500)
excluded = _bc.allKinFrom(m_currentBlock.parentHash, 6);
excluded.insert(m_currentBlock.hash());
unsigned ii = 0;
- DEV_TIMED_ABOVE(uncleCheck, 500)
+ DEV_TIMED_ABOVE("uncleCheck", 500)
for (auto const& i: rlp[2])
{
try
@@ -752,11 +764,11 @@ u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportR
}
}
- DEV_TIMED_ABOVE(applyRewards, 500)
+ DEV_TIMED_ABOVE("applyRewards", 500)
applyRewards(rewarded);
// Commit all cached state changes to the state trie.
- DEV_TIMED_ABOVE(commit, 500)
+ DEV_TIMED_ABOVE("commit", 500)
commit();
// Hash the state trie and check against the state_root hash in m_currentBlock.
diff --git a/libethereum/State.h b/libethereum/State.h
index 93a4f4ade..6ad8fed09 100644
--- a/libethereum/State.h
+++ b/libethereum/State.h
@@ -58,6 +58,8 @@ using errinfo_transactionIndex = boost::error_info;
using errinfo_receipts = boost::error_info>;
+using errinfo_transaction = boost::error_info;
+using errinfo_phase = boost::error_info;
using errinfo_required_LogBloom = boost::error_info;
using errinfo_got_LogBloom = boost::error_info;
using LogBloomRequirementError = boost::tuple;
diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp
index 58ecc44fa..40a7914d3 100644
--- a/libethereum/Transaction.cpp
+++ b/libethereum/Transaction.cpp
@@ -77,6 +77,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, TransactionException cons
{
case TransactionException::None: _out << "None"; break;
case TransactionException::BadRLP: _out << "BadRLP"; break;
+ case TransactionException::InvalidFormat: _out << "InvalidFormat"; break;
case TransactionException::OutOfGasIntrinsic: _out << "OutOfGasIntrinsic"; break;
case TransactionException::InvalidSignature: _out << "InvalidSignature"; break;
case TransactionException::InvalidNonce: _out << "InvalidNonce"; break;
@@ -100,7 +101,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
try
{
if (!rlp.isList())
- BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("transaction RLP must be a list"));
+ BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction RLP must be a list"));
m_nonce = rlp[field = 0].toInt();
m_gasPrice = rlp[field = 1].toInt();
@@ -110,7 +111,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
m_value = rlp[field = 4].toInt();
if (!rlp[field = 5].isData())
- BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("transaction data RLP must be an array"));
+ BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction data RLP must be an array"));
m_data = rlp[field = 5].toBytes();
byte v = rlp[field = 6].toInt() - 27;
@@ -118,7 +119,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
h256 s = rlp[field = 8].toInt();
if (rlp.itemCount() > 9)
- BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("to many fields in the transaction RLP"));
+ BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("to many fields in the transaction RLP"));
m_vrs = SignatureStruct{ r, s, v };
if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid())
diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h
index e9b1cbf80..4de9d7e92 100644
--- a/libethereum/Transaction.h
+++ b/libethereum/Transaction.h
@@ -50,6 +50,7 @@ enum class TransactionException
None = 0,
Unknown,
BadRLP,
+ InvalidFormat,
OutOfGasIntrinsic, ///< Too little gas to pay for the base transaction cost.
InvalidSignature,
InvalidNonce,
diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp
index b4fa215ad..a86f6abf3 100644
--- a/libethereum/TransactionQueue.cpp
+++ b/libethereum/TransactionQueue.cpp
@@ -39,20 +39,22 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallb
Transaction t;
ImportResult ir;
{
- UpgradableGuard l(m_lock);
+ UpgradableGuard l(m_lock);
- ir = check_WITH_LOCK(h, _ik);
- if (ir != ImportResult::Success)
- return ir;
+ ir = check_WITH_LOCK(h, _ik);
+ if (ir != ImportResult::Success)
+ return ir;
- try {
- t = Transaction(_transactionRLP, CheckTransaction::Everything);
- UpgradeGuard ul(l);
- ir = manageImport_WITH_LOCK(h, t, _cb);
- }
- catch (...) {
- return ImportResult::Malformed;
- }
+ try
+ {
+ t = Transaction(_transactionRLP, CheckTransaction::Everything);
+ UpgradeGuard ul(l);
+ ir = manageImport_WITH_LOCK(h, t, _cb);
+ }
+ catch (...)
+ {
+ return ImportResult::Malformed;
+ }
}
// cdebug << "import-END: Nonce of" << t.sender() << "now" << maxNonce(t.sender());
return ir;
diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp
index 34ee05966..c96b6f40d 100644
--- a/libevmasm/Assembly.cpp
+++ b/libevmasm/Assembly.cpp
@@ -41,7 +41,7 @@ void Assembly::append(Assembly const& _a)
if (i.type() == Tag || i.type() == PushTag)
i.setData(i.data() + m_usedTags);
else if (i.type() == PushSub || i.type() == PushSubSize)
- i.setData(i.data() + m_usedTags);
+ i.setData(i.data() + m_subs.size());
append(i);
}
m_deposit = newDeposit;
@@ -136,10 +136,10 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con
_out << " PUSH [tag" << dec << i.data() << "]";
break;
case PushSub:
- _out << " PUSH [$" << h256(i.data()).abridged() << "]";
+ _out << " PUSH [$" << h256(i.data()).abridgedMiddle() << "]";
break;
case PushSubSize:
- _out << " PUSH #[$" << h256(i.data()).abridged() << "]";
+ _out << " PUSH #[$" << h256(i.data()).abridgedMiddle() << "]";
break;
case PushProgramSize:
_out << " PUSHSIZE";
diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp
index a0c5e19a6..e005ece18 100644
--- a/libevmasm/AssemblyItem.cpp
+++ b/libevmasm/AssemblyItem.cpp
@@ -110,10 +110,10 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
_out << " PushData " << hex << (unsigned)_item.data();
break;
case PushSub:
- _out << " PushSub " << hex << h256(_item.data()).abridged();
+ _out << " PushSub " << hex << h256(_item.data()).abridgedMiddle();
break;
case PushSubSize:
- _out << " PushSubSize " << hex << h256(_item.data()).abridged();
+ _out << " PushSubSize " << hex << h256(_item.data()).abridgedMiddle();
break;
case PushProgramSize:
_out << " PushProgramSize";
diff --git a/libjsconsole/CMakeLists.txt b/libjsconsole/CMakeLists.txt
index e8f98de88..761435fe1 100644
--- a/libjsconsole/CMakeLists.txt
+++ b/libjsconsole/CMakeLists.txt
@@ -14,6 +14,7 @@ include_directories(BEFORE ${V8_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${READLINE_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
+include_directories(${CURL_INCLUDE_DIRS})
set(EXECUTABLE jsconsole)
@@ -24,7 +25,12 @@ add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} jsengine)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
-target_link_libraries(${EXECUTABLE} web3jsonrpc)
+target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_SERVER_LIBRARIES})
+
+target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
+if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
+ eth_copy_dlls(${EXECUTABLE} CURL_DLLS)
+endif()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
diff --git a/libjsconsole/CURLRequest.cpp b/libjsconsole/CURLRequest.cpp
new file mode 100644
index 000000000..c07059372
--- /dev/null
+++ b/libjsconsole/CURLRequest.cpp
@@ -0,0 +1,66 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file CURLRequest.cpp
+ * @author Marek Kotewicz
+ * @date 2015
+ * Ethereum client.
+ */
+
+#include "CURLRequest.h"
+
+using namespace std;
+
+static size_t write_data(void *buffer, size_t elementSize, size_t numberOfElements, void *userp)
+{
+ static_cast(userp)->write((const char *)buffer, elementSize * numberOfElements);
+ return elementSize * numberOfElements;
+}
+
+void CURLRequest::commonCURLPreparation()
+{
+ m_resultBuffer.str("");
+ curl_easy_setopt(m_curl, CURLOPT_URL, (m_url + "?").c_str());
+ curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, write_data);
+ curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &m_resultBuffer);
+}
+
+std::tuple CURLRequest::commonCURLPerform()
+{
+ CURLcode res = curl_easy_perform(m_curl);
+ if (res != CURLE_OK) {
+ throw runtime_error(curl_easy_strerror(res));
+ }
+ long httpCode = 0;
+ curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &httpCode);
+ return make_tuple(httpCode, m_resultBuffer.str());
+}
+
+std::tuple CURLRequest::post()
+{
+ commonCURLPreparation();
+ curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_body.c_str());
+
+ struct curl_slist *headerList = NULL;
+ headerList = curl_slist_append(headerList, "Content-Type: application/json");
+ curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headerList);
+
+ auto result = commonCURLPerform();
+
+ curl_slist_free_all(headerList);
+ return result;
+}
diff --git a/libjsconsole/CURLRequest.h b/libjsconsole/CURLRequest.h
new file mode 100644
index 000000000..e025d1eb9
--- /dev/null
+++ b/libjsconsole/CURLRequest.h
@@ -0,0 +1,58 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file CURLRequest.h
+ * @author Marek Kotewicz
+ * @date 2015
+ * Ethereum client.
+ */
+
+// based on http://stackoverflow.com/questions/1011339/how-do-you-make-a-http-request-with-c/27026683#27026683
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+class CURLRequest
+{
+public:
+ CURLRequest(): m_curl(curl_easy_init()) {}
+ ~CURLRequest()
+ {
+ if (m_curl)
+ curl_easy_cleanup(m_curl);
+ }
+
+ void setUrl(std::string _url) { m_url = _url; }
+ void setBody(std::string _body) { m_body = _body; }
+
+ std::tuple post();
+
+private:
+ std::string m_url;
+ std::string m_body;
+
+ CURL* m_curl;
+ std::stringstream m_resultBuffer;
+
+ void commonCURLPreparation();
+ std::tuple commonCURLPerform();
+};
+
+
diff --git a/libjsconsole/JSConsole.cpp b/libjsconsole/JSConsole.cpp
index 29d547242..61376de79 100644
--- a/libjsconsole/JSConsole.cpp
+++ b/libjsconsole/JSConsole.cpp
@@ -20,65 +20,5 @@
* Ethereum client.
*/
-#include
-#include
-#include
-#include "JSConsole.h"
-#include "JSV8Connector.h"
-
-// TODO! make readline optional!
-#include
-#include
-
-using namespace std;
-using namespace dev;
-using namespace dev::eth;
-
-JSConsole::JSConsole(WebThreeDirect& _web3, shared_ptr const& _accounts):
- m_engine(),
- m_printer(m_engine)
-{
- m_jsonrpcConnector.reset(new JSV8Connector(m_engine));
- (void)_web3; (void)_accounts;
-// m_jsonrpcServer.reset(new WebThreeStubServer(*m_jsonrpcConnector.get(), _web3, _accounts, vector()));
-}
-
-void JSConsole::readExpression() const
-{
- string cmd = "";
- g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << flush; rl_forced_update_display(); };
- bool isEmpty = true;
- int openBrackets = 0;
- do {
- char* buff = readline(promptForIndentionLevel(openBrackets).c_str());
- isEmpty = !(buff && *buff);
- if (!isEmpty)
- {
- cmd += string(buff);
- cmd += " ";
- free(buff);
- int open = count(cmd.begin(), cmd.end(), '{');
- open += count(cmd.begin(), cmd.end(), '(');
- int closed = count(cmd.begin(), cmd.end(), '}');
- closed += count(cmd.begin(), cmd.end(), ')');
- openBrackets = open - closed;
- }
- } while (openBrackets > 0);
-
- if (!isEmpty)
- {
- add_history(cmd.c_str());
- auto value = m_engine.eval(cmd.c_str());
- string result = m_printer.prettyPrint(value).cstr();
- cout << result << endl;
- }
-}
-
-std::string JSConsole::promptForIndentionLevel(int _i) const
-{
- if (_i == 0)
- return "> ";
-
- return string((_i + 1) * 2, ' ');
-}
+#include "JSConsole.h"
diff --git a/libjsconsole/JSConsole.h b/libjsconsole/JSConsole.h
index 2e5144a5d..50f6d6ae5 100644
--- a/libjsconsole/JSConsole.h
+++ b/libjsconsole/JSConsole.h
@@ -22,32 +22,66 @@
#pragma once
-#include
-#include
-
-namespace dev { class WebThreeStubServer; }
-namespace jsonrpc { class AbstractServerConnector; }
+#include
+// TODO! make readline optional!
+#include
+#include
namespace dev
{
namespace eth
{
-class AccountHolder;
-
+template
class JSConsole
{
public:
- JSConsole(WebThreeDirect& _web3, std::shared_ptr const& _accounts);
- void readExpression() const;
+ JSConsole(): m_engine(Engine()), m_printer(Printer(m_engine)) {}
+ ~JSConsole() {}
+
+ void readExpression() const
+ {
+ std::string cmd = "";
+ g_logPost = [](std::string const& a, char const*) { std::cout << "\r \r" << a << std::endl << std::flush; rl_forced_update_display(); };
+
+ bool isEmpty = true;
+ int openBrackets = 0;
+ do {
+ char* buff = readline(promptForIndentionLevel(openBrackets).c_str());
+ isEmpty = !(buff && *buff);
+ if (!isEmpty)
+ {
+ cmd += std::string(buff);
+ cmd += " ";
+ free(buff);
+ int open = std::count(cmd.begin(), cmd.end(), '{');
+ open += std::count(cmd.begin(), cmd.end(), '(');
+ int closed = std::count(cmd.begin(), cmd.end(), '}');
+ closed += std::count(cmd.begin(), cmd.end(), ')');
+ openBrackets = open - closed;
+ }
+ } while (openBrackets > 0);
+
+ if (!isEmpty)
+ {
+ add_history(cmd.c_str());
+ auto value = m_engine.eval(cmd.c_str());
+ std::string result = m_printer.prettyPrint(value).cstr();
+ std::cout << result << std::endl;
+ }
+ }
+
+protected:
+ Engine m_engine;
+ Printer m_printer;
-private:
- std::string promptForIndentionLevel(int _i) const;
+ virtual std::string promptForIndentionLevel(int _i) const
+ {
+ if (_i == 0)
+ return "> ";
- JSV8Engine m_engine;
- JSV8Printer m_printer;
- std::unique_ptr m_jsonrpcServer;
- std::unique_ptr m_jsonrpcConnector;
+ return std::string((_i + 1) * 2, ' ');
+ }
};
}
diff --git a/libjsconsole/JSLocalConsole.cpp b/libjsconsole/JSLocalConsole.cpp
new file mode 100644
index 000000000..04c6104a6
--- /dev/null
+++ b/libjsconsole/JSLocalConsole.cpp
@@ -0,0 +1,34 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file JSLocalConsole.cpp
+ * @author Marek Kotewicz
+ * @date 2015
+ * Ethereum client.
+ */
+
+#include
+#include "JSLocalConsole.h"
+#include "JSV8Connector.h"
+
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+
+JSLocalConsole::JSLocalConsole()
+{
+ m_jsonrpcConnector.reset(new JSV8Connector(m_engine));
+}
diff --git a/libjsconsole/JSLocalConsole.h b/libjsconsole/JSLocalConsole.h
new file mode 100644
index 000000000..48922faee
--- /dev/null
+++ b/libjsconsole/JSLocalConsole.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file JSLocalConsole.h
+ * @author Marek Kotewicz
+ * @date 2015
+ * Ethereum client.
+ */
+
+#pragma once
+
+#include
+#include
+#include "JSConsole.h"
+
+class WebThreeStubServer;
+namespace jsonrpc { class AbstractServerConnector; }
+
+namespace dev
+{
+namespace eth
+{
+
+class JSLocalConsole: public JSConsole
+{
+public:
+ JSLocalConsole();
+ virtual ~JSLocalConsole() {}
+
+ jsonrpc::AbstractServerConnector* connector() { return m_jsonrpcConnector.get(); }
+
+private:
+ std::unique_ptr m_jsonrpcConnector;
+};
+
+}
+}
diff --git a/libjsconsole/JSRemoteConsole.cpp b/libjsconsole/JSRemoteConsole.cpp
new file mode 100644
index 000000000..b42c5b340
--- /dev/null
+++ b/libjsconsole/JSRemoteConsole.cpp
@@ -0,0 +1,23 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file JSRemoteConsole.cpp
+ * @author Marek Kotewicz
+ * @date 2015
+ * Ethereum client.
+ */
+
+#include "JSRemoteConsole.h"
diff --git a/libjsconsole/JSRemoteConsole.h b/libjsconsole/JSRemoteConsole.h
new file mode 100644
index 000000000..2baf516f6
--- /dev/null
+++ b/libjsconsole/JSRemoteConsole.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file JSRemoteConsole.h
+ * @author Marek Kotewicz
+ * @date 2015
+ * Ethereum client.
+ */
+
+#pragma once
+
+#include
+#include
+#include "JSV8RemoteConnector.h"
+#include "JSConsole.h"
+
+namespace dev
+{
+namespace eth
+{
+
+class JSRemoteConsole: public JSConsole
+{
+
+public:
+ JSRemoteConsole(std::string _url): m_connector(m_engine, _url) {}
+ virtual ~JSRemoteConsole() {}
+
+private:
+ JSV8RemoteConnector m_connector;
+
+};
+
+}
+}
diff --git a/libjsconsole/JSV8Connector.h b/libjsconsole/JSV8Connector.h
index 98cef4c2c..34c38fed1 100644
--- a/libjsconsole/JSV8Connector.h
+++ b/libjsconsole/JSV8Connector.h
@@ -43,7 +43,7 @@ public:
bool SendResponse(std::string const& _response, void* _addInfo = nullptr);
// implement JSV8RPC interface
- void onSend(char const* payload);
+ void onSend(char const* _payload);
};
}
diff --git a/libjsconsole/JSV8RemoteConnector.cpp b/libjsconsole/JSV8RemoteConnector.cpp
new file mode 100644
index 000000000..72e64faae
--- /dev/null
+++ b/libjsconsole/JSV8RemoteConnector.cpp
@@ -0,0 +1,20 @@
+//
+// Created by Marek Kotewicz on 15/06/15.
+//
+
+#include "JSV8RemoteConnector.h"
+
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+
+void JSV8RemoteConnector::onSend(char const* _payload)
+{
+ m_request.setUrl(m_url);
+ m_request.setBody(_payload);
+ long code;
+ string response;
+ tie(code, response) = m_request.post();
+ (void)code;
+ m_lastResponse = response.c_str();
+}
diff --git a/libjsconsole/JSV8RemoteConnector.h b/libjsconsole/JSV8RemoteConnector.h
new file mode 100644
index 000000000..5d28094ad
--- /dev/null
+++ b/libjsconsole/JSV8RemoteConnector.h
@@ -0,0 +1,32 @@
+//
+// Created by Marek Kotewicz on 15/06/15.
+//
+
+#pragma once
+
+#include
+#include
+#include "CURLRequest.h"
+
+namespace dev
+{
+namespace eth
+{
+
+class JSV8RemoteConnector : public JSV8RPC
+{
+
+public:
+ JSV8RemoteConnector(JSV8Engine const& _engine, std::string _url): JSV8RPC(_engine), m_url(_url) {}
+ virtual ~JSV8RemoteConnector() {}
+
+ // implement JSV8RPC interface
+ void onSend(char const* _payload);
+
+private:
+ std::string m_url;
+ CURLRequest m_request;
+};
+
+}
+}
diff --git a/libjsengine/JSResources.cmake b/libjsengine/JSResources.cmake
index d4370a8da..15e788778 100644
--- a/libjsengine/JSResources.cmake
+++ b/libjsengine/JSResources.cmake
@@ -1,8 +1,9 @@
set(web3 "${CMAKE_CURRENT_LIST_DIR}/../libjsqrc/ethereumjs/dist/web3.js")
+set(admin "${CMAKE_CURRENT_LIST_DIR}/../libjsqrc/admin.js")
set(pretty_print "${CMAKE_CURRENT_LIST_DIR}/PrettyPrint.js")
set(common "${CMAKE_CURRENT_LIST_DIR}/Common.js")
set(ETH_RESOURCE_NAME "JSEngineResources")
set(ETH_RESOURCE_LOCATION "${CMAKE_CURRENT_BINARY_DIR}")
-set(ETH_RESOURCES "web3" "pretty_print" "common")
+set(ETH_RESOURCES "web3" "pretty_print" "common" "admin")
diff --git a/libjsengine/JSV8Engine.cpp b/libjsengine/JSV8Engine.cpp
index 4e06f0f65..ebf0a0e72 100644
--- a/libjsengine/JSV8Engine.cpp
+++ b/libjsengine/JSV8Engine.cpp
@@ -143,9 +143,11 @@ JSV8Engine::JSV8Engine(): m_scope(new JSV8Scope())
JSEngineResources resources;
string common = resources.loadResourceAsString("common");
string web3 = resources.loadResourceAsString("web3");
+ string admin = resources.loadResourceAsString("admin");
eval(common.c_str());
eval(web3.c_str());
eval("web3 = require('web3');");
+ eval(admin.c_str());
}
JSV8Engine::~JSV8Engine()
diff --git a/libjsengine/JSV8Engine.h b/libjsengine/JSV8Engine.h
index 56459c5d0..563642d73 100644
--- a/libjsengine/JSV8Engine.h
+++ b/libjsengine/JSV8Engine.h
@@ -22,7 +22,10 @@
#pragma once
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
#include
+#pragma clang diagnostic pop
#include "JSEngine.h"
namespace dev
diff --git a/libsolidity/ArrayUtils.cpp b/libsolidity/ArrayUtils.cpp
index e138e9519..3be12af72 100644
--- a/libsolidity/ArrayUtils.cpp
+++ b/libsolidity/ArrayUtils.cpp
@@ -231,6 +231,181 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
m_context << u256(0);
}
+void ArrayUtils::copyArrayToMemory(const ArrayType& _sourceType, bool _padToWordBoundaries) const
+{
+ solAssert(
+ _sourceType.getBaseType()->getCalldataEncodedSize() > 0,
+ "Nested arrays not yet implemented here."
+ );
+ unsigned baseSize = 1;
+ if (!_sourceType.isByteArray())
+ // We always pad the elements, regardless of _padToWordBoundaries.
+ baseSize = _sourceType.getBaseType()->getCalldataEncodedSize();
+
+ if (_sourceType.location() == DataLocation::CallData)
+ {
+ if (!_sourceType.isDynamicallySized())
+ m_context << _sourceType.getLength();
+ if (_sourceType.getBaseType()->getCalldataEncodedSize() > 1)
+ m_context << u256(baseSize) << eth::Instruction::MUL;
+ // stack: target source_offset source_len
+ m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5;
+ // stack: target source_offset source_len source_len source_offset target
+ m_context << eth::Instruction::CALLDATACOPY;
+ m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP;
+ }
+ else if (_sourceType.location() == DataLocation::Memory)
+ {
+ // memcpy using the built-in contract
+ retrieveLength(_sourceType);
+ if (_sourceType.isDynamicallySized())
+ {
+ // change pointer to data part
+ m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP1;
+ }
+ // convert length to size
+ if (baseSize > 1)
+ m_context << u256(baseSize) << eth::Instruction::MUL;
+ // stack: