Browse Source

Merge branch 'develop' into client_ref

Conflicts:
	libethereum/Client.cpp
cl-refactor
Marek Kotewicz 10 years ago
parent
commit
af008ef54a
  1. 16
      CMakeLists.txt
  2. 3
      CodingStandards.txt
  3. 5
      alethzero/CMakeLists.txt
  4. 198
      alethzero/DownloadView.cpp
  5. 14
      alethzero/DownloadView.h
  6. 18
      alethzero/ExportState.cpp
  7. 76
      alethzero/Main.ui
  8. 85
      alethzero/MainWin.cpp
  9. 11
      alethzero/MainWin.h
  10. 5
      alethzero/OurWebThreeStubServer.cpp
  11. 2
      alethzero/OurWebThreeStubServer.h
  12. 5
      cmake/EthCompilerSettings.cmake
  13. 28
      cmake/Findv8.cmake
  14. 2698
      eth/main.cpp
  15. 2
      ethconsole/CMakeLists.txt
  16. 171
      ethkey/KeyAux.h
  17. 4
      ethminer/CMakeLists.txt
  18. 77
      ethminer/MinerAux.h
  19. 22
      ethvm/main.cpp
  20. 4
      evmjit/CMakeLists.txt
  21. 18
      evmjit/evmcc/CMakeLists.txt
  22. 210
      evmjit/evmcc/evmcc.cpp
  23. 56
      evmjit/include/evmjit/DataTypes.h
  24. 162
      evmjit/include/evmjit/JIT.h
  25. 17
      evmjit/libevmjit-cpp/Env.cpp
  26. 41
      evmjit/libevmjit-cpp/JitVM.cpp
  27. 6
      evmjit/libevmjit-cpp/JitVM.h
  28. 29
      evmjit/libevmjit-cpp/Utils.h
  29. 674
      evmjit/libevmjit/Arith256.cpp
  30. 27
      evmjit/libevmjit/Arith256.h
  31. 3
      evmjit/libevmjit/Array.cpp
  32. 2
      evmjit/libevmjit/BasicBlock.h
  33. 8
      evmjit/libevmjit/CMakeLists.txt
  34. 27
      evmjit/libevmjit/Cache.cpp
  35. 9
      evmjit/libevmjit/Cache.h
  36. 39
      evmjit/libevmjit/Common.h
  37. 152
      evmjit/libevmjit/Compiler.cpp
  38. 4
      evmjit/libevmjit/Compiler.h
  39. 25
      evmjit/libevmjit/CompilerHelper.h
  40. 5
      evmjit/libevmjit/ExecStats.cpp
  41. 35
      evmjit/libevmjit/ExecStats.h
  42. 192
      evmjit/libevmjit/ExecutionEngine.cpp
  43. 59
      evmjit/libevmjit/ExecutionEngine.h
  44. 24
      evmjit/libevmjit/Ext.cpp
  45. 2
      evmjit/libevmjit/Ext.h
  46. 1
      evmjit/libevmjit/GasMeter.h
  47. 5
      evmjit/libevmjit/Instruction.cpp
  48. 5
      evmjit/libevmjit/Instruction.h
  49. 234
      evmjit/libevmjit/JIT.cpp
  50. 6
      evmjit/libevmjit/Memory.cpp
  51. 97
      evmjit/libevmjit/Optimizer.cpp
  52. 2
      evmjit/libevmjit/Optimizer.h
  53. 43
      evmjit/libevmjit/Runtime.cpp
  54. 30
      evmjit/libevmjit/Runtime.h
  55. 63
      evmjit/libevmjit/RuntimeData.h
  56. 56
      evmjit/libevmjit/RuntimeManager.cpp
  57. 6
      evmjit/libevmjit/RuntimeManager.h
  58. 120
      evmjit/libevmjit/Stack.cpp
  59. 8
      evmjit/libevmjit/Stack.h
  60. 3
      evmjit/libevmjit/Type.cpp
  61. 9
      evmjit/libevmjit/Type.h
  62. 25
      evmjit/libevmjit/interface.cpp
  63. 12
      exp/CMakeLists.txt
  64. 2
      exp/main.cpp
  65. 1
      extdep/getstuff.bat
  66. 43
      libdevcore/Base64.cpp
  67. 11
      libdevcore/Common.cpp
  68. 20
      libdevcore/Common.h
  69. 99
      libdevcore/CommonData.h
  70. 29
      libdevcore/FixedHash.h
  71. 2
      libdevcore/Guards.h
  72. 84
      libdevcore/RLP.cpp
  73. 68
      libdevcore/RLP.h
  74. 160
      libdevcore/RangeMask.h
  75. 22
      libdevcore/TrieCommon.h
  76. 162
      libdevcore/TrieDB.h
  77. 42
      libdevcrypto/Common.cpp
  78. 5
      libdevcrypto/Common.h
  79. 2
      libdevcrypto/CryptoPP.cpp
  80. 1
      libethash-cl/CMakeLists.txt
  81. 76
      libethash-cl/ethash_cl_miner.cpp
  82. 31
      libethash-cl/ethash_cl_miner.h
  83. 2
      libethcore/CMakeLists.txt
  84. 24
      libethcore/Common.h
  85. 29
      libethcore/Ethash.cpp
  86. 7
      libethcore/Ethash.h
  87. 132
      libethcore/Transaction.cpp
  88. 179
      libethcore/Transaction.h
  89. 4
      libethereum/Account.h
  90. 95
      libethereum/BasicGasPricer.cpp
  91. 53
      libethereum/BasicGasPricer.h
  92. 186
      libethereum/BlockChain.cpp
  93. 13
      libethereum/BlockChain.h
  94. 522
      libethereum/BlockChainSync.cpp
  95. 59
      libethereum/BlockChainSync.h
  96. 4
      libethereum/BlockDetails.h
  97. 74
      libethereum/BlockQueue.cpp
  98. 13
      libethereum/BlockQueue.h
  99. 268
      libethereum/Client.cpp
  100. 40
      libethereum/Client.h

16
CMakeLists.txt

@ -203,7 +203,9 @@ eth_format_option(ROCKSDB)
eth_format_option(GUI) eth_format_option(GUI)
eth_format_option(TESTS) eth_format_option(TESTS)
eth_format_option(NOBOOST) eth_format_option(NOBOOST)
eth_format_option(ROCKSDB)
eth_format_option(TOOLS) eth_format_option(TOOLS)
eth_format_option(ETHKEY)
eth_format_option(ETHASHCL) eth_format_option(ETHASHCL)
eth_format_option(JSCONSOLE) eth_format_option(JSCONSOLE)
eth_format_option_on_decent_platform(SERPENT) eth_format_option_on_decent_platform(SERPENT)
@ -234,6 +236,15 @@ elseif (BUNDLE STREQUAL "full")
set(TOOLS ON) set(TOOLS ON)
set(TESTS ON) set(TESTS ON)
set(FATDB ON) set(FATDB ON)
elseif (BUNDLE STREQUAL "cli")
set(SERPENT ${DECENT_PLATFORM})
set(SOLIDITY ON)
set(USENPM ON)
set(GUI OFF)
# set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON)
set(TESTS ON)
set(FATDB ON)
elseif (BUNDLE STREQUAL "core") elseif (BUNDLE STREQUAL "core")
set(SERPENT OFF) set(SERPENT OFF)
set(SOLIDITY ON) set(SOLIDITY ON)
@ -312,6 +323,7 @@ message("-- Hardware identification support ${CPUID_FO
message("-- HTTP Request support ${CURL_FOUND}") message("-- HTTP Request support ${CURL_FOUND}")
message("-- VMTRACE VM execution tracing ${VMTRACE}") message("-- VMTRACE VM execution tracing ${VMTRACE}")
message("-- PROFILING Profiling support ${PROFILING}") message("-- PROFILING Profiling support ${PROFILING}")
message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}")
message("-- FATDB Full database exploring ${FATDB}") message("-- FATDB Full database exploring ${FATDB}")
message("-- JSONRPC JSON-RPC support ${JSONRPC}") message("-- JSONRPC JSON-RPC support ${JSONRPC}")
message("-- USENPM Javascript source building ${USENPM}") message("-- USENPM Javascript source building ${USENPM}")
@ -325,7 +337,6 @@ message("-- SERPENT Build Serpent language components ${SERPENT}
message("-- GUI Build GUI components ${GUI}") message("-- GUI Build GUI components ${GUI}")
message("-- NCURSES Build NCurses components ${NCURSES}") message("-- NCURSES Build NCurses components ${NCURSES}")
message("-- TESTS Build tests ${TESTS}") message("-- TESTS Build tests ${TESTS}")
message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}")
message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}") message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}")
message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}") message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}")
message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}") message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}")
@ -362,8 +373,6 @@ else ()
set(GENERAL 0) set(GENERAL 0)
endif () endif ()
message("GENERAL ${GENERAL}")
add_subdirectory(libdevcore) add_subdirectory(libdevcore)
if (GENERAL) if (GENERAL)
add_subdirectory(libevmcore) add_subdirectory(libevmcore)
@ -398,6 +407,7 @@ if (JSCONSOLE)
endif () endif ()
if (NOT WIN32) if (NOT WIN32)
add_definitions(-DETH_HAVE_SECP256K1)
add_subdirectory(secp256k1) add_subdirectory(secp256k1)
endif () endif ()

3
CodingStandards.txt

@ -196,7 +196,7 @@ a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector<int>; rather than
b. Generally avoid shortening a standard form that already includes all important information: b. Generally avoid shortening a standard form that already includes all important information:
- e.g. stick to shared_ptr<X> rather than shortening to ptr<X>. - e.g. stick to shared_ptr<X> rather than shortening to ptr<X>.
c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently. c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently.
- e.g. using Guard = boost::lock_guard<std::mutex>; ///< Guard is used throughout the codebase since it's clear in meaning and used commonly. - e.g. using Guard = std::lock_guard<std::mutex>; ///< Guard is used throughout the codebase since it's clear in meaning and used commonly.
d. In general expressions should be roughly as important/semantically meaningful as the space they occupy. d. In general expressions should be roughly as important/semantically meaningful as the space they occupy.
@ -226,4 +226,3 @@ a. Includes should go in order of lower level (STL -> boost -> libdevcore -> lib
#include <libethereum/Defaults.h> #include <libethereum/Defaults.h>
b. The only exception to the above rule is the top of a .cpp file where its corresponding header should be located. b. The only exception to the above rule is the top of a .cpp file where its corresponding header should be located.

5
alethzero/CMakeLists.txt

@ -7,6 +7,11 @@ if (${CMAKE_MAJOR_VERSION} GREATER 2)
cmake_policy(SET CMP0043 OLD) cmake_policy(SET CMP0043 OLD)
endif() endif()
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "3.6") AND NOT APPLE)
# Supress warnings for qt headers for clang+ccache
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override")
endif ()
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)

198
alethzero/DownloadView.cpp

@ -30,56 +30,176 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
DownloadView::DownloadView(QWidget* _p): QWidget(_p) SyncView::SyncView(QWidget* _p): QWidget(_p)
{ {
} }
void DownloadView::paintEvent(QPaintEvent*) void SyncView::paintEvent(QPaintEvent*)
{ {
QPainter p(this); QPainter painter(this);
painter.fillRect(rect(), Qt::white);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::HighQualityAntialiasing, true);
p.fillRect(rect(), Qt::white); if (!m_client || !isVisible() || !rect().width() || !rect().height())
if (!m_man || m_man->chainEmpty() || !m_man->subCount())
return; return;
double ratio = (double)rect().width() / rect().height(); DownloadMan const* man = m_client->downloadMan();
if (ratio < 1) BlockQueueStatus bqs = m_client->blockQueueStatus();
ratio = 1 / ratio; SyncStatus sync = m_client->syncStatus();
double n = min(16.0, min(rect().width(), rect().height()) / ceil(sqrt(m_man->chainSize() / ratio)));
// QSizeF area(rect().width() / floor(rect().width() / n), rect().height() / floor(rect().height() / n)); unsigned syncFrom = m_client->numberFromHash(PendingBlockHash);
QSizeF area(n, n); unsigned syncImported = syncFrom;
QPointF pos(0, 0); unsigned syncImporting = syncImported + bqs.importing;
unsigned syncVerified = syncImporting + bqs.verified;
unsigned syncVerifying = syncVerified + bqs.verifying;
unsigned syncUnverified = syncVerifying + bqs.unverified;
unsigned syncCount = syncUnverified + bqs.unknown - syncFrom;
auto bg = m_man->blocksGot(); // best effort guess. assumes there's no forks.
unsigned subCount = m_man->subCount(); unsigned downloadFrom = sync.state == SyncState::Idle ? m_lastSyncFrom : m_client->numberFromHash(m_client->isKnown(man->firstBlock()) ? man->firstBlock() : PendingBlockHash);
if (subCount == 0) unsigned downloadCount = sync.state == SyncState::Idle ? m_lastSyncCount : sync.blocksTotal;
return; unsigned downloadDone = downloadFrom + (sync.state == SyncState::Idle ? m_lastSyncCount : sync.blocksReceived);
unsigned dh = 360 / subCount; unsigned downloadPoint = downloadFrom + (sync.state == SyncState::Idle ? m_lastSyncCount : man->overview().lastComplete);
for (unsigned i = bg.all().first, ei = bg.all().second; i < ei; ++i)
unsigned hashFrom = sync.state == SyncState::Hashes ? m_client->numberFromHash(PendingBlockHash) : downloadFrom;
unsigned hashCount = sync.state == SyncState::Hashes ? sync.hashesTotal : downloadCount;
unsigned hashDone = hashFrom + (sync.state == SyncState::Hashes ? sync.hashesReceived : hashCount);
QString labelText = QString("PV%1").arg(sync.protocolVersion);
QColor labelBack = QColor::fromHsv(sync.protocolVersion == 60 ? 30 : sync.protocolVersion == 61 ? 120 : 240, 25, 200);
QColor labelFore = labelBack.darker();
switch (sync.state)
{ {
int s = -2; case SyncState::Hashes:
if (bg.contains(i)) if (!syncCount || !sync.hashesEstimated)
s = -1; {
else m_lastSyncFrom = min(hashFrom, m_lastSyncFrom);
m_lastSyncCount = max(hashFrom + hashCount, m_lastSyncFrom + m_lastSyncCount) - m_lastSyncFrom;
m_wasEstimate = sync.hashesEstimated;
}
break;
case SyncState::Blocks:
if (m_wasEstimate)
{
m_lastSyncFrom = downloadFrom;
m_lastSyncCount = downloadCount;
m_wasEstimate = false;
}
break;
case SyncState::Idle:
if (!syncCount)
{ {
unsigned h = 0; m_lastSyncFrom = (unsigned)-1;
m_man->foreachSub([&](DownloadSub const& sub) m_lastSyncCount = 0;
{ labelBack = QColor::fromHsv(0, 0, 200);
if (sub.askedContains(i)) labelFore = Qt::white;
s = h; labelText = "Idle";
h++;
});
} }
if (s == -2) default: break;
p.fillRect(QRectF(QPointF(pos) + QPointF(3 * area.width() / 8, 3 * area.height() / 8), area / 4), Qt::black); }
else if (s == -1)
p.fillRect(QRectF(QPointF(pos) + QPointF(1 * area.width() / 8, 1 * area.height() / 8), area * 3 / 4), Qt::black); unsigned from = min(min(hashFrom, downloadFrom), min(syncFrom, m_lastSyncFrom));
else unsigned count = max(max(hashFrom + hashCount, downloadFrom + downloadCount), max(syncFrom + syncCount, m_lastSyncFrom + m_lastSyncCount)) - from;
p.fillRect(QRectF(QPointF(pos) + QPointF(1 * area.width() / 8, 1 * area.height() / 8), area * 3 / 4), QColor::fromHsv(s * dh, 64, 128));
/* cnote << "Range " << from << "-" << (from + count) << "(" << hashFrom << "+" << hashCount << "," << downloadFrom << "+" << downloadCount << "," << syncFrom << "+" << syncCount << ")";
pos.setX(pos.x() + n); auto r = [&](unsigned u) {
if (pos.x() >= rect().width() - n) return toString((u - from) * 100 / count) + "%";
pos = QPoint(0, pos.y() + n); };
if (count)
{
cnote << "Hashes:" << r(hashDone) << " Blocks:" << r(downloadFlank) << r(downloadDone) << r(downloadPoint);
cnote << "Importing:" << r(syncFrom) << r(syncImported) << r(syncImporting) << r(syncVerified) << r(syncVerifying) << r(syncUnverified);
}
*/
float const squareSize = min(rect().width(), rect().height());
auto middleRect = [&](float w, float h) {
return QRectF(rect().width() / 2 - w / 2, rect().height() / 2 - h / 2, w, h);
};
auto middle = [&](float x) {
return middleRect(squareSize * x, squareSize * x);
};
auto pieProgress = [&](unsigned h, unsigned s, unsigned v, float row, float thickness, unsigned nfrom, unsigned ncount) {
auto arcLen = [&](unsigned x) {
return x * -5760.f / count;
};
auto arcPos = [&](unsigned x) {
return int(90 * 16.f + arcLen(x - from)) % 5760;
};
painter.setPen(QPen(QColor::fromHsv(h, s, v), squareSize * thickness, Qt::SolidLine, Qt::FlatCap));
painter.setBrush(Qt::NoBrush);
painter.drawArc(middle(0.5 + row / 2), arcPos(nfrom), arcLen(ncount));
};
auto pieProgress2 = [&](unsigned h, unsigned s, unsigned v, float row, float orbit, float thickness, unsigned nfrom, unsigned ncount) {
pieProgress(h, s, v, row - orbit, thickness, nfrom, ncount);
pieProgress(h, s, v, row + orbit, thickness, nfrom, ncount);
};
auto pieLabel = [&](QString text, float points, QColor fore, QColor back) {
painter.setBrush(QBrush(back));
painter.setFont(QFont("Helvetica", points, QFont::Bold));
QRectF r = painter.boundingRect(middle(1.f), Qt::AlignCenter, text);
r.adjust(-r.width() / 4, -r.height() / 8, r.width() / 4, r.height() / 8);
painter.setPen(QPen(fore, r.height() / 20));
painter.drawRoundedRect(r, r.height() / 4, r.height() / 4);
painter.drawText(r, Qt::AlignCenter, text);
};
float lineHeight = painter.boundingRect(rect(), Qt::AlignTop | Qt::AlignHCenter, "Ay").height();
auto hProgress = [&](unsigned h, unsigned s, unsigned v, float row, float thickness, unsigned nfrom, unsigned ncount) {
QRectF r = rect();
painter.setPen(QPen(QColor::fromHsv(h, s, v), r.height() * thickness * 3, Qt::SolidLine, Qt::FlatCap));
painter.setBrush(Qt::NoBrush);
auto y = row * (r.height() - lineHeight) + lineHeight;
painter.drawLine(QPointF((nfrom - from) * r.width() / count, y), QPointF((nfrom + ncount - from) * r.width() / count, y));
};
auto hProgress2 = [&](unsigned h, unsigned s, unsigned v, float row, float orbit, float thickness, unsigned nfrom, unsigned ncount) {
hProgress(h, s, v, row - orbit * 3, thickness, nfrom, ncount);
hProgress(h, s, v, row + orbit * 3, thickness, nfrom, ncount);
};
auto hLabel = [&](QString text, float points, QColor fore, QColor back) {
painter.setBrush(QBrush(back));
painter.setFont(QFont("Helvetica", points, QFont::Bold));
QRectF r = painter.boundingRect(rect(), Qt::AlignTop | Qt::AlignHCenter, text);
r.adjust(-r.width() / 4, r.height() / 8, r.width() / 4, 3 * r.height() / 8);
painter.setPen(QPen(fore, r.height() / 20));
painter.drawRoundedRect(r, r.height() / 4, r.height() / 4);
painter.drawText(r, Qt::AlignCenter, text);
};
function<void(unsigned h, unsigned s, unsigned v, float row, float thickness, unsigned nfrom, unsigned ncount)> progress;
function<void(unsigned h, unsigned s, unsigned v, float row, float orbit, float thickness, unsigned nfrom, unsigned ncount)> progress2;
function<void(QString text, float points, QColor fore, QColor back)> label;
if (rect().width() / rect().height() > 5)
{
progress = hProgress;
progress2 = hProgress2;
label = hLabel;
} }
else if (rect().height() / rect().width() > 5)
{
}
else
{
progress = pieProgress;
progress2 = pieProgress2;
label = pieLabel;
}
if (sync.state != SyncState::Idle)
{
progress(0, 0, 220, 0.4f, 0.02f, from, hashDone - from); // Download rail
progress(240, 25, 170, 0.4f, 0.02f, downloadDone, downloadPoint - downloadDone); // Latest download point
progress(240, 50, 120, 0.4f, 0.04f, from, downloadDone - from); // Downloaded
}
progress(0, 0, 220, 0.8f, 0.01f, from, count); // Sync rail
progress(0, 0, 170, 0.8f, 0.02f, from, syncUnverified - from); // Verification rail
progress2(60, 25, 170, 0.8f, 0.06f, 0.005f, from, syncVerifying - from); // Verifying.
progress2(120, 25, 170, 0.8f, 0.06f, 0.005f, from, syncVerified - from); // Verified.
progress(120, 50, 120, 0.8f, 0.05f, from, syncFrom - from); // Imported.
progress(0, 0, 120, 0.8f, 0.02f, syncFrom, syncImporting - syncFrom); // Importing.
if (sync.state != SyncState::Idle || (sync.state == SyncState::Idle && !syncCount))
label(labelText, 11, labelFore, labelBack);
} }

14
alethzero/DownloadView.h

@ -32,21 +32,25 @@
#endif #endif
namespace dev { namespace eth { namespace dev { namespace eth {
class DownloadMan; class Client;
}} }}
class DownloadView: public QWidget class SyncView: public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
DownloadView(QWidget* _p = nullptr); SyncView(QWidget* _p = nullptr);
void setDownloadMan(dev::eth::DownloadMan const* _man) { m_man = _man; } void setEthereum(dev::eth::Client const* _c) { m_client = _c; }
protected: protected:
virtual void paintEvent(QPaintEvent*); virtual void paintEvent(QPaintEvent*);
private: private:
dev::eth::DownloadMan const* m_man = nullptr; dev::eth::Client const* m_client = nullptr;
unsigned m_lastSyncFrom = (unsigned)-1;
unsigned m_lastSyncCount = 0;
bool m_wasEstimate = false;
}; };

18
alethzero/ExportState.cpp

@ -127,11 +127,21 @@ void ExportStateDialog::fillContracts()
ui->contracts->clear(); ui->contracts->clear();
ui->accounts->setEnabled(true); ui->accounts->setEnabled(true);
ui->contracts->setEnabled(true); ui->contracts->setEnabled(true);
for (auto i: ethereum()->addresses(m_block)) try
{ {
string r = m_main->render(i); for (auto i: ethereum()->addresses(m_block))
(new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(ethereum()->balanceAt(i)).c_str()).arg(QString::fromStdString(r)).arg((unsigned)ethereum()->countAt(i)), ethereum()->codeAt(i).empty() ? ui->accounts : ui->contracts)) {
->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size)); string r = m_main->render(i);
(new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(ethereum()->balanceAt(i)).c_str()).arg(QString::fromStdString(r)).arg((unsigned)ethereum()->countAt(i)), ethereum()->codeAt(i).empty() ? ui->accounts : ui->contracts))
->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size));
}
}
catch (InterfaceNotSupported const&)
{
ui->accounts->setEnabled(false);
ui->contracts->setEnabled(false);
ui->json->setEnabled(false);
ui->json->setText(QString("This feature requires compilation with FATDB support."));
} }
} }

76
alethzero/Main.ui

@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>AlethZero Ethereum Client</string> <string>AlethZero ++Ethereum</string>
</property> </property>
<property name="dockNestingEnabled"> <property name="dockNestingEnabled">
<bool>true</bool> <bool>true</bool>
@ -44,14 +44,14 @@
<string>0 bytes used</string> <string>0 bytes used</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="syncStatus"> <widget class="QLabel" name="syncStatus">
<property name="text"> <property name="text">
<string></string> <string/>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="chainStatus"> <widget class="QLabel" name="chainStatus">
<property name="text"> <property name="text">
@ -192,10 +192,14 @@
<addaction name="injectBlock"/> <addaction name="injectBlock"/>
<addaction name="forceMining"/> <addaction name="forceMining"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="vmInterpreter"/>
<addaction name="vmJIT"/>
<addaction name="vmSmart"/>
<addaction name="separator"/>
<addaction name="usePrivate"/> <addaction name="usePrivate"/>
<addaction name="jitvm"/>
<addaction name="retryUnknown"/> <addaction name="retryUnknown"/>
<addaction name="confirm"/> <addaction name="confirm"/>
<addaction name="rewindChain"/>
</widget> </widget>
<widget class="QMenu" name="menu_View"> <widget class="QMenu" name="menu_View">
<property name="title"> <property name="title">
@ -224,6 +228,7 @@
<string>&amp;Config</string> <string>&amp;Config</string>
</property> </property>
<addaction name="gasPrices"/> <addaction name="gasPrices"/>
<addaction name="sentinel"/>
</widget> </widget>
<addaction name="menu_File"/> <addaction name="menu_File"/>
<addaction name="menu_View"/> <addaction name="menu_View"/>
@ -703,7 +708,7 @@
</layout> </layout>
</widget> </widget>
</widget> </widget>
<widget class="QDockWidget" name="dockWidget_8"> <widget class="QDockWidget" name="blockChainDockWidget">
<property name="features"> <property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set> <set>QDockWidget::DockWidgetFeatureMask</set>
</property> </property>
@ -1145,7 +1150,7 @@ font-size: 14pt</string>
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="DownloadView" name="downloadView" native="true"/> <widget class="SyncView" name="downloadView" native="true"/>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -1696,17 +1701,6 @@ font-size: 14pt</string>
<string>&amp;Clear Pending</string> <string>&amp;Clear Pending</string>
</property> </property>
</action> </action>
<action name="jitvm">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="text">
<string>Use &amp;LLVM-EVM</string>
</property>
</action>
<action name="killAccount"> <action name="killAccount">
<property name="text"> <property name="text">
<string>&amp;Kill Account</string> <string>&amp;Kill Account</string>
@ -1782,6 +1776,40 @@ font-size: 14pt</string>
<string>&amp;Gas Prices...</string> <string>&amp;Gas Prices...</string>
</property> </property>
</action> </action>
<action name="vmInterpreter">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Interpreter</string>
</property>
</action>
<action name="vmJIT">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>JIT</string>
</property>
</action>
<action name="vmSmart">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Smart</string>
</property>
</action>
<action name="sentinel">
<property name="text">
<string>&amp;Sentinel...</string>
</property>
</action>
<action name="rewindChain">
<property name="text">
<string>&amp;Rewind Chain...</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>
@ -1792,7 +1820,7 @@ font-size: 14pt</string>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>DownloadView</class> <class>SyncView</class>
<extends>QWidget</extends> <extends>QWidget</extends>
<header>DownloadView.h</header> <header>DownloadView.h</header>
<container>1</container> <container>1</container>

85
alethzero/MainWin.cpp

@ -136,6 +136,12 @@ Main::Main(QWidget *parent) :
QtWebEngine::initialize(); QtWebEngine::initialize();
setWindowFlags(Qt::Window); setWindowFlags(Qt::Window);
ui->setupUi(this); ui->setupUi(this);
if (c_network == eth::Network::Olympic)
setWindowTitle("AlethZero Olympic");
else if (c_network == eth::Network::Frontier)
setWindowTitle("AlethZero Frontier");
g_logPost = [=](string const& s, char const* c) g_logPost = [=](string const& s, char const* c)
{ {
simpleDebugOut(s, c); simpleDebugOut(s, c);
@ -239,18 +245,31 @@ Main::Main(QWidget *parent) :
ethereum()->setDefault(LatestBlock); ethereum()->setDefault(LatestBlock);
m_vmSelectionGroup = new QActionGroup{ui->menu_Debug};
m_vmSelectionGroup->addAction(ui->vmInterpreter);
m_vmSelectionGroup->addAction(ui->vmJIT);
m_vmSelectionGroup->addAction(ui->vmSmart);
m_vmSelectionGroup->setExclusive(true);
#if ETH_EVMJIT
ui->vmSmart->setChecked(true); // Default when JIT enabled
on_vmSmart_triggered();
#else
ui->vmInterpreter->setChecked(true);
ui->vmJIT->setEnabled(false);
ui->vmSmart->setEnabled(false);
#endif
readSettings(); readSettings();
m_transact = new Transact(this, this); m_transact = new Transact(this, this);
m_transact->setWindowFlags(Qt::Dialog); m_transact->setWindowFlags(Qt::Dialog);
m_transact->setWindowModality(Qt::WindowModal); m_transact->setWindowModality(Qt::WindowModal);
connect(ui->blockChainDockWidget, &QDockWidget::visibilityChanged, [=]() { refreshBlockChain(); });
#if !ETH_FATDB #if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts); removeDockWidget(ui->dockWidget_accounts);
#endif
#if !ETH_EVMJIT
ui->jitvm->setEnabled(false);
ui->jitvm->setChecked(false);
#endif #endif
installWatches(); installWatches();
startTimer(100); startTimer(100);
@ -296,6 +315,14 @@ void Main::on_gasPrices_triggered()
} }
} }
void Main::on_sentinel_triggered()
{
bool ok;
QString sentinel = QInputDialog::getText(nullptr, "Enter sentinel address", "Enter the sentinel address for bad block reporting (e.g. http://badblockserver.com:8080). Enter nothing to disable.", QLineEdit::Normal, QString::fromStdString(ethereum()->sentinel()), &ok);
if (ok)
ethereum()->setSentinel(sentinel.toStdString());
}
void Main::on_newIdentity_triggered() void Main::on_newIdentity_triggered()
{ {
KeyPair kp = KeyPair::create(); KeyPair kp = KeyPair::create();
@ -723,7 +750,8 @@ void Main::writeSettings()
s.setValue("url", ui->urlEdit->text()); s.setValue("url", ui->urlEdit->text());
s.setValue("privateChain", m_privateChain); s.setValue("privateChain", m_privateChain);
s.setValue("verbosity", ui->verbosity->value()); s.setValue("verbosity", ui->verbosity->value());
s.setValue("jitvm", ui->jitvm->isChecked()); if (auto vm = m_vmSelectionGroup->checkedAction())
s.setValue("vm", vm->text());
bytes d = m_webThree->saveNetwork(); bytes d = m_webThree->saveNetwork();
if (!d.empty()) if (!d.empty())
@ -814,8 +842,28 @@ void Main::readSettings(bool _skipGeometry)
m_privateChain = s.value("privateChain", "").toString(); m_privateChain = s.value("privateChain", "").toString();
ui->usePrivate->setChecked(m_privateChain.size()); ui->usePrivate->setChecked(m_privateChain.size());
ui->verbosity->setValue(s.value("verbosity", 1).toInt()); ui->verbosity->setValue(s.value("verbosity", 1).toInt());
ui->jitvm->setChecked(s.value("jitvm", true).toBool());
on_jitvm_triggered(); #if ETH_EVMJIT // We care only if JIT is enabled. Otherwise it can cause misconfiguration.
auto vmName = s.value("vm").toString();
if (!vmName.isEmpty())
{
if (vmName == ui->vmInterpreter->text())
{
ui->vmInterpreter->setChecked(true);
on_vmInterpreter_triggered();
}
else if (vmName == ui->vmJIT->text())
{
ui->vmJIT->setChecked(true);
on_vmJIT_triggered();
}
else if (vmName == ui->vmSmart->text())
{
ui->vmSmart->setChecked(true);
on_vmSmart_triggered();
}
}
#endif
ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html
on_urlEdit_returnPressed(); on_urlEdit_returnPressed();
@ -992,10 +1040,19 @@ void Main::on_usePrivate_triggered()
on_killBlockchain_triggered(); on_killBlockchain_triggered();
} }
void Main::on_jitvm_triggered() void Main::on_vmInterpreter_triggered() { VMFactory::setKind(VMKind::Interpreter); }
void Main::on_vmJIT_triggered() { VMFactory::setKind(VMKind::JIT); }
void Main::on_vmSmart_triggered() { VMFactory::setKind(VMKind::Smart); }
void Main::on_rewindChain_triggered()
{ {
bool jit = ui->jitvm->isChecked(); bool ok;
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); int n = QInputDialog::getInt(this, "Rewind Chain", "Enter the number of the new chain head.", ethereum()->number() * 9 / 10, 1, ethereum()->number(), 1, &ok);
if (ok)
{
ethereum()->rewind(n);
refreshAll();
}
} }
void Main::on_urlEdit_returnPressed() void Main::on_urlEdit_returnPressed()
@ -1252,7 +1309,7 @@ void Main::refreshBlockCount()
auto d = ethereum()->blockChain().details(); auto d = ethereum()->blockChain().details();
BlockQueueStatus b = ethereum()->blockQueueStatus(); BlockQueueStatus b = ethereum()->blockQueueStatus();
SyncStatus sync = ethereum()->syncStatus(); SyncStatus sync = ethereum()->syncStatus();
QString syncStatus = EthereumHost::stateName(sync.state); QString syncStatus = QString("PV%1 %2").arg(sync.protocolVersion).arg(EthereumHost::stateName(sync.state));
if (sync.state == SyncState::Hashes) if (sync.state == SyncState::Hashes)
syncStatus += QString(": %1/%2%3").arg(sync.hashesReceived).arg(sync.hashesEstimated ? "~" : "").arg(sync.hashesTotal); syncStatus += QString(": %1/%2%3").arg(sync.hashesReceived).arg(sync.hashesEstimated ? "~" : "").arg(sync.hashesTotal);
if (sync.state == SyncState::Blocks || sync.state == SyncState::NewBlocks) if (sync.state == SyncState::Blocks || sync.state == SyncState::NewBlocks)
@ -1269,7 +1326,7 @@ void Main::on_turboMining_triggered()
void Main::refreshBlockChain() void Main::refreshBlockChain()
{ {
if (!ui->blocks->isVisible() && isVisible()) if (!(ui->blockChainDockWidget->isVisible() || !tabifiedDockWidgets(ui->blockChainDockWidget).isEmpty()))
return; return;
DEV_TIMED_FUNCTION_ABOVE(500); DEV_TIMED_FUNCTION_ABOVE(500);
@ -1947,12 +2004,12 @@ void Main::on_net_triggered()
web3()->setNetworkPreferences(netPrefs(), ui->dropPeers->isChecked()); web3()->setNetworkPreferences(netPrefs(), ui->dropPeers->isChecked());
ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : h256()); ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : h256());
web3()->startNetwork(); web3()->startNetwork();
ui->downloadView->setDownloadMan(ethereum()->downloadMan()); ui->downloadView->setEthereum(ethereum());
ui->enode->setText(QString::fromStdString(web3()->enode())); ui->enode->setText(QString::fromStdString(web3()->enode()));
} }
else else
{ {
ui->downloadView->setDownloadMan(nullptr); ui->downloadView->setEthereum(nullptr);
writeSettings(); writeSettings();
web3()->stopNetwork(); web3()->stopNetwork();
} }

11
alethzero/MainWin.h

@ -44,6 +44,7 @@
#include "Connect.h" #include "Connect.h"
class QListWidgetItem; class QListWidgetItem;
class QActionGroup;
namespace Ui { namespace Ui {
class Main; class Main;
@ -182,8 +183,11 @@ private slots:
void on_forceMining_triggered(); void on_forceMining_triggered();
void on_usePrivate_triggered(); void on_usePrivate_triggered();
void on_turboMining_triggered(); void on_turboMining_triggered();
void on_jitvm_triggered();
void on_retryUnknown_triggered(); void on_retryUnknown_triggered();
void on_vmInterpreter_triggered();
void on_vmJIT_triggered();
void on_vmSmart_triggered();
void on_rewindChain_triggered();
// Debugger // Debugger
void on_debugCurrent_triggered(); void on_debugCurrent_triggered();
@ -196,6 +200,7 @@ private slots:
// Config // Config
void on_gasPrices_triggered(); void on_gasPrices_triggered();
void on_sentinel_triggered();
void refreshWhisper(); void refreshWhisper();
void refreshBlockChain(); void refreshBlockChain();
@ -271,6 +276,8 @@ private:
dev::Address m_nameReg; dev::Address m_nameReg;
dev::Address m_beneficiary; dev::Address m_beneficiary;
QActionGroup* m_vmSelectionGroup = nullptr;
QList<QPair<QString, QString>> m_consoleHistory; QList<QPair<QString, QString>> m_consoleHistory;
QMutex m_logLock; QMutex m_logLock;
QString m_logHistory; QString m_logHistory;
@ -286,6 +293,6 @@ private:
std::unique_ptr<DappHost> m_dappHost; std::unique_ptr<DappHost> m_dappHost;
DappLoader* m_dappLoader; DappLoader* m_dappLoader;
QWebEnginePage* m_webPage; QWebEnginePage* m_webPage;
Connect m_connect; Connect m_connect;
}; };

5
alethzero/OurWebThreeStubServer.cpp

@ -99,10 +99,11 @@ bool OurAccountHolder::showUnknownCallNotice(TransactionSkeleton const& _t, bool
"REJECT UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!"); "REJECT UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!");
} }
void OurAccountHolder::authenticate(TransactionSkeleton const& _t) h256 OurAccountHolder::authenticate(TransactionSkeleton const& _t)
{ {
Guard l(x_queued); Guard l(x_queued);
m_queued.push(_t); m_queued.push(_t);
return h256();
} }
void OurAccountHolder::doValidations() void OurAccountHolder::doValidations()
@ -130,7 +131,7 @@ void OurAccountHolder::doValidations()
else else
// sign and submit. // sign and submit.
if (Secret s = m_main->retrieveSecret(t.from)) if (Secret s = m_main->retrieveSecret(t.from))
m_main->ethereum()->submitTransaction(s, t); m_main->ethereum()->submitTransaction(t, s);
} }
} }

2
alethzero/OurWebThreeStubServer.h

@ -43,7 +43,7 @@ protected:
// easiest to return keyManager.addresses(); // easiest to return keyManager.addresses();
virtual dev::AddressHash realAccounts() const override; virtual dev::AddressHash realAccounts() const override;
// use web3 to submit a signed transaction to accept // use web3 to submit a signed transaction to accept
virtual void authenticate(dev::eth::TransactionSkeleton const& _t) override; virtual dev::h256 authenticate(dev::eth::TransactionSkeleton const& _t) override;
private: private:
bool showAuthenticationPopup(std::string const& _title, std::string const& _text); bool showAuthenticationPopup(std::string const& _title, std::string const& _text);

5
cmake/EthCompilerSettings.cmake

@ -47,9 +47,10 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 /wd4267 /wd4180 /wd4290 /wd4244 /wd4800 -D_WIN32_WINNT=0x0501 /DNOMINMAX /DMINIUPNP_STATICLIB) add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 /wd4267 /wd4180 /wd4290 /wd4244 /wd4800 -D_WIN32_WINNT=0x0501 /DNOMINMAX /DMINIUPNP_STATICLIB)
# disable empty object file warning # disable empty object file warning
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
# warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification
# warning LNK4099: pdb was not found with lib # warning LNK4099: pdb was not found with lib
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075") # stack size 16MB
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075 /STACK:16777216")
# windows likes static # windows likes static
if (NOT ETH_STATIC) if (NOT ETH_STATIC)

28
cmake/Findv8.cmake

@ -31,12 +31,32 @@ set(V8_LIBRARIES ${V8_LIBRARY})
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
find_library( find_library(
V8_LIBRARY_DEBUG V8_LIBRARY
NAMES v8d NAMES v8_base
DOC "v8 debug library" DOC "v8 base library"
)
find_library(
V8_NO_SNAPSHOT_LIBRARY
NAMES v8_nosnapshot
DOC "v8 nosnapshot library"
) )
set(V8_LIBRARIES optimized ${V8_LIBRARIES} debug ${V8_LIBRARY_DEBUG}) set(V8_LIBRARIES ${V8_LIBRARY} ${V8_NO_SNAPSHOT_LIBRARY})
find_library(
V8_LIBRARY_DEBUG
NAMES v8_based
DOC "v8 base library"
)
find_library(
V8_NO_SNAPSHOT_LIBRARY_DEBUG
NAMES v8_nosnapshotd
DOC "v8 nosnapshot library"
)
set(V8_LIBRARIES "ws2_32" "winmm" optimized ${V8_LIBRARIES} debug ${V8_LIBRARY_DEBUG} ${V8_NO_SNAPSHOT_LIBRARY_DEBUG})
endif() endif()

2698
eth/main.cpp

File diff suppressed because it is too large

2
ethconsole/CMakeLists.txt

@ -13,9 +13,7 @@ set(EXECUTABLE ethconsole)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)

171
ethkey/KeyAux.h

@ -32,6 +32,7 @@
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include <libethcore/KeyManager.h> #include <libethcore/KeyManager.h>
#include <libethcore/ICAP.h> #include <libethcore/ICAP.h>
#include <libethcore/Transaction.h>
#include "BuildInfo.h" #include "BuildInfo.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -105,7 +106,9 @@ public:
ImportWithAddress, ImportWithAddress,
Export, Export,
Recode, Recode,
Kill Kill,
SignTx,
DecodeTx,
}; };
KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {} KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {}
@ -131,8 +134,13 @@ public:
auto v = argv[++i]; auto v = argv[++i];
m_kdfParams[n] = v; m_kdfParams[n] = v;
} }
else if (arg == "--new-bare") else if (arg == "--sign-tx" && i + 1 < argc)
m_mode = OperationMode::NewBare; {
m_mode = OperationMode::SignTx;
m_signKey = argv[++i];
}
else if (arg == "--decode-tx")
m_mode = OperationMode::DecodeTx;
else if (arg == "--import-bare") else if (arg == "--import-bare")
m_mode = OperationMode::ImportBare; m_mode = OperationMode::ImportBare;
else if (arg == "--list-bare") else if (arg == "--list-bare")
@ -173,7 +181,7 @@ public:
m_mode = OperationMode::Recode; m_mode = OperationMode::Recode;
else if (arg == "--no-icap") else if (arg == "--no-icap")
m_icap = false; m_icap = false;
else if (m_mode == OperationMode::ImportBare || m_mode == OperationMode::InspectBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare) else if (m_mode == OperationMode::DecodeTx || m_mode == OperationMode::SignTx || m_mode == OperationMode::ImportBare || m_mode == OperationMode::InspectBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare)
m_inputs.push_back(arg); m_inputs.push_back(arg);
else else
return false; return false;
@ -209,6 +217,127 @@ public:
} }
} }
} }
else if (m_mode == OperationMode::DecodeTx)
{
string const& i = m_inputs[0];
bytes b = fromHex(i);
if (b.empty())
{
std::string s = contentsString(i);
b = fromHex(s);
if (b.empty())
b = asBytes(s);
}
if (b.empty())
cerr << "Unknown file or bad hex: " << i << endl;
else
try
{
TransactionBase t(b, CheckTransaction::Everything);
cout << "Transaction " << t.sha3().hex() << endl;
if (t.isCreation())
{
cout << " type: creation" << endl;
cout << " code: " << toHex(t.data()) << endl;
}
else
{
cout << " type: message" << endl;
cout << " to: " << t.to().hex() << endl;
cout << " data: " << (t.data().empty() ? "none" : toHex(t.data())) << endl;
}
cout << " from: " << t.from().hex() << endl;
cout << " value: " << formatBalance(t.value()) << " (" << t.value() << " wei)" << endl;
cout << " nonce: " << t.nonce() << endl;
cout << " gas: " << t.gas() << endl;
cout << " gas price: " << formatBalance(t.gasPrice()) << " (" << t.gasPrice() << " wei)" << endl;
cout << " signing hash: " << t.sha3(WithoutSignature).hex() << endl;
cout << " v: " << (int)t.signature().v << endl;
cout << " r: " << t.signature().r << endl;
cout << " s: " << t.signature().s << endl;
}
catch (Exception& ex)
{
cerr << "Invalid transaction: " << ex.what() << endl;
}
}
else if (m_mode == OperationMode::SignTx)
{
Secret s;
string json = contentsString(m_signKey);
if (!json.empty())
{
SecretStore store(m_secretsPath);
s = Secret(store.secret(store.readKeyContent(json), [&](){ return getPassword("Enter password for key: "); }));
}
else
{
if (h128 u = fromUUID(m_signKey))
{
SecretStore store(m_secretsPath);
s = Secret(store.secret(u, [&](){ return getPassword("Enter password for key: "); }));
}
else if (Address a = Address(m_signKey))
{
KeyManager wallet(m_walletPath, m_secretsPath);
if (wallet.exists())
{
openWallet(wallet);
s = wallet.secret(a, [&](){ return getPassword("Enter password for key: "); });
}
else
{
cerr << "Wallet doesn't exist." << endl;
exit(-1);
}
}
else
{
cerr << "Bad file, UUID and address: " << m_signKey << endl;
exit(-1);
}
}
if (!s)
{
cerr << "UUID/address not found: " << m_signKey << endl;
exit(-1);
}
for (string const& i: m_inputs)
{
bytes b = fromHex(i);
bool isFile = false;
if (b.empty())
{
isFile = true;
std::string s = contentsString(i);
b = fromHex(s);
if (b.empty())
b = asBytes(s);
}
if (b.empty())
cerr << "Unknown file or bad hex: " << i << endl;
else
try
{
TransactionBase t(b, CheckTransaction::None);
t.sign(s);
cout << t.sha3() << ": ";
if (isFile)
{
writeFile(i + ".signed", t.data());
cout << i + ".signed" << endl;
}
else
cout << toHex(t.data()) << endl;
}
catch (Exception& ex)
{
cerr << "Invalid transaction: " << ex.what() << endl;
}
}
}
else if (m_mode < OperationMode::CreateWallet) else if (m_mode < OperationMode::CreateWallet)
{ {
SecretStore store(m_secretsPath); SecretStore store(m_secretsPath);
@ -297,17 +426,7 @@ public:
{ {
KeyManager wallet(m_walletPath, m_secretsPath); KeyManager wallet(m_walletPath, m_secretsPath);
if (wallet.exists()) if (wallet.exists())
while (true) openWallet(wallet);
{
if (wallet.load(m_masterPassword))
break;
if (!m_masterPassword.empty())
{
cout << "Password invalid. Try again." << endl;
m_masterPassword.clear();
}
m_masterPassword = getPassword("Please enter your MASTER password: ");
}
else else
{ {
cerr << "Couldn't open wallet. Does it exist?" << endl; cerr << "Couldn't open wallet. Does it exist?" << endl;
@ -419,6 +538,10 @@ public:
<< " --wallet-path <path> Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl << " --wallet-path <path> Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl
<< " -m, --master <password> Specify wallet (master) password." << endl << " -m, --master <password> Specify wallet (master) password." << endl
<< endl << endl
<< "Transaction operating modes:" << endl
<< " -d,--decode-tx [<hex>|<file>] Decode given transaction." << endl
<< " -s,--sign-tx [ <address>|<uuid>|<file> ] [ <hex>|<file> , ... ] (Re-)Sign given transaction." << endl
<< endl
<< "Encryption configuration:" << endl << "Encryption configuration:" << endl
<< " --kdf <kdfname> Specify KDF to use when encrypting (default: sc rypt)" << endl << " --kdf <kdfname> Specify KDF to use when encrypting (default: sc rypt)" << endl
<< " --kdf-param <name> <value> Specify a parameter for the KDF." << endl << " --kdf-param <name> <value> Specify a parameter for the KDF." << endl
@ -445,6 +568,21 @@ public:
} }
private: private:
void openWallet(KeyManager& _w)
{
while (true)
{
if (_w.load(m_masterPassword))
break;
if (!m_masterPassword.empty())
{
cout << "Password invalid. Try again." << endl;
m_masterPassword.clear();
}
m_masterPassword = getPassword("Please enter your MASTER password: ");
}
}
KDF kdf() const { return m_kdf == "pbkdf2" ? KDF::PBKDF2_SHA256 : KDF::Scrypt; } KDF kdf() const { return m_kdf == "pbkdf2" ? KDF::PBKDF2_SHA256 : KDF::Scrypt; }
/// Operating mode. /// Operating mode.
@ -468,6 +606,9 @@ private:
/// Importing /// Importing
strings m_inputs; strings m_inputs;
/// Signing
string m_signKey;
string m_kdf = "scrypt"; string m_kdf = "scrypt";
map<string, string> m_kdfParams; map<string, string> m_kdfParams;
// string m_cipher; // string m_cipher;

4
ethminer/CMakeLists.txt

@ -9,6 +9,9 @@ if (JSONRPC)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
endif() endif()
if (ETHASHCL)
include_directories(${OpenCL_INCLUDE_DIRS})
endif ()
set(EXECUTABLE ethminer) set(EXECUTABLE ethminer)
@ -40,4 +43,3 @@ if (APPLE)
else() else()
eth_install_executable(${EXECUTABLE}) eth_install_executable(${EXECUTABLE})
endif() endif()

77
ethminer/MinerAux.h

@ -40,6 +40,9 @@
#include <libethcore/ProofOfWork.h> #include <libethcore/ProofOfWork.h>
#include <libethcore/EthashAux.h> #include <libethcore/EthashAux.h>
#include <libethcore/Farm.h> #include <libethcore/Farm.h>
#if ETH_ETHASHCL || !ETH_TRUE
#include <libethash-cl/ethash_cl_miner.h>
#endif
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h> #include <jsonrpccpp/server/connectors/httpserver.h>
@ -78,6 +81,12 @@ inline std::string credits()
} }
class BadArgument: public Exception {}; class BadArgument: public Exception {};
struct MiningChannel: public LogChannel
{
static const char* name() { return EthGreen "miner"; }
static const int verbosity = 2;
};
#define minelog clog(MiningChannel)
class MinerCLI class MinerCLI
{ {
@ -128,6 +137,35 @@ public:
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument()); BOOST_THROW_EXCEPTION(BadArgument());
} }
#if ETH_ETHASHCL || !ETH_TRUE
else if (arg == "--cl-global-work" && i + 1 < argc)
try {
m_globalWorkSizeMultiplier = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
else if (arg == "--cl-local-work" && i + 1 < argc)
try {
m_localWorkSize = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
else if (arg == "--cl-ms-per-batch" && i + 1 < argc)
try {
m_msPerBatch = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
#endif
else if (arg == "--list-devices") else if (arg == "--list-devices")
m_shouldListDevices = true; m_shouldListDevices = true;
else if (arg == "--allow-opencl-cpu") else if (arg == "--allow-opencl-cpu")
@ -217,7 +255,7 @@ public:
auto boundary = bi.boundary(); auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i])); m = boost::to_lower_copy(string(argv[++i]));
bi.nonce = h64(m); bi.nonce = h64(m);
auto r = EthashAux::eval(bi.seedHash(), powHash, bi.nonce); auto r = EthashAux::eval(seedHash, powHash, bi.nonce);
bool valid = r.value < boundary; bool valid = r.value < boundary;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl; cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl; cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
@ -265,18 +303,23 @@ public:
ProofOfWork::CPUMiner::setNumInstances(m_miningThreads); ProofOfWork::CPUMiner::setNumInstances(m_miningThreads);
else if (m_minerType == MinerType::GPU) else if (m_minerType == MinerType::GPU)
{ {
#if ETH_ETHASHCL || !ETH_TRUE
if (!ProofOfWork::GPUMiner::configureGPU( if (!ProofOfWork::GPUMiner::configureGPU(
m_localWorkSize,
m_globalWorkSizeMultiplier,
m_msPerBatch,
m_openclPlatform, m_openclPlatform,
m_openclDevice, m_openclDevice,
m_clAllowCPU, m_clAllowCPU,
m_extraGPUMemory, m_extraGPUMemory,
m_currentBlock m_currentBlock
)) ))
{
cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl;
exit(1); exit(1);
}
ProofOfWork::GPUMiner::setNumInstances(m_miningThreads); ProofOfWork::GPUMiner::setNumInstances(m_miningThreads);
#else
cerr << "Selected GPU mining without having compiled with -DETHASHCL=1" << endl;
exit(1);
#endif
} }
if (mode == OperationMode::DAGInit) if (mode == OperationMode::DAGInit)
doInitDAG(m_initDAG); doInitDAG(m_initDAG);
@ -317,7 +360,12 @@ public:
<< " --allow-opencl-cpu Allows CPU to be considered as an OpenCL device if the OpenCL platform supports it." << endl << " --allow-opencl-cpu Allows CPU to be considered as an OpenCL device if the OpenCL platform supports it." << endl
<< " --list-devices List the detected OpenCL devices and exit." << endl << " --list-devices List the detected OpenCL devices and exit." << endl
<< " --current-block Let the miner know the current block number at configuration time. Will help determine DAG size and required GPU memory." << endl << " --current-block Let the miner know the current block number at configuration time. Will help determine DAG size and required GPU memory." << endl
#if ETH_ETHASHCL || !ETH_TRUE
<< " --cl-extragpu-mem Set the memory (in MB) you believe your GPU requires for stuff other than mining. Windows rendering e.t.c.." << endl << " --cl-extragpu-mem Set the memory (in MB) you believe your GPU requires for stuff other than mining. Windows rendering e.t.c.." << endl
<< " --cl-local-work Set the OpenCL local work size. Default is " << toString(ethash_cl_miner::c_defaultLocalWorkSize) << endl
<< " --cl-global-work Set the OpenCL global work size as a multiple of the local work size. Default is " << toString(ethash_cl_miner::c_defaultGlobalWorkSizeMultiplier) << " * " << toString(ethash_cl_miner::c_defaultLocalWorkSize) << endl
<< " --cl-ms-per-batch Set the OpenCL target milliseconds per batch (global workgroup size). Default is " << toString(ethash_cl_miner::c_defaultMSPerBatch) << ". If 0 is given then no autoadjustment of global work size will happen" << endl
#endif
; ;
} }
@ -442,14 +490,14 @@ private:
for (unsigned i = 0; !completed; ++i) for (unsigned i = 0; !completed; ++i)
{ {
if (current) if (current)
cnote << "Mining on PoWhash" << current.headerHash << ": " << f.miningProgress(); minelog << "Mining on PoWhash" << current.headerHash << ": " << f.miningProgress();
else else
cnote << "Getting work package..."; minelog << "Getting work package...";
Json::Value v = rpc.eth_getWork(); Json::Value v = rpc.eth_getWork();
h256 hh(v[0].asString()); h256 hh(v[0].asString());
h256 newSeedHash(v[1].asString()); h256 newSeedHash(v[1].asString());
if (current.seedHash != newSeedHash) if (current.seedHash != newSeedHash)
cnote << "Grabbing DAG for" << newSeedHash; minelog << "Grabbing DAG for" << newSeedHash;
if (!(dag = EthashAux::full(newSeedHash, true, [&](unsigned _pc){ cout << "\rCreating DAG. " << _pc << "% done..." << flush; return 0; }))) if (!(dag = EthashAux::full(newSeedHash, true, [&](unsigned _pc){ cout << "\rCreating DAG. " << _pc << "% done..." << flush; return 0; })))
BOOST_THROW_EXCEPTION(DAGCreationFailure()); BOOST_THROW_EXCEPTION(DAGCreationFailure());
if (m_precompute) if (m_precompute)
@ -459,10 +507,10 @@ private:
current.headerHash = hh; current.headerHash = hh;
current.seedHash = newSeedHash; current.seedHash = newSeedHash;
current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight); current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight);
cnote << "Got work package:"; minelog << "Got work package:";
cnote << " Header-hash:" << current.headerHash.hex(); minelog << " Header-hash:" << current.headerHash.hex();
cnote << " Seedhash:" << current.seedHash.hex(); minelog << " Seedhash:" << current.seedHash.hex();
cnote << " Target: " << h256(current.boundary).hex(); minelog << " Target: " << h256(current.boundary).hex();
f.setWork(current); f.setWork(current);
} }
this_thread::sleep_for(chrono::milliseconds(_recheckPeriod)); this_thread::sleep_for(chrono::milliseconds(_recheckPeriod));
@ -506,7 +554,12 @@ private:
unsigned m_miningThreads = UINT_MAX; unsigned m_miningThreads = UINT_MAX;
bool m_shouldListDevices = false; bool m_shouldListDevices = false;
bool m_clAllowCPU = false; bool m_clAllowCPU = false;
boost::optional<uint64_t> m_currentBlock; #if ETH_ETHASHCL || !ETH_TRUE
unsigned m_globalWorkSizeMultiplier = ethash_cl_miner::c_defaultGlobalWorkSizeMultiplier;
unsigned m_localWorkSize = ethash_cl_miner::c_defaultLocalWorkSize;
unsigned m_msPerBatch = ethash_cl_miner::c_defaultMSPerBatch;
#endif
uint64_t m_currentBlock = 0;
// default value is 350MB of GPU memory for other stuff (windows system rendering, e.t.c.) // default value is 350MB of GPU memory for other stuff (windows system rendering, e.t.c.)
unsigned m_extraGPUMemory = 350000000; unsigned m_extraGPUMemory = 350000000;

22
ethvm/main.cpp

@ -46,8 +46,7 @@ void help()
#if ETH_EVMJIT || !ETH_TRUE #if ETH_EVMJIT || !ETH_TRUE
<< endl << endl
<< "VM options:" << endl << "VM options:" << endl
<< " -J,--jit Enable LLVM VM (default: off)." << endl << " --vm <vm-kind> Select VM. Options are: interpreter, jit, smart. (default: interpreter)" << endl
<< " --smart Enable smart VM (default: off)." << endl
#endif #endif
<< endl << endl
<< "Options for trace:" << endl << "Options for trace:" << endl
@ -97,10 +96,21 @@ int main(int argc, char** argv)
else if (arg == "-V" || arg == "--version") else if (arg == "-V" || arg == "--version")
version(); version();
#if ETH_EVMJIT #if ETH_EVMJIT
else if (arg == "-J" || arg == "--jit") else if (arg == "--vm" && i + 1 < argc)
VMFactory::setKind(VMKind::JIT); {
else if (arg == "--smart") string vmKind = argv[++i];
VMFactory::setKind(VMKind::Smart); if (vmKind == "interpreter")
VMFactory::setKind(VMKind::Interpreter);
else if (vmKind == "jit")
VMFactory::setKind(VMKind::JIT);
else if (vmKind == "smart")
VMFactory::setKind(VMKind::Smart);
else
{
cerr << "Unknown VM kind: " << vmKind << endl;
return -1;
}
}
#endif #endif
else if (arg == "--mnemonics") else if (arg == "--mnemonics")
st.setShowMnemonics(); st.setShowMnemonics();

4
evmjit/CMakeLists.txt

@ -29,7 +29,3 @@ add_subdirectory(libevmjit)
if(EVMJIT_CPP) if(EVMJIT_CPP)
add_subdirectory(libevmjit-cpp) add_subdirectory(libevmjit-cpp)
endif() endif()
if(EVMJIT_TOOLS)
add_subdirectory(evmcc)
endif()

18
evmjit/evmcc/CMakeLists.txt

@ -1,18 +0,0 @@
set(TARGET_NAME evmcc)
set(SOURCES
evmcc.cpp
)
source_group("" FILES ${SOURCES})
add_executable(${TARGET_NAME} ${SOURCES})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "tools")
include_directories(../..)
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ethereum)
target_link_libraries(${TARGET_NAME} ${Boost_PROGRAM_OPTIONS_LIBRARIES})
install(TARGETS ${TARGET_NAME} DESTINATION bin )

210
evmjit/evmcc/evmcc.cpp

@ -1,210 +0,0 @@
#include <chrono>
#include <iostream>
#include <fstream>
#include <ostream>
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/Support/raw_os_ostream.h>
#include <llvm/Support/Signals.h>
#include <llvm/Support/PrettyStackTrace.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libevm/ExtVMFace.h>
#include <evmjit/libevmjit/Compiler.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
void parseProgramOptions(int _argc, char** _argv, boost::program_options::variables_map& _varMap)
{
namespace opt = boost::program_options;
opt::options_description explicitOpts("Allowed options");
explicitOpts.add_options()
("help,h", "show usage information")
("compile,c", "compile the code to LLVM IR")
("interpret,i", "compile the code to LLVM IR and execute")
("gas,g", opt::value<size_t>(), "set initial gas for execution")
("disassemble,d", "dissassemble the code")
("dump-cfg", "dump control flow graph to graphviz file")
("dont-optimize", "turn off optimizations")
("optimize-stack", "optimize stack use between basic blocks (default: on)")
("rewrite-switch", "rewrite LLVM switch to branches (default: on)")
("output-ll", opt::value<std::string>(), "dump generated LLVM IR to file")
("output-bc", opt::value<std::string>(), "dump generated LLVM bitcode to file")
("show-logs", "output LOG statements to stderr")
("verbose,V", "enable verbose output");
opt::options_description implicitOpts("Input files");
implicitOpts.add_options()
("input-file", opt::value<std::string>(), "input file");
opt::options_description allOpts("");
allOpts.add(explicitOpts).add(implicitOpts);
opt::positional_options_description inputOpts;
inputOpts.add("input-file", 1);
const char* errorMsg = nullptr;
try
{
auto parser = opt::command_line_parser(_argc, _argv).options(allOpts).positional(inputOpts);
opt::store(parser.run(), _varMap);
opt::notify(_varMap);
}
catch (boost::program_options::error& err)
{
errorMsg = err.what();
}
if (!errorMsg && _varMap.count("input-file") == 0)
errorMsg = "missing input file name";
if (_varMap.count("disassemble") == 0
&& _varMap.count("compile") == 0
&& _varMap.count("interpret") == 0)
{
errorMsg = "at least one of -c, -i, -d is required";
}
if (errorMsg || _varMap.count("help"))
{
if (errorMsg)
std::cerr << "Error: " << errorMsg << std::endl;
std::cout << "Usage: " << _argv[0] << " <options> input-file " << std::endl
<< explicitOpts << std::endl;
std::exit(errorMsg ? 1 : 0);
}
}
int main(int argc, char** argv)
{
llvm::sys::PrintStackTraceOnErrorSignal();
llvm::PrettyStackTraceProgram X(argc, argv);
boost::program_options::variables_map options;
parseProgramOptions(argc, argv, options);
auto inputFile = options["input-file"].as<std::string>();
std::ifstream ifs(inputFile);
if (!ifs.is_open())
{
std::cerr << "cannot open input file " << inputFile << std::endl;
exit(1);
}
std::string src((std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>()));
boost::algorithm::trim(src);
using namespace dev;
bytes bytecode = fromHex(src);
if (options.count("disassemble"))
{
std::string assembly = eth::disassemble(bytecode);
std::cout << assembly << std::endl;
}
if (options.count("compile") || options.count("interpret"))
{
size_t initialGas = 10000;
if (options.count("gas"))
initialGas = options["gas"].as<size_t>();
auto compilationStartTime = std::chrono::high_resolution_clock::now();
eth::jit::Compiler::Options compilerOptions;
compilerOptions.dumpCFG = options.count("dump-cfg") > 0;
bool optimize = options.count("dont-optimize") == 0;
compilerOptions.optimizeStack = optimize || options.count("optimize-stack") > 0;
compilerOptions.rewriteSwitchToBranches = optimize || options.count("rewrite-switch") > 0;
auto compiler = eth::jit::Compiler(compilerOptions);
auto module = compiler.compile(bytecode, "main");
auto compilationEndTime = std::chrono::high_resolution_clock::now();
module->dump();
if (options.count("output-ll"))
{
auto outputFile = options["output-ll"].as<std::string>();
std::ofstream ofs(outputFile);
if (!ofs.is_open())
{
std::cerr << "cannot open output file " << outputFile << std::endl;
exit(1);
}
llvm::raw_os_ostream ros(ofs);
module->print(ros, nullptr);
ofs.close();
}
if (options.count("output-bc"))
{
auto outputFile = options["output-bc"].as<std::string>();
std::ofstream ofs(outputFile);
if (!ofs.is_open())
{
std::cerr << "cannot open output file " << outputFile << std::endl;
exit(1);
}
llvm::raw_os_ostream ros(ofs);
llvm::WriteBitcodeToFile(module.get(), ros);
ros.flush();
ofs.close();
}
if (options.count("verbose"))
{
std::cerr << "*** Compilation time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(compilationEndTime - compilationStartTime).count()
<< std::endl;
}
if (options.count("interpret"))
{
using namespace eth::jit;
ExecutionEngine engine;
eth::jit::u256 gas = initialGas;
// Create random runtime data
RuntimeData data;
data.set(RuntimeData::Gas, gas);
data.set(RuntimeData::Address, (u160)Address(1122334455667788));
data.set(RuntimeData::Caller, (u160)Address(0xfacefacefaceface));
data.set(RuntimeData::Origin, (u160)Address(101010101010101010));
data.set(RuntimeData::CallValue, 0xabcd);
data.set(RuntimeData::CallDataSize, 3);
data.set(RuntimeData::GasPrice, 1003);
data.set(RuntimeData::CoinBase, (u160)Address(101010101010101015));
data.set(RuntimeData::TimeStamp, 1005);
data.set(RuntimeData::Number, 1006);
data.set(RuntimeData::Difficulty, 16);
data.set(RuntimeData::GasLimit, 1008);
data.set(RuntimeData::CodeSize, bytecode.size());
data.callData = (uint8_t*)"abc";
data.code = bytecode.data();
// BROKEN: env_* functions must be implemented & RuntimeData struct created
// TODO: Do not compile module again
auto result = engine.run(bytecode, &data, nullptr);
return static_cast<int>(result);
}
}
return 0;
}

56
evmjit/include/evmjit/DataTypes.h

@ -1,56 +0,0 @@
#pragma once
#include <cstdint>
#include <functional>
namespace dev
{
namespace evmjit
{
struct h256
{
uint64_t words[4];
};
inline bool operator==(h256 _h1, h256 _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
}
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
i256() = default;
i256(h256 _h)
{
a = _h.words[0];
b = _h.words[1];
c = _h.words[2];
d = _h.words[3];
}
};
}
}
namespace std
{
template<> struct hash<dev::evmjit::h256>
{
size_t operator()(dev::evmjit::h256 const& _h) const
{
/// This implementation expects the argument to be a full 256-bit Keccak hash.
/// It does nothing more than returning a slice of the input hash.
return static_cast<size_t>(_h.words[0]);
};
};
}

162
evmjit/include/evmjit/JIT.h

@ -1,19 +1,147 @@
#pragma once #pragma once
#include "evmjit/DataTypes.h" #include <cstdint>
#include <cstring>
#include <functional>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{ {
namespace jit
using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
/// Representation of 256-bit hash value
struct h256
{ {
class ExecutionEngine; uint64_t words[4];
} };
inline bool operator==(h256 const& _h1, h256 const& _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
} }
namespace evmjit /// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t words[4];
i256() = default;
i256(h256 const& _h) { std::memcpy(this, &_h, sizeof(*this)); }
};
// TODO: Merge with ExecutionContext
struct RuntimeData
{
enum Index
{
Gas,
GasPrice,
CallData,
CallDataSize,
Address,
Caller,
Origin,
CallValue,
CoinBase,
Difficulty,
GasLimit,
Number,
Timestamp,
Code,
CodeSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
};
static size_t const numElements = CodeSize + 1;
int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr;
uint64_t callDataSize = 0;
i256 address;
i256 caller;
i256 origin;
i256 callValue;
i256 coinBase;
i256 difficulty;
i256 gasLimit;
uint64_t number = 0;
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type
struct Env;
enum class ReturnCode
{ {
// Success codes
Stop = 0,
Return = 1,
Suicide = 2,
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMError = -101,
UnexpectedException = -111,
LinkerWorkaround = -299,
};
class ExecutionContext
{
public:
ExecutionContext() = default;
ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); }
ExecutionContext(ExecutionContext const&) = delete;
ExecutionContext& operator=(ExecutionContext const&) = delete;
EXPORT ~ExecutionContext();
void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; }
byte const* code() const { return m_data->code; }
uint64_t codeSize() const { return m_data->codeSize; }
h256 const& codeHash() const { return m_data->codeHash; }
bytes_ref getReturnData() const;
protected:
RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
byte* m_memData = nullptr;
uint64_t m_memSize = 0;
uint64_t m_memCap = 0;
public:
/// Reference to returned data (RETURN opcode used)
bytes_ref returnData;
};
class JIT class JIT
{ {
@ -23,14 +151,26 @@ public:
/// Returns `true` if the EVM code has been compiled and loaded into memory. /// Returns `true` if the EVM code has been compiled and loaded into memory.
/// In this case the code can be executed without overhead. /// In this case the code can be executed without overhead.
/// \param _codeHash The Keccak hash of the EVM code. /// \param _codeHash The Keccak hash of the EVM code.
static bool isCodeReady(h256 _codeHash); EXPORT static bool isCodeReady(h256 const& _codeHash);
private: /// Compile the given EVM code to machine code and make available for execution.
friend class dev::eth::jit::ExecutionEngine; EXPORT static void compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash);
static uint64_t getCode(h256 _codeHash); EXPORT static ReturnCode exec(ExecutionContext& _context);
static void mapCode(h256 _codeHash, uint64_t _funcAddr);
}; };
} }
} }
namespace std
{
template<> struct hash<dev::evmjit::h256>
{
size_t operator()(dev::evmjit::h256 const& _h) const
{
/// This implementation expects the argument to be a full 256-bit Keccak hash.
/// It does nothing more than returning a slice of the input hash.
return static_cast<size_t>(_h.words[0]);
};
};
}

17
evmjit/libevmjit-cpp/Env.cpp

@ -3,7 +3,6 @@
#include <libdevcore/SHA3.h> #include <libdevcore/SHA3.h>
#include <libevmcore/Params.h> #include <libevmcore/Params.h>
#include <libevm/ExtVMFace.h> #include <libevm/ExtVMFace.h>
#include <evmjit/DataTypes.h>
#include "Utils.h" #include "Utils.h"
@ -21,15 +20,15 @@ extern "C"
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value)
{ {
auto index = llvm2eth(*_index); auto index = jit2eth(*_index);
auto value = _env->store(index); // Interface uses native endianness auto value = _env->store(index); // Interface uses native endianness
*o_value = eth2llvm(value); *o_value = eth2jit(value);
} }
EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value) EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value)
{ {
auto index = llvm2eth(*_index); auto index = jit2eth(*_index);
auto value = llvm2eth(*_value); auto value = jit2eth(*_value);
if (value == 0 && _env->store(index) != 0) // If delete if (value == 0 && _env->store(index) != 0) // If delete
_env->sub.refunds += c_sstoreRefundGas; // Increase refund counter _env->sub.refunds += c_sstoreRefundGas; // Increase refund counter
@ -40,17 +39,17 @@ extern "C"
EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value) EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value)
{ {
auto u = _env->balance(right160(*_address)); auto u = _env->balance(right160(*_address));
*o_value = eth2llvm(u); *o_value = eth2jit(u);
} }
EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash) EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash)
{ {
*o_hash = _env->blockhash(llvm2eth(*_number)); *o_hash = _env->blockhash(jit2eth(*_number));
} }
EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
{ {
auto endowment = llvm2eth(*_endowment); auto endowment = jit2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{ {
u256 gas = *io_gas; u256 gas = *io_gas;
@ -65,7 +64,7 @@ extern "C"
EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{ {
CallParameters params; CallParameters params;
params.value = llvm2eth(*_value); params.value = jit2eth(*_value);
params.senderAddress = _env->myAddress; params.senderAddress = _env->myAddress;
params.receiveAddress = right160(*_receiveAddress); params.receiveAddress = right160(*_receiveAddress);
params.codeAddress = right160(*_codeAddress); params.codeAddress = right160(*_codeAddress);

41
evmjit/libevmjit-cpp/JitVM.cpp

@ -7,7 +7,6 @@
#include <libdevcore/SHA3.h> #include <libdevcore/SHA3.h>
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
#include "Utils.h" #include "Utils.h"
@ -20,8 +19,6 @@ extern "C" void env_sload(); // fake declaration for linker symbol stripping wor
bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{ {
using namespace jit;
auto rejected = false; auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope // TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max) rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
@ -40,36 +37,38 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice); m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
m_data.callData = _ext.data.data(); m_data.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size(); m_data.callDataSize = _ext.data.size();
m_data.address = eth2llvm(fromAddress(_ext.myAddress)); m_data.address = eth2jit(fromAddress(_ext.myAddress));
m_data.caller = eth2llvm(fromAddress(_ext.caller)); m_data.caller = eth2jit(fromAddress(_ext.caller));
m_data.origin = eth2llvm(fromAddress(_ext.origin)); m_data.origin = eth2jit(fromAddress(_ext.origin));
m_data.callValue = eth2llvm(_ext.value); m_data.callValue = eth2jit(_ext.value);
m_data.coinBase = eth2llvm(fromAddress(_ext.currentBlock.coinbaseAddress)); m_data.coinBase = eth2jit(fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.difficulty = eth2llvm(_ext.currentBlock.difficulty); m_data.difficulty = eth2jit(_ext.currentBlock.difficulty);
m_data.gasLimit = eth2llvm(_ext.currentBlock.gasLimit); m_data.gasLimit = eth2jit(_ext.currentBlock.gasLimit);
m_data.number = static_cast<decltype(m_data.number)>(_ext.currentBlock.number); m_data.number = static_cast<decltype(m_data.number)>(_ext.currentBlock.number);
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp); m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data(); m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size(); m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2llvm(_ext.codeHash); m_data.codeHash = eth2jit(_ext.codeHash);
auto env = reinterpret_cast<Env*>(&_ext); // Pass pointer to ExtVMFace casted to evmjit::Env* opaque type.
auto exitCode = m_engine.run(&m_data, env); // JIT will do nothing with the pointer, just pass it to Env callback functions implemented in Env.cpp.
m_context.init(m_data, reinterpret_cast<evmjit::Env*>(&_ext));
auto exitCode = evmjit::JIT::exec(m_context);
switch (exitCode) switch (exitCode)
{ {
case ReturnCode::Suicide: case evmjit::ReturnCode::Suicide:
_ext.suicide(right160(llvm2eth(m_data.address))); _ext.suicide(right160(jit2eth(m_data.address)));
break; break;
case ReturnCode::BadJumpDestination: case evmjit::ReturnCode::BadJumpDestination:
BOOST_THROW_EXCEPTION(BadJumpDestination()); BOOST_THROW_EXCEPTION(BadJumpDestination());
case ReturnCode::OutOfGas: case evmjit::ReturnCode::OutOfGas:
BOOST_THROW_EXCEPTION(OutOfGas()); BOOST_THROW_EXCEPTION(OutOfGas());
case ReturnCode::StackUnderflow: case evmjit::ReturnCode::StackUnderflow: // FIXME: Remove support for detail errors
BOOST_THROW_EXCEPTION(StackUnderflow()); BOOST_THROW_EXCEPTION(StackUnderflow());
case ReturnCode::BadInstruction: case evmjit::ReturnCode::BadInstruction:
BOOST_THROW_EXCEPTION(BadInstruction()); BOOST_THROW_EXCEPTION(BadInstruction());
case ReturnCode::LinkerWorkaround: // never happens case evmjit::ReturnCode::LinkerWorkaround: // never happens
env_sload(); // but forces linker to include env_* JIT callback functions env_sload(); // but forces linker to include env_* JIT callback functions
break; break;
default: default:
@ -77,7 +76,7 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
} }
io_gas = m_data.gas; io_gas = m_data.gas;
return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)}; return {std::get<0>(m_context.returnData), std::get<1>(m_context.returnData)};
} }
} }

6
evmjit/libevmjit-cpp/JitVM.h

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <libevm/VMFace.h> #include <libevm/VMFace.h>
#include <evmjit/libevmjit/ExecutionEngine.h> #include <evmjit/JIT.h>
namespace dev namespace dev
{ {
@ -14,8 +14,8 @@ public:
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final; virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final;
private: private:
jit::RuntimeData m_data; evmjit::RuntimeData m_data;
jit::ExecutionEngine m_engine; evmjit::ExecutionContext m_context;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT
}; };

29
evmjit/libevmjit-cpp/Utils.h

@ -1,40 +1,41 @@
#pragma once #pragma once
#include <evmjit/DataTypes.h> #include <evmjit/JIT.h>
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
inline u256 llvm2eth(evmjit::i256 _i) /// Converts EVM JIT representation of 256-bit integer to eth type dev::u256.
inline u256 jit2eth(evmjit::i256 _i)
{ {
u256 u = 0; u256 u = _i.words[3];
u |= _i.d;
u <<= 64; u <<= 64;
u |= _i.c; u |= _i.words[2];
u <<= 64; u <<= 64;
u |= _i.b; u |= _i.words[1];
u <<= 64; u <<= 64;
u |= _i.a; u |= _i.words[0];
return u; return u;
} }
inline evmjit::i256 eth2llvm(u256 _u) /// Converts eth type dev::u256 to EVM JIT representation of 256-bit integer.
inline evmjit::i256 eth2jit(u256 _u)
{ {
evmjit::i256 i; evmjit::i256 i;
u256 mask = 0xFFFFFFFFFFFFFFFF; i.words[0] = static_cast<uint64_t>(_u);
i.a = static_cast<uint64_t>(_u & mask);
_u >>= 64; _u >>= 64;
i.b = static_cast<uint64_t>(_u & mask); i.words[1] = static_cast<uint64_t>(_u);
_u >>= 64; _u >>= 64;
i.c = static_cast<uint64_t>(_u & mask); i.words[2] = static_cast<uint64_t>(_u);
_u >>= 64; _u >>= 64;
i.d = static_cast<uint64_t>(_u & mask); i.words[3] = static_cast<uint64_t>(_u);
return i; return i;
} }
inline evmjit::h256 eth2llvm(h256 _u) /// Converts eth type dev::h256 to EVM JIT representation of 256-bit hash value.
inline evmjit::h256 eth2jit(h256 _u)
{ {
/// Just directly copies memory /// Just directly copies memory
return *(evmjit::h256*)&_u; return *(evmjit::h256*)&_u;

674
evmjit/libevmjit/Arith256.cpp

@ -4,6 +4,7 @@
#include <iomanip> #include <iomanip>
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
@ -32,186 +33,364 @@ void Arith256::debug(llvm::Value* _value, char _c)
createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)}); createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)});
} }
llvm::Function* Arith256::getMulFunc() llvm::Function* Arith256::getMulFunc(llvm::Module& _module)
{ {
auto& func = m_mul; static const auto funcName = "evm.mul.i256";
if (!func) if (auto func = _module.getFunction(funcName))
{ return func;
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule()); llvm::Type* argTypes[] = {Type::Word, Type::Word};
func->setDoesNotThrow(); auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotAccessMemory(); func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto i64 = Type::Size;
auto i128 = builder.getIntNTy(128);
auto i256 = Type::Word;
auto c64 = Constant::get(64);
auto c128 = Constant::get(128);
auto c192 = Constant::get(192);
auto x_lo = builder.CreateTrunc(x, i64, "x.lo");
auto y_lo = builder.CreateTrunc(y, i64, "y.lo");
auto x_mi = builder.CreateTrunc(builder.CreateLShr(x, c64), i64);
auto y_mi = builder.CreateTrunc(builder.CreateLShr(y, c64), i64);
auto x_hi = builder.CreateTrunc(builder.CreateLShr(x, c128), i128);
auto y_hi = builder.CreateTrunc(builder.CreateLShr(y, c128), i128);
auto t1 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_lo, i128));
auto t2 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_mi, i128));
auto t3 = builder.CreateMul(builder.CreateZExt(x_lo, i128), y_hi);
auto t4 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_lo, i128));
auto t5 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_mi, i128));
auto t6 = builder.CreateMul(builder.CreateZExt(x_mi, i128), y_hi);
auto t7 = builder.CreateMul(x_hi, builder.CreateZExt(y_lo, i128));
auto t8 = builder.CreateMul(x_hi, builder.CreateZExt(y_mi, i128));
auto p = builder.CreateZExt(t1, i256);
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i256), c64));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i256), c64));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t5, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t6, i256), c192));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t7, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t8, i256), c192));
builder.CreateRet(p);
return func;
}
auto x = &func->getArgumentList().front(); llvm::Function* Arith256::getMul512Func(llvm::Module& _module)
x->setName("x"); {
auto y = x->getNextNode(); static const auto funcName = "evm.mul.i512";
y->setName("y"); if (auto func = _module.getFunction(funcName))
return func;
auto i512Ty = llvm::IntegerType::get(_module.getContext(), 512);
auto func = llvm::Function::Create(llvm::FunctionType::get(i512Ty, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto i128 = builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = builder.CreateZExt(builder.CreateTrunc(x, i128, "x.lo"), i256);
auto y_lo = builder.CreateZExt(builder.CreateTrunc(y, i128, "y.lo"), i256);
auto x_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256);
auto y_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256);
auto mul256Func = getMulFunc(_module);
auto t1 = builder.CreateCall(mul256Func, {x_lo, y_lo});
auto t2 = builder.CreateCall(mul256Func, {x_lo, y_hi});
auto t3 = builder.CreateCall(mul256Func, {x_hi, y_lo});
auto t4 = builder.CreateCall(mul256Func, {x_hi, y_hi});
auto p = builder.CreateZExt(t1, i512Ty);
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i512Ty), builder.getIntN(512, 128)));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i512Ty), builder.getIntN(512, 128)));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i512Ty), builder.getIntN(512, 256)));
builder.CreateRet(p);
InsertPointGuard guard{m_builder};
auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(bb);
auto i64 = Type::Size;
auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word;
auto c64 = Constant::get(64);
auto c128 = Constant::get(128);
auto c192 = Constant::get(192);
auto x_lo = m_builder.CreateTrunc(x, i64, "x.lo");
auto y_lo = m_builder.CreateTrunc(y, i64, "y.lo");
auto x_mi = m_builder.CreateTrunc(m_builder.CreateLShr(x, c64), i64);
auto y_mi = m_builder.CreateTrunc(m_builder.CreateLShr(y, c64), i64);
auto x_hi = m_builder.CreateTrunc(m_builder.CreateLShr(x, c128), i128);
auto y_hi = m_builder.CreateTrunc(m_builder.CreateLShr(y, c128), i128);
auto t1 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_lo, i128));
auto t2 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_mi, i128));
auto t3 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), y_hi);
auto t4 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_lo, i128));
auto t5 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_mi, i128));
auto t6 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), y_hi);
auto t7 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_lo, i128));
auto t8 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_mi, i128));
auto p = m_builder.CreateZExt(t1, i256);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i256), c64));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i256), c64));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t5, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t6, i256), c192));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t7, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t8, i256), c192));
m_builder.CreateRet(p);
}
return func; return func;
} }
llvm::Function* Arith256::getMul512Func() namespace
{ {
auto& func = m_mul512; llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName)
if (!func) {
{ // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
auto i512 = m_builder.getIntNTy(512); // The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule()); auto retType = llvm::VectorType::get(_type, 2);
func->setDoesNotThrow(); auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
func->setDoesNotAccessMemory(); func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto zero = llvm::ConstantInt::get(_type, 0);
auto one = llvm::ConstantInt::get(_type, 1);
auto x = &func->getArgumentList().front();
x->setName("x");
auto yArg = x->getNextNode();
yArg->setName("y");
auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func);
auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func);
auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func);
auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func);
auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func);
auto builder = llvm::IRBuilder<>{entryBB};
auto yLEx = builder.CreateICmpULE(yArg, x);
auto r0 = x;
builder.CreateCondBr(yLEx, mainBB, returnBB);
builder.SetInsertPoint(mainBB);
auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type);
// both y and r are non-zero
auto yLz = builder.CreateCall(ctlzIntr, {yArg, builder.getInt1(true)}, "y.lz");
auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz");
auto i0 = builder.CreateNUWSub(yLz, rLz, "i0");
auto y0 = builder.CreateShl(yArg, i0);
builder.CreateBr(loopBB);
builder.SetInsertPoint(loopBB);
auto yPhi = builder.CreatePHI(_type, 2, "y.phi");
auto rPhi = builder.CreatePHI(_type, 2, "r.phi");
auto iPhi = builder.CreatePHI(_type, 2, "i.phi");
auto qPhi = builder.CreatePHI(_type, 2, "q.phi");
auto rUpdate = builder.CreateNUWSub(rPhi, yPhi);
auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
auto rGEy = builder.CreateICmpUGE(rPhi, yPhi);
auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1");
auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q");
auto iZero = builder.CreateICmpEQ(iPhi, zero);
builder.CreateCondBr(iZero, returnBB, continueBB);
builder.SetInsertPoint(continueBB);
auto i2 = builder.CreateNUWSub(iPhi, one);
auto q2 = builder.CreateShl(q1, one);
auto y2 = builder.CreateLShr(yPhi, one);
builder.CreateBr(loopBB);
yPhi->addIncoming(y0, mainBB);
yPhi->addIncoming(y2, continueBB);
rPhi->addIncoming(r0, mainBB);
rPhi->addIncoming(r1, continueBB);
iPhi->addIncoming(i0, mainBB);
iPhi->addIncoming(i2, continueBB);
qPhi->addIncoming(zero, mainBB);
qPhi->addIncoming(q2, continueBB);
builder.SetInsertPoint(returnBB);
auto qRet = builder.CreatePHI(_type, 2, "q.ret");
qRet->addIncoming(zero, entryBB);
qRet->addIncoming(q1, loopBB);
auto rRet = builder.CreatePHI(_type, 2, "r.ret");
rRet->addIncoming(r0, entryBB);
rRet->addIncoming(r1, loopBB);
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0");
ret = builder.CreateInsertElement(ret, rRet, 1, "ret");
builder.CreateRet(ret);
auto x = &func->getArgumentList().front(); return func;
x->setName("x"); }
auto y = x->getNextNode(); }
y->setName("y");
llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.udivrem.i256";
if (auto func = _module.getFunction(funcName))
return func;
return createUDivRemFunc(Type::Word, _module, funcName);
}
llvm::Function* Arith256::getUDivRem512Func(llvm::Module& _module)
{
static const auto funcName = "evm.udivrem.i512";
if (auto func = _module.getFunction(funcName))
return func;
return createUDivRemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName);
}
llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module)
{
static const auto funcName = "evm.udiv.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto udivremFunc = getUDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto udivrem = builder.CreateCall(udivremFunc, {x, y});
auto udiv = builder.CreateExtractElement(udivrem, uint64_t(0));
builder.CreateRet(udiv);
InsertPointGuard guard{m_builder};
auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(bb);
auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = m_builder.CreateZExt(m_builder.CreateTrunc(x, i128, "x.lo"), i256);
auto y_lo = m_builder.CreateZExt(m_builder.CreateTrunc(y, i128, "y.lo"), i256);
auto x_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256);
auto y_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256);
auto t1 = createCall(getMulFunc(), {x_lo, y_lo});
auto t2 = createCall(getMulFunc(), {x_lo, y_hi});
auto t3 = createCall(getMulFunc(), {x_hi, y_lo});
auto t4 = createCall(getMulFunc(), {x_hi, y_hi});
auto p = m_builder.CreateZExt(t1, i512);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i512), m_builder.getIntN(512, 128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i512), m_builder.getIntN(512, 128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i512), m_builder.getIntN(512, 256)));
m_builder.CreateRet(p);
}
return func; return func;
} }
llvm::Function* Arith256::getDivFunc(llvm::Type* _type) namespace
{
llvm::Function* createURemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName)
{ {
auto& func = _type == Type::Word ? m_div : m_div512; auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : Arith256::getUDivRem512Func(_module);
if (!func) auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
{ func->setDoesNotThrow();
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research func->setDoesNotAccessMemory();
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder
llvm::Type* argTypes[] = {_type, _type}; auto x = &func->getArgumentList().front();
auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef<llvm::Type*>{argTypes}); x->setName("x");
auto funcName = _type == Type::Word ? "div" : "div512"; auto y = x->getNextNode();
func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule()); y->setName("y");
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto zero = llvm::ConstantInt::get(_type, 0); auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto one = llvm::ConstantInt::get(_type, 1); auto builder = llvm::IRBuilder<>{bb};
auto udivrem = builder.CreateCall(udivremFunc, {x, y});
auto r = builder.CreateExtractElement(udivrem, uint64_t(1));
builder.CreateRet(r);
auto x = &func->getArgumentList().front(); return func;
x->setName("x"); }
auto yArg = x->getNextNode(); }
yArg->setName("y");
InsertPointGuard guard{m_builder}; llvm::Function* Arith256::getURem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.urem.i256";
if (auto func = _module.getFunction(funcName))
return func;
return createURemFunc(Type::Word, _module, funcName);
}
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func); llvm::Function* Arith256::getURem512Func(llvm::Module& _module)
auto mainBB = llvm::BasicBlock::Create(m_builder.getContext(), "Main", func); {
auto loopBB = llvm::BasicBlock::Create(m_builder.getContext(), "Loop", func); static const auto funcName = "evm.urem.i512";
auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", func); if (auto func = _module.getFunction(funcName))
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); return func;
return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName);
}
m_builder.SetInsertPoint(entryBB); llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module)
auto yNonZero = m_builder.CreateICmpNE(yArg, zero); {
auto yLEx = m_builder.CreateICmpULE(yArg, x); static const auto funcName = "evm.sdivrem.i256";
auto r0 = m_builder.CreateSelect(yNonZero, x, zero, "r0"); if (auto func = _module.getFunction(funcName))
m_builder.CreateCondBr(m_builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB); return func;
m_builder.SetInsertPoint(mainBB);
auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type);
// both y and r are non-zero
auto yLz = m_builder.CreateCall(ctlzIntr, {yArg, m_builder.getInt1(true)}, "y.lz");
auto rLz = m_builder.CreateCall(ctlzIntr, {r0, m_builder.getInt1(true)}, "r.lz");
auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0");
auto y0 = m_builder.CreateShl(yArg, i0);
m_builder.CreateBr(loopBB);
m_builder.SetInsertPoint(loopBB);
auto yPhi = m_builder.CreatePHI(_type, 2, "y.phi");
auto rPhi = m_builder.CreatePHI(_type, 2, "r.phi");
auto iPhi = m_builder.CreatePHI(_type, 2, "i.phi");
auto qPhi = m_builder.CreatePHI(_type, 2, "q.phi");
auto rUpdate = m_builder.CreateNUWSub(rPhi, yPhi);
auto qUpdate = m_builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
auto rGEy = m_builder.CreateICmpUGE(rPhi, yPhi);
auto r1 = m_builder.CreateSelect(rGEy, rUpdate, rPhi, "r1");
auto q1 = m_builder.CreateSelect(rGEy, qUpdate, qPhi, "q");
auto iZero = m_builder.CreateICmpEQ(iPhi, zero);
m_builder.CreateCondBr(iZero, returnBB, continueBB);
m_builder.SetInsertPoint(continueBB); auto udivremFunc = getUDivRem256Func(_module);
auto i2 = m_builder.CreateNUWSub(iPhi, one);
auto q2 = m_builder.CreateShl(q1, one); auto retType = llvm::VectorType::get(Type::Word, 2);
auto y2 = m_builder.CreateLShr(yPhi, one); auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
m_builder.CreateBr(loopBB); func->setDoesNotThrow();
func->setDoesNotAccessMemory();
yPhi->addIncoming(y0, mainBB);
yPhi->addIncoming(y2, continueBB); auto x = &func->getArgumentList().front();
rPhi->addIncoming(r0, mainBB); x->setName("x");
rPhi->addIncoming(r1, continueBB); auto y = x->getNextNode();
iPhi->addIncoming(i0, mainBB); y->setName("y");
iPhi->addIncoming(i2, continueBB);
qPhi->addIncoming(zero, mainBB); auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func);
qPhi->addIncoming(q2, continueBB); auto builder = llvm::IRBuilder<>{bb};
auto xIsNeg = builder.CreateICmpSLT(x, Constant::get(0));
auto xNeg = builder.CreateSub(Constant::get(0), x);
auto xAbs = builder.CreateSelect(xIsNeg, xNeg, x);
auto yIsNeg = builder.CreateICmpSLT(y, Constant::get(0));
auto yNeg = builder.CreateSub(Constant::get(0), y);
auto yAbs = builder.CreateSelect(yIsNeg, yNeg, y);
auto res = builder.CreateCall(udivremFunc, {xAbs, yAbs});
auto qAbs = builder.CreateExtractElement(res, uint64_t(0));
auto rAbs = builder.CreateExtractElement(res, 1);
// the remainder has the same sign as dividend
auto rNeg = builder.CreateSub(Constant::get(0), rAbs);
auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs);
auto qNeg = builder.CreateSub(Constant::get(0), qAbs);
auto xyOpposite = builder.CreateXor(xIsNeg, yIsNeg);
auto q = builder.CreateSelect(xyOpposite, qNeg, qAbs);
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0));
ret = builder.CreateInsertElement(ret, r, 1);
builder.CreateRet(ret);
return func;
}
llvm::Function* Arith256::getSDiv256Func(llvm::Module& _module)
{
static const auto funcName = "evm.sdiv.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto sdivremFunc = getSDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y});
auto q = builder.CreateExtractElement(sdivrem, uint64_t(0));
builder.CreateRet(q);
return func;
}
llvm::Function* Arith256::getSRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.srem.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto sdivremFunc = getSDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y});
auto r = builder.CreateExtractElement(sdivrem, uint64_t(1));
builder.CreateRet(r);
m_builder.SetInsertPoint(returnBB);
auto qRet = m_builder.CreatePHI(_type, 2, "q.ret");
qRet->addIncoming(zero, entryBB);
qRet->addIncoming(q1, loopBB);
auto rRet = m_builder.CreatePHI(_type, 2, "r.ret");
rRet->addIncoming(r0, entryBB);
rRet->addIncoming(r1, loopBB);
auto ret = m_builder.CreateInsertValue(llvm::UndefValue::get(retType), qRet, 0, "ret0");
ret = m_builder.CreateInsertValue(ret, rRet, 1, "ret");
m_builder.CreateRet(ret);
}
return func; return func;
} }
@ -260,14 +439,15 @@ llvm::Function* Arith256::getExpFunc()
m_builder.CreateCondBr(eOdd, updateBB, continueBB); m_builder.CreateCondBr(eOdd, updateBB, continueBB);
m_builder.SetInsertPoint(updateBB); m_builder.SetInsertPoint(updateBB);
auto r0 = createCall(getMulFunc(), {r, b}); auto mul256Func = getMulFunc(*getModule());
auto r0 = createCall(mul256Func, {r, b});
m_builder.CreateBr(continueBB); m_builder.CreateBr(continueBB);
m_builder.SetInsertPoint(continueBB); m_builder.SetInsertPoint(continueBB);
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1");
r1->addIncoming(r, bodyBB); r1->addIncoming(r, bodyBB);
r1->addIncoming(r0, updateBB); r1->addIncoming(r0, updateBB);
auto b1 = createCall(getMulFunc(), {b, b}); auto b1 = createCall(mul256Func, {b, b});
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1");
m_builder.CreateBr(headerBB); m_builder.CreateBr(headerBB);
@ -284,137 +464,6 @@ llvm::Function* Arith256::getExpFunc()
return m_exp; return m_exp;
} }
llvm::Function* Arith256::getAddModFunc()
{
if (!m_addmod)
{
auto i512Ty = m_builder.getIntNTy(512);
llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word};
m_addmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "addmod", getModule());
m_addmod->setDoesNotThrow();
m_addmod->setDoesNotAccessMemory();
auto x = &m_addmod->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto mod = y->getNextNode();
mod->setName("m");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_addmod);
m_builder.SetInsertPoint(entryBB);
auto x512 = m_builder.CreateZExt(x, i512Ty, "x512");
auto y512 = m_builder.CreateZExt(y, i512Ty, "y512");
auto m512 = m_builder.CreateZExt(mod, i512Ty, "m512");
auto s = m_builder.CreateAdd(x512, y512, "s");
auto d = createCall(getDivFunc(i512Ty), {s, m512});
auto r = m_builder.CreateExtractValue(d, 1, "r");
m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word));
}
return m_addmod;
}
llvm::Function* Arith256::getMulModFunc()
{
if (!m_mulmod)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word};
m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule());
m_mulmod->setDoesNotThrow();
m_mulmod->setDoesNotAccessMemory();
auto i512Ty = m_builder.getIntNTy(512);
auto x = &m_mulmod->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto mod = y->getNextNode();
mod->setName("mod");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod);
m_builder.SetInsertPoint(entryBB);
auto p = createCall(getMul512Func(), {x, y});
auto m = m_builder.CreateZExt(mod, i512Ty, "m");
auto d = createCall(getDivFunc(i512Ty), {p, m});
auto r = m_builder.CreateExtractValue(d, 1, "r");
r = m_builder.CreateTrunc(r, Type::Word);
m_builder.CreateRet(r);
}
return m_mulmod;
}
llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2)
{
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
{
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
return Constant::get(c1->getValue() * c2->getValue());
}
return createCall(getMulFunc(), {_arg1, _arg2});
}
std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
// {
// if (!c2->getValue())
// return std::make_pair(Constant::get(0), Constant::get(0));
// auto div = Constant::get(c1->getValue().udiv(c2->getValue()));
// auto mod = Constant::get(c1->getValue().urem(c2->getValue()));
// return std::make_pair(div, mod);
// }
// }
auto r = createCall(getDivFunc(Type::Word), {_arg1, _arg2});
auto div = m_builder.CreateExtractValue(r, 0, "div");
auto mod = m_builder.CreateExtractValue(r, 1, "mod");
return std::make_pair(div, mod);
}
std::pair<llvm::Value*, llvm::Value*> Arith256::sdiv(llvm::Value* _x, llvm::Value* _y)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_x))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_y))
// {
// if (!c2->getValue())
// return std::make_pair(Constant::get(0), Constant::get(0));
// auto div = Constant::get(c1->getValue().sdiv(c2->getValue()));
// auto mod = Constant::get(c1->getValue().srem(c2->getValue()));
// return std::make_pair(div, mod);
// }
// }
auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0));
auto xNeg = m_builder.CreateSub(Constant::get(0), _x);
auto xAbs = m_builder.CreateSelect(xIsNeg, xNeg, _x);
auto yIsNeg = m_builder.CreateICmpSLT(_y, Constant::get(0));
auto yNeg = m_builder.CreateSub(Constant::get(0), _y);
auto yAbs = m_builder.CreateSelect(yIsNeg, yNeg, _y);
auto res = div(xAbs, yAbs);
// the reminder has the same sign as dividend
auto rNeg = m_builder.CreateSub(Constant::get(0), res.second);
res.second = m_builder.CreateSelect(xIsNeg, rNeg, res.second);
auto qNeg = m_builder.CreateSub(Constant::get(0), res.first);
auto xyOpposite = m_builder.CreateXor(xIsNeg, yIsNeg);
res.first = m_builder.CreateSelect(xyOpposite, qNeg, res.first);
return res;
}
llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2)
{ {
// while (e != 0) { // while (e != 0) {
@ -445,49 +494,6 @@ llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2)
return createCall(getExpFunc(), {_arg1, _arg2}); return createCall(getExpFunc(), {_arg1, _arg2});
} }
llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
// {
// if (auto c3 = llvm::dyn_cast<llvm::ConstantInt>(_arg3))
// {
// if (!c3->getValue())
// return Constant::get(0);
// auto s = c1->getValue().zext(256+64) + c2->getValue().zext(256+64);
// auto r = s.urem(c3->getValue().zext(256+64)).trunc(256);
// return Constant::get(r);
// }
// }
// }
return createCall(getAddModFunc(), {_arg1, _arg2, _arg3});
}
llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
// {
// if (auto c3 = llvm::dyn_cast<llvm::ConstantInt>(_arg3))
// {
// if (!c3->getValue())
// return Constant::get(0);
// auto p = c1->getValue().zext(512) * c2->getValue().zext(512);
// auto r = p.urem(c3->getValue().zext(512)).trunc(256);
// return Constant::get(r);
// }
// }
// }
return createCall(getMulModFunc(), {_arg1, _arg2, _arg3});
}
} }
} }
} }

27
evmjit/libevmjit/Arith256.h

@ -14,30 +14,25 @@ class Arith256 : public CompilerHelper
public: public:
Arith256(llvm::IRBuilder<>& _builder); Arith256(llvm::IRBuilder<>& _builder);
llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2);
std::pair<llvm::Value*, llvm::Value*> div(llvm::Value* _arg1, llvm::Value* _arg2);
std::pair<llvm::Value*, llvm::Value*> sdiv(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
llvm::Value* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
void debug(llvm::Value* _value, char _c); void debug(llvm::Value* _value, char _c);
static llvm::Function* getMulFunc(llvm::Module& _module);
static llvm::Function* getMul512Func(llvm::Module& _module);
static llvm::Function* getUDiv256Func(llvm::Module& _module);
static llvm::Function* getURem256Func(llvm::Module& _module);
static llvm::Function* getURem512Func(llvm::Module& _module);
static llvm::Function* getUDivRem256Func(llvm::Module& _module);
static llvm::Function* getSDiv256Func(llvm::Module& _module);
static llvm::Function* getSRem256Func(llvm::Module& _module);
static llvm::Function* getSDivRem256Func(llvm::Module& _module);
static llvm::Function* getUDivRem512Func(llvm::Module& _module);
private: private:
llvm::Function* getMulFunc();
llvm::Function* getMul512Func();
llvm::Function* getDivFunc(llvm::Type* _type);
llvm::Function* getExpFunc(); llvm::Function* getExpFunc();
llvm::Function* getAddModFunc();
llvm::Function* getMulModFunc();
llvm::Function* m_mul = nullptr;
llvm::Function* m_mul512 = nullptr;
llvm::Function* m_div = nullptr;
llvm::Function* m_div512 = nullptr;
llvm::Function* m_exp = nullptr; llvm::Function* m_exp = nullptr;
llvm::Function* m_addmod = nullptr;
llvm::Function* m_mulmod = nullptr;
llvm::Function* m_debug = nullptr; llvm::Function* m_debug = nullptr;
}; };

3
evmjit/libevmjit/Array.cpp

@ -6,7 +6,6 @@
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
#include "Runtime.h"
#include "Utils.h" #include "Utils.h"
namespace dev namespace dev
@ -17,7 +16,6 @@ namespace jit
{ {
static const auto c_reallocStep = 1; static const auto c_reallocStep = 1;
static const auto c_reallocMultipier = 2;
llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name) llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name)
{ {
@ -56,7 +54,6 @@ llvm::Function* Array::createArrayPushFunc()
m_builder.SetInsertPoint(reallocBB); m_builder.SetInsertPoint(reallocBB);
auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap"); auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap");
//newCap = m_builder.CreateNUWMul(newCap, m_builder.getInt64(c_reallocMultipier));
auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32 auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32
auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes"); auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes");
auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes"); auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes");

2
evmjit/libevmjit/BasicBlock.h

@ -11,7 +11,7 @@ namespace eth
{ {
namespace jit namespace jit
{ {
using namespace evmjit;
using instr_idx = uint64_t; using instr_idx = uint64_t;
class BasicBlock class BasicBlock

8
evmjit/libevmjit/CMakeLists.txt

@ -1,26 +1,22 @@
set(TARGET_NAME evmjit) set(TARGET_NAME evmjit)
set(SOURCES set(SOURCES
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h
Arith256.cpp Arith256.h Arith256.cpp Arith256.h
Array.cpp Array.h Array.cpp Array.h
BasicBlock.cpp BasicBlock.h BasicBlock.cpp BasicBlock.h
Cache.cpp Cache.h Cache.cpp Cache.h
Common.h Common.h
Compiler.cpp Compiler.h Compiler.cpp Compiler.h
CompilerHelper.cpp CompilerHelper.h CompilerHelper.cpp CompilerHelper.h
${EVMJIT_INCLUDE_DIR}/evmjit/DataTypes.h
Endianness.cpp Endianness.h Endianness.cpp Endianness.h
ExecStats.cpp ExecStats.h ExecStats.cpp ExecStats.h
ExecutionEngine.cpp ExecutionEngine.h
Ext.cpp Ext.h Ext.cpp Ext.h
GasMeter.cpp GasMeter.h GasMeter.cpp GasMeter.h
Instruction.cpp Instruction.h Instruction.cpp Instruction.h
interface.cpp interface.h interface.cpp interface.h
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h
Memory.cpp Memory.h Memory.cpp Memory.h
Optimizer.cpp Optimizer.h Optimizer.cpp Optimizer.h
Runtime.cpp Runtime.h
RuntimeData.h
RuntimeManager.cpp RuntimeManager.h RuntimeManager.cpp RuntimeManager.h
Stack.cpp Stack.h Stack.cpp Stack.h
Type.cpp Type.h Type.cpp Type.h

27
evmjit/libevmjit/Cache.cpp

@ -12,15 +12,13 @@
#include <llvm/Support/raw_os_ostream.h> #include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "ExecutionEngine.h" #include "ExecStats.h"
#include "Utils.h" #include "Utils.h"
#include "BuildInfo.gen.h" #include "BuildInfo.gen.h"
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
namespace namespace
@ -29,7 +27,7 @@ namespace
std::mutex x_cacheMutex; std::mutex x_cacheMutex;
CacheMode g_mode; CacheMode g_mode;
std::unique_ptr<llvm::MemoryBuffer> g_lastObject; std::unique_ptr<llvm::MemoryBuffer> g_lastObject;
ExecutionEngineListener* g_listener; JITListener* g_listener;
static const size_t c_versionStampLength = 32; static const size_t c_versionStampLength = 32;
llvm::StringRef getLibVersionStamp() llvm::StringRef getLibVersionStamp()
@ -44,15 +42,25 @@ namespace
} }
} }
ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener) ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener)
{ {
static ObjectCache objectCache;
Guard g{x_cacheMutex}; Guard g{x_cacheMutex};
g_mode = _mode; g_mode = _mode;
g_listener = _listener; g_listener = _listener;
return &objectCache;
if (g_mode == CacheMode::clear)
{
Cache::clear();
g_mode = CacheMode::off;
}
if (g_mode != CacheMode::off)
{
static ObjectCache objectCache;
return &objectCache;
}
return nullptr;
} }
void Cache::clear() void Cache::clear()
@ -185,4 +193,3 @@ std::unique_ptr<llvm::MemoryBuffer> ObjectCache::getObject(llvm::Module const* _
} }
} }
}

9
evmjit/libevmjit/Cache.h

@ -14,11 +14,9 @@ namespace llvm
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{ {
namespace jit class JITListener;
{
class ExecutionEngineListener;
enum class CacheMode enum class CacheMode
{ {
@ -47,7 +45,7 @@ public:
class Cache class Cache
{ {
public: public:
static ObjectCache* getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener); static ObjectCache* init(CacheMode _mode, JITListener* _listener);
static std::unique_ptr<llvm::Module> getObject(std::string const& id); static std::unique_ptr<llvm::Module> getObject(std::string const& id);
/// Clears cache storage /// Clears cache storage
@ -59,4 +57,3 @@ public:
} }
} }
}

39
evmjit/libevmjit/Common.h

@ -1,53 +1,16 @@
#pragma once #pragma once
#include <tuple>
#include <cstdint> #include <cstdint>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
using byte = uint8_t; using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
using code_iterator = byte const*; using code_iterator = byte const*;
enum class ReturnCode
{
// Success codes
Stop = 0,
Return = 1,
Suicide = 2,
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMConfigError = -101,
LLVMCompileError = -102,
LLVMLinkError = -103,
UnexpectedException = -111,
LinkerWorkaround = -299,
};
#define UNTESTED assert(false) #define UNTESTED assert(false)
} }
} }
}

152
evmjit/libevmjit/Compiler.cpp

@ -49,6 +49,11 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
return _curr + offset; return _curr + offset;
}; };
// Skip all STOPs in the end
for (; _codeEnd != _codeBegin; --_codeEnd)
if (*(_codeEnd - 1) != static_cast<byte>(Instruction::STOP))
break;
auto begin = _codeBegin; // begin of current block auto begin = _codeBegin; // begin of current block
bool nextJumpDest = false; bool nextJumpDest = false;
for (auto curr = begin, next = begin; curr != _codeEnd; curr = next) for (auto curr = begin, next = begin; curr != _codeEnd; curr = next)
@ -159,10 +164,10 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
// TODO: Create Stop basic block on demand // TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm(); auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm();
m_builder.CreateCondBr(normalFlow, firstBB, abortBB, Type::expectTrue); m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue);
for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt)
{ {
@ -178,7 +183,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
m_builder.SetInsertPoint(m_stopBB); m_builder.SetInsertPoint(m_stopBB);
runtimeManager.exit(ReturnCode::Stop); runtimeManager.exit(ReturnCode::Stop);
m_builder.SetInsertPoint(abortBB); m_builder.SetInsertPoint(m_abortBB);
runtimeManager.exit(ReturnCode::OutOfGas); runtimeManager.exit(ReturnCode::OutOfGas);
removeDeadBlocks(); removeDeadBlocks();
@ -270,44 +275,96 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
{ {
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = _arith.mul(lhs, rhs); auto res = m_builder.CreateMul(lhs, rhs);
stack.push(res); stack.push(res);
break; break;
} }
case Instruction::DIV: case Instruction::DIV:
{ {
auto lhs = stack.pop(); auto d = stack.pop();
auto rhs = stack.pop(); auto n = stack.pop();
auto res = _arith.div(lhs, rhs); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
stack.push(res.first); n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateUDiv(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
stack.push(r);
break; break;
} }
case Instruction::SDIV: case Instruction::SDIV:
{ {
auto lhs = stack.pop(); auto d = stack.pop();
auto rhs = stack.pop(); auto n = stack.pop();
auto res = _arith.sdiv(lhs, rhs); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
stack.push(res.first); auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateSDiv(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
auto dNeg = m_builder.CreateSub(Constant::get(0), d);
r = m_builder.CreateSelect(divByMinusOne, dNeg, r); // protect against undef i256.min / -1
stack.push(r);
break; break;
} }
case Instruction::MOD: case Instruction::MOD:
{ {
auto lhs = stack.pop(); auto d = stack.pop();
auto rhs = stack.pop(); auto n = stack.pop();
auto res = _arith.div(lhs, rhs); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
stack.push(res.second); n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateURem(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
stack.push(r);
break; break;
} }
case Instruction::SMOD: case Instruction::SMOD:
{ {
auto lhs = stack.pop(); auto d = stack.pop();
auto rhs = stack.pop(); auto n = stack.pop();
auto res = _arith.sdiv(lhs, rhs); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
stack.push(res.second); auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateSRem(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
r = m_builder.CreateSelect(divByMinusOne, Constant::get(0), r); // protect against undef i256.min / -1
stack.push(r);
break;
}
case Instruction::ADDMOD:
{
auto i512Ty = m_builder.getIntNTy(512);
auto a = stack.pop();
auto b = stack.pop();
auto m = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0));
a = m_builder.CreateZExt(a, i512Ty);
b = m_builder.CreateZExt(b, i512Ty);
m = m_builder.CreateZExt(m, i512Ty);
auto s = m_builder.CreateNUWAdd(a, b);
s = m_builder.CreateURem(s, m);
s = m_builder.CreateTrunc(s, Type::Word);
s = m_builder.CreateSelect(divByZero, Constant::get(0), s);
stack.push(s);
break;
}
case Instruction::MULMOD:
{
auto i512Ty = m_builder.getIntNTy(512);
auto a = stack.pop();
auto b = stack.pop();
auto m = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0));
m = m_builder.CreateZExt(m, i512Ty);
// TODO: Add support for i256 x i256 -> i512 in LowerEVM pass
llvm::Value* p = m_builder.CreateCall(Arith256::getMul512Func(*_basicBlock.llvm()->getParent()->getParent()), {a, b});
p = m_builder.CreateURem(p, m);
p = m_builder.CreateTrunc(p, Type::Word);
p = m_builder.CreateSelect(divByZero, Constant::get(0), p);
stack.push(p);
break; break;
} }
@ -417,48 +474,29 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::BYTE: case Instruction::BYTE:
{ {
const auto byteNum = stack.pop(); const auto idx = stack.pop();
auto value = stack.pop(); auto value = Endianness::toBE(m_builder, stack.pop());
value = Endianness::toBE(m_builder, value); auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid");
auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes");
auto safeByteNum = m_builder.CreateZExt(m_builder.CreateTrunc(byteNum, m_builder.getIntNTy(5)), Type::lowPrecision); // Trim index, large values can crash // TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access.
auto byte = m_builder.CreateExtractElement(bytes, safeByteNum, "byte"); auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5));
// TODO: Workaround for LLVM bug. DAG Builder used sext on index instead of zext
safeIdx = m_builder.CreateZExt(safeIdx, Type::Size);
auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte");
value = m_builder.CreateZExt(byte, Type::Word); value = m_builder.CreateZExt(byte, Type::Word);
value = m_builder.CreateSelect(idxValid, value, Constant::get(0));
auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32));
value = m_builder.CreateSelect(byteNumValid, value, Constant::get(0));
stack.push(value); stack.push(value);
break; break;
} }
case Instruction::ADDMOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto mod = stack.pop();
auto res = _arith.addmod(lhs, rhs, mod);
stack.push(res);
break;
}
case Instruction::MULMOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto mod = stack.pop();
auto res = _arith.mulmod(lhs, rhs, mod);
stack.push(res);
break;
}
case Instruction::SIGNEXTEND: case Instruction::SIGNEXTEND:
{ {
auto idx = stack.pop(); auto idx = stack.pop();
auto word = stack.pop(); auto word = stack.pop();
auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32"); auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32");
auto k32 = m_builder.CreateZExt(k32_, Type::lowPrecision); auto k32 = m_builder.CreateZExt(k32_, Type::Size);
auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8"); auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8");
// test for word >> (k * 8 + 7) // test for word >> (k * 8 + 7)
@ -495,11 +533,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::POP: case Instruction::POP:
{ {
auto val = stack.pop(); stack.pop();
static_cast<void>(val);
// Generate a dummy use of val to make sure that a get(0) will be emitted at this point,
// so that StackUnderflow will be thrown
// m_builder.CreateICmpEQ(val, val, "dummy");
break; break;
} }
@ -656,7 +690,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
} }
case Instruction::CODESIZE: case Instruction::CODESIZE:
// TODO: Use constant
stack.push(_runtimeManager.getCodeSize()); stack.push(_runtimeManager.getCodeSize());
break; break;
@ -729,8 +762,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::CALLDATALOAD: case Instruction::CALLDATALOAD:
{ {
auto index = stack.pop(); auto idx = stack.pop();
auto value = _ext.calldataload(index); auto value = _ext.calldataload(idx);
stack.push(value); stack.push(value);
break; break;
} }
@ -797,7 +830,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::STOP: case Instruction::STOP:
{ {
m_builder.CreateRet(Constant::get(ReturnCode::Stop)); m_builder.CreateBr(m_stopBB);
break; break;
} }
@ -824,7 +857,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
} }
default: // Invalid instruction - abort default: // Invalid instruction - abort
m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction)); m_builder.CreateBr(m_abortBB);
it = _basicBlock.end() - 1; // finish block compilation it = _basicBlock.end() - 1; // finish block compilation
} }
} }
@ -942,4 +975,3 @@ void Compiler::dump()
} }
} }
} }

4
evmjit/libevmjit/Compiler.h

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "Common.h"
#include "BasicBlock.h" #include "BasicBlock.h"
namespace dev namespace dev
@ -65,6 +64,9 @@ private:
/// Stop basic block - terminates execution with STOP code (0) /// Stop basic block - terminates execution with STOP code (0)
llvm::BasicBlock* m_stopBB = nullptr; llvm::BasicBlock* m_stopBB = nullptr;
/// Abort basic block - terminates execution with OOG-like state
llvm::BasicBlock* m_abortBB = nullptr;
/// Block with a jump table. /// Block with a jump table.
std::unique_ptr<BasicBlock> m_jumpTableBlock; std::unique_ptr<BasicBlock> m_jumpTableBlock;

25
evmjit/libevmjit/CompilerHelper.h

@ -37,7 +37,6 @@ protected:
friend class RuntimeHelper; friend class RuntimeHelper;
}; };
/// Compiler helper that depends on runtime data /// Compiler helper that depends on runtime data
class RuntimeHelper : public CompilerHelper class RuntimeHelper : public CompilerHelper
{ {
@ -50,29 +49,7 @@ private:
RuntimeManager& m_runtimeManager; RuntimeManager& m_runtimeManager;
}; };
using InsertPointGuard = llvm::IRBuilderBase::InsertPointGuard;
/// Saves the insert point of the IR builder and restores it when destructed
struct InsertPointGuard
{
InsertPointGuard(llvm::IRBuilder<>& _builder) :
m_builder(_builder),
m_insertBB(m_builder.GetInsertBlock()),
m_insertPt(m_builder.GetInsertPoint())
{}
InsertPointGuard(const InsertPointGuard&) = delete;
void operator=(InsertPointGuard) = delete;
~InsertPointGuard()
{
m_builder.SetInsertPoint(m_insertBB, m_insertPt);
}
private:
llvm::IRBuilder<>& m_builder;
llvm::BasicBlock* m_insertBB;
llvm::BasicBlock::iterator m_insertPt;
};
} }
} }

5
evmjit/libevmjit/ExecStats.cpp

@ -8,9 +8,7 @@
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
void ExecStats::stateChanged(ExecState _state) void ExecStats::stateChanged(ExecState _state)
@ -95,4 +93,3 @@ StatsCollector::~StatsCollector()
} }
} }
}

35
evmjit/libevmjit/ExecStats.h

@ -1,19 +1,43 @@
#pragma once #pragma once
#include <memory>
#include <vector> #include <vector>
#include <string> #include <string>
#include <chrono> #include <chrono>
#include "ExecutionEngine.h"
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
enum class ExecState
{ {
namespace jit Started,
CacheLoad,
CacheWrite,
Compilation,
Optimization,
CodeGen,
Execution,
Return,
Finished
};
class JITListener
{ {
public:
JITListener() = default;
JITListener(JITListener const&) = delete;
JITListener& operator=(JITListener) = delete;
virtual ~JITListener() {}
virtual void executionStarted() {}
virtual void executionEnded() {}
class ExecStats : public ExecutionEngineListener virtual void stateChanged(ExecState) {}
};
class ExecStats : public JITListener
{ {
public: public:
using clock = std::chrono::high_resolution_clock; using clock = std::chrono::high_resolution_clock;
@ -42,4 +66,3 @@ public:
} }
} }
}

192
evmjit/libevmjit/ExecutionEngine.cpp

@ -1,192 +0,0 @@
#include "ExecutionEngine.h"
#include <array>
#include <mutex>
#include <iostream>
#include <unordered_map>
#include <cstdlib>
#include <cstring>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ManagedStatic.h>
#include "preprocessor/llvm_includes_end.h"
#include "evmjit/JIT.h"
#include "Runtime.h"
#include "Compiler.h"
#include "Optimizer.h"
#include "Cache.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev
{
namespace eth
{
namespace jit
{
using evmjit::JIT;
namespace
{
using EntryFuncPtr = ReturnCode(*)(Runtime*);
std::string codeHash(i256 const& _hash)
{
static const auto size = sizeof(_hash);
static const auto hexChars = "0123456789abcdef";
std::string str;
str.resize(size * 2);
auto outIt = str.rbegin(); // reverse for BE
auto& arr = *(std::array<byte, size>*)&_hash;
for (auto b : arr)
{
*(outIt++) = hexChars[b & 0xf];
*(outIt++) = hexChars[b >> 4];
}
return str;
}
void printVersion()
{
std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n"
<< " EVMJIT version " << EVMJIT_VERSION << "\n"
#ifdef NDEBUG
<< " Optimized build, " EVMJIT_VERSION_FULL "\n"
#else
<< " DEBUG build, " EVMJIT_VERSION_FULL "\n"
#endif
<< " Built " << __DATE__ << " (" << __TIME__ << ")\n"
<< std::endl;
}
namespace cl = llvm::cl;
cl::opt<bool> g_optimize{"O", cl::desc{"Optimize"}};
cl::opt<CacheMode> g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"},
cl::values(
clEnumValN(CacheMode::on, "1", "Enabled"),
clEnumValN(CacheMode::off, "0", "Disabled"),
clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."),
clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."),
clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."),
clEnumValN(CacheMode::preload, "p", "Preload all cached objects."),
clEnumValEnd)};
cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}};
cl::opt<bool> g_dump{"dump", cl::desc{"Dump LLVM IR module"}};
void parseOptions()
{
static llvm::llvm_shutdown_obj shutdownObj{};
cl::AddExtraVersionPrinter(printVersion);
cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler");
}
}
ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
{
static std::once_flag flag;
std::call_once(flag, parseOptions);
std::unique_ptr<ExecStats> listener{new ExecStats};
listener->stateChanged(ExecState::Started);
bool preloadCache = g_cache == CacheMode::preload;
if (preloadCache)
g_cache = CacheMode::on;
// TODO: Do not pseudo-init the cache every time
auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr;
static std::unique_ptr<llvm::ExecutionEngine> ee;
if (!ee)
{
if (g_cache == CacheMode::clear)
Cache::clear();
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext()));
// FIXME: LLVM 3.7: test on Windows
auto triple = llvm::Triple(llvm::sys::getProcessTriple());
if (triple.getOS() == llvm::Triple::OSType::Win32)
triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
module->setTargetTriple(triple.str());
llvm::EngineBuilder builder(std::move(module));
builder.setEngineKind(llvm::EngineKind::JIT);
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None);
ee.reset(builder.create());
if (!CHECK(ee))
return ReturnCode::LLVMConfigError;
ee->setObjectCache(objectCache);
// FIXME: Disabled during API changes
//if (preloadCache)
// Cache::preload(*ee, funcCache);
}
static StatsCollector statsCollector;
auto mainFuncName = codeHash(_data->codeHash);
m_runtime.init(_data, _env);
// TODO: Remove cast
auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(_data->codeHash);
if (!entryFuncPtr)
{
auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr;
if (!module)
{
listener->stateChanged(ExecState::Compilation);
assert(_data->code || !_data->codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler{{}}.compile(_data->code, _data->code + _data->codeSize, mainFuncName);
if (g_optimize)
{
listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
}
if (g_dump)
module->dump();
ee->addModule(std::move(module));
listener->stateChanged(ExecState::CodeGen);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError;
JIT::mapCode(_data->codeHash, (uint64_t)entryFuncPtr); // FIXME: Remove cast
}
listener->stateChanged(ExecState::Execution);
auto returnCode = entryFuncPtr(&m_runtime);
listener->stateChanged(ExecState::Return);
if (returnCode == ReturnCode::Return)
returnData = m_runtime.getReturnData(); // Save reference to return data
listener->stateChanged(ExecState::Finished);
if (g_stats)
statsCollector.stats.push_back(std::move(listener));
return returnCode;
}
}
}
}

59
evmjit/libevmjit/ExecutionEngine.h

@ -1,59 +0,0 @@
#pragma once
#include <memory>
#include "Runtime.h"
namespace dev
{
namespace eth
{
namespace jit
{
enum class ExecState
{
Started,
CacheLoad,
CacheWrite,
Compilation,
Optimization,
CodeGen,
Execution,
Return,
Finished
};
class ExecutionEngineListener
{
public:
ExecutionEngineListener() = default;
ExecutionEngineListener(ExecutionEngineListener const&) = delete;
ExecutionEngineListener& operator=(ExecutionEngineListener) = delete;
virtual ~ExecutionEngineListener() {}
virtual void executionStarted() {}
virtual void executionEnded() {}
virtual void stateChanged(ExecState) {}
};
class ExecutionEngine
{
public:
ExecutionEngine() = default;
ExecutionEngine(ExecutionEngine const&) = delete;
ExecutionEngine& operator=(ExecutionEngine) = delete;
EXPORT ReturnCode run(RuntimeData* _data, Env* _env);
/// Reference to returned data (RETURN opcode used)
bytes_ref returnData;
private:
Runtime m_runtime;
};
}
}
}

24
evmjit/libevmjit/Ext.cpp

@ -45,7 +45,6 @@ std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})},
}}; }};
return descs; return descs;
@ -101,12 +100,27 @@ void Ext::sstore(llvm::Value* _index, llvm::Value* _value)
createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness
} }
llvm::Value* Ext::calldataload(llvm::Value* _index) llvm::Value* Ext::calldataload(llvm::Value* _idx)
{ {
auto ret = getArgAlloca(); auto ret = getArgAlloca();
createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret}); auto result = m_builder.CreateBitCast(ret, Type::BytePtr);
ret = m_builder.CreateLoad(ret);
return Endianness::toNative(m_builder, ret); auto callDataSize = getRuntimeManager().getCallDataSize();
auto callDataSize64 = m_builder.CreateTrunc(callDataSize, Type::Size);
auto idxValid = m_builder.CreateICmpULT(_idx, callDataSize);
auto idx = m_builder.CreateTrunc(m_builder.CreateSelect(idxValid, _idx, callDataSize), Type::Size, "idx");
auto end = m_builder.CreateNUWAdd(idx, m_builder.getInt64(32));
end = m_builder.CreateSelect(m_builder.CreateICmpULE(end, callDataSize64), end, callDataSize64);
auto copySize = m_builder.CreateNUWSub(end, idx);
auto padSize = m_builder.CreateNUWSub(m_builder.getInt64(32), copySize);
auto dataBegin = m_builder.CreateGEP(Type::Byte, getRuntimeManager().getCallData(), idx);
m_builder.CreateMemCpy(result, dataBegin, copySize, 1);
auto pad = m_builder.CreateGEP(Type::Byte, result, copySize);
m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1);
m_argCounter = 0; // Release args allocas. TODO: This is a bad design
return Endianness::toNative(m_builder, m_builder.CreateLoad(ret));
} }
llvm::Value* Ext::balance(llvm::Value* _address) llvm::Value* Ext::balance(llvm::Value* _address)

2
evmjit/libevmjit/Ext.h

@ -35,7 +35,6 @@ enum class EnvFunc
log, log,
blockhash, blockhash,
extcode, extcode,
calldataload, // Helper function, not client Env interface
_size _size
}; };
@ -63,7 +62,6 @@ private:
Memory& m_memoryMan; Memory& m_memoryMan;
llvm::Value* m_size; llvm::Value* m_size;
llvm::Value* m_data = nullptr;
std::array<llvm::Function*, sizeOf<EnvFunc>::value> m_funcs; std::array<llvm::Function*, sizeOf<EnvFunc>::value> m_funcs;
std::array<llvm::Value*, 8> m_argAllocas; std::array<llvm::Value*, 8> m_argAllocas;

1
evmjit/libevmjit/GasMeter.h

@ -10,6 +10,7 @@ namespace eth
namespace jit namespace jit
{ {
class RuntimeManager; class RuntimeManager;
using namespace evmjit;
class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper
{ {

5
evmjit/libevmjit/Instruction.cpp

@ -6,9 +6,7 @@
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
llvm::APInt readPushData(code_iterator& _curr, code_iterator _end) llvm::APInt readPushData(code_iterator& _curr, code_iterator _end)
@ -39,4 +37,3 @@ void skipPushData(code_iterator& _curr, code_iterator _end)
} }
} }
}

5
evmjit/libevmjit/Instruction.h

@ -9,9 +9,7 @@ namespace llvm
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
/// Virtual machine bytecode instruction. /// Virtual machine bytecode instruction.
@ -236,4 +234,3 @@ void skipPushData(code_iterator& _curr, code_iterator _end);
} }
} }
}

234
evmjit/libevmjit/JIT.cpp

@ -1,45 +1,251 @@
#include "evmjit/JIT.h" #include "evmjit/JIT.h"
#include <unordered_map> #include <array>
#include <mutex>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ManagedStatic.h>
#include "preprocessor/llvm_includes_end.h"
#include "Compiler.h"
#include "Optimizer.h"
#include "Cache.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev namespace dev
{ {
namespace evmjit namespace evmjit
{ {
using namespace eth::jit;
namespace namespace
{ {
using ExecFunc = ReturnCode(*)(ExecutionContext*);
class JITImpl: JIT std::string hash2str(i256 const& _hash)
{ {
public: static const auto size = sizeof(_hash);
std::unordered_map<h256, uint64_t> codeMap; static const auto hexChars = "0123456789abcdef";
std::string str;
str.resize(size * 2);
auto outIt = str.rbegin(); // reverse for BE
auto& arr = *(std::array<byte, size>*)&_hash;
for (auto b : arr)
{
*(outIt++) = hexChars[b & 0xf];
*(outIt++) = hexChars[b >> 4];
}
return str;
}
void printVersion()
{
std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n"
<< " EVMJIT version " << EVMJIT_VERSION << "\n"
#ifdef NDEBUG
<< " Optimized build, " EVMJIT_VERSION_FULL "\n"
#else
<< " DEBUG build, " EVMJIT_VERSION_FULL "\n"
#endif
<< " Built " << __DATE__ << " (" << __TIME__ << ")\n"
<< std::endl;
}
namespace cl = llvm::cl;
cl::opt<bool> g_optimize{"O", cl::desc{"Optimize"}};
cl::opt<CacheMode> g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"},
cl::values(
clEnumValN(CacheMode::on, "1", "Enabled"),
clEnumValN(CacheMode::off, "0", "Disabled"),
clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."),
clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."),
clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."),
clEnumValN(CacheMode::preload, "p", "Preload all cached objects."),
clEnumValEnd)};
cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}};
cl::opt<bool> g_dump{"dump", cl::desc{"Dump LLVM IR module"}};
void parseOptions()
{
static llvm::llvm_shutdown_obj shutdownObj{};
cl::AddExtraVersionPrinter(printVersion);
cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler");
}
class JITImpl
{
std::unique_ptr<llvm::ExecutionEngine> m_engine;
mutable std::mutex x_codeMap;
std::unordered_map<h256, ExecFunc> m_codeMap;
public:
static JITImpl& instance() static JITImpl& instance()
{ {
static JITImpl s_instance; static JITImpl s_instance;
return s_instance; return s_instance;
} }
JITImpl();
llvm::ExecutionEngine& engine() { return *m_engine; }
ExecFunc getExecFunc(h256 const& _codeHash) const;
void mapExecFunc(h256 _codeHash, ExecFunc _funcAddr);
ExecFunc compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash);
}; };
JITImpl::JITImpl()
{
parseOptions();
bool preloadCache = g_cache == CacheMode::preload;
if (preloadCache)
g_cache = CacheMode::on;
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext()));
// FIXME: LLVM 3.7: test on Windows
auto triple = llvm::Triple(llvm::sys::getProcessTriple());
if (triple.getOS() == llvm::Triple::OSType::Win32)
triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
module->setTargetTriple(triple.str());
llvm::EngineBuilder builder(std::move(module));
builder.setEngineKind(llvm::EngineKind::JIT);
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None);
m_engine.reset(builder.create());
// TODO: Update cache listener
m_engine->setObjectCache(Cache::init(g_cache, nullptr));
// FIXME: Disabled during API changes
//if (preloadCache)
// Cache::preload(*m_engine, funcCache);
}
ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const
{
std::lock_guard<std::mutex> lock{x_codeMap};
auto it = m_codeMap.find(_codeHash);
if (it != m_codeMap.end())
return it->second;
return nullptr;
}
void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr)
{
std::lock_guard<std::mutex> lock{x_codeMap};
m_codeMap.emplace(std::move(_codeHash), _funcAddr);
}
ExecFunc JITImpl::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash)
{
auto name = hash2str(_codeHash);
auto module = Cache::getObject(name);
if (!module)
{
// TODO: Listener support must be redesigned. These should be a feature of JITImpl
//listener->stateChanged(ExecState::Compilation);
assert(_code || !_codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler{{}}.compile(_code, _code + _codeSize, name);
if (g_optimize)
{
//listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
prepare(*module);
}
if (g_dump)
module->dump();
m_engine->addModule(std::move(module));
//listener->stateChanged(ExecState::CodeGen);
return (ExecFunc)m_engine->getFunctionAddress(name);
}
} // anonymous namespace } // anonymous namespace
bool JIT::isCodeReady(h256 _codeHash) bool JIT::isCodeReady(h256 const& _codeHash)
{ {
return JITImpl::instance().codeMap.count(_codeHash) != 0; return JITImpl::instance().getExecFunc(_codeHash) != nullptr;
} }
uint64_t JIT::getCode(h256 _codeHash) void JIT::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash)
{ {
auto& codeMap = JITImpl::instance().codeMap; auto& jit = JITImpl::instance();
auto it = codeMap.find(_codeHash); auto execFunc = jit.compile(_code, _codeSize, _codeHash);
if (it != codeMap.end()) if (execFunc) // FIXME: What with error?
return it->second; jit.mapExecFunc(_codeHash, execFunc);
return 0;
} }
void JIT::mapCode(h256 _codeHash, uint64_t _funcAddr) ReturnCode JIT::exec(ExecutionContext& _context)
{ {
JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); //std::unique_ptr<ExecStats> listener{new ExecStats};
//listener->stateChanged(ExecState::Started);
//static StatsCollector statsCollector;
auto& jit = JITImpl::instance();
auto codeHash = _context.codeHash();
auto execFunc = jit.getExecFunc(codeHash);
if (!execFunc)
{
execFunc = jit.compile(_context.code(), _context.codeSize(), codeHash);
if (!execFunc)
return ReturnCode::LLVMError;
jit.mapExecFunc(codeHash, execFunc);
}
//listener->stateChanged(ExecState::Execution);
auto returnCode = execFunc(&_context);
//listener->stateChanged(ExecState::Return);
if (returnCode == ReturnCode::Return)
_context.returnData = _context.getReturnData(); // Save reference to return data
//listener->stateChanged(ExecState::Finished);
// if (g_stats)
// statsCollector.stats.push_back(std::move(listener));
return returnCode;
}
extern "C" void ext_free(void* _data) noexcept;
ExecutionContext::~ExecutionContext()
{
if (m_memData)
ext_free(m_memData); // Use helper free to check memory leaks
}
bytes_ref ExecutionContext::getReturnData() const
{
auto data = m_data->callData;
auto size = static_cast<size_t>(m_data->callDataSize);
if (data < m_memData || data >= m_memData + m_memSize || size == 0)
{
assert(size == 0); // data can be an invalid pointer only if size is 0
m_data->callData = nullptr;
return {};
}
return bytes_ref{data, size};
} }
} }

6
evmjit/libevmjit/Memory.cpp

@ -5,7 +5,6 @@
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "Type.h" #include "Type.h"
#include "Runtime.h"
#include "GasMeter.h" #include "GasMeter.h"
#include "Endianness.h" #include "Endianness.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
@ -191,8 +190,7 @@ llvm::Value* Memory::getSize()
llvm::Value* Memory::getBytePtr(llvm::Value* _index) llvm::Value* Memory::getBytePtr(llvm::Value* _index)
{ {
auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 return m_builder.CreateGEP(getData(), _index, "ptr");
return m_builder.CreateGEP(getData(), idx, "ptr");
} }
void Memory::require(llvm::Value* _offset, llvm::Value* _size) void Memory::require(llvm::Value* _offset, llvm::Value* _size)
@ -235,7 +233,7 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value*
auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero"); auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero");
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); auto src = m_builder.CreateGEP(_srcPtr, idx64, "src");
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64 auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx");
auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx"); auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx");
auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx);
auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx); auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx);

97
evmjit/libevmjit/Optimizer.cpp

@ -1,11 +1,16 @@
#include "Optimizer.h" #include "Optimizer.h"
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/LegacyPassManager.h> #include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/Scalar.h> #include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/IPO.h> #include <llvm/Transforms/IPO.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "Arith256.h"
#include "Type.h"
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -16,7 +21,7 @@ namespace jit
bool optimize(llvm::Module& _module) bool optimize(llvm::Module& _module)
{ {
auto pm = llvm::legacy::PassManager{}; auto pm = llvm::legacy::PassManager{};
//pm.add(llvm::createFunctionInliningPass(2, 2)); // Problem with APInt value bigger than 64bit pm.add(llvm::createFunctionInliningPass(2, 2));
pm.add(llvm::createCFGSimplificationPass()); pm.add(llvm::createCFGSimplificationPass());
pm.add(llvm::createInstructionCombiningPass()); pm.add(llvm::createInstructionCombiningPass());
pm.add(llvm::createAggressiveDCEPass()); pm.add(llvm::createAggressiveDCEPass());
@ -24,6 +29,96 @@ bool optimize(llvm::Module& _module)
return pm.run(_module); return pm.run(_module);
} }
namespace
{
class LowerEVMPass: public llvm::BasicBlockPass
{
static char ID;
public:
LowerEVMPass():
llvm::BasicBlockPass(ID)
{}
virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override;
using llvm::BasicBlockPass::doFinalization;
virtual bool doFinalization(llvm::Module& _module) override;
};
char LowerEVMPass::ID = 0;
bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb)
{
auto modified = false;
auto module = _bb.getParent()->getParent();
auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512);
for (auto it = _bb.begin(); it != _bb.end(); )
{
auto& inst = *it++;
llvm::Function* func = nullptr;
if (inst.getType() == Type::Word)
{
switch (inst.getOpcode())
{
case llvm::Instruction::Mul:
func = Arith256::getMulFunc(*module);
break;
case llvm::Instruction::UDiv:
func = Arith256::getUDiv256Func(*module);
break;
case llvm::Instruction::URem:
func = Arith256::getURem256Func(*module);
break;
case llvm::Instruction::SDiv:
func = Arith256::getSDiv256Func(*module);
break;
case llvm::Instruction::SRem:
func = Arith256::getSRem256Func(*module);
break;
}
}
else if (inst.getType() == i512Ty)
{
switch (inst.getOpcode())
{
case llvm::Instruction::URem:
func = Arith256::getURem512Func(*module);
break;
}
}
if (func)
{
auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}, "", &inst);
inst.replaceAllUsesWith(call);
inst.eraseFromParent();
modified = true;
}
}
return modified;
}
bool LowerEVMPass::doFinalization(llvm::Module&)
{
return false;
}
}
bool prepare(llvm::Module& _module)
{
auto pm = llvm::legacy::PassManager{};
pm.add(llvm::createDeadCodeEliminationPass());
pm.add(new LowerEVMPass{});
return pm.run(_module);
}
} }
} }
} }

2
evmjit/libevmjit/Optimizer.h

@ -14,6 +14,8 @@ namespace jit
bool optimize(llvm::Module& _module); bool optimize(llvm::Module& _module);
bool prepare(llvm::Module& _module);
} }
} }
} }

43
evmjit/libevmjit/Runtime.cpp

@ -1,43 +0,0 @@
#include "Runtime.h"
#include <cassert>
namespace dev
{
namespace eth
{
namespace jit
{
void Runtime::init(RuntimeData* _data, Env* _env)
{
m_data = _data;
m_env = _env;
}
extern "C" void ext_free(void* _data) noexcept;
Runtime::~Runtime()
{
if (m_memData)
ext_free(m_memData); // Use helper free to check memory leaks
}
bytes_ref Runtime::getReturnData() const
{
auto data = m_data->callData;
auto size = static_cast<size_t>(m_data->callDataSize);
if (data < m_memData || data >= m_memData + m_memSize || size == 0)
{
assert(size == 0); // data can be an invalid pointer only if size is 0
m_data->callData = nullptr;
return {};
}
return bytes_ref{data, size};
}
}
}
}

30
evmjit/libevmjit/Runtime.h

@ -1,30 +0,0 @@
#pragma once
#include "RuntimeData.h"
namespace dev
{
namespace eth
{
namespace jit
{
class Runtime
{
public:
void init(RuntimeData* _data, Env* _env);
EXPORT ~Runtime();
bytes_ref getReturnData() const;
private:
RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
byte* m_memData = nullptr;
uint64_t m_memSize = 0;
uint64_t m_memCap = 0;
};
}
}
}

63
evmjit/libevmjit/RuntimeData.h

@ -1,63 +0,0 @@
#pragma once
#include "evmjit/DataTypes.h"
#include "Common.h"
namespace dev
{
namespace eth
{
namespace jit
{
using evmjit::i256;
using evmjit::h256;
struct RuntimeData
{
enum Index
{
Gas,
GasPrice,
CallData,
CallDataSize,
Address,
Caller,
Origin,
CallValue,
CoinBase,
Difficulty,
GasLimit,
Number,
Timestamp,
Code,
CodeSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
};
int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr;
uint64_t callDataSize = 0;
i256 address;
i256 caller;
i256 origin;
i256 callValue;
i256 coinBase;
i256 difficulty;
i256 gasLimit;
uint64_t number = 0;
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type
struct Env;
}
}
}

56
evmjit/libevmjit/RuntimeManager.cpp

@ -64,22 +64,22 @@ llvm::Twine getName(RuntimeData::Index _index)
{ {
switch (_index) switch (_index)
{ {
default: return "data"; default: return "";
case RuntimeData::Address: return "address"; case RuntimeData::Gas: return "msg.gas";
case RuntimeData::Caller: return "caller"; case RuntimeData::GasPrice: return "tx.gasprice";
case RuntimeData::Origin: return "origin"; case RuntimeData::CallData: return "msg.data.ptr";
case RuntimeData::CallValue: return "callvalue"; case RuntimeData::CallDataSize: return "msg.data.size";
case RuntimeData::GasPrice: return "gasprice"; case RuntimeData::Address: return "this.address";
case RuntimeData::CoinBase: return "coinbase"; case RuntimeData::Caller: return "msg.caller";
case RuntimeData::Difficulty: return "difficulty"; case RuntimeData::Origin: return "tx.origin";
case RuntimeData::GasLimit: return "gaslimit"; case RuntimeData::CallValue: return "msg.value";
case RuntimeData::CallData: return "callData"; case RuntimeData::CoinBase: return "block.coinbase";
case RuntimeData::Code: return "code"; case RuntimeData::Difficulty: return "block.difficulty";
case RuntimeData::CodeSize: return "code"; case RuntimeData::GasLimit: return "block.gaslimit";
case RuntimeData::CallDataSize: return "callDataSize"; case RuntimeData::Number: return "block.number";
case RuntimeData::Gas: return "gas"; case RuntimeData::Timestamp: return "block.timestamp";
case RuntimeData::Number: return "number"; case RuntimeData::Code: return "code.ptr";
case RuntimeData::Timestamp: return "timestamp"; case RuntimeData::CodeSize: return "code.size";
} }
} }
} }
@ -93,10 +93,8 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
// Unpack data // Unpack data
auto rtPtr = getRuntimePtr(); auto rtPtr = getRuntimePtr();
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data"); m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr");
assert(m_dataPtr->getType() == Type::RuntimeDataPtr); assert(m_dataPtr->getType() == Type::RuntimeDataPtr);
m_gasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), m_dataPtr, 0, "gas");
assert(m_gasPtr->getType() == Type::Gas->getPointerTo());
m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem"); m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem");
assert(m_memPtr->getType() == Array::getType()->getPointerTo()); assert(m_memPtr->getType() == Array::getType()->getPointerTo());
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env"); m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env");
@ -105,6 +103,13 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize"); m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize");
m_builder.CreateStore(m_builder.getInt64(0), m_stackSize); m_builder.CreateStore(m_builder.getInt64(0), m_stackSize);
auto data = m_builder.CreateLoad(m_dataPtr, "data");
for (unsigned i = 0; i < m_dataElts.size(); ++i)
m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i)));
m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr");
m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr);
llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr}; llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr};
m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule()); m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule());
m_checkStackLimit->setDoesNotThrow(); m_checkStackLimit->setDoesNotThrow();
@ -180,7 +185,7 @@ llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
llvm::Value* RuntimeManager::get(RuntimeData::Index _index) llvm::Value* RuntimeManager::get(RuntimeData::Index _index)
{ {
return getBuilder().CreateLoad(getPtr(_index), getName(_index)); return m_dataElts[_index];
} }
void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value)
@ -194,8 +199,7 @@ void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size
{ {
auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo()); auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo());
auto mem = getBuilder().CreateLoad(memPtr, "memory"); auto mem = getBuilder().CreateLoad(memPtr, "memory");
auto idx = m_builder.CreateTrunc(_offset, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 // TODO: Report bug & fix to LLVM auto returnDataPtr = getBuilder().CreateGEP(mem, _offset);
auto returnDataPtr = getBuilder().CreateGEP(mem, idx);
set(RuntimeData::ReturnData, returnDataPtr); set(RuntimeData::ReturnData, returnDataPtr);
auto size64 = getBuilder().CreateTrunc(_size, Type::Size); auto size64 = getBuilder().CreateTrunc(_size, Type::Size);
@ -212,6 +216,8 @@ void RuntimeManager::exit(ReturnCode _returnCode)
if (m_stack) if (m_stack)
m_stack->free(); m_stack->free();
auto extGasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr");
m_builder.CreateStore(getGas(), extGasPtr);
m_builder.CreateRet(Constant::get(_returnCode)); m_builder.CreateRet(Constant::get(_returnCode));
} }
@ -265,9 +271,7 @@ llvm::Value* RuntimeManager::getCallDataSize()
llvm::Value* RuntimeManager::getGas() llvm::Value* RuntimeManager::getGas()
{ {
auto gas = get(RuntimeData::Gas); return getBuilder().CreateLoad(getGasPtr(), "gas");
assert(gas->getType() == Type::Gas);
return gas;
} }
llvm::Value* RuntimeManager::getGasPtr() llvm::Value* RuntimeManager::getGasPtr()
@ -285,7 +289,7 @@ llvm::Value* RuntimeManager::getMem()
void RuntimeManager::setGas(llvm::Value* _gas) void RuntimeManager::setGas(llvm::Value* _gas)
{ {
assert(_gas->getType() == Type::Gas); assert(_gas->getType() == Type::Gas);
set(RuntimeData::Gas, _gas); getBuilder().CreateStore(_gas, getGasPtr());
} }
} }

6
evmjit/libevmjit/RuntimeManager.h

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <array>
#include "CompilerHelper.h" #include "CompilerHelper.h"
#include "Type.h" #include "Type.h"
#include "RuntimeData.h"
#include "Instruction.h" #include "Instruction.h"
namespace dev namespace dev
@ -11,6 +12,7 @@ namespace eth
{ {
namespace jit namespace jit
{ {
using namespace evmjit;
class Stack; class Stack;
class RuntimeManager: public CompilerHelper class RuntimeManager: public CompilerHelper
@ -61,6 +63,8 @@ private:
llvm::Value* m_memPtr = nullptr; llvm::Value* m_memPtr = nullptr;
llvm::Value* m_envPtr = nullptr; llvm::Value* m_envPtr = nullptr;
std::array<llvm::Value*, RuntimeData::numElements> m_dataElts;
llvm::Value* m_stackSize = nullptr; llvm::Value* m_stackSize = nullptr;
llvm::Function* m_checkStackLimit = nullptr; llvm::Function* m_checkStackLimit = nullptr;

120
evmjit/libevmjit/Stack.cpp

@ -5,7 +5,6 @@
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
#include "Runtime.h"
#include "Utils.h" #include "Utils.h"
#include <set> // DEBUG only #include <set> // DEBUG only
@ -23,96 +22,6 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
m_stack(_builder, "stack") m_stack(_builder, "stack")
{} {}
llvm::Function* Stack::getPushFunc()
{
auto& func = m_push;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.push", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::WordPtr};
auto extPushFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_push", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto value = rt->getNextNode();
value->setName("value");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(entryBB);
auto a = m_builder.CreateAlloca(Type::Word);
m_builder.CreateStore(value, a);
createCall(extPushFunc, {rt, a});
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getSetFunc()
{
auto& func = m_set;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.set", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr};
auto extSetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_set", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto value = index->getNextNode();
value->setName("value");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(entryBB);
auto a = m_builder.CreateAlloca(Type::Word);
m_builder.CreateStore(value, a);
createCall(extSetFunc, {rt, index, a});
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getPopFunc()
{
auto& func = m_pop;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.pop", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size};
auto extPopFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_pop", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto jmpBuf = index->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
m_builder.SetInsertPoint(entryBB);
auto ok = createCall(extPopFunc, {rt, index});
m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight
m_builder.SetInsertPoint(underflowBB);
m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getGetFunc() llvm::Function* Stack::getGetFunc()
{ {
auto& func = m_get; auto& func = m_get;
@ -175,32 +84,3 @@ void Stack::push(llvm::Value* _value)
} }
} }
} }
extern "C"
{
using namespace dev::eth::jit;
EXPORT void ext_calldataload(RuntimeData* _rtData, i256* _index, byte* o_value)
{
// It asumes all indexes are less than 2^64
auto index = _index->a;
if (_index->b || _index->c || _index->d) // if bigger that 2^64
index = std::numeric_limits<decltype(index)>::max(); // set max to fill with 0 leter
auto data = _rtData->callData;
auto size = _rtData->callDataSize;
for (auto i = 0; i < 32; ++i)
{
if (index < size)
{
o_value[i] = data[index];
++index; // increment only if in range
}
else
o_value[i] = 0;
}
}
} // extern "C"

8
evmjit/libevmjit/Stack.h

@ -24,18 +24,10 @@ public:
void free() { m_stack.free(); } void free() { m_stack.free(); }
private: private:
llvm::Function* getPopFunc();
llvm::Function* getPushFunc();
llvm::Function* getGetFunc(); llvm::Function* getGetFunc();
llvm::Function* getSetFunc();
RuntimeManager& m_runtimeManager; RuntimeManager& m_runtimeManager;
llvm::Function* m_pop = nullptr;
llvm::Function* m_push = nullptr;
llvm::Function* m_get = nullptr; llvm::Function* m_get = nullptr;
llvm::Function* m_set = nullptr;
Array m_stack; Array m_stack;
}; };

3
evmjit/libevmjit/Type.cpp

@ -13,7 +13,6 @@ namespace jit
llvm::IntegerType* Type::Word; llvm::IntegerType* Type::Word;
llvm::PointerType* Type::WordPtr; llvm::PointerType* Type::WordPtr;
llvm::IntegerType* Type::lowPrecision;
llvm::IntegerType* Type::Bool; llvm::IntegerType* Type::Bool;
llvm::IntegerType* Type::Size; llvm::IntegerType* Type::Size;
llvm::IntegerType* Type::Gas; llvm::IntegerType* Type::Gas;
@ -34,8 +33,6 @@ void Type::init(llvm::LLVMContext& _context)
{ {
Word = llvm::Type::getIntNTy(_context, 256); Word = llvm::Type::getIntNTy(_context, 256);
WordPtr = Word->getPointerTo(); WordPtr = Word->getPointerTo();
lowPrecision = llvm::Type::getInt64Ty(_context);
// TODO: Size should be architecture-dependent
Bool = llvm::Type::getInt1Ty(_context); Bool = llvm::Type::getInt1Ty(_context);
Size = llvm::Type::getInt64Ty(_context); Size = llvm::Type::getInt64Ty(_context);
Gas = Size; Gas = Size;

9
evmjit/libevmjit/Type.h

@ -4,9 +4,9 @@
#include <llvm/IR/Type.h> #include <llvm/IR/Type.h>
#include <llvm/IR/Constants.h> #include <llvm/IR/Constants.h>
#include <llvm/IR/Metadata.h> #include <llvm/IR/Metadata.h>
#include "preprocessor/llvm_includes_end.h" // FIXME: LLVM 3.7: check if needed #include "preprocessor/llvm_includes_end.h"
#include "Common.h" #include "evmjit/JIT.h" // ReturnCode
namespace dev namespace dev
{ {
@ -14,16 +14,13 @@ namespace eth
{ {
namespace jit namespace jit
{ {
using namespace evmjit;
struct Type struct Type
{ {
static llvm::IntegerType* Word; static llvm::IntegerType* Word;
static llvm::PointerType* WordPtr; static llvm::PointerType* WordPtr;
/// Type for doing low precision arithmetics where 256-bit precision is not supported by native target
/// @TODO: Use 64-bit for now. In 128-bit compiler-rt library functions are required
static llvm::IntegerType* lowPrecision;
static llvm::IntegerType* Bool; static llvm::IntegerType* Bool;
static llvm::IntegerType* Size; static llvm::IntegerType* Size;
static llvm::IntegerType* Gas; static llvm::IntegerType* Gas;

25
evmjit/libevmjit/interface.cpp

@ -1,29 +1,28 @@
#include "ExecutionEngine.h" #include "evmjit/JIT.h"
extern "C" extern "C"
{ {
using namespace dev::evmjit;
using namespace dev::eth::jit; EXPORT void* evmjit_create(RuntimeData* _data, Env* _env) noexcept
EXPORT void* evmjit_create() noexcept
{ {
// TODO: Make sure ExecutionEngine constructor does not throw if (!_data)
return new(std::nothrow) ExecutionEngine; return nullptr;
// TODO: Make sure ExecutionEngine constructor does not throw + make JIT/ExecutionEngine interface all nothrow
return new(std::nothrow) ExecutionContext{*_data, _env};
} }
EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept EXPORT void evmjit_destroy(ExecutionContext* _context) noexcept
{ {
delete _engine; delete _context;
} }
EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept EXPORT int evmjit_run(ExecutionContext* _context) noexcept
{ {
if (!_engine || !_data)
return static_cast<int>(ReturnCode::UnexpectedException);
try try
{ {
auto returnCode = _engine->run(_data, _env); auto returnCode = JIT::exec(*_context);
return static_cast<int>(returnCode); return static_cast<int>(returnCode);
} }
catch(...) catch(...)

12
exp/CMakeLists.txt

@ -6,6 +6,9 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${DB_INCLUDE_DIRS}) include_directories(${DB_INCLUDE_DIRS})
if (ETHASHCL)
include_directories(${OpenCL_INCLUDE_DIRS})
endif ()
set(EXECUTABLE exp) set(EXECUTABLE exp)
@ -27,11 +30,6 @@ target_link_libraries(${EXECUTABLE} p2p)
if (ETHASHCL) if (ETHASHCL)
target_link_libraries(${EXECUTABLE} ethash-cl) target_link_libraries(${EXECUTABLE} ethash-cl)
target_link_libraries(${EXECUTABLE} ethash) target_link_libraries(${EXECUTABLE} ethash)
target_link_libraries(${EXECUTABLE} OpenCL) target_link_libraries(${EXECUTABLE} ${OpenCL_LIBRARIES})
endif() endif()
install( TARGETS ${EXECUTABLE} DESTINATION bin) install( TARGETS ${EXECUTABLE} DESTINATION bin)

2
exp/main.cpp

@ -135,7 +135,7 @@ int main()
DownloadSub s0(man); DownloadSub s0(man);
DownloadSub s1(man); DownloadSub s1(man);
DownloadSub s2(man); DownloadSub s2(man);
man.resetToChain(h256s({u256(0), u256(1), u256(2), u256(3), u256(4), u256(5), u256(6), u256(7), u256(8)})); man.resetToChain(h256s({u256(0), u256(1), u256(2), u256(3), u256(4), u256(5), u256(6), u256(7), u256(8)}), 0);
assert((s0.nextFetch(2) == h256Set{(u256)7, (u256)8})); assert((s0.nextFetch(2) == h256Set{(u256)7, (u256)8}));
assert((s1.nextFetch(2) == h256Set{(u256)5, (u256)6})); assert((s1.nextFetch(2) == h256Set{(u256)5, (u256)6}));
assert((s2.nextFetch(2) == h256Set{(u256)3, (u256)4})); assert((s2.nextFetch(2) == h256Set{(u256)3, (u256)4}));

1
extdep/getstuff.bat

@ -14,6 +14,7 @@ call :download leveldb 1.2
call :download microhttpd 0.9.2 call :download microhttpd 0.9.2
call :download qt 5.4.1 call :download qt 5.4.1
call :download miniupnpc 1.9 call :download miniupnpc 1.9
call :download v8 3.15.9
goto :EOF goto :EOF

43
libdevcore/Base64.cpp

@ -27,6 +27,8 @@
/// Originally by René Nyffenegger, modified by some other guy and then devified by Gav Wood. /// Originally by René Nyffenegger, modified by some other guy and then devified by Gav Wood.
#include "Base64.h" #include "Base64.h"
using namespace std;
using namespace dev; using namespace dev;
static inline bool is_base64(byte c) static inline bool is_base64(byte c)
@ -44,14 +46,14 @@ static inline byte find_base64_char_index(byte c)
else return 1 + find_base64_char_index('/'); else return 1 + find_base64_char_index('/');
} }
std::string dev::toBase64(bytesConstRef _in) string dev::toBase64(bytesConstRef _in)
{ {
static const char base64_chars[] = static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"
"0123456789+/"; "0123456789+/";
std::string ret; string ret;
int i = 0; int i = 0;
int j = 0; int j = 0;
byte char_array_3[3]; byte char_array_3[3];
@ -60,15 +62,17 @@ std::string dev::toBase64(bytesConstRef _in)
auto buf = _in.data(); auto buf = _in.data();
auto bufLen = _in.size(); auto bufLen = _in.size();
while (bufLen--) { while (bufLen--)
{
char_array_3[i++] = *(buf++); char_array_3[i++] = *(buf++);
if (i == 3) { if (i == 3)
{
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f; char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++) for (i = 0; i < 4; i++)
ret += base64_chars[char_array_4[i]]; ret += base64_chars[char_array_4[i]];
i = 0; i = 0;
} }
@ -76,7 +80,7 @@ std::string dev::toBase64(bytesConstRef _in)
if (i) if (i)
{ {
for(j = i; j < 3; j++) for (j = i; j < 3; j++)
char_array_3[j] = '\0'; char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
@ -84,28 +88,31 @@ std::string dev::toBase64(bytesConstRef _in)
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f; char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++) for (j = 0; j < i + 1; j++)
ret += base64_chars[char_array_4[j]]; ret += base64_chars[char_array_4[j]];
while((i++ < 3)) while (i++ < 3)
ret += '='; ret += '=';
} }
return ret; return ret;
} }
bytes dev::fromBase64(std::string const& encoded_string) bytes dev::fromBase64(string const& encoded_string)
{ {
auto in_len = encoded_string.size(); auto in_len = encoded_string.size();
int i = 0; int i = 0;
int j = 0; int j = 0;
int in_ = 0; int in_ = 0;
byte char_array_4[4], char_array_3[3]; byte char_array_3[3];
byte char_array_4[4];
bytes ret; bytes ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { while (in_len-- && encoded_string[in_] != '=' && is_base64(encoded_string[in_]))
{
char_array_4[i++] = encoded_string[in_]; in_++; char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) { if (i == 4)
{
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
char_array_4[i] = find_base64_char_index(char_array_4[i]); char_array_4[i] = find_base64_char_index(char_array_4[i]);
@ -119,7 +126,8 @@ bytes dev::fromBase64(std::string const& encoded_string)
} }
} }
if (i) { if (i)
{
for (j = i; j < 4; j++) for (j = i; j < 4; j++)
char_array_4[j] = 0; char_array_4[j] = 0;
@ -130,7 +138,8 @@ bytes dev::fromBase64(std::string const& encoded_string)
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]); for (j = 0; j < i - 1; j++)
ret.push_back(char_array_3[j]);
} }
return ret; return ret;

11
libdevcore/Common.cpp

@ -28,14 +28,17 @@ using namespace dev;
namespace dev namespace dev
{ {
char const* Version = "0.9.27"; char const* Version = "0.9.29";
const u256 UndefinedU256 = ~(u256)0; const u256 UndefinedU256 = ~(u256)0;
void HasInvariants::checkInvariants() const void InvariantChecker::checkInvariants() const
{ {
if (!invariants()) if (!m_this->invariants())
BOOST_THROW_EXCEPTION(FailedInvariant()); {
cwarn << "Invariant failed in" << m_function << "at" << m_file << ":" << m_line;
::boost::exception_detail::throw_exception_(FailedInvariant(), m_function, m_file, m_line);
}
} }
struct TimerChannel: public LogChannel { static const char* name(); static const int verbosity = 0; }; struct TimerChannel: public LogChannel { static const char* name(); static const int verbosity = 0; };

20
libdevcore/Common.h

@ -41,6 +41,7 @@
#include <functional> #include <functional>
#include <string> #include <string>
#include <chrono> #include <chrono>
#include <boost/current_function.hpp>
#include <boost/functional/hash.hpp> #include <boost/functional/hash.hpp>
#pragma warning(push) #pragma warning(push)
#pragma GCC diagnostic push #pragma GCC diagnostic push
@ -63,6 +64,8 @@ using byte = uint8_t;
#define DEV_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} #define DEV_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
#define DEV_IF_NO_ELSE(X) if(!(X)){}else
namespace dev namespace dev
{ {
@ -163,10 +166,6 @@ private:
class HasInvariants class HasInvariants
{ {
public: public:
/// Check invariants are met, throw if not.
void checkInvariants() const;
protected:
/// Reimplement to specify the invariants. /// Reimplement to specify the invariants.
virtual bool invariants() const = 0; virtual bool invariants() const = 0;
}; };
@ -175,16 +174,22 @@ protected:
class InvariantChecker class InvariantChecker
{ {
public: public:
InvariantChecker(HasInvariants* _this): m_this(_this) { m_this->checkInvariants(); } InvariantChecker(HasInvariants* _this, char const* _fn, char const* _file, int _line): m_this(_this), m_function(_fn), m_file(_file), m_line(_line) { checkInvariants(); }
~InvariantChecker() { m_this->checkInvariants(); } ~InvariantChecker() { checkInvariants(); }
private: private:
/// Check invariants are met, throw if not.
void checkInvariants() const;
HasInvariants const* m_this; HasInvariants const* m_this;
char const* m_function;
char const* m_file;
int m_line;
}; };
/// Scope guard for invariant check in a class derived from HasInvariants. /// Scope guard for invariant check in a class derived from HasInvariants.
#if ETH_DEBUG #if ETH_DEBUG
#define DEV_INVARIANT_CHECK { ::dev::InvariantChecker __dev_invariantCheck(this); } #define DEV_INVARIANT_CHECK ::dev::InvariantChecker __dev_invariantCheck(this, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)
#else #else
#define DEV_INVARIANT_CHECK (void)0; #define DEV_INVARIANT_CHECK (void)0;
#endif #endif
@ -235,6 +240,7 @@ enum class WithExisting: int
{ {
Trust = 0, Trust = 0,
Verify, Verify,
Rescue,
Kill Kill
}; };

99
libdevcore/CommonData.h

@ -50,8 +50,8 @@ enum class HexPrefix
/// Convert a series of bytes to the corresponding string of hex duplets. /// Convert a series of bytes to the corresponding string of hex duplets.
/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte. /// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte.
/// @example toHex("A\x69") == "4169" /// @example toHex("A\x69") == "4169"
template <class _T> template <class T>
std::string toHex(_T const& _data, int _w = 2, HexPrefix _prefix = HexPrefix::DontAdd) std::string toHex(T const& _data, int _w = 2, HexPrefix _prefix = HexPrefix::DontAdd)
{ {
std::ostringstream ret; std::ostringstream ret;
unsigned ii = 0; unsigned ii = 0;
@ -99,27 +99,27 @@ bytes asNibbles(bytesConstRef const& _s);
/// Converts a templated integer value to the big-endian byte-stream represented on a templated collection. /// Converts a templated integer value to the big-endian byte-stream represented on a templated collection.
/// The size of the collection object will be unchanged. If it is too small, it will not represent the /// The size of the collection object will be unchanged. If it is too small, it will not represent the
/// value properly, if too big then the additional elements will be zeroed out. /// value properly, if too big then the additional elements will be zeroed out.
/// @a _Out will typically be either std::string or bytes. /// @a Out will typically be either std::string or bytes.
/// @a _T will typically by unsigned, u160, u256 or bigint. /// @a T will typically by unsigned, u160, u256 or bigint.
template <class _T, class _Out> template <class T, class Out>
inline void toBigEndian(_T _val, _Out& o_out) inline void toBigEndian(T _val, Out& o_out)
{ {
for (auto i = o_out.size(); i != 0; _val >>= 8, i--) for (auto i = o_out.size(); i != 0; _val >>= 8, i--)
{ {
_T v = _val & (_T)0xff; T v = _val & (T)0xff;
o_out[i - 1] = (typename _Out::value_type)(uint8_t)v; o_out[i - 1] = (typename Out::value_type)(uint8_t)v;
} }
} }
/// Converts a big-endian byte-stream represented on a templated collection to a templated integer value. /// Converts a big-endian byte-stream represented on a templated collection to a templated integer value.
/// @a _In will typically be either std::string or bytes. /// @a _In will typically be either std::string or bytes.
/// @a _T will typically by unsigned, u160, u256 or bigint. /// @a T will typically by unsigned, u160, u256 or bigint.
template <class _T, class _In> template <class T, class _In>
inline _T fromBigEndian(_In const& _bytes) inline T fromBigEndian(_In const& _bytes)
{ {
_T ret = (_T)0; T ret = (T)0;
for (auto i: _bytes) for (auto i: _bytes)
ret = (_T)((ret << 8) | (byte)(typename std::make_unsigned<typename _In::value_type>::type)i); ret = (T)((ret << 8) | (byte)(typename std::make_unsigned<typename _In::value_type>::type)i);
return ret; return ret;
} }
@ -131,11 +131,11 @@ inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); ret
/// Convenience function for toBigEndian. /// Convenience function for toBigEndian.
/// @returns a byte array just big enough to represent @a _val. /// @returns a byte array just big enough to represent @a _val.
template <class _T> template <class T>
inline bytes toCompactBigEndian(_T _val, unsigned _min = 0) inline bytes toCompactBigEndian(T _val, unsigned _min = 0)
{ {
int i = 0; int i = 0;
for (_T v = _val; v; ++i, v >>= 8) {} for (T v = _val; v; ++i, v >>= 8) {}
bytes ret(std::max<unsigned>(_min, i), 0); bytes ret(std::max<unsigned>(_min, i), 0);
toBigEndian(_val, ret); toBigEndian(_val, ret);
return ret; return ret;
@ -147,11 +147,11 @@ inline bytes toCompactBigEndian(byte _val, unsigned _min = 0)
/// Convenience function for toBigEndian. /// Convenience function for toBigEndian.
/// @returns a string just big enough to represent @a _val. /// @returns a string just big enough to represent @a _val.
template <class _T> template <class T>
inline std::string toCompactBigEndianString(_T _val, unsigned _min = 0) inline std::string toCompactBigEndianString(T _val, unsigned _min = 0)
{ {
int i = 0; int i = 0;
for (_T v = _val; v; ++i, v >>= 8) {} for (T v = _val; v; ++i, v >>= 8) {}
std::string ret(std::max<unsigned>(_min, i), '\0'); std::string ret(std::max<unsigned>(_min, i), '\0');
toBigEndian(_val, ret); toBigEndian(_val, ret);
return ret; return ret;
@ -179,8 +179,8 @@ std::string escaped(std::string const& _s, bool _all = true);
/// Determines the length of the common prefix of the two collections given. /// Determines the length of the common prefix of the two collections given.
/// @returns the number of elements both @a _t and @a _u share, in order, at the beginning. /// @returns the number of elements both @a _t and @a _u share, in order, at the beginning.
/// @example commonPrefix("Hello world!", "Hello, world!") == 5 /// @example commonPrefix("Hello world!", "Hello, world!") == 5
template <class _T, class _U> template <class T, class _U>
unsigned commonPrefix(_T const& _t, _U const& _u) unsigned commonPrefix(T const& _t, _U const& _u)
{ {
unsigned s = std::min<unsigned>(_t.size(), _u.size()); unsigned s = std::min<unsigned>(_t.size(), _u.size());
for (unsigned i = 0;; ++i) for (unsigned i = 0;; ++i)
@ -196,8 +196,8 @@ std::string randomWord();
// General datatype convenience functions. // General datatype convenience functions.
/// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero. /// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero.
template <class _T> template <class T>
inline unsigned bytesRequired(_T _i) inline unsigned bytesRequired(T _i)
{ {
unsigned i = 0; unsigned i = 0;
for (; _i != 0; ++i, _i >>= 8) {} for (; _i != 0; ++i, _i >>= 8) {}
@ -206,39 +206,39 @@ inline unsigned bytesRequired(_T _i)
/// Trims a given number of elements from the front of a collection. /// Trims a given number of elements from the front of a collection.
/// Only works for POD element types. /// Only works for POD element types.
template <class _T> template <class T>
void trimFront(_T& _t, unsigned _elements) void trimFront(T& _t, unsigned _elements)
{ {
static_assert(std::is_pod<typename _T::value_type>::value, ""); static_assert(std::is_pod<typename T::value_type>::value, "");
memmove(_t.data(), _t.data() + _elements, (_t.size() - _elements) * sizeof(_t[0])); memmove(_t.data(), _t.data() + _elements, (_t.size() - _elements) * sizeof(_t[0]));
_t.resize(_t.size() - _elements); _t.resize(_t.size() - _elements);
} }
/// Pushes an element on to the front of a collection. /// Pushes an element on to the front of a collection.
/// Only works for POD element types. /// Only works for POD element types.
template <class _T, class _U> template <class T, class _U>
void pushFront(_T& _t, _U _e) void pushFront(T& _t, _U _e)
{ {
static_assert(std::is_pod<typename _T::value_type>::value, ""); static_assert(std::is_pod<typename T::value_type>::value, "");
_t.push_back(_e); _t.push_back(_e);
memmove(_t.data() + 1, _t.data(), (_t.size() - 1) * sizeof(_e)); memmove(_t.data() + 1, _t.data(), (_t.size() - 1) * sizeof(_e));
_t[0] = _e; _t[0] = _e;
} }
/// Concatenate two vectors of elements of POD types. /// Concatenate two vectors of elements of POD types.
template <class _T> template <class T>
inline std::vector<_T>& operator+=(std::vector<typename std::enable_if<std::is_pod<_T>::value, _T>::type>& _a, std::vector<_T> const& _b) inline std::vector<T>& operator+=(std::vector<typename std::enable_if<std::is_pod<T>::value, T>::type>& _a, std::vector<T> const& _b)
{ {
auto s = _a.size(); auto s = _a.size();
_a.resize(_a.size() + _b.size()); _a.resize(_a.size() + _b.size());
memcpy(_a.data() + s, _b.data(), _b.size() * sizeof(_T)); memcpy(_a.data() + s, _b.data(), _b.size() * sizeof(T));
return _a; return _a;
} }
/// Concatenate two vectors of elements. /// Concatenate two vectors of elements.
template <class _T> template <class T>
inline std::vector<_T>& operator+=(std::vector<typename std::enable_if<!std::is_pod<_T>::value, _T>::type>& _a, std::vector<_T> const& _b) inline std::vector<T>& operator+=(std::vector<typename std::enable_if<!std::is_pod<T>::value, T>::type>& _a, std::vector<T> const& _b)
{ {
_a.reserve(_a.size() + _b.size()); _a.reserve(_a.size() + _b.size());
for (auto& i: _b) for (auto& i: _b)
@ -289,16 +289,16 @@ template <class T, class U> std::vector<T> operator+(std::vector<T> _a, U const&
} }
/// Concatenate two vectors of elements. /// Concatenate two vectors of elements.
template <class _T> template <class T>
inline std::vector<_T> operator+(std::vector<_T> const& _a, std::vector<_T> const& _b) inline std::vector<T> operator+(std::vector<T> const& _a, std::vector<T> const& _b)
{ {
std::vector<_T> ret(_a); std::vector<T> ret(_a);
return ret += _b; return ret += _b;
} }
/// Merge two sets of elements. /// Merge two sets of elements.
template <class _T> template <class T>
inline std::set<_T>& operator+=(std::set<_T>& _a, std::set<_T> const& _b) inline std::set<T>& operator+=(std::set<T>& _a, std::set<T> const& _b)
{ {
for (auto& i: _b) for (auto& i: _b)
_a.insert(i); _a.insert(i);
@ -306,13 +306,28 @@ inline std::set<_T>& operator+=(std::set<_T>& _a, std::set<_T> const& _b)
} }
/// Merge two sets of elements. /// Merge two sets of elements.
template <class _T> template <class T>
inline std::set<_T> operator+(std::set<_T> const& _a, std::set<_T> const& _b) inline std::set<T> operator+(std::set<T> const& _a, std::set<T> const& _b)
{ {
std::set<_T> ret(_a); std::set<T> ret(_a);
return ret += _b; return ret += _b;
} }
template <class A, class B>
std::unordered_map<A, B>& operator+=(std::unordered_map<A, B>& _x, std::unordered_map<A, B> const& _y)
{
for (auto const& i: _y)
_x.insert(i);
return _x;
}
template <class A, class B>
std::unordered_map<A, B> operator+(std::unordered_map<A, B> const& _x, std::unordered_map<A, B> const& _y)
{
std::unordered_map<A, B> ret(_x);
return ret += _y;
}
/// Make normal string from fixed-length string. /// Make normal string from fixed-length string.
std::string toString(string32 const& _s); std::string toString(string32 const& _s);

29
libdevcore/FixedHash.h

@ -24,6 +24,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <cstdint>
#include <random> #include <random>
#include <algorithm> #include <algorithm>
#include "CommonData.h" #include "CommonData.h"
@ -31,6 +32,10 @@
namespace dev namespace dev
{ {
/// Compile-time calculation of Log2 of constant values.
template <unsigned N> struct StaticLog2 { enum { result = 1 + StaticLog2<N/2>::result }; };
template <> struct StaticLog2<1> { enum { result = 0 }; };
extern std::random_device s_fixedHashEngine; extern std::random_device s_fixedHashEngine;
/// Fixed-size raw-byte array container type, with an API optimised for storing hashes. /// Fixed-size raw-byte array container type, with an API optimised for storing hashes.
@ -77,7 +82,7 @@ public:
explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); }
/// Explicitly construct, copying from a string. /// Explicitly construct, copying from a string.
explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent): FixedHash(_t == FromHex ? fromHex(_s) : dev::asBytes(_s), _ht) {} explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent): FixedHash(_t == FromHex ? fromHex(_s, WhenError::Throw) : dev::asBytes(_s), _ht) {}
/// Convert to arithmetic type. /// Convert to arithmetic type.
operator Arith() const { return fromBigEndian<Arith>(m_data); } operator Arith() const { return fromBigEndian<Arith>(m_data); }
@ -102,7 +107,10 @@ public:
FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; }
FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; } FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; }
/// @returns true if all bytes in @a _c are set in this object. // Big-endian increment.
FixedHash& operator++() { for (unsigned i = size; i > 0 && !++m_data[--i]; ) {} return *this; }
/// @returns true if all one-bits in @a _c are set in this object.
bool contains(FixedHash const& _c) const { return (*this & _c) == _c; } bool contains(FixedHash const& _c) const { return (*this & _c) == _c; }
/// @returns a particular byte from the hash. /// @returns a particular byte from the hash.
@ -146,7 +154,7 @@ public:
{ {
FixedHash ret; FixedHash ret;
for (auto& i: ret.m_data) for (auto& i: ret.m_data)
i = std::uniform_int_distribution<uint16_t>(0, 255)(_eng); i = (uint8_t)std::uniform_int_distribution<uint16_t>(0, 255)(_eng);
return ret; return ret;
} }
@ -171,18 +179,21 @@ public:
template <unsigned P, unsigned M> inline FixedHash<M> bloomPart() const template <unsigned P, unsigned M> inline FixedHash<M> bloomPart() const
{ {
static_assert((M & (M - 1)) == 0, "M must be power-of-two"); unsigned const c_bloomBits = M * 8;
static const unsigned c_bloomBits = M * 8; unsigned const c_mask = c_bloomBits - 1;
unsigned mask = c_bloomBits - 1; unsigned const c_bloomBytes = (StaticLog2<c_bloomBits>::result + 7) / 8;
unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8;
static_assert((M & (M - 1)) == 0, "M must be power-of-two");
static_assert(P * c_bloomBytes <= N, "out of range");
FixedHash<M> ret; FixedHash<M> ret;
byte const* p = data(); byte const* p = data();
for (unsigned i = 0; i < P; ++i) for (unsigned i = 0; i < P; ++i)
{ {
unsigned index = 0; unsigned index = 0;
for (unsigned j = 0; j < bloomBytes; ++j, ++p) for (unsigned j = 0; j < c_bloomBytes; ++j, ++p)
index = (index << 8) | *p; index = (index << 8) | *p;
index &= mask; index &= c_mask;
ret[M - 1 - index / 8] |= (1 << (index % 8)); ret[M - 1 - index / 8] |= (1 << (index % 8));
} }
return ret; return ret;

2
libdevcore/Guards.h

@ -95,7 +95,7 @@ using SpinGuard = std::lock_guard<SpinLock>;
* Mutex m; * Mutex m;
* int d; * int d;
* ... * ...
* ETH_(m) * ETH_GUARDED(m)
* { * {
* for (auto d = 50; d > 25; --d) * for (auto d = 50; d > 25; --d)
* foo(d); * foo(d);

84
libdevcore/RLP.cpp

@ -49,12 +49,12 @@ RLP::iterator& RLP::iterator::operator++()
{ {
if (m_remaining) if (m_remaining)
{ {
m_lastItem.retarget(m_lastItem.next().data(), m_remaining); m_currentItem.retarget(m_currentItem.next().data(), m_remaining);
m_lastItem = m_lastItem.cropped(0, RLP(m_lastItem, ThrowOnFail | FailIfTooSmall).actualSize()); m_currentItem = m_currentItem.cropped(0, sizeAsEncoded(m_currentItem));
m_remaining -= std::min<unsigned>(m_remaining, m_lastItem.size()); m_remaining -= std::min<size_t>(m_remaining, m_currentItem.size());
} }
else else
m_lastItem.retarget(m_lastItem.next().data(), 0); m_currentItem.retarget(m_currentItem.next().data(), 0);
return *this; return *this;
} }
@ -63,28 +63,28 @@ RLP::iterator::iterator(RLP const& _parent, bool _begin)
if (_begin && _parent.isList()) if (_begin && _parent.isList())
{ {
auto pl = _parent.payload(); auto pl = _parent.payload();
m_lastItem = pl.cropped(0, RLP(pl, ThrowOnFail | FailIfTooSmall).actualSize()); m_currentItem = pl.cropped(0, sizeAsEncoded(pl));
m_remaining = pl.size() - m_lastItem.size(); m_remaining = pl.size() - m_currentItem.size();
} }
else else
{ {
m_lastItem = _parent.data().cropped(_parent.data().size()); m_currentItem = _parent.data().cropped(_parent.data().size());
m_remaining = 0; m_remaining = 0;
} }
} }
RLP RLP::operator[](unsigned _i) const RLP RLP::operator[](size_t _i) const
{ {
if (_i < m_lastIndex) if (_i < m_lastIndex)
{ {
m_lastEnd = RLP(payload(), ThrowOnFail | FailIfTooSmall).actualSize(); m_lastEnd = sizeAsEncoded(payload());
m_lastItem = payload().cropped(0, m_lastEnd); m_lastItem = payload().cropped(0, m_lastEnd);
m_lastIndex = 0; m_lastIndex = 0;
} }
for (; m_lastIndex < _i && m_lastItem.size(); ++m_lastIndex) for (; m_lastIndex < _i && m_lastItem.size(); ++m_lastIndex)
{ {
m_lastItem = payload().cropped(m_lastEnd); m_lastItem = payload().cropped(m_lastEnd);
m_lastItem = m_lastItem.cropped(0, RLP(m_lastItem, ThrowOnFail | FailIfTooSmall).actualSize()); m_lastItem = m_lastItem.cropped(0, sizeAsEncoded(m_lastItem));
m_lastEnd += m_lastItem.size(); m_lastEnd += m_lastItem.size();
} }
return RLP(m_lastItem, ThrowOnFail | FailIfTooSmall); return RLP(m_lastItem, ThrowOnFail | FailIfTooSmall);
@ -100,7 +100,7 @@ RLPs RLP::toList() const
return ret; return ret;
} }
unsigned RLP::actualSize() const size_t RLP::actualSize() const
{ {
if (isNull()) if (isNull())
return 0; return 0;
@ -142,7 +142,7 @@ bool RLP::isInt() const
} }
else if (n < c_rlpListStart) else if (n < c_rlpListStart)
{ {
if ((int)m_data.size() <= 1 + n - c_rlpDataIndLenZero) if (m_data.size() <= size_t(1 + n - c_rlpDataIndLenZero))
BOOST_THROW_EXCEPTION(BadRLP()); BOOST_THROW_EXCEPTION(BadRLP());
return m_data[1 + n - c_rlpDataIndLenZero] != 0; return m_data[1 + n - c_rlpDataIndLenZero] != 0;
} }
@ -151,63 +151,75 @@ bool RLP::isInt() const
return false; return false;
} }
unsigned RLP::length() const size_t RLP::length() const
{ {
if (isNull()) if (isNull())
return 0; return 0;
requireGood(); requireGood();
unsigned ret = 0; size_t ret = 0;
byte n = m_data[0]; byte const n = m_data[0];
if (n < c_rlpDataImmLenStart) if (n < c_rlpDataImmLenStart)
return 1; return 1;
else if (n <= c_rlpDataIndLenZero) else if (n <= c_rlpDataIndLenZero)
return n - c_rlpDataImmLenStart; return n - c_rlpDataImmLenStart;
else if (n < c_rlpListStart) else if (n < c_rlpListStart)
{ {
if ((int)m_data.size() <= n - c_rlpDataIndLenZero) if (m_data.size() <= size_t(n - c_rlpDataIndLenZero))
BOOST_THROW_EXCEPTION(BadRLP()); BOOST_THROW_EXCEPTION(BadRLP());
if ((int)m_data.size() > 1) if (m_data.size() > 1)
if (m_data[1] == 0) if (m_data[1] == 0)
BOOST_THROW_EXCEPTION(BadRLP()); BOOST_THROW_EXCEPTION(BadRLP());
for (int i = 0; i < n - c_rlpDataIndLenZero; ++i) unsigned lengthSize = n - c_rlpDataIndLenZero;
if (lengthSize > sizeof(ret))
// We did not check, but would most probably not fit in our memory.
BOOST_THROW_EXCEPTION(UndersizeRLP());
for (unsigned i = 0; i < lengthSize; ++i)
ret = (ret << 8) | m_data[i + 1]; ret = (ret << 8) | m_data[i + 1];
} }
else if (n <= c_rlpListIndLenZero) else if (n <= c_rlpListIndLenZero)
return n - c_rlpListStart; return n - c_rlpListStart;
else else
{ {
if ((int)m_data.size() <= n - c_rlpListIndLenZero) unsigned lengthSize = n - c_rlpListIndLenZero;
if (m_data.size() <= lengthSize)
BOOST_THROW_EXCEPTION(BadRLP()); BOOST_THROW_EXCEPTION(BadRLP());
if ((int)m_data.size() > 1) if (m_data.size() > 1)
if (m_data[1] == 0) if (m_data[1] == 0)
BOOST_THROW_EXCEPTION(BadRLP()); BOOST_THROW_EXCEPTION(BadRLP());
for (int i = 0; i < n - c_rlpListIndLenZero; ++i) if (lengthSize > sizeof(ret))
// We did not check, but would most probably not fit in our memory.
BOOST_THROW_EXCEPTION(UndersizeRLP());
for (unsigned i = 0; i < lengthSize; ++i)
ret = (ret << 8) | m_data[i + 1]; ret = (ret << 8) | m_data[i + 1];
} }
// We have to be able to add payloadOffset to length without overflow.
// This rejects roughly 4GB-sized RLPs on some platforms.
if (ret >= std::numeric_limits<size_t>::max() - 0x100)
BOOST_THROW_EXCEPTION(UndersizeRLP());
return ret; return ret;
} }
unsigned RLP::items() const size_t RLP::items() const
{ {
if (isList()) if (isList())
{ {
bytesConstRef d = payload().cropped(0, length()); bytesConstRef d = payload().cropped(0, length());
unsigned i = 0; size_t i = 0;
for (; d.size(); ++i) for (; d.size(); ++i)
d = d.cropped(RLP(d, ThrowOnFail | FailIfTooSmall).actualSize()); d = d.cropped(sizeAsEncoded(d));
return i; return i;
} }
return 0; return 0;
} }
RLPStream& RLPStream::appendRaw(bytesConstRef _s, unsigned _itemCount) RLPStream& RLPStream::appendRaw(bytesConstRef _s, size_t _itemCount)
{ {
m_out.insert(m_out.end(), _s.begin(), _s.end()); m_out.insert(m_out.end(), _s.begin(), _s.end());
noteAppended(_itemCount); noteAppended(_itemCount);
return *this; return *this;
} }
void RLPStream::noteAppended(unsigned _itemCount) void RLPStream::noteAppended(size_t _itemCount)
{ {
if (!_itemCount) if (!_itemCount)
return; return;
@ -223,7 +235,7 @@ void RLPStream::noteAppended(unsigned _itemCount)
{ {
auto p = m_listStack.back().second; auto p = m_listStack.back().second;
m_listStack.pop_back(); m_listStack.pop_back();
unsigned s = m_out.size() - p; // list size size_t s = m_out.size() - p; // list size
auto brs = bytesRequired(s); auto brs = bytesRequired(s);
unsigned encodeSize = s < c_rlpListImmLenCount ? 1 : (1 + brs); unsigned encodeSize = s < c_rlpListImmLenCount ? 1 : (1 + brs);
// cdebug << "s: " << s << ", p: " << p << ", m_out.size(): " << m_out.size() << ", encodeSize: " << encodeSize << " (br: " << brs << ")"; // cdebug << "s: " << s << ", p: " << p << ", m_out.size(): " << m_out.size() << ", encodeSize: " << encodeSize << " (br: " << brs << ")";
@ -232,19 +244,21 @@ void RLPStream::noteAppended(unsigned _itemCount)
memmove(m_out.data() + p + encodeSize, m_out.data() + p, os - p); memmove(m_out.data() + p + encodeSize, m_out.data() + p, os - p);
if (s < c_rlpListImmLenCount) if (s < c_rlpListImmLenCount)
m_out[p] = (byte)(c_rlpListStart + s); m_out[p] = (byte)(c_rlpListStart + s);
else else if (c_rlpListIndLenZero + brs <= 0xff)
{ {
m_out[p] = (byte)(c_rlpListIndLenZero + brs); m_out[p] = (byte)(c_rlpListIndLenZero + brs);
byte* b = &(m_out[p + brs]); byte* b = &(m_out[p + brs]);
for (; s; s >>= 8) for (; s; s >>= 8)
*(b--) = (byte)s; *(b--) = (byte)s;
} }
else
BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("itemCount too large for RLP"));
} }
_itemCount = 1; // for all following iterations, we've effectively appended a single item only since we completed a list. _itemCount = 1; // for all following iterations, we've effectively appended a single item only since we completed a list.
} }
} }
RLPStream& RLPStream::appendList(unsigned _items) RLPStream& RLPStream::appendList(size_t _items)
{ {
// cdebug << "appendList(" << _items << ")"; // cdebug << "appendList(" << _items << ")";
if (_items) if (_items)
@ -266,10 +280,10 @@ RLPStream& RLPStream::appendList(bytesConstRef _rlp)
RLPStream& RLPStream::append(bytesConstRef _s, bool _compact) RLPStream& RLPStream::append(bytesConstRef _s, bool _compact)
{ {
unsigned s = _s.size(); size_t s = _s.size();
byte const* d = _s.data(); byte const* d = _s.data();
if (_compact) if (_compact)
for (unsigned i = 0; i < _s.size() && !*d; ++i, --s, ++d) {} for (size_t i = 0; i < _s.size() && !*d; ++i, --s, ++d) {}
if (s == 1 && *d < c_rlpDataImmLenStart) if (s == 1 && *d < c_rlpDataImmLenStart)
m_out.push_back(*d); m_out.push_back(*d);
@ -299,6 +313,8 @@ RLPStream& RLPStream::append(bigint _i)
else else
{ {
auto brbr = bytesRequired(br); auto brbr = bytesRequired(br);
if (c_rlpDataIndLenZero + brbr > 0xff)
BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("Number too large for RLP"));
m_out.push_back((byte)(c_rlpDataIndLenZero + brbr)); m_out.push_back((byte)(c_rlpDataIndLenZero + brbr));
pushInt(br, brbr); pushInt(br, brbr);
} }
@ -308,9 +324,11 @@ RLPStream& RLPStream::append(bigint _i)
return *this; return *this;
} }
void RLPStream::pushCount(unsigned _count, byte _base) void RLPStream::pushCount(size_t _count, byte _base)
{ {
auto br = bytesRequired(_count); auto br = bytesRequired(_count);
if (int(br) + _base > 0xff)
BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("Count too large for RLP"));
m_out.push_back((byte)(br + _base)); // max 8 bytes. m_out.push_back((byte)(br + _base)); // max 8 bytes.
pushInt(_count, br); pushInt(_count, br);
} }
@ -320,7 +338,7 @@ std::ostream& dev::operator<<(std::ostream& _out, RLP const& _d)
if (_d.isNull()) if (_d.isNull())
_out << "null"; _out << "null";
else if (_d.isInt()) else if (_d.isInt())
_out << std::showbase << std::hex << std::nouppercase << _d.toInt<bigint>(RLP::LaisezFaire) << dec; _out << std::showbase << std::hex << std::nouppercase << _d.toInt<bigint>(RLP::LaissezFaire) << dec;
else if (_d.isData()) else if (_d.isData())
_out << escaped(_d.toString(), false); _out << escaped(_d.toString(), false);
else if (_d.isList()) else if (_d.isList())

68
libdevcore/RLP.h

@ -71,7 +71,7 @@ public:
FailIfTooSmall = 16, FailIfTooSmall = 16,
Strict = ThrowOnFail | FailIfTooBig, Strict = ThrowOnFail | FailIfTooBig,
VeryStrict = ThrowOnFail | FailIfTooBig | FailIfTooSmall, VeryStrict = ThrowOnFail | FailIfTooBig | FailIfTooSmall,
LaisezFaire = AllowNonCanon LaissezFaire = AllowNonCanon
}; };
using Strictness = int; using Strictness = int;
@ -113,12 +113,12 @@ public:
bool isInt() const; bool isInt() const;
/// @returns the number of items in the list, or zero if it isn't a list. /// @returns the number of items in the list, or zero if it isn't a list.
unsigned itemCount() const { return isList() ? items() : 0; } size_t itemCount() const { return isList() ? items() : 0; }
unsigned itemCountStrict() const { if (!isList()) BOOST_THROW_EXCEPTION(BadCast()); return items(); } size_t itemCountStrict() const { if (!isList()) BOOST_THROW_EXCEPTION(BadCast()); return items(); }
/// @returns the number of bytes in the data, or zero if it isn't data. /// @returns the number of bytes in the data, or zero if it isn't data.
unsigned size() const { return isData() ? length() : 0; } size_t size() const { return isData() ? length() : 0; }
unsigned sizeStrict() const { if (!isData()) BOOST_THROW_EXCEPTION(BadCast()); return length(); } size_t sizeStrict() const { if (!isData()) BOOST_THROW_EXCEPTION(BadCast()); return length(); }
/// Equality operators; does best-effort conversion and checks for equality. /// Equality operators; does best-effort conversion and checks for equality.
bool operator==(char const* _s) const { return isData() && toString() == _s; } bool operator==(char const* _s) const { return isData() && toString() == _s; }
@ -137,7 +137,7 @@ public:
/// Subscript operator. /// Subscript operator.
/// @returns the list item @a _i if isList() and @a _i < listItems(), or RLP() otherwise. /// @returns the list item @a _i if isList() and @a _i < listItems(), or RLP() otherwise.
/// @note if used to access items in ascending order, this is efficient. /// @note if used to access items in ascending order, this is efficient.
RLP operator[](unsigned _i) const; RLP operator[](size_t _i) const;
using element_type = RLP; using element_type = RLP;
@ -152,16 +152,16 @@ public:
iterator& operator++(); iterator& operator++();
iterator operator++(int) { auto ret = *this; operator++(); return ret; } iterator operator++(int) { auto ret = *this; operator++(); return ret; }
RLP operator*() const { return RLP(m_lastItem); } RLP operator*() const { return RLP(m_currentItem); }
bool operator==(iterator const& _cmp) const { return m_lastItem == _cmp.m_lastItem; } bool operator==(iterator const& _cmp) const { return m_currentItem == _cmp.m_currentItem; }
bool operator!=(iterator const& _cmp) const { return !operator==(_cmp); } bool operator!=(iterator const& _cmp) const { return !operator==(_cmp); }
private: private:
iterator() {} iterator() {}
iterator(RLP const& _parent, bool _begin); iterator(RLP const& _parent, bool _begin);
unsigned m_remaining = 0; size_t m_remaining = 0;
bytesConstRef m_lastItem; bytesConstRef m_currentItem;
}; };
/// @brief Iterator into beginning of sub-item list (valid only if we are a list). /// @brief Iterator into beginning of sub-item list (valid only if we are a list).
@ -247,7 +247,7 @@ public:
if (itemCount() != N || !isList()) if (itemCount() != N || !isList())
BOOST_THROW_EXCEPTION(BadCast()); BOOST_THROW_EXCEPTION(BadCast());
std::array<T, N> ret; std::array<T, N> ret;
for (unsigned i = 0; i < N; ++i) for (size_t i = 0; i < N; ++i)
{ {
ret[i] = (T)operator[](i); ret[i] = (T)operator[](i);
} }
@ -259,19 +259,21 @@ public:
{ {
requireGood(); requireGood();
if ((!isInt() && !(_flags & AllowNonCanon)) || isList() || isNull()) if ((!isInt() && !(_flags & AllowNonCanon)) || isList() || isNull())
{
if (_flags & ThrowOnFail) if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast()); BOOST_THROW_EXCEPTION(BadCast());
else else
return 0; return 0;
else {} }
auto p = payload(); auto p = payload();
if (p.size() > intTraits<_T>::maxSize && (_flags & FailIfTooBig)) if (p.size() > intTraits<_T>::maxSize && (_flags & FailIfTooBig))
{
if (_flags & ThrowOnFail) if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast()); BOOST_THROW_EXCEPTION(BadCast());
else else
return 0; return 0;
else {} }
return fromBigEndian<_T>(p); return fromBigEndian<_T>(p);
} }
@ -280,14 +282,15 @@ public:
{ {
requireGood(); requireGood();
if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)) || (length() < _N::size && (_flags & FailIfTooSmall))) if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)) || (length() < _N::size && (_flags & FailIfTooSmall)))
{
if (_flags & ThrowOnFail) if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast()); BOOST_THROW_EXCEPTION(BadCast());
else else
return _N(); return _N();
else{} }
_N ret; _N ret;
size_t s = std::min((size_t)_N::size, (size_t)length()); size_t s = std::min<size_t>(_N::size, length());
memcpy(ret.data() + _N::size - s, payload().data(), s); memcpy(ret.data() + _N::size - s, payload().data(), s);
return ret; return ret;
} }
@ -298,9 +301,9 @@ public:
/// @returns the data payload. Valid for all types. /// @returns the data payload. Valid for all types.
bytesConstRef payload() const { auto l = length(); if (l > m_data.size()) BOOST_THROW_EXCEPTION(BadRLP()); return m_data.cropped(payloadOffset(), l); } bytesConstRef payload() const { auto l = length(); if (l > m_data.size()) BOOST_THROW_EXCEPTION(BadRLP()); return m_data.cropped(payloadOffset(), l); }
/// @returns the theoretical size of this item. /// @returns the theoretical size of this item as encoded in the data.
/// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work. /// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work.
unsigned actualSize() const; size_t actualSize() const;
private: private:
/// Disable construction from rvalue /// Disable construction from rvalue
@ -316,20 +319,23 @@ private:
unsigned lengthSize() const { if (isData() && m_data[0] > c_rlpDataIndLenZero) return m_data[0] - c_rlpDataIndLenZero; if (isList() && m_data[0] > c_rlpListIndLenZero) return m_data[0] - c_rlpListIndLenZero; return 0; } unsigned lengthSize() const { if (isData() && m_data[0] > c_rlpDataIndLenZero) return m_data[0] - c_rlpDataIndLenZero; if (isList() && m_data[0] > c_rlpListIndLenZero) return m_data[0] - c_rlpListIndLenZero; return 0; }
/// @returns the size in bytes of the payload, as given by the RLP as opposed to as inferred from m_data. /// @returns the size in bytes of the payload, as given by the RLP as opposed to as inferred from m_data.
unsigned length() const; size_t length() const;
/// @returns the number of bytes into the data that the payload starts. /// @returns the number of bytes into the data that the payload starts.
unsigned payloadOffset() const { return isSingleByte() ? 0 : (1 + lengthSize()); } size_t payloadOffset() const { return isSingleByte() ? 0 : (1 + lengthSize()); }
/// @returns the number of data items. /// @returns the number of data items.
unsigned items() const; size_t items() const;
/// @returns the size encoded into the RLP in @a _data and throws if _data is too short.
static size_t sizeAsEncoded(bytesConstRef _data) { return RLP(_data, ThrowOnFail | FailIfTooSmall).actualSize(); }
/// Our byte data. /// Our byte data.
bytesConstRef m_data; bytesConstRef m_data;
/// The list-indexing cache. /// The list-indexing cache.
mutable unsigned m_lastIndex = (unsigned)-1; mutable size_t m_lastIndex = (size_t)-1;
mutable unsigned m_lastEnd = 0; mutable size_t m_lastEnd = 0;
mutable bytesConstRef m_lastItem; mutable bytesConstRef m_lastItem;
}; };
@ -343,7 +349,7 @@ public:
RLPStream() {} RLPStream() {}
/// Initializes the RLPStream as a list of @a _listItems items. /// Initializes the RLPStream as a list of @a _listItems items.
explicit RLPStream(unsigned _listItems) { appendList(_listItems); } explicit RLPStream(size_t _listItems) { appendList(_listItems); }
~RLPStream() {} ~RLPStream() {}
@ -359,7 +365,7 @@ public:
template <unsigned N> RLPStream& append(FixedHash<N> _s, bool _compact = false, bool _allOrNothing = false) { return _allOrNothing && !_s ? append(bytesConstRef()) : append(_s.ref(), _compact); } template <unsigned N> RLPStream& append(FixedHash<N> _s, bool _compact = false, bool _allOrNothing = false) { return _allOrNothing && !_s ? append(bytesConstRef()) : append(_s.ref(), _compact); }
/// Appends an arbitrary RLP fragment - this *must* be a single item unless @a _itemCount is given. /// Appends an arbitrary RLP fragment - this *must* be a single item unless @a _itemCount is given.
RLPStream& append(RLP const& _rlp, unsigned _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); } RLPStream& append(RLP const& _rlp, size_t _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); }
/// Appends a sequence of data to the stream as a list. /// Appends a sequence of data to the stream as a list.
template <class _T> RLPStream& append(std::vector<_T> const& _s) { return appendVector(_s); } template <class _T> RLPStream& append(std::vector<_T> const& _s) { return appendVector(_s); }
@ -370,14 +376,14 @@ public:
template <class T, class U> RLPStream& append(std::pair<T, U> const& _s) { appendList(2); append(_s.first); append(_s.second); return *this; } template <class T, class U> RLPStream& append(std::pair<T, U> const& _s) { appendList(2); append(_s.first); append(_s.second); return *this; }
/// Appends a list. /// Appends a list.
RLPStream& appendList(unsigned _items); RLPStream& appendList(size_t _items);
RLPStream& appendList(bytesConstRef _rlp); RLPStream& appendList(bytesConstRef _rlp);
RLPStream& appendList(bytes const& _rlp) { return appendList(&_rlp); } RLPStream& appendList(bytes const& _rlp) { return appendList(&_rlp); }
RLPStream& appendList(RLPStream const& _s) { return appendList(&_s.out()); } RLPStream& appendList(RLPStream const& _s) { return appendList(&_s.out()); }
/// Appends raw (pre-serialised) RLP data. Use with caution. /// Appends raw (pre-serialised) RLP data. Use with caution.
RLPStream& appendRaw(bytesConstRef _rlp, unsigned _itemCount = 1); RLPStream& appendRaw(bytesConstRef _rlp, size_t _itemCount = 1);
RLPStream& appendRaw(bytes const& _rlp, unsigned _itemCount = 1) { return appendRaw(&_rlp, _itemCount); } RLPStream& appendRaw(bytes const& _rlp, size_t _itemCount = 1) { return appendRaw(&_rlp, _itemCount); }
/// Shift operators for appending data items. /// Shift operators for appending data items.
template <class T> RLPStream& operator<<(T _data) { return append(_data); } template <class T> RLPStream& operator<<(T _data) { return append(_data); }
@ -392,14 +398,14 @@ public:
void swapOut(bytes& _dest) { if(!m_listStack.empty()) BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("listStack is not empty")); swap(m_out, _dest); } void swapOut(bytes& _dest) { if(!m_listStack.empty()) BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("listStack is not empty")); swap(m_out, _dest); }
private: private:
void noteAppended(unsigned _itemCount = 1); void noteAppended(size_t _itemCount = 1);
/// Push the node-type byte (using @a _base) along with the item count @a _count. /// Push the node-type byte (using @a _base) along with the item count @a _count.
/// @arg _count is number of characters for strings, data-bytes for ints, or items for lists. /// @arg _count is number of characters for strings, data-bytes for ints, or items for lists.
void pushCount(unsigned _count, byte _offset); void pushCount(size_t _count, byte _offset);
/// Push an integer as a raw big-endian byte-stream. /// Push an integer as a raw big-endian byte-stream.
template <class _T> void pushInt(_T _i, unsigned _br) template <class _T> void pushInt(_T _i, size_t _br)
{ {
m_out.resize(m_out.size() + _br); m_out.resize(m_out.size() + _br);
byte* b = &m_out.back(); byte* b = &m_out.back();
@ -410,7 +416,7 @@ private:
/// Our output byte stream. /// Our output byte stream.
bytes m_out; bytes m_out;
std::vector<std::pair<unsigned, unsigned>> m_listStack; std::vector<std::pair<size_t, size_t>> m_listStack;
}; };
template <class _T> void rlpListAux(RLPStream& _out, _T _t) { _out << _t; } template <class _T> void rlpListAux(RLPStream& _out, _T _t) { _out << _t; }

160
libdevcore/RangeMask.h

@ -24,6 +24,7 @@
#include <map> #include <map>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <iterator>
#include <iostream> #include <iostream>
#include <assert.h> #include <assert.h>
@ -35,6 +36,12 @@ class RLPStream;
using UnsignedRange = std::pair<unsigned, unsigned>; using UnsignedRange = std::pair<unsigned, unsigned>;
using UnsignedRanges = std::vector<UnsignedRange>; using UnsignedRanges = std::vector<UnsignedRange>;
/**
* Set of elements of a certain "ground range" representable by unions of ranges inside this
* ground range.
* Ranges are given as pairs (begin, end), denoting the interval [begin, end), i.e. end is excluded.
* Supports set-theoretic operators, size and iteration.
*/
template <class T> template <class T>
class RangeMask class RangeMask
{ {
@ -44,14 +51,19 @@ public:
using Range = std::pair<T, T>; using Range = std::pair<T, T>;
using Ranges = std::vector<Range>; using Ranges = std::vector<Range>;
/// Constructs an empty range mask with empty ground range.
RangeMask(): m_all(0, 0) {} RangeMask(): m_all(0, 0) {}
/// Constructs an empty range mask with ground range [_begin, _end).
RangeMask(T _begin, T _end): m_all(_begin, _end) {} RangeMask(T _begin, T _end): m_all(_begin, _end) {}
/// Constructs an empty range mask with ground range _c.
RangeMask(Range const& _c): m_all(_c) {} RangeMask(Range const& _c): m_all(_c) {}
/// @returns the union with the range mask _m, taking also the union of the ground ranges.
RangeMask unionedWith(RangeMask const& _m) const { return operator+(_m); } RangeMask unionedWith(RangeMask const& _m) const { return operator+(_m); }
RangeMask operator+(RangeMask const& _m) const { return RangeMask(*this) += _m; } RangeMask operator+(RangeMask const& _m) const { return RangeMask(*this) += _m; }
RangeMask lowest(T _items) const /// @returns a new range mask containing the smallest _items elements (not ranges).
RangeMask lowest(decltype(T{} - T{}) _items) const
{ {
RangeMask ret(m_all); RangeMask ret(m_all);
for (auto i = m_ranges.begin(); i != m_ranges.end() && _items; ++i) for (auto i = m_ranges.begin(); i != m_ranges.end() && _items; ++i)
@ -59,8 +71,10 @@ public:
return ret; return ret;
} }
/// @returns the complement of the range mask relative to the ground range.
RangeMask operator~() const { return inverted(); } RangeMask operator~() const { return inverted(); }
/// @returns a copy of this range mask representing the complement relative to the ground range.
RangeMask inverted() const RangeMask inverted() const
{ {
RangeMask ret(m_all); RangeMask ret(m_all);
@ -76,6 +90,8 @@ public:
return ret; return ret;
} }
/// Changes the range mask to its complement relative to the ground range and returns a
/// reference to itself.
RangeMask& invert() { return *this = inverted(); } RangeMask& invert() { return *this = inverted(); }
template <class S> RangeMask operator-(S const& _m) const { auto ret = *this; return ret -= _m; } template <class S> RangeMask operator-(S const& _m) const { auto ret = *this; return ret -= _m; }
@ -92,61 +108,13 @@ public:
return *this; return *this;
} }
RangeMask& operator+=(Range const& _m) { return unionWith(_m); } RangeMask& operator+=(Range const& _m) { return unionWith(_m); }
RangeMask& unionWith(Range const& _m) /// Modifies this range mask to also include the range _m, which has to be a subset of
{ /// the ground range.
for (auto i = _m.first; i < _m.second;) RangeMask& unionWith(Range const& _m);
{
assert(i >= m_all.first);
assert(i < m_all.second);
// for each number, we find the element equal or next lower. this, if any, must contain the value.
auto uit = m_ranges.upper_bound(i);
auto it = uit == m_ranges.begin() ? m_ranges.end() : std::prev(uit);
if (it == m_ranges.end() || it->second < i)
// lower range is too low to merge.
// if the next higher range is too high.
if (uit == m_ranges.end() || uit->first > _m.second)
{
// just create a new range
m_ranges[i] = _m.second;
break;
}
else
{
if (uit->first == i)
// move i to end of range
i = uit->second;
else
{
// merge with the next higher range
// move i to end of range
i = m_ranges[i] = uit->second;
i = uit->second;
m_ranges.erase(uit);
}
}
else if (it->second == i)
{
// if the next higher range is too high.
if (uit == m_ranges.end() || uit->first > _m.second)
{
// merge with the next lower range
m_ranges[it->first] = _m.second;
break;
}
else
{
// merge with both next lower & next higher.
i = m_ranges[it->first] = uit->second;
m_ranges.erase(uit);
}
}
else
i = it->second;
}
return *this;
}
/// Adds the single element _i to the range mask.
RangeMask& operator+=(T _m) { return unionWith(_m); } RangeMask& operator+=(T _m) { return unionWith(_m); }
/// Adds the single element _i to the range mask.
RangeMask& unionWith(T _i) RangeMask& unionWith(T _i)
{ {
return operator+=(Range(_i, _i + 1)); return operator+=(Range(_i, _i + 1));
@ -181,10 +149,12 @@ public:
m_all = std::make_pair(0, 0); m_all = std::make_pair(0, 0);
} }
/// @returns the ground range.
std::pair<T, T> const& all() const { return m_all; } std::pair<T, T> const& all() const { return m_all; }
/// Extends the ground range to include _i.
void extendAll(T _i) { m_all = std::make_pair(std::min(m_all.first, _i), std::max(m_all.second, _i + 1)); } void extendAll(T _i) { m_all = std::make_pair(std::min(m_all.first, _i), std::max(m_all.second, _i + 1)); }
class const_iterator class const_iterator: public std::iterator<std::forward_iterator_tag, T>
{ {
friend class RangeMask; friend class RangeMask;
@ -208,6 +178,8 @@ public:
const_iterator begin() const { return const_iterator(*this, false); } const_iterator begin() const { return const_iterator(*this, false); }
const_iterator end() const { return const_iterator(*this, true); } const_iterator end() const { return const_iterator(*this, true); }
/// @returns the smallest element in the range mask that is larger than _t or the end of the
/// base range if such an element does not exist.
T next(T _t) const T next(T _t) const
{ {
_t++; _t++;
@ -219,6 +191,7 @@ public:
return uit == m_ranges.end() ? m_all.second : uit->first; return uit == m_ranges.end() ? m_all.second : uit->first;
} }
/// @returns the number of elements (not ranges) in the range mask.
size_t size() const size_t size() const
{ {
size_t c = 0; size_t c = 0;
@ -227,8 +200,24 @@ public:
return c; return c;
} }
size_t firstOut() const
{
if (m_ranges.empty() || !m_ranges.count(m_all.first))
return m_all.first;
return m_ranges.at(m_all.first);
}
size_t lastIn() const
{
if (m_ranges.empty())
return m_all.first;
return m_ranges.rbegin()->second - 1;
}
private: private:
/// The ground range.
UnsignedRange m_all; UnsignedRange m_all;
/// Mapping begin -> end containing the ranges.
std::map<T, T> m_ranges; std::map<T, T> m_ranges;
}; };
@ -241,4 +230,65 @@ template <class T> inline std::ostream& operator<<(std::ostream& _out, RangeMask
return _out; return _out;
} }
template <class T>
RangeMask<T>& RangeMask<T>::unionWith(typename RangeMask<T>::Range const& _m)
{
for (auto i = _m.first; i < _m.second;)
{
assert(i >= m_all.first);
assert(i < m_all.second);
// For each number, we find the element equal or next lower. this, if any, must contain the value.
// First range that starts after i.
auto rangeAfter = m_ranges.upper_bound(i);
// Range before rangeAfter or "end" if the rangeAfter is the first ever...
auto it = rangeAfter == m_ranges.begin() ? m_ranges.end() : std::prev(rangeAfter);
if (it == m_ranges.end() || it->second < i)
{
// i is either before the first range or between two ranges (with some distance
// so that we cannot merge it onto "it").
// lower range is too low to merge.
// if the next higher range is too high.
if (rangeAfter == m_ranges.end() || rangeAfter->first > _m.second)
{
// just create a new range
m_ranges[i] = _m.second;
break;
}
else
{
if (rangeAfter->first == i)
// move i to end of range
i = rangeAfter->second;
else
{
// merge with the next higher range
// move i to end of range
i = m_ranges[i] = rangeAfter->second;
m_ranges.erase(rangeAfter);
}
}
}
else if (it->second == i)
{
// The range before i ends with i.
// if the next higher range is too high.
if (rangeAfter == m_ranges.end() || rangeAfter->first > _m.second)
{
// merge with the next lower range
m_ranges[it->first] = _m.second;
break;
}
else
{
// merge with both next lower & next higher.
i = m_ranges[it->first] = rangeAfter->second;
m_ranges.erase(rangeAfter);
}
}
else
i = it->second;
}
return *this;
}
} }

22
libdevcore/TrieCommon.h

@ -32,26 +32,38 @@ inline byte nibble(bytesConstRef _data, unsigned _i)
return (_i & 1) ? (_data[_i / 2] & 15) : (_data[_i / 2] >> 4); return (_i & 1) ? (_data[_i / 2] & 15) : (_data[_i / 2] >> 4);
} }
inline unsigned sharedNibbles(bytesConstRef _a, unsigned _ab, unsigned _ae, bytesConstRef _b, unsigned _bb, unsigned _be) /// Interprets @a _first and @a _second as vectors of nibbles and returns the length of the longest common
/// prefix of _first[_beginFirst..._endFirst] and _second[_beginSecond..._endSecond].
inline unsigned sharedNibbles(bytesConstRef _first, unsigned _beginFirst, unsigned _endFirst, bytesConstRef _second, unsigned _beginSecond, unsigned _endSecond)
{ {
unsigned ret = 0; unsigned ret = 0;
for (unsigned ai = _ab, bi = _bb; ai < _ae && bi < _be && nibble(_a, ai) == nibble(_b, bi); ++ai, ++bi, ++ret) {} while (_beginFirst < _endFirst && _beginSecond < _endSecond && nibble(_first, _beginFirst) == nibble(_second, _beginSecond))
{
++_beginFirst;
++_beginSecond;
++ret;
}
return ret; return ret;
} }
/**
* Nibble-based view on a bytesConstRef.
*/
struct NibbleSlice struct NibbleSlice
{ {
bytesConstRef data; bytesConstRef data;
unsigned offset; unsigned offset;
NibbleSlice(bytesConstRef _d = bytesConstRef(), unsigned _o = 0): data(_d), offset(_o) {} NibbleSlice(bytesConstRef _data = bytesConstRef(), unsigned _offset = 0): data(_data), offset(_offset) {}
byte operator[](unsigned _index) const { return nibble(data, offset + _index); } byte operator[](unsigned _index) const { return nibble(data, offset + _index); }
unsigned size() const { return data.size() * 2 - offset; } unsigned size() const { return data.size() * 2 - offset; }
bool empty() const { return !size(); } bool empty() const { return !size(); }
NibbleSlice mid(unsigned _index) const { return NibbleSlice(data, offset + _index); } NibbleSlice mid(unsigned _index) const { return NibbleSlice(data, offset + _index); }
void clear() { data.reset(); offset = 0; } void clear() { data.reset(); offset = 0; }
/// @returns true iff _k is a prefix of this.
bool contains(NibbleSlice _k) const { return shared(_k) == _k.size(); } bool contains(NibbleSlice _k) const { return shared(_k) == _k.size(); }
/// @returns the number of shared nibbles at the beginning of this and _k.
unsigned shared(NibbleSlice _k) const { return sharedNibbles(data, offset, offset + size(), _k.data, _k.offset, _k.offset + _k.size()); } unsigned shared(NibbleSlice _k) const { return sharedNibbles(data, offset, offset + size(), _k.data, _k.offset, _k.offset + _k.size()); }
/** /**
* @brief Determine if we, a full key, are situated prior to a particular key-prefix. * @brief Determine if we, a full key, are situated prior to a particular key-prefix.
@ -60,8 +72,8 @@ struct NibbleSlice
*/ */
bool isEarlierThan(NibbleSlice _k) const bool isEarlierThan(NibbleSlice _k) const
{ {
unsigned i; unsigned i = 0;
for (i = 0; i < _k.size() && i < size(); ++i) for (; i < _k.size() && i < size(); ++i)
if (operator[](i) < _k[i]) // Byte is lower - we're earlier.. if (operator[](i) < _k[i]) // Byte is lower - we're earlier..
return true; return true;
else if (operator[](i) > _k[i]) // Byte is higher - we're not earlier. else if (operator[](i) > _k[i]) // Byte is higher - we're not earlier.

162
libdevcore/TrieDB.h

@ -66,7 +66,7 @@ class GenericTrieDB
public: public:
using DB = _DB; using DB = _DB;
GenericTrieDB(DB* _db = nullptr): m_db(_db) {} explicit GenericTrieDB(DB* _db = nullptr): m_db(_db) {}
GenericTrieDB(DB* _db, h256 const& _root, Verification _v = Verification::Normal) { open(_db, _root, _v); } GenericTrieDB(DB* _db, h256 const& _root, Verification _v = Verification::Normal) { open(_db, _root, _v); }
~GenericTrieDB() {} ~GenericTrieDB() {}
@ -96,11 +96,72 @@ public:
/// True if the trie is initialised but empty (i.e. that the DB contains the root node which is empty). /// True if the trie is initialised but empty (i.e. that the DB contains the root node which is empty).
bool isEmpty() const { return m_root == c_shaNull && node(m_root).size(); } bool isEmpty() const { return m_root == c_shaNull && node(m_root).size(); }
h256 const& root() const { if (!node(m_root).size()) BOOST_THROW_EXCEPTION(BadRoot()); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return m_root; } // patch the root in the case of the empty trie. TODO: handle this properly. h256 const& root() const { if (node(m_root).empty()) BOOST_THROW_EXCEPTION(BadRoot()); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return m_root; } // patch the root in the case of the empty trie. TODO: handle this properly.
std::string at(bytes const& _key) const { return at(&_key); }
std::string at(bytesConstRef _key) const;
void insert(bytes const& _key, bytes const& _value) { insert(&_key, &_value); }
void insert(bytesConstRef _key, bytes const& _value) { insert(_key, &_value); }
void insert(bytes const& _key, bytesConstRef _value) { insert(&_key, _value); }
void insert(bytesConstRef _key, bytesConstRef _value);
void remove(bytes const& _key) { remove(&_key); }
void remove(bytesConstRef _key);
bool contains(bytes const& _key) { return contains(&_key); }
bool contains(bytesConstRef _key) { return !at(_key).empty(); }
class iterator
{
public:
using value_type = std::pair<bytesConstRef, bytesConstRef>;
iterator() {}
explicit iterator(GenericTrieDB const* _db);
iterator(GenericTrieDB const* _db, bytesConstRef _key);
iterator& operator++() { next(); return *this; }
value_type operator*() const { return at(); }
value_type operator->() const { return at(); }
bool operator==(iterator const& _c) const { return _c.m_trail == m_trail; }
bool operator!=(iterator const& _c) const { return _c.m_trail != m_trail; }
value_type at() const;
private:
void next();
void next(NibbleSlice _key);
struct Node
{
std::string rlp;
std::string key; // as hexPrefixEncoding.
byte child; // 255 -> entering, 16 -> actually at the node, 17 -> exiting, 0-15 -> actual children.
// 255 -> 16 -> 0 -> 1 -> ... -> 15 -> 17
void setChild(unsigned _i) { child = _i; }
void setFirstChild() { child = 16; }
void incrementChild() { child = child == 16 ? 0 : child == 15 ? 17 : (child + 1); }
bool operator==(Node const& _c) const { return rlp == _c.rlp && key == _c.key && child == _c.child; }
bool operator!=(Node const& _c) const { return !operator==(_c); }
};
protected:
std::vector<Node> m_trail;
GenericTrieDB<DB> const* m_that;
};
iterator begin() const { return iterator(this); }
iterator end() const { return iterator(); }
iterator lower_bound(bytesConstRef _key) const { return iterator(this, _key); }
void debugPrint() {} void debugPrint() {}
void descendKey(h256 _k, h256Hash& _keyMask, bool _wasExt, std::ostream* _out, int _indent = 0) const /// Used for debugging, scans the whole trie.
void descendKey(h256 const& _k, h256Hash& _keyMask, bool _wasExt, std::ostream* _out, int _indent = 0) const
{ {
_keyMask.erase(_k); _keyMask.erase(_k);
if (_k == m_root && _k == c_shaNull) // root allowed to be empty if (_k == m_root && _k == c_shaNull) // root allowed to be empty
@ -108,6 +169,7 @@ public:
descendList(RLP(node(_k)), _keyMask, _wasExt, _out, _indent); // if not, it must be a list descendList(RLP(node(_k)), _keyMask, _wasExt, _out, _indent); // if not, it must be a list
} }
/// Used for debugging, scans the whole trie.
void descendEntry(RLP const& _r, h256Hash& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const void descendEntry(RLP const& _r, h256Hash& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const
{ {
if (_r.isData() && _r.size() == 32) if (_r.isData() && _r.size() == 32)
@ -118,6 +180,7 @@ public:
BOOST_THROW_EXCEPTION(InvalidTrie()); BOOST_THROW_EXCEPTION(InvalidTrie());
} }
/// Used for debugging, scans the whole trie.
void descendList(RLP const& _r, h256Hash& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const void descendList(RLP const& _r, h256Hash& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const
{ {
if (_r.isList() && _r.itemCount() == 2 && (!_wasExt || _out)) if (_r.isList() && _r.itemCount() == 2 && (!_wasExt || _out))
@ -139,6 +202,7 @@ public:
BOOST_THROW_EXCEPTION(InvalidTrie()); BOOST_THROW_EXCEPTION(InvalidTrie());
} }
/// Used for debugging, scans the whole trie.
h256Hash leftOvers(std::ostream* _out = nullptr) const h256Hash leftOvers(std::ostream* _out = nullptr) const
{ {
h256Hash k = m_db->keys(); h256Hash k = m_db->keys();
@ -146,11 +210,14 @@ public:
return k; return k;
} }
/// Used for debugging, scans the whole trie.
void debugStructure(std::ostream& _out) const void debugStructure(std::ostream& _out) const
{ {
leftOvers(&_out); leftOvers(&_out);
} }
/// Used for debugging, scans the whole trie.
/// @param _requireNoLeftOvers if true, requires that all keys are reachable.
bool check(bool _requireNoLeftOvers) const bool check(bool _requireNoLeftOvers) const
{ {
try try
@ -164,66 +231,6 @@ public:
} }
} }
std::string at(bytes const& _key) const { return at(&_key); }
std::string at(bytesConstRef _key) const;
void insert(bytes const& _key, bytes const& _value) { insert(&_key, &_value); }
void insert(bytesConstRef _key, bytes const& _value) { insert(_key, &_value); }
void insert(bytes const& _key, bytesConstRef _value) { insert(&_key, _value); }
void insert(bytesConstRef _key, bytesConstRef _value);
void remove(bytes const& _key) { remove(&_key); }
void remove(bytesConstRef _key);
bool contains(bytes const& _key) { return contains(&_key); }
bool contains(bytesConstRef _key) { return !at(_key).empty(); }
class iterator
{
public:
using value_type = std::pair<bytesConstRef, bytesConstRef>;
iterator() {}
iterator(GenericTrieDB const* _db);
iterator(GenericTrieDB const* _db, bytesConstRef _key);
iterator& operator++() { next(); return *this; }
value_type operator*() const { return at(); }
value_type operator->() const { return at(); }
bool operator==(iterator const& _c) const { return _c.m_trail == m_trail; }
bool operator!=(iterator const& _c) const { return _c.m_trail != m_trail; }
value_type at() const;
private:
void next();
void next(NibbleSlice _key);
struct Node
{
std::string rlp;
std::string key; // as hexPrefixEncoding.
byte child; // 255 -> entering, 16 -> actually at the node, 17 -> exiting, 0-15 -> actual children.
// 255 -> 16 -> 0 -> 1 -> ... -> 15 -> 17
void setChild(unsigned _i) { child = _i; }
void setFirstChild() { child = 16; }
void incrementChild() { child = child == 16 ? 0 : child == 15 ? 17 : (child + 1); }
bool operator==(Node const& _c) const { return rlp == _c.rlp && key == _c.key && child == _c.child; }
bool operator!=(Node const& _c) const { return !operator==(_c); }
};
protected:
std::vector<Node> m_trail;
GenericTrieDB<DB> const* m_that;
};
iterator begin() const { return this; }
iterator end() const { return iterator(); }
iterator lower_bound(bytesConstRef _key) const { return iterator(this, _key); }
protected: protected:
DB* db() const { return m_db; } DB* db() const { return m_db; }
@ -279,12 +286,12 @@ private:
bool isTwoItemNode(RLP const& _n) const; bool isTwoItemNode(RLP const& _n) const;
std::string deref(RLP const& _n) const; std::string deref(RLP const& _n) const;
std::string node(h256 _h) const { return m_db->lookup(_h); } std::string node(h256 const& _h) const { return m_db->lookup(_h); }
// These are low-level node insertion functions that just go straight through into the DB. // These are low-level node insertion functions that just go straight through into the DB.
h256 forceInsertNode(bytesConstRef _v) { auto h = sha3(_v); forceInsertNode(h, _v); return h; } h256 forceInsertNode(bytesConstRef _v) { auto h = sha3(_v); forceInsertNode(h, _v); return h; }
void forceInsertNode(h256 _h, bytesConstRef _v) { m_db->insert(_h, _v); } void forceInsertNode(h256 const& _h, bytesConstRef _v) { m_db->insert(_h, _v); }
void forceKillNode(h256 _h) { m_db->kill(_h); } void forceKillNode(h256 const& _h) { m_db->kill(_h); }
// This are semantically-aware node insertion functions that only kills when the node's // This are semantically-aware node insertion functions that only kills when the node's
// data is < 32 bytes. It can safely be used when pruning the trie but won't work correctly // data is < 32 bytes. It can safely be used when pruning the trie but won't work correctly
@ -305,6 +312,9 @@ std::ostream& operator<<(std::ostream& _out, GenericTrieDB<DB> const& _db)
return _out; return _out;
} }
/**
* Different view on a GenericTrieDB that can use different key types.
*/
template <class Generic, class _KeyType> template <class Generic, class _KeyType>
class SpecificTrieDB: public Generic class SpecificTrieDB: public Generic
{ {
@ -753,14 +763,14 @@ template <class DB> void GenericTrieDB<DB>::insert(bytesConstRef _key, bytesCons
tdebug << "Insert" << toHex(_key.cropped(0, 4)) << "=>" << toHex(_value); tdebug << "Insert" << toHex(_key.cropped(0, 4)) << "=>" << toHex(_value);
#endif #endif
std::string rv = node(m_root); std::string rootValue = node(m_root);
assert(rv.size()); assert(rootValue.size());
bytes b = mergeAt(RLP(rv), m_root, NibbleSlice(_key), _value); bytes b = mergeAt(RLP(rootValue), m_root, NibbleSlice(_key), _value);
// mergeAt won't attempt to delete the node if it's less than 32 bytes // mergeAt won't attempt to delete the node if it's less than 32 bytes
// However, we know it's the root node and thus always hashed. // However, we know it's the root node and thus always hashed.
// So, if it's less than 32 (and thus should have been deleted but wasn't) then we delete it here. // So, if it's less than 32 (and thus should have been deleted but wasn't) then we delete it here.
if (rv.size() < 32) if (rootValue.size() < 32)
forceKillNode(m_root); forceKillNode(m_root);
m_root = forceInsertNode(&b); m_root = forceInsertNode(&b);
} }
@ -1066,11 +1076,11 @@ template <class DB> bytes GenericTrieDB<DB>::place(RLP const& _orig, NibbleSlice
killNode(_orig); killNode(_orig);
if (_orig.isEmpty()) if (_orig.isEmpty())
return (RLPStream(2) << hexPrefixEncode(_k, true) << _s).out(); return rlpList(hexPrefixEncode(_k, true), _s);
assert(_orig.isList() && (_orig.itemCount() == 2 || _orig.itemCount() == 17)); assert(_orig.isList() && (_orig.itemCount() == 2 || _orig.itemCount() == 17));
if (_orig.itemCount() == 2) if (_orig.itemCount() == 2)
return (RLPStream(2) << _orig[0] << _s).out(); return rlpList(_orig[0], _s);
auto s = RLPStream(17); auto s = RLPStream(17);
for (unsigned i = 0; i < 16; ++i) for (unsigned i = 0; i < 16; ++i)
@ -1152,7 +1162,7 @@ template <class DB> bytes GenericTrieDB<DB>::graft(RLP const& _orig)
} }
assert(n.itemCount() == 2); assert(n.itemCount() == 2);
return (RLPStream(2) << hexPrefixEncode(keyOf(_orig), keyOf(n), isLeaf(n)) << n[1]).out(); return rlpList(hexPrefixEncode(keyOf(_orig), keyOf(n), isLeaf(n)), n[1]);
// auto ret = // auto ret =
// std::cout << keyOf(_orig) << " ++ " << keyOf(n) << " == " << keyOf(RLP(ret)) << std::endl; // std::cout << keyOf(_orig) << " ++ " << keyOf(n) << " == " << keyOf(RLP(ret)) << std::endl;
// return ret; // return ret;
@ -1201,11 +1211,7 @@ template <class DB> bytes GenericTrieDB<DB>::branch(RLP const& _orig)
for (unsigned i = 0; i < 16; ++i) for (unsigned i = 0; i < 16; ++i)
if (i == b) if (i == b)
if (isLeaf(_orig) || k.size() > 1) if (isLeaf(_orig) || k.size() > 1)
{ streamNode(r, rlpList(hexPrefixEncode(k.mid(1), isLeaf(_orig)), _orig[1]));
RLPStream bottom(2);
bottom << hexPrefixEncode(k.mid(1), isLeaf(_orig)) << _orig[1];
streamNode(r, bottom.out());
}
else else
r << _orig[1]; r << _orig[1];
else else

42
libdevcrypto/Common.cpp

@ -22,6 +22,7 @@
#include "Common.h" #include "Common.h"
#include <random> #include <random>
#include <cstdint>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <mutex> #include <mutex>
@ -29,6 +30,7 @@
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libdevcore/SHA3.h> #include <libdevcore/SHA3.h>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include <libdevcore/RLP.h>
#if ETH_HAVE_SECP256K1 #if ETH_HAVE_SECP256K1
#include <secp256k1/secp256k1.h> #include <secp256k1/secp256k1.h>
#endif #endif
@ -46,7 +48,6 @@ struct Secp256k1Context
~Secp256k1Context() { secp256k1_stop(); } ~Secp256k1Context() { secp256k1_stop(); }
}; };
static Secp256k1Context s_secp256k1; static Secp256k1Context s_secp256k1;
void dev::crypto::secp256k1Init() { (void)s_secp256k1; }
#endif #endif
static Secp256k1PP s_secp256k1pp; static Secp256k1PP s_secp256k1pp;
@ -91,6 +92,11 @@ Address dev::toAddress(Secret const& _secret)
return toAddress(p); return toAddress(p);
} }
Address dev::toAddress(Address const& _from, u256 const& _nonce)
{
return right160(sha3(rlpList(_from, _nonce)));
}
void dev::encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher) void dev::encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher)
{ {
bytes io = _plain.toBytes(); bytes io = _plain.toBytes();
@ -184,17 +190,40 @@ bytes dev::decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _ci
Public dev::recover(Signature const& _sig, h256 const& _message) Public dev::recover(Signature const& _sig, h256 const& _message)
{ {
#ifdef ETH_HAVE_SECP256K1
bytes o(65);
int pubkeylen;
if (!secp256k1_ecdsa_recover_compact(_message.data(), h256::size, _sig.data(), o.data(), &pubkeylen, false, _sig[64]))
return Public();
return FixedHash<64>(o.data()+1, Public::ConstructFromPointer);
#else
return s_secp256k1pp.recover(_sig, _message.ref()); return s_secp256k1pp.recover(_sig, _message.ref());
#endif
} }
Signature dev::sign(Secret const& _k, h256 const& _hash) Signature dev::sign(Secret const& _k, h256 const& _hash)
{ {
#ifdef ETH_HAVE_SECP256K1
Signature s;
int v;
if (!secp256k1_ecdsa_sign_compact(_hash.data(), h256::size, s.data(), _k.data(), Nonce::get().data(), &v))
return Signature();
s[64] = v;
return s;
#else
return s_secp256k1pp.sign(_k, _hash); return s_secp256k1pp.sign(_k, _hash);
#endif
} }
bool dev::verify(Public const& _p, Signature const& _s, h256 const& _hash) bool dev::verify(Public const& _p, Signature const& _s, h256 const& _hash)
{ {
if (!_p)
return false;
#ifdef ETH_HAVE_SECP256K1
return _p == recover(_s, _hash);
#else
return s_secp256k1pp.verify(_p, _s, _hash.ref(), true); return s_secp256k1pp.verify(_p, _s, _hash.ref(), true);
#endif
} }
bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen) bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen)
@ -234,16 +263,9 @@ bytes dev::scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uin
KeyPair KeyPair::create() KeyPair KeyPair::create()
{ {
static boost::thread_specific_ptr<mt19937_64> s_eng;
static unsigned s_id = 0;
if (!s_eng.get())
s_eng.reset(new mt19937_64(time(0) + chrono::high_resolution_clock::now().time_since_epoch().count() + ++s_id));
uniform_int_distribution<uint16_t> d(0, 255);
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
KeyPair ret(FixedHash<32>::random(*s_eng.get())); KeyPair ret(FixedHash<32>::random());
if (ret.address()) if (ret.address())
return ret; return ret;
} }
@ -325,7 +347,7 @@ void Nonce::initialiseIfNeeded()
std::mt19937_64 s_eng(time(0) + chrono::high_resolution_clock::now().time_since_epoch().count()); std::mt19937_64 s_eng(time(0) + chrono::high_resolution_clock::now().time_since_epoch().count());
std::uniform_int_distribution<uint16_t> d(0, 255); std::uniform_int_distribution<uint16_t> d(0, 255);
for (unsigned i = 0; i < 32; ++i) for (unsigned i = 0; i < 32; ++i)
m_value[i] = byte(d(s_eng)); m_value[i] = (uint8_t)d(s_eng);
} }
if (!m_value) if (!m_value)
BOOST_THROW_EXCEPTION(InvalidState()); BOOST_THROW_EXCEPTION(InvalidState());

5
libdevcrypto/Common.h

@ -85,6 +85,9 @@ Address toAddress(Public const& _public);
/// @returns 0 if it's not a valid secret key. /// @returns 0 if it's not a valid secret key.
Address toAddress(Secret const& _secret); Address toAddress(Secret const& _secret);
// Convert transaction from and nonce to address.
Address toAddress(Address const& _from, u256 const& _nonce);
/// Encrypts plain text using Public key. /// Encrypts plain text using Public key.
void encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher); void encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
@ -177,8 +180,6 @@ namespace crypto
{ {
struct InvalidState: public dev::Exception {}; struct InvalidState: public dev::Exception {};
void secp256k1Init();
/// Key derivation /// Key derivation
h256 kdf(Secret const& _priv, h256 const& _hash); h256 kdf(Secret const& _priv, h256 const& _hash);

2
libdevcrypto/CryptoPP.cpp

@ -103,7 +103,7 @@ bool Secp256k1PP::decryptECIES(Secret const& _k, bytes& io_text)
// interop w/go ecies implementation // interop w/go ecies implementation
// io_cipher[0] must be 2, 3, or 4, else invalidpublickey // io_cipher[0] must be 2, 3, or 4, else invalidpublickey
if (io_text[0] < 2 || io_text[0] > 4) if (io_text.empty() || io_text[0] < 2 || io_text[0] > 4)
// invalid message: publickey // invalid message: publickey
return false; return false;

1
libethash-cl/CMakeLists.txt

@ -20,6 +20,7 @@ file(GLOB OUR_HEADERS "*.h")
set(HEADERS ${OUR_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h) set(HEADERS ${OUR_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${OpenCL_INCLUDE_DIRS}) include_directories(${OpenCL_INCLUDE_DIRS})
include_directories(..) include_directories(..)
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})

76
libethash-cl/ethash_cl_miner.cpp

@ -50,6 +50,10 @@
using namespace std; using namespace std;
unsigned const ethash_cl_miner::c_defaultLocalWorkSize = 64;
unsigned const ethash_cl_miner::c_defaultGlobalWorkSizeMultiplier = 4096; // * CL_DEFAULT_LOCAL_WORK_SIZE
unsigned const ethash_cl_miner::c_defaultMSPerBatch = 0;
// TODO: If at any point we can use libdevcore in here then we should switch to using a LogChannel // TODO: If at any point we can use libdevcore in here then we should switch to using a LogChannel
#define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl #define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl
// Types of OpenCL devices we are interested in // Types of OpenCL devices we are interested in
@ -140,15 +144,21 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
bool ethash_cl_miner::configureGPU( bool ethash_cl_miner::configureGPU(
unsigned _platformId, unsigned _platformId,
unsigned _localWorkSize,
unsigned _globalWorkSize,
unsigned _msPerBatch,
bool _allowCPU, bool _allowCPU,
unsigned _extraGPUMemory, unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock uint64_t _currentBlock
) )
{ {
s_workgroupSize = _localWorkSize;
s_initialGlobalWorkSize = _globalWorkSize;
s_msPerBatch = _msPerBatch;
s_allowCPU = _allowCPU; s_allowCPU = _allowCPU;
s_extraRequiredGPUMem = _extraGPUMemory; s_extraRequiredGPUMem = _extraGPUMemory;
// by default let's only consider the DAG of the first epoch // by default let's only consider the DAG of the first epoch
uint64_t dagSize = _currentBlock ? ethash_get_datasize(*_currentBlock) : 1073739904U; uint64_t dagSize = ethash_get_datasize(_currentBlock);
uint64_t requiredSize = dagSize + _extraGPUMemory; uint64_t requiredSize = dagSize + _extraGPUMemory;
return searchForAllDevices(_platformId, [&requiredSize](cl::Device const _device) -> bool return searchForAllDevices(_platformId, [&requiredSize](cl::Device const _device) -> bool
{ {
@ -175,6 +185,9 @@ bool ethash_cl_miner::configureGPU(
bool ethash_cl_miner::s_allowCPU = false; bool ethash_cl_miner::s_allowCPU = false;
unsigned ethash_cl_miner::s_extraRequiredGPUMem; unsigned ethash_cl_miner::s_extraRequiredGPUMem;
unsigned ethash_cl_miner::s_msPerBatch = ethash_cl_miner::c_defaultMSPerBatch;
unsigned ethash_cl_miner::s_workgroupSize = ethash_cl_miner::c_defaultLocalWorkSize;
unsigned ethash_cl_miner::s_initialGlobalWorkSize = ethash_cl_miner::c_defaultGlobalWorkSizeMultiplier * ethash_cl_miner::c_defaultLocalWorkSize;
bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback) bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback)
{ {
@ -254,7 +267,6 @@ void ethash_cl_miner::finish()
bool ethash_cl_miner::init( bool ethash_cl_miner::init(
uint8_t const* _dag, uint8_t const* _dag,
uint64_t _dagSize, uint64_t _dagSize,
unsigned _workgroupSize,
unsigned _platformId, unsigned _platformId,
unsigned _deviceId unsigned _deviceId
) )
@ -299,14 +311,18 @@ bool ethash_cl_miner::init(
m_context = cl::Context(vector<cl::Device>(&device, &device + 1)); m_context = cl::Context(vector<cl::Device>(&device, &device + 1));
m_queue = cl::CommandQueue(m_context, device); m_queue = cl::CommandQueue(m_context, device);
// use requested workgroup size, but we require multiple of 8 // make sure that global work size is evenly divisible by the local workgroup size
m_workgroupSize = ((_workgroupSize + 7) / 8) * 8; m_globalWorkSize = s_initialGlobalWorkSize;
if (m_globalWorkSize % s_workgroupSize != 0)
m_globalWorkSize = ((m_globalWorkSize / s_workgroupSize) + 1) * s_workgroupSize;
// remember the device's address bits
m_deviceBits = device.getInfo<CL_DEVICE_ADDRESS_BITS>();
// patch source code // patch source code
// note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled // 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 // 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); string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
addDefinition(code, "GROUP_SIZE", m_workgroupSize); addDefinition(code, "GROUP_SIZE", s_workgroupSize);
addDefinition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES)); addDefinition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
addDefinition(code, "ACCESSES", ETHASH_ACCESSES); addDefinition(code, "ACCESSES", ETHASH_ACCESSES);
addDefinition(code, "MAX_OUTPUTS", c_maxSearchResults); addDefinition(code, "MAX_OUTPUTS", c_maxSearchResults);
@ -323,7 +339,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG("Printing program log"); ETHCL_LOG("Printing program log");
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str()); ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
} }
catch (cl::Error const& err) catch (cl::Error const&)
{ {
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str()); ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
return false; return false;
@ -415,9 +431,8 @@ bool ethash_cl_miner::init(
return true; return true;
} }
void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook, unsigned _msPerBatch) void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook)
{ {
(void)_msPerBatch;
try try
{ {
struct pending_batch struct pending_batch
@ -454,10 +469,9 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
unsigned buf = 0; unsigned buf = 0;
random_device engine; random_device engine;
uint64_t start_nonce = uniform_int_distribution<uint64_t>()(engine); uint64_t start_nonce = uniform_int_distribution<uint64_t>()(engine);
for (;; start_nonce += m_batchSize) for (;; start_nonce += m_globalWorkSize)
{ {
// chrono::high_resolution_clock::time_point t = chrono::high_resolution_clock::now(); auto t = chrono::high_resolution_clock::now();
// supply output buffer to kernel // supply output buffer to kernel
m_searchKernel.setArg(0, m_searchBuffer[buf]); m_searchKernel.setArg(0, m_searchBuffer[buf]);
if (m_dagChunksCount == 1) if (m_dagChunksCount == 1)
@ -466,7 +480,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_searchKernel.setArg(6, start_nonce); m_searchKernel.setArg(6, start_nonce);
// execute it! // execute it!
m_queue.enqueueNDRangeKernel(m_searchKernel, cl::NullRange, m_batchSize, m_workgroupSize); m_queue.enqueueNDRangeKernel(m_searchKernel, cl::NullRange, m_globalWorkSize, s_workgroupSize);
pending.push({ start_nonce, buf }); pending.push({ start_nonce, buf });
buf = (buf + 1) % c_bufferCount; buf = (buf + 1) % c_bufferCount;
@ -486,7 +500,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_queue.enqueueUnmapMemObject(m_searchBuffer[batch.buf], results); m_queue.enqueueUnmapMemObject(m_searchBuffer[batch.buf], results);
bool exit = num_found && hook.found(nonces, num_found); bool exit = num_found && hook.found(nonces, num_found);
exit |= hook.searched(batch.start_nonce, m_batchSize); // always report searched before exit exit |= hook.searched(batch.start_nonce, m_globalWorkSize); // always report searched before exit
if (exit) if (exit)
break; break;
@ -497,19 +511,31 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
pending.pop(); pending.pop();
} }
/* chrono::high_resolution_clock::duration d = chrono::high_resolution_clock::now() - t; // adjust global work size depending on last search time
if (d > chrono::milliseconds(_msPerBatch * 10 / 9)) if (s_msPerBatch)
{ {
cerr << "Batch of" << m_batchSize << "took" << chrono::duration_cast<chrono::milliseconds>(d).count() << "ms, >>" << _msPerBatch << "ms."; // Global work size must be:
m_batchSize = max<unsigned>(128, m_batchSize * 9 / 10); // - less than or equal to 2 ^ DEVICE_BITS - 1
cerr << "New batch size" << m_batchSize; // - divisible by lobal work size (workgroup size)
auto d = chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - t);
if (d != chrono::milliseconds(0)) // if duration is zero, we did not get in the actual searh/or search not finished
{
if (d > chrono::milliseconds(s_msPerBatch * 10 / 9))
{
// cerr << "Batch of " << m_globalWorkSize << " took " << chrono::duration_cast<chrono::milliseconds>(d).count() << " ms, >> " << _msPerBatch << " ms." << endl;
m_globalWorkSize = max<unsigned>(128, m_globalWorkSize + s_workgroupSize);
// cerr << "New global work size" << m_globalWorkSize << endl;
}
else if (d < chrono::milliseconds(s_msPerBatch * 9 / 10))
{
// cerr << "Batch of " << m_globalWorkSize << " took " << chrono::duration_cast<chrono::milliseconds>(d).count() << " ms, << " << _msPerBatch << " ms." << endl;
m_globalWorkSize = min<unsigned>(pow(2, m_deviceBits) - 1, m_globalWorkSize - s_workgroupSize);
// Global work size should never be less than the workgroup size
m_globalWorkSize = max<unsigned>(s_workgroupSize, m_globalWorkSize);
// cerr << "New global work size" << m_globalWorkSize << endl;
}
}
} }
else if (d < chrono::milliseconds(_msPerBatch * 9 / 10))
{
cerr << "Batch of" << m_batchSize << "took" << chrono::duration_cast<chrono::milliseconds>(d).count() << "ms, <<" << _msPerBatch << "ms.";
m_batchSize = m_batchSize * 10 / 9;
cerr << "New batch size" << m_batchSize;
}*/
} }
// not safe to return until this is ready // not safe to return until this is ready

31
libethash-cl/ethash_cl_miner.h

@ -1,6 +1,6 @@
#pragma once #pragma once
#define __CL_ENABLE_EXCEPTIONS #define __CL_ENABLE_EXCEPTIONS
#define CL_USE_DEPRECATED_OPENCL_2_0_APIS #define CL_USE_DEPRECATED_OPENCL_2_0_APIS
#if defined(__clang__) #if defined(__clang__)
@ -12,7 +12,6 @@
#include "cl.hpp" #include "cl.hpp"
#endif #endif
#include <boost/optional.hpp>
#include <time.h> #include <time.h>
#include <functional> #include <functional>
#include <libethash/ethash.h> #include <libethash/ethash.h>
@ -20,7 +19,7 @@
class ethash_cl_miner class ethash_cl_miner
{ {
private: private:
enum { c_maxSearchResults = 63, c_bufferCount = 2, c_hashBatchSize = 1024, c_searchBatchSize = 1024 * 16 }; enum { c_maxSearchResults = 63, c_bufferCount = 2, c_hashBatchSize = 1024 };
public: public:
struct search_hook struct search_hook
@ -45,24 +44,34 @@ public:
static void listDevices(); static void listDevices();
static bool configureGPU( static bool configureGPU(
unsigned _platformId, unsigned _platformId,
unsigned _localWorkSize,
unsigned _globalWorkSize,
unsigned _msPerBatch,
bool _allowCPU, bool _allowCPU,
unsigned _extraGPUMemory, unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock uint64_t _currentBlock
); );
bool init( bool init(
uint8_t const* _dag, uint8_t const* _dag,
uint64_t _dagSize, uint64_t _dagSize,
unsigned _workgroupSize = 64,
unsigned _platformId = 0, unsigned _platformId = 0,
unsigned _deviceId = 0 unsigned _deviceId = 0
); );
void finish(); void finish();
void search(uint8_t const* _header, uint64_t _target, search_hook& _hook, unsigned _msPerBatch = 100); void search(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 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 search_chunk(uint8_t const*_header, uint64_t _target, search_hook& _hook);
/* -- default values -- */
/// Default value of the local work size. Also known as workgroup size.
static unsigned const c_defaultLocalWorkSize;
/// Default value of the global work size as a multiplier of the local work size
static unsigned const c_defaultGlobalWorkSizeMultiplier;
/// Default value of the milliseconds per global work size (per batch)
static unsigned const c_defaultMSPerBatch;
private: private:
static std::vector<cl::Device> getDevices(std::vector<cl::Platform> const& _platforms, unsigned _platformId); static std::vector<cl::Device> getDevices(std::vector<cl::Platform> const& _platforms, unsigned _platformId);
@ -76,10 +85,16 @@ private:
cl::Buffer m_header; cl::Buffer m_header;
cl::Buffer m_hashBuffer[c_bufferCount]; cl::Buffer m_hashBuffer[c_bufferCount];
cl::Buffer m_searchBuffer[c_bufferCount]; cl::Buffer m_searchBuffer[c_bufferCount];
unsigned m_workgroupSize; unsigned m_globalWorkSize;
unsigned m_batchSize = c_searchBatchSize;
bool m_openclOnePointOne; bool m_openclOnePointOne;
unsigned m_deviceBits;
/// The local work size for the search
static unsigned s_workgroupSize;
/// The initial global work size for the searches
static unsigned s_initialGlobalWorkSize;
/// The target milliseconds per batch for the search. If 0, then no adjustment will happen
static unsigned s_msPerBatch;
/// Allow CPU to appear as an OpenCL device or not. Default is false /// Allow CPU to appear as an OpenCL device or not. Default is false
static bool s_allowCPU; static bool s_allowCPU;
/// GPU memory required for other things, like window rendering e.t.c. /// GPU memory required for other things, like window rendering e.t.c.

2
libethcore/CMakeLists.txt

@ -28,7 +28,7 @@ add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ethash) target_link_libraries(${EXECUTABLE} ethash)
target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} devcrypto)
#target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} evmcore)
if (ETHASHCL) if (ETHASHCL)
target_link_libraries(${EXECUTABLE} ethash-cl) target_link_libraries(${EXECUTABLE} ethash-cl)

24
libethcore/Common.h

@ -97,10 +97,13 @@ enum class RelativeBlock: BlockNumber
Pending = PendingBlock Pending = PendingBlock
}; };
class Transaction;
struct ImportRoute struct ImportRoute
{ {
h256s deadBlocks; h256s deadBlocks;
h256s liveBlocks; h256s liveBlocks;
std::vector<Transaction> goodTranactions;
}; };
enum class ImportResult enum class ImportResult
@ -129,10 +132,10 @@ struct ImportRequirements
}; };
/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight. /// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight.
class Signal template<typename... Args> class Signal
{ {
public: public:
using Callback = std::function<void()>; using Callback = std::function<void(Args...)>;
class HandlerAux class HandlerAux
{ {
@ -141,7 +144,7 @@ public:
public: public:
~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; } ~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; }
void reset() { m_s = nullptr; } void reset() { m_s = nullptr; }
void fire() { m_h(); } void fire(Args&&... _args) { m_h(std::forward<Args>(_args)...); }
private: private:
HandlerAux(unsigned _i, Signal* _s, Callback const& _h): m_i(_i), m_s(_s), m_h(_h) {} HandlerAux(unsigned _i, Signal* _s, Callback const& _h): m_i(_i), m_s(_s), m_h(_h) {}
@ -154,7 +157,8 @@ public:
~Signal() ~Signal()
{ {
for (auto const& h : m_fire) for (auto const& h : m_fire)
h.second->reset(); if (auto l = h.second.lock())
l->reset();
} }
std::shared_ptr<HandlerAux> add(Callback const& _h) std::shared_ptr<HandlerAux> add(Callback const& _h)
@ -165,13 +169,18 @@ public:
return h; return h;
} }
void operator()() { for (auto const& f: m_fire) f.second->fire(); } void operator()(Args&... _args)
{
for (auto const& f: m_fire)
if (auto h = f.second.lock())
h->fire(std::forward<Args>(_args)...);
}
private: private:
std::map<unsigned, std::shared_ptr<Signal::HandlerAux>> m_fire; std::map<unsigned, std::weak_ptr<typename Signal::HandlerAux>> m_fire;
}; };
using Handler = std::shared_ptr<Signal::HandlerAux>; template<class... Args> using Handler = std::shared_ptr<typename Signal<Args...>::HandlerAux>;
struct TransactionSkeleton struct TransactionSkeleton
{ {
@ -182,6 +191,7 @@ struct TransactionSkeleton
bytes data; bytes data;
u256 gas = UndefinedU256; u256 gas = UndefinedU256;
u256 gasPrice = UndefinedU256; u256 gasPrice = UndefinedU256;
u256 nonce = UndefinedU256;
}; };
void badBlock(bytesConstRef _header, std::string const& _err); void badBlock(bytesConstRef _header, std::string const& _err);

29
libethcore/Ethash.cpp

@ -373,7 +373,7 @@ void Ethash::GPUMiner::workLoop()
this_thread::sleep_for(chrono::milliseconds(500)); this_thread::sleep_for(chrono::milliseconds(500));
} }
bytesConstRef dagData = dag->data(); bytesConstRef dagData = dag->data();
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device); m_miner->init(dagData.data(), dagData.size(), s_platformId, device);
} }
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
@ -409,16 +409,39 @@ void Ethash::GPUMiner::listDevices()
} }
bool Ethash::GPUMiner::configureGPU( bool Ethash::GPUMiner::configureGPU(
unsigned _localWorkSize,
unsigned _globalWorkSizeMultiplier,
unsigned _msPerBatch,
unsigned _platformId, unsigned _platformId,
unsigned _deviceId, unsigned _deviceId,
bool _allowCPU, bool _allowCPU,
unsigned _extraGPUMemory, unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock uint64_t _currentBlock
) )
{ {
s_platformId = _platformId; s_platformId = _platformId;
s_deviceId = _deviceId; s_deviceId = _deviceId;
return ethash_cl_miner::configureGPU(_platformId, _allowCPU, _extraGPUMemory, _currentBlock);
if (_localWorkSize != 32 && _localWorkSize != 64 && _localWorkSize != 128)
{
cout << "Given localWorkSize of " << toString(_localWorkSize) << "is invalid. Must be either 32,64, or 128" << endl;
return false;
}
if (!ethash_cl_miner::configureGPU(
_platformId,
_localWorkSize,
_globalWorkSizeMultiplier * _localWorkSize,
_msPerBatch,
_allowCPU,
_extraGPUMemory,
_currentBlock)
)
{
cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl;
return false;
}
return true;
} }
#endif #endif

7
libethcore/Ethash.h

@ -88,7 +88,7 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); } static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); }
static std::string platformInfo(); static std::string platformInfo();
static void listDevices() {} static void listDevices() {}
static bool configureGPU(unsigned, unsigned, bool, unsigned, boost::optional<uint64_t>) { return false; } static bool configureGPU(unsigned, unsigned, unsigned, unsigned, unsigned, bool, unsigned, uint64_t) { return false; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
protected: protected:
void kickOff() override void kickOff() override
@ -118,11 +118,14 @@ public:
static unsigned getNumDevices(); static unsigned getNumDevices();
static void listDevices(); static void listDevices();
static bool configureGPU( static bool configureGPU(
unsigned _localWorkSize,
unsigned _globalWorkSizeMultiplier,
unsigned _msPerBatch,
unsigned _platformId, unsigned _platformId,
unsigned _deviceId, unsigned _deviceId,
bool _allowCPU, bool _allowCPU,
unsigned _extraGPUMemory, unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock uint64_t _currentBlock
); );
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); }

132
libethcore/Transaction.cpp

@ -0,0 +1,132 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TransactionBase.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <libdevcore/vector_ref.h>
#include <libdevcore/Log.h>
#include <libdevcore/CommonIO.h>
#include <libdevcrypto/Common.h>
#include <libethcore/Exceptions.h>
#include "Transaction.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
TransactionBase::TransactionBase(TransactionSkeleton const& _ts, Secret const& _s):
m_type(_ts.creation ? ContractCreation : MessageCall),
m_nonce(_ts.nonce),
m_value(_ts.value),
m_receiveAddress(_ts.to),
m_gasPrice(_ts.gasPrice),
m_gas(_ts.gas),
m_data(_ts.data),
m_sender(_ts.from)
{
if (_s)
sign(_s);
}
TransactionBase::TransactionBase(bytesConstRef _rlpData, CheckTransaction _checkSig)
{
int field = 0;
RLP rlp(_rlpData);
try
{
if (!rlp.isList())
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction RLP must be a list"));
m_nonce = rlp[field = 0].toInt<u256>();
m_gasPrice = rlp[field = 1].toInt<u256>();
m_gas = rlp[field = 2].toInt<u256>();
m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash<Address>(RLP::VeryStrict);
m_value = rlp[field = 4].toInt<u256>();
if (!rlp[field = 5].isData())
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<byte>() - 27;
h256 r = rlp[field = 7].toInt<u256>();
h256 s = rlp[field = 8].toInt<u256>();
if (rlp.itemCount() > 9)
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())
BOOST_THROW_EXCEPTION(InvalidSignature());
if (_checkSig == CheckTransaction::Everything)
m_sender = sender();
}
catch (Exception& _e)
{
_e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes()));
throw;
}
}
Address const& TransactionBase::safeSender() const noexcept
{
try
{
return sender();
}
catch (...)
{
cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information();
return ZeroAddress;
}
}
Address const& TransactionBase::sender() const
{
if (!m_sender)
{
auto p = recover(m_vrs, sha3(WithoutSignature));
if (!p)
BOOST_THROW_EXCEPTION(InvalidSignature());
m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));
}
return m_sender;
}
void TransactionBase::sign(Secret const& _priv)
{
auto sig = dev::sign(_priv, sha3(WithoutSignature));
SignatureStruct sigStruct = *(SignatureStruct const*)&sig;
if (sigStruct.isValid())
m_vrs = sigStruct;
}
void TransactionBase::streamRLP(RLPStream& _s, IncludeSignature _sig) const
{
if (m_type == NullTransaction)
return;
_s.appendList((_sig ? 3 : 0) + 6);
_s << m_nonce << m_gasPrice << m_gas;
if (m_type == MessageCall)
_s << m_receiveAddress;
else
_s << "";
_s << m_value << m_data;
if (_sig)
_s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s;
}

179
libethcore/Transaction.h

@ -0,0 +1,179 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TransactionBase.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
#include <libethcore/Common.h>
namespace dev
{
namespace eth
{
/// Named-boolean type to encode whether a signature be included in the serialisation process.
enum IncludeSignature
{
WithoutSignature = 0, ///< Do not include a signature.
WithSignature = 1, ///< Do include a signature.
};
enum class CheckTransaction
{
None,
Cheap,
Everything
};
/// Encodes a transaction, ready to be exported to or freshly imported from RLP.
class TransactionBase
{
public:
/// Constructs a null transaction.
TransactionBase() {}
/// Constructs a transaction from a transaction skeleton & optional secret.
TransactionBase(TransactionSkeleton const& _ts, Secret const& _s = Secret());
/// Constructs a signed message-call transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
/// Constructs a signed contract-creation transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
/// Constructs an unsigned message-call transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs an unsigned contract-creation transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs a transaction from the given RLP.
explicit TransactionBase(bytesConstRef _rlp, CheckTransaction _checkSig);
/// Constructs a transaction from the given RLP.
explicit TransactionBase(bytes const& _rlp, CheckTransaction _checkSig): TransactionBase(&_rlp, _checkSig) {}
/// Checks equality of transactions.
bool operator==(TransactionBase const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; }
/// Checks inequality of transactions.
bool operator!=(TransactionBase const& _c) const { return !operator==(_c); }
/// @returns sender of the transaction from the signature (and hash).
Address const& sender() const;
/// Like sender() but will never throw. @returns a null Address if the signature is invalid.
Address const& safeSender() const noexcept;
/// Force the sender to a particular value. This will result in an invalid transaction RLP.
void forceSender(Address const& _a) { m_sender = _a; }
/// @returns true if transaction is non-null.
explicit operator bool() const { return m_type != NullTransaction; }
/// @returns true if transaction is contract-creation.
bool isCreation() const { return m_type == ContractCreation; }
/// @returns true if transaction is message-call.
bool isMessageCall() const { return m_type == MessageCall; }
/// Serialises this transaction to an RLPStream.
void streamRLP(RLPStream& _s, IncludeSignature _sig = WithSignature) const;
/// @returns the RLP serialisation of this transaction.
bytes rlp(IncludeSignature _sig = WithSignature) const { RLPStream s; streamRLP(s, _sig); return s.out(); }
/// @returns the SHA3 hash of the RLP serialisation of this transaction.
h256 sha3(IncludeSignature _sig = WithSignature) const { if (_sig == WithSignature && m_hashWith) return m_hashWith; RLPStream s; streamRLP(s, _sig); auto ret = dev::sha3(s.out()); if (_sig == WithSignature) m_hashWith = ret; return ret; }
/// @returns the amount of ETH to be transferred by this (message-call) transaction, in Wei. Synonym for endowment().
u256 value() const { return m_value; }
/// @returns the amount of ETH to be endowed by this (contract-creation) transaction, in Wei. Synonym for value().
u256 endowment() const { return m_value; }
/// @returns the base fee and thus the implied exchange rate of ETH to GAS.
u256 gasPrice() const { return m_gasPrice; }
/// @returns the total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
u256 gas() const { return m_gas; }
/// @returns the receiving address of the message-call transaction (undefined for contract-creation transactions).
Address receiveAddress() const { return m_receiveAddress; }
/// Synonym for receiveAddress().
Address to() const { return m_receiveAddress; }
/// Synonym for safeSender().
Address from() const { return safeSender(); }
/// @returns the data associated with this (message-call) transaction. Synonym for initCode().
bytes const& data() const { return m_data; }
/// @returns the initialisation code associated with this (contract-creation) transaction. Synonym for data().
bytes const& initCode() const { return m_data; }
/// @returns the transaction-count of the sender.
u256 nonce() const { return m_nonce; }
/// @returns the signature of the transaction. Encodes the sender.
SignatureStruct const& signature() const { return m_vrs; }
void sign(Secret const& _priv); ///< Sign the transaction.
protected:
/// Type of transaction.
enum Type
{
NullTransaction, ///< Null transaction.
ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored.
MessageCall ///< Transaction to invoke a message call - receiveAddress() is used.
};
Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction?
u256 m_nonce; ///< The transaction-count of the sender.
u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
Address m_receiveAddress; ///< The receiving address of the transaction.
u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS.
u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction.
SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender.
mutable h256 m_hashWith; ///< Cached hash of transaction with signature.
mutable Address m_sender; ///< Cached sender, determined from signature.
mutable bigint m_gasRequired = 0; ///< Memoised amount required for the transaction to run.
};
/// Nice name for vector of Transaction.
using TransactionBases = std::vector<TransactionBase>;
/// Simple human-readable stream-shift operator.
inline std::ostream& operator<<(std::ostream& _out, TransactionBase const& _t)
{
_out << _t.sha3().abridged() << "{";
if (_t.receiveAddress())
_out << _t.receiveAddress().abridged();
else
_out << "[CREATE]";
_out << "/" << _t.data().size() << "$" << _t.value() << "+" << _t.gas() << "@" << _t.gasPrice();
_out << "<-" << _t.safeSender().abridged() << " #" << _t.nonce() << "}";
return _out;
}
}
}

4
libethereum/Account.h

@ -152,8 +152,7 @@ public:
h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; } h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; }
/// Sets the code of the account. Must only be called when isFreshCode() returns true. /// Sets the code of the account. Must only be called when isFreshCode() returns true.
void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); } void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = std::move(_code); changed(); }
void setCode(bytes const& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); }
/// @returns true if the account's code is available through code(). /// @returns true if the account's code is available through code().
bool codeCacheValid() const { return m_codeHash == EmptySHA3 || m_codeHash == c_contractConceptionCodeHash || m_codeCache.size(); } bool codeCacheValid() const { return m_codeHash == EmptySHA3 || m_codeHash == c_contractConceptionCodeHash || m_codeCache.size(); }
@ -206,4 +205,3 @@ private:
} }
} }

95
libethereum/BasicGasPricer.cpp

@ -0,0 +1,95 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BasicGasPricer.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#include <boost/math/distributions/normal.hpp>
#include "BasicGasPricer.h"
#include "BlockChain.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
void BasicGasPricer::update(BlockChain const& _bc)
{
unsigned c = 0;
h256 p = _bc.currentHash();
m_gasPerBlock = _bc.info(p).gasLimit;
map<u256, u256> dist;
u256 total = 0;
// make gasPrice versus gasUsed distribution for the last 1000 blocks
while (c < 1000 && p)
{
BlockInfo bi = _bc.info(p);
if (bi.transactionsRoot != EmptyTrie)
{
auto bb = _bc.block(p);
RLP r(bb);
BlockReceipts brs(_bc.receipts(bi.hash()));
size_t i = 0;
for (auto const& tr: r[1])
{
Transaction tx(tr.data(), CheckTransaction::None);
u256 gu = brs.receipts[i].gasUsed();
dist[tx.gasPrice()] += gu;
total += gu;
i++;
}
}
p = bi.parentHash;
++c;
}
// fill m_octiles with weighted gasPrices
if (total > 0)
{
m_octiles[0] = dist.begin()->first;
// calc mean
u256 mean = 0;
for (auto const& i: dist)
mean += i.first * i.second;
mean /= total;
// calc standard deviation
u256 sdSquared = 0;
for (auto const& i: dist)
sdSquared += i.second * (i.first - mean) * (i.first - mean);
sdSquared /= total;
if (sdSquared)
{
long double sd = sqrt(sdSquared.convert_to<long double>());
long double normalizedSd = sd / mean.convert_to<long double>();
// calc octiles normalized to gaussian distribution
boost::math::normal gauss(1.0, (normalizedSd > 0.01) ? normalizedSd : 0.01);
for (size_t i = 1; i < 8; i++)
m_octiles[i] = u256(mean.convert_to<long double>() * boost::math::quantile(gauss, i / 8.0));
m_octiles[8] = dist.rbegin()->first;
}
else
{
for (size_t i = 0; i < 9; i++)
m_octiles[i] = (i + 1) * mean / 5;
}
}
}

53
libethereum/BasicGasPricer.h

@ -0,0 +1,53 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BasicGasPricer.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <array>
#include "GasPricer.h"
namespace dev
{
namespace eth
{
class BasicGasPricer: public GasPricer
{
public:
explicit BasicGasPricer(u256 _weiPerRef, u256 _refsPerBlock): m_weiPerRef(_weiPerRef), m_refsPerBlock(_refsPerBlock) {}
void setRefPrice(u256 _weiPerRef) { if ((bigint)m_refsPerBlock * _weiPerRef > std::numeric_limits<u256>::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_weiPerRef = _weiPerRef; }
void setRefBlockFees(u256 _refsPerBlock) { if ((bigint)m_weiPerRef * _refsPerBlock > std::numeric_limits<u256>::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_refsPerBlock = _refsPerBlock; }
u256 ask(State const&) const override { return m_weiPerRef * m_refsPerBlock / m_gasPerBlock; }
u256 bid(TransactionPriority _p = TransactionPriority::Medium) const override { return m_octiles[(int)_p] > 0 ? m_octiles[(int)_p] : (m_weiPerRef * m_refsPerBlock / m_gasPerBlock); }
void update(BlockChain const& _bc) override;
private:
u256 m_weiPerRef;
u256 m_refsPerBlock;
u256 m_gasPerBlock = 3141592;
std::array<u256, 9> m_octiles;
};
}
}

186
libethereum/BlockChain.cpp

@ -127,7 +127,7 @@ static const unsigned c_minCacheSize = 1024 * 1024 * 32;
#endif #endif
BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, WithExisting _we, ProgressCallback const& _p) BlockChain::BlockChain(bytes const& _genesisBlock, std::string const& _path, WithExisting _we, ProgressCallback const& _p)
{ {
// initialise deathrow. // initialise deathrow.
m_cacheUsage.resize(c_collectionQueueSize); m_cacheUsage.resize(c_collectionQueueSize);
@ -137,8 +137,7 @@ BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, WithExisti
m_genesisBlock = _genesisBlock; m_genesisBlock = _genesisBlock;
m_genesisHash = sha3(RLP(m_genesisBlock)[0].data()); m_genesisHash = sha3(RLP(m_genesisBlock)[0].data());
open(_path, _we); if (open(_path, _we) != c_minorProtocolVersion)
if (_we == WithExisting::Verify)
rebuild(_path, _p); rebuild(_path, _p);
} }
@ -147,24 +146,41 @@ BlockChain::~BlockChain()
close(); close();
} }
void BlockChain::open(std::string const& _path, WithExisting _we) unsigned BlockChain::open(std::string const& _path, WithExisting _we)
{ {
std::string path = _path.empty() ? Defaults::get()->m_dbPath : _path; string path = _path.empty() ? Defaults::get()->m_dbPath : _path;
boost::filesystem::create_directories(path); string chainPath = path + "/" + toHex(m_genesisHash.ref().cropped(0, 4));
string extrasPath = chainPath + "/" + toString(c_databaseVersion);
boost::filesystem::create_directories(extrasPath);
bytes status = contents(extrasPath + "/minor");
unsigned lastMinor = c_minorProtocolVersion;
DEV_IGNORE_EXCEPTIONS(lastMinor = (unsigned)RLP(status));
if (c_minorProtocolVersion != lastMinor)
{
cnote << "Killing extras database (DB minor version:" << lastMinor << " != our miner version: " << c_minorProtocolVersion << ").";
DEV_IGNORE_EXCEPTIONS(boost::filesystem::remove_all(extrasPath + "/details.old"));
boost::filesystem::rename(extrasPath + "/extras", extrasPath + "/extras.old");
boost::filesystem::remove_all(extrasPath + "/state");
writeFile(extrasPath + "/minor", rlp(c_minorProtocolVersion));
lastMinor = (unsigned)RLP(status);
}
if (_we == WithExisting::Kill) if (_we == WithExisting::Kill)
{ {
boost::filesystem::remove_all(path + "/blocks"); cnote << "Killing blockchain & extras database (WithExisting::Kill).";
boost::filesystem::remove_all(path + "/details"); boost::filesystem::remove_all(chainPath + "/blocks");
boost::filesystem::remove_all(extrasPath + "/extras");
} }
ldb::Options o; ldb::Options o;
o.create_if_missing = true; o.create_if_missing = true;
o.max_open_files = 256; o.max_open_files = 256;
ldb::DB::Open(o, path + "/blocks", &m_blocksDB); ldb::DB::Open(o, chainPath + "/blocks", &m_blocksDB);
ldb::DB::Open(o, path + "/details", &m_extrasDB); ldb::DB::Open(o, extrasPath + "/extras", &m_extrasDB);
if (!m_blocksDB || !m_extrasDB) if (!m_blocksDB || !m_extrasDB)
{ {
if (boost::filesystem::space(path + "/blocks").available < 1024) if (boost::filesystem::space(chainPath + "/blocks").available < 1024)
{ {
cwarn << "Not enough available space found on hard drive. Please free some up and then re-run. Bailing."; cwarn << "Not enough available space found on hard drive. Please free some up and then re-run. Bailing.";
BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace()); BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace());
@ -176,6 +192,8 @@ void BlockChain::open(std::string const& _path, WithExisting _we)
} }
} }
m_writeOptions.sync = true;
if (_we != WithExisting::Verify && !details(m_genesisHash)) if (_we != WithExisting::Verify && !details(m_genesisHash))
{ {
// Insert details of genesis block. // Insert details of genesis block.
@ -194,7 +212,8 @@ void BlockChain::open(std::string const& _path, WithExisting _we)
m_lastBlockHash = l.empty() ? m_genesisHash : *(h256*)l.data(); m_lastBlockHash = l.empty() ? m_genesisHash : *(h256*)l.data();
m_lastBlockNumber = number(m_lastBlockHash); m_lastBlockNumber = number(m_lastBlockHash);
cnote << "Opened blockchain DB. Latest: " << currentHash(); cnote << "Opened blockchain DB. Latest: " << currentHash() << (lastMinor == c_minorProtocolVersion ? "(rebuild not needed)" : "*** REBUILD NEEDED ***");
return lastMinor;
} }
void BlockChain::close() void BlockChain::close()
@ -208,11 +227,11 @@ void BlockChain::close()
m_blocks.clear(); m_blocks.clear();
} }
#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned, unsigned)> const& _progress, bool _prepPoW) void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned, unsigned)> const& _progress, bool _prepPoW)
{ {
std::string path = _path.empty() ? Defaults::get()->m_dbPath : _path; string path = _path.empty() ? Defaults::get()->m_dbPath : _path;
string chainPath = path + "/" + toHex(m_genesisHash.ref().cropped(0, 4));
string extrasPath = chainPath + "/" + toString(c_databaseVersion);
#if ETH_PROFILING_GPERF #if ETH_PROFILING_GPERF
ProfilerStart("BlockChain_rebuild.log"); ProfilerStart("BlockChain_rebuild.log");
@ -220,16 +239,21 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
unsigned originalNumber = m_lastBlockNumber; unsigned originalNumber = m_lastBlockNumber;
///////////////////////////////
// TODO
// - KILL ALL STATE/CHAIN
// - REINSERT ALL BLOCKS
///////////////////////////////
// Keep extras DB around, but under a temp name // Keep extras DB around, but under a temp name
delete m_extrasDB; delete m_extrasDB;
m_extrasDB = nullptr; m_extrasDB = nullptr;
IGNORE_EXCEPTIONS(boost::filesystem::remove_all(path + "/details.old")); boost::filesystem::rename(path + "/details", path + "/extras.old");
boost::filesystem::rename(path + "/details", path + "/details.old");
ldb::DB* oldExtrasDB; ldb::DB* oldExtrasDB;
ldb::Options o; ldb::Options o;
o.create_if_missing = true; o.create_if_missing = true;
ldb::DB::Open(o, path + "/details.old", &oldExtrasDB); ldb::DB::Open(o, extrasPath + "/extras.old", &oldExtrasDB);
ldb::DB::Open(o, path + "/details", &m_extrasDB); ldb::DB::Open(o, extrasPath + "/extras", &m_extrasDB);
// Open a fresh state DB // Open a fresh state DB
State s(State::openDB(path, WithExisting::Kill), BaseState::CanonGenesis); State s(State::openDB(path, WithExisting::Kill), BaseState::CanonGenesis);
@ -251,7 +275,7 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
h256 lastHash = m_lastBlockHash; h256 lastHash = m_lastBlockHash;
Timer t; Timer t;
for (unsigned d = 1; d < originalNumber; ++d) for (unsigned d = 1; d <= originalNumber; ++d)
{ {
if (!(d % 1000)) if (!(d % 1000))
{ {
@ -289,7 +313,7 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
#endif #endif
delete oldExtrasDB; delete oldExtrasDB;
boost::filesystem::remove_all(path + "/details.old"); boost::filesystem::remove_all(path + "/extras.old");
} }
LastHashes BlockChain::lastHashes(unsigned _n) const LastHashes BlockChain::lastHashes(unsigned _n) const
@ -315,6 +339,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
h256s fresh; h256s fresh;
h256s dead; h256s dead;
h256s badBlocks; h256s badBlocks;
Transactions goodTransactions;
unsigned count = 0; unsigned count = 0;
for (VerifiedBlock const& block: blocks) for (VerifiedBlock const& block: blocks)
if (!badBlocks.empty()) if (!badBlocks.empty())
@ -325,10 +350,11 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
{ {
// Nonce & uncle nonces already verified in verification thread at this point. // Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r; ImportRoute r;
DEV_TIMED_ABOVE("Block import", 500) DEV_TIMED_ABOVE("Block import " + toString(block.verified.info.number), 500)
r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles); r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.liveBlocks; fresh += r.liveBlocks;
dead += r.deadBlocks; dead += r.deadBlocks;
goodTransactions += r.goodTranactions;
++count; ++count;
} }
catch (dev::eth::UnknownParent) catch (dev::eth::UnknownParent)
@ -355,7 +381,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
badBlocks.push_back(block.verified.info.hash()); badBlocks.push_back(block.verified.info.hash());
} }
} }
return make_tuple(ImportRoute{dead, fresh}, _bq.doneDrain(badBlocks), count); return make_tuple(ImportRoute{dead, fresh, goodTransactions}, _bq.doneDrain(badBlocks), count);
} }
pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, ImportRequirements::value _ir) noexcept pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, ImportRequirements::value _ir) noexcept
@ -475,6 +501,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
BlockReceipts br; BlockReceipts br;
u256 td; u256 td;
Transactions goodTransactions;
#if ETH_CATCH #if ETH_CATCH
try try
#endif #endif
@ -488,6 +515,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
{ {
blb.blooms.push_back(s.receipt(i).bloom()); blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i)); br.receipts.push_back(s.receipt(i));
goodTransactions.push_back(s.pending()[i]);
} }
s.cleanup(true); s.cleanup(true);
@ -728,7 +756,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
dead.push_back(h); dead.push_back(h);
else else
fresh.push_back(h); fresh.push_back(h);
return ImportRoute{dead, fresh}; return ImportRoute{dead, fresh, move(goodTransactions)};
} }
void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end) void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end)
@ -773,6 +801,75 @@ void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end)
} }
} }
void BlockChain::rescue(OverlayDB& _db)
{
cout << "Rescuing database..." << endl;
unsigned u = 1;
while (true)
{
try {
if (isKnown(numberHash(u)))
u *= 2;
else
break;
}
catch (...)
{
break;
}
}
unsigned l = u / 2;
cout << "Finding last likely block number..." << endl;
while (u - l > 1)
{
unsigned m = (u + l) / 2;
cout << " " << m << flush;
if (isKnown(numberHash(m)))
l = m;
else
u = m;
}
cout << " lowest is " << l << endl;
for (;; --l)
{
h256 h = numberHash(l);
cout << "Checking validity of " << l << " (" << h << ")..." << flush;
try
{
cout << "block..." << flush;
BlockInfo bi = info(h);
cout << "details..." << flush;
BlockDetails bd = details(h);
cout << "state..." << flush;
if (_db.exists(bi.stateRoot))
break;
}
catch (...) {}
}
cout << "OK." << endl;
rewind(l);
}
void BlockChain::rewind(unsigned _newHead)
{
DEV_WRITE_GUARDED(x_lastBlockHash)
{
if (_newHead >= m_lastBlockNumber)
return;
m_lastBlockHash = numberHash(_newHead);
m_lastBlockNumber = _newHead;
auto o = m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&m_lastBlockHash, 32));
if (!o.ok())
{
cwarn << "Error writing to extras database: " << o.ToString();
cout << "Put" << toHex(bytesConstRef(ldb::Slice("best"))) << "=>" << toHex(bytesConstRef(ldb::Slice((char const*)&m_lastBlockHash, 32)));
cwarn << "Fail writing to extras database. Bombing out.";
exit(-1);
}
}
}
tuple<h256s, h256, unsigned> BlockChain::treeRoute(h256 const& _from, h256 const& _to, bool _common, bool _pre, bool _post) const tuple<h256s, h256, unsigned> BlockChain::treeRoute(h256 const& _from, h256 const& _to, bool _common, bool _pre, bool _post) const
{ {
// cdebug << "treeRoute" << _from << "..." << _to; // cdebug << "treeRoute" << _from << "..." << _to;
@ -845,33 +942,21 @@ template <class T> static unsigned getHashSize(unordered_map<h256, T> const& _ma
void BlockChain::updateStats() const void BlockChain::updateStats() const
{ {
{ m_lastStats.memBlocks = 0;
ReadGuard l(x_blocks); DEV_READ_GUARDED(x_blocks)
m_lastStats.memBlocks = 0;
for (auto const& i: m_blocks) for (auto const& i: m_blocks)
m_lastStats.memBlocks += i.second.size() + 64; m_lastStats.memBlocks += i.second.size() + 64;
} DEV_READ_GUARDED(x_details)
{
ReadGuard l(x_details);
m_lastStats.memDetails = getHashSize(m_details); m_lastStats.memDetails = getHashSize(m_details);
} DEV_READ_GUARDED(x_logBlooms)
{ DEV_READ_GUARDED(x_blocksBlooms)
ReadGuard l1(x_logBlooms); m_lastStats.memLogBlooms = getHashSize(m_logBlooms) + getHashSize(m_blocksBlooms);
ReadGuard l2(x_blocksBlooms); DEV_READ_GUARDED(x_receipts)
m_lastStats.memLogBlooms = getHashSize(m_logBlooms) + getHashSize(m_blocksBlooms);
}
{
ReadGuard l(x_receipts);
m_lastStats.memReceipts = getHashSize(m_receipts); m_lastStats.memReceipts = getHashSize(m_receipts);
} DEV_READ_GUARDED(x_blockHashes)
{
ReadGuard l(x_blockHashes);
m_lastStats.memBlockHashes = getHashSize(m_blockHashes); m_lastStats.memBlockHashes = getHashSize(m_blockHashes);
} DEV_READ_GUARDED(x_transactionAddresses)
{
ReadGuard l(x_transactionAddresses);
m_lastStats.memTransactionAddresses = getHashSize(m_transactionAddresses); m_lastStats.memTransactionAddresses = getHashSize(m_transactionAddresses);
}
} }
void BlockChain::garbageCollect(bool _force) void BlockChain::garbageCollect(bool _force)
@ -928,10 +1013,8 @@ void BlockChain::garbageCollect(bool _force)
void BlockChain::checkConsistency() void BlockChain::checkConsistency()
{ {
{ DEV_WRITE_GUARDED(x_details)
WriteGuard l(x_details);
m_details.clear(); m_details.clear();
}
ldb::Iterator* it = m_blocksDB->NewIterator(m_readOptions); ldb::Iterator* it = m_blocksDB->NewIterator(m_readOptions);
for (it->SeekToFirst(); it->Valid(); it->Next()) for (it->SeekToFirst(); it->Valid(); it->Next())
if (it->key().size() == 32) if (it->key().size() == 32)
@ -943,13 +1026,9 @@ void BlockChain::checkConsistency()
{ {
auto dp = details(p); auto dp = details(p);
if (asserts(contains(dp.children, h))) if (asserts(contains(dp.children, h)))
{
cnote << "Apparently the database is corrupt. Not much we can do at this stage..."; cnote << "Apparently the database is corrupt. Not much we can do at this stage...";
}
if (assertsEqual(dp.number, dh.number - 1)) if (assertsEqual(dp.number, dh.number - 1))
{
cnote << "Apparently the database is corrupt. Not much we can do at this stage..."; cnote << "Apparently the database is corrupt. Not much we can do at this stage...";
}
} }
} }
delete it; delete it;
@ -1062,7 +1141,8 @@ bool BlockChain::isKnown(h256 const& _hash) const
if (d.empty()) if (d.empty())
return false; return false;
} }
return true; // return true;
return details(_hash).number <= m_lastBlockNumber; // to allow rewind functionality.
} }
bytes BlockChain::block(h256 const& _hash) const bytes BlockChain::block(h256 const& _hash) const

13
libethereum/BlockChain.h

@ -94,7 +94,7 @@ using ProgressCallback = std::function<void(unsigned, unsigned)>;
class BlockChain class BlockChain
{ {
public: public:
BlockChain(bytes const& _genesisBlock, std::string _path, WithExisting _we, ProgressCallback const& _p = ProgressCallback()); BlockChain(bytes const& _genesisBlock, std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _p = ProgressCallback());
~BlockChain(); ~BlockChain();
/// Attempt a database re-open. /// Attempt a database re-open.
@ -142,6 +142,9 @@ public:
BlockReceipts receipts(h256 const& _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); } BlockReceipts receipts(h256 const& _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); }
BlockReceipts receipts() const { return receipts(currentHash()); } BlockReceipts receipts() const { return receipts(currentHash()); }
/// Get the transaction receipt by transaction hash. Thread-safe.
TransactionReceipt transactionReceipt(h256 const& _transactionHash) const {TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytesConstRef(); return receipts(ta.blockHash).receipts[ta.index]; }
/// Get a list of transaction hashes for a given block. Thread-safe. /// Get a list of transaction hashes for a given block. Thread-safe.
TransactionHashes transactionHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; } TransactionHashes transactionHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; }
TransactionHashes transactionHashes() const { return transactionHashes(currentHash()); } TransactionHashes transactionHashes() const { return transactionHashes(currentHash()); }
@ -212,6 +215,12 @@ public:
/// Will call _progress with the progress in this operation first param done, second total. /// Will call _progress with the progress in this operation first param done, second total.
void rebuild(std::string const& _path, ProgressCallback const& _progress = std::function<void(unsigned, unsigned)>(), bool _prepPoW = false); void rebuild(std::string const& _path, ProgressCallback const& _progress = std::function<void(unsigned, unsigned)>(), bool _prepPoW = false);
/// Alter the head of the chain to some prior block along it.
void rewind(unsigned _newHead);
/// Rescue the database.
void rescue(OverlayDB& _db);
/** @returns a tuple of: /** @returns a tuple of:
* - an vector of hashes of all blocks between @a _from and @a _to, all blocks are ordered first by a number of * - an vector of hashes of all blocks between @a _from and @a _to, all blocks are ordered first by a number of
* blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent; * blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent;
@ -264,7 +273,7 @@ public:
private: private:
static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); } static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); }
void open(std::string const& _path, WithExisting _we = WithExisting::Trust); unsigned open(std::string const& _path, WithExisting _we = WithExisting::Trust);
void close(); void close();
template<class T, unsigned N> T queryExtras(h256 const& _h, std::unordered_map<h256, T>& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const template<class T, unsigned N> T queryExtras(h256 const& _h, std::unordered_map<h256, T>& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const

522
libethereum/BlockChainSync.cpp

@ -38,7 +38,19 @@ using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace p2p; using namespace p2p;
unsigned const c_chainReorgSize = 30000; unsigned const c_chainReorgSize = 30000; /// Added to estimated hashes to account for potential chain reorganiation
unsigned const c_hashSubchainSize = 8192; /// PV61 subchain size
std::ostream& dev::eth::operator<<(std::ostream& _out, SyncStatus const& _sync)
{
_out << "protocol: " << _sync.protocolVersion << endl;
_out << "state: " << EthereumHost::stateName(_sync.state) << " ";
if (_sync.state == SyncState::Hashes)
_out << _sync.hashesReceived << "/" << (_sync.hashesEstimated ? "~" : "") << _sync.hashesTotal;
if (_sync.state == SyncState::Blocks || _sync.state == SyncState::NewBlocks)
_out << _sync.blocksReceived << "/" << _sync.blocksTotal;
return _out;
}
BlockChainSync::BlockChainSync(EthereumHost& _host): BlockChainSync::BlockChainSync(EthereumHost& _host):
m_host(_host) m_host(_host)
@ -68,22 +80,25 @@ DownloadMan& BlockChainSync::downloadMan()
void BlockChainSync::abortSync() void BlockChainSync::abortSync()
{ {
downloadMan().resetToChain(h256s()); downloadMan().reset();
} }
void BlockChainSync::onPeerStatus(std::shared_ptr<EthereumPeer> _peer) void BlockChainSync::onPeerStatus(std::shared_ptr<EthereumPeer> _peer)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
std::shared_ptr<Session> session = _peer->session();
if (!session)
return; // Expired
if (_peer->m_genesisHash != host().chain().genesisHash()) if (_peer->m_genesisHash != host().chain().genesisHash())
_peer->disable("Invalid genesis hash"); _peer->disable("Invalid genesis hash");
else if (_peer->m_protocolVersion != host().protocolVersion() && _peer->m_protocolVersion != EthereumHost::c_oldProtocolVersion) else if (_peer->m_protocolVersion != host().protocolVersion() && _peer->m_protocolVersion != EthereumHost::c_oldProtocolVersion)
_peer->disable("Invalid protocol version."); _peer->disable("Invalid protocol version.");
else if (_peer->m_networkId != host().networkId()) else if (_peer->m_networkId != host().networkId())
_peer->disable("Invalid network identifier."); _peer->disable("Invalid network identifier.");
else if (_peer->session()->info().clientVersion.find("/v0.7.0/") != string::npos) else if (session->info().clientVersion.find("/v0.7.0/") != string::npos)
_peer->disable("Blacklisted client version."); _peer->disable("Blacklisted client version.");
else if (host().isBanned(_peer->session()->id())) else if (host().isBanned(session->id()))
_peer->disable("Peer banned for previous bad behaviour."); _peer->disable("Peer banned for previous bad behaviour.");
else else
{ {
@ -91,7 +106,6 @@ void BlockChainSync::onPeerStatus(std::shared_ptr<EthereumPeer> _peer)
_peer->m_expectedHashes = hashes; _peer->m_expectedHashes = hashes;
onNewPeer(_peer); onNewPeer(_peer);
} }
DEV_INVARIANT_CHECK;
} }
unsigned BlockChainSync::estimatedHashes() const unsigned BlockChainSync::estimatedHashes() const
@ -114,7 +128,6 @@ void BlockChainSync::requestBlocks(std::shared_ptr<EthereumPeer> _peer)
{ {
clog(NetAllDetail) << "Waiting for block queue before downloading blocks"; clog(NetAllDetail) << "Waiting for block queue before downloading blocks";
pauseSync(); pauseSync();
_peer->setIdle();
return; return;
} }
_peer->requestBlocks(); _peer->requestBlocks();
@ -137,13 +150,14 @@ void BlockChainSync::logNewBlock(h256 const& _h)
void BlockChainSync::onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const& _r) void BlockChainSync::onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const& _r)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
unsigned itemCount = _r.itemCount(); unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks"); clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
_peer->setIdle();
if (m_state != SyncState::Blocks && m_state != SyncState::NewBlocks && m_state != SyncState::Waiting) if (m_state != SyncState::Blocks && m_state != SyncState::NewBlocks && m_state != SyncState::Waiting)
clog(NetWarn) << "Unexpected Blocks received!"; {
clog(NetMessageSummary) << "Ignoring unexpected blocks";
return;
}
if (m_state == SyncState::Waiting) if (m_state == SyncState::Waiting)
{ {
clog(NetAllDetail) << "Ignored blocks while waiting"; clog(NetAllDetail) << "Ignored blocks while waiting";
@ -184,6 +198,7 @@ void BlockChainSync::onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const
case ImportResult::BadChain: case ImportResult::BadChain:
logNewBlock(h); logNewBlock(h);
_peer->disable("Malformed block received."); _peer->disable("Malformed block received.");
restartSync();
return; return;
case ImportResult::FutureTimeKnown: case ImportResult::FutureTimeKnown:
@ -281,17 +296,22 @@ void BlockChainSync::onPeerNewBlock(std::shared_ptr<EthereumPeer> _peer, RLP con
case ImportResult::FutureTimeUnknown: case ImportResult::FutureTimeUnknown:
case ImportResult::UnknownParent: case ImportResult::UnknownParent:
{
logNewBlock(h); logNewBlock(h);
clog(NetMessageSummary) << "Received block with no known parent. Resyncing..."; u256 totalDifficulty = _r[1].toInt<u256>();
resetSyncFor(_peer, h, _r[1].toInt<u256>()); if (totalDifficulty > _peer->m_totalDifficulty)
{
clog(NetMessageDetail) << "Received block with no known parent. Resyncing...";
resetSyncFor(_peer, h, totalDifficulty);
}
break; break;
}
default:; default:;
} }
DEV_GUARDED(_peer->x_knownBlocks) DEV_GUARDED(_peer->x_knownBlocks)
_peer->m_knownBlocks.insert(h); _peer->m_knownBlocks.insert(h);
} }
DEV_INVARIANT_CHECK;
} }
PV60Sync::PV60Sync(EthereumHost& _host): PV60Sync::PV60Sync(EthereumHost& _host):
@ -305,6 +325,7 @@ SyncStatus PV60Sync::status() const
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
SyncStatus res; SyncStatus res;
res.state = m_state; res.state = m_state;
res.protocolVersion = EthereumHost::c_oldProtocolVersion;
if (m_state == SyncState::Hashes) if (m_state == SyncState::Hashes)
{ {
res.hashesTotal = m_estimatedHashes; res.hashesTotal = m_estimatedHashes;
@ -344,26 +365,30 @@ void PV60Sync::restartSync()
{ {
resetSync(); resetSync();
host().bq().clear(); host().bq().clear();
if (isSyncing()) std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
transition(m_syncer.lock(), SyncState::Idle); if (syncer)
transition(syncer, SyncState::Idle);
} }
void PV60Sync::completeSync() void PV60Sync::completeSync()
{ {
if (isSyncing()) std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
transition(m_syncer.lock(), SyncState::Idle); if (syncer)
transition(syncer, SyncState::Idle);
} }
void PV60Sync::pauseSync() void PV60Sync::pauseSync()
{ {
if (isSyncing()) std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
setState(m_syncer.lock(), SyncState::Waiting, true); if (syncer)
transition(syncer, SyncState::Waiting, true);
} }
void PV60Sync::continueSync() void PV60Sync::continueSync()
{ {
if (isSyncing()) std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
transition(m_syncer.lock(), SyncState::Blocks); if (syncer)
transition(syncer, SyncState::Blocks);
} }
void PV60Sync::onNewPeer(std::shared_ptr<EthereumPeer> _peer) void PV60Sync::onNewPeer(std::shared_ptr<EthereumPeer> _peer)
@ -378,29 +403,12 @@ void PV60Sync::transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, boo
if (m_state == SyncState::Idle && _s != SyncState::Idle) if (m_state == SyncState::Idle && _s != SyncState::Idle)
_peer->m_requireTransactions = true; _peer->m_requireTransactions = true;
RLPStream s;
if (_s == SyncState::Hashes) if (_s == SyncState::Hashes)
{ {
if (m_state == SyncState::Idle) if (m_state == SyncState::Idle || m_state == SyncState::Hashes)
{ {
if (isSyncing(_peer)) m_estimatedHashes = _peer->m_expectedHashes - c_chainReorgSize;
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!"; syncHashes(_peer);
m_syncingLatestHash = _peer->m_latestHash;
m_syncingTotalDifficulty = _peer->m_totalDifficulty;
setState(_peer, _s, true);
_peer->requestHashes(m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash);
DEV_INVARIANT_CHECK;
return;
}
else if (m_state == SyncState::Hashes)
{
if (!isSyncing(_peer))
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
setState(_peer, _s, true);
_peer->requestHashes(m_syncingLastReceivedHash);
DEV_INVARIANT_CHECK;
return; return;
} }
} }
@ -462,7 +470,6 @@ void PV60Sync::transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, boo
} }
else if (_s == SyncState::Idle) else if (_s == SyncState::Idle)
{ {
host().foreachPeer([this](std::shared_ptr<EthereumPeer> _p) { _p->setIdle(); return true; });
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks) if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks)
{ {
clog(NetMessageDetail) << "Finishing blocks fetch..."; clog(NetMessageDetail) << "Finishing blocks fetch...";
@ -473,7 +480,6 @@ void PV60Sync::transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, boo
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary. // NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
_peer->m_sub.doneFetch(); _peer->m_sub.doneFetch();
_peer->setIdle();
setState(_peer, SyncState::Idle, false); setState(_peer, SyncState::Idle, false);
} }
else if (m_state == SyncState::Hashes) else if (m_state == SyncState::Hashes)
@ -502,7 +508,9 @@ void PV60Sync::setNeedsSyncing(std::shared_ptr<EthereumPeer> _peer, h256 const&
if (_peer->m_latestHash) if (_peer->m_latestHash)
noteNeedsSyncing(_peer); noteNeedsSyncing(_peer);
_peer->session()->addNote("sync", string(isSyncing(_peer) ? "ongoing" : "holding") + (needsSyncing(_peer) ? " & needed" : "")); shared_ptr<Session> session = _peer->session();
if (session)
session->addNote("sync", string(isSyncing(_peer) ? "ongoing" : "holding") + (needsSyncing(_peer) ? " & needed" : ""));
} }
bool PV60Sync::needsSyncing(std::shared_ptr<EthereumPeer> _peer) const bool PV60Sync::needsSyncing(std::shared_ptr<EthereumPeer> _peer) const
@ -562,7 +570,6 @@ void PV60Sync::attemptSync(std::shared_ptr<EthereumPeer> _peer)
else else
{ {
clog(NetAllDetail) << "Yes. Their chain is better."; clog(NetAllDetail) << "Yes. Their chain is better.";
m_estimatedHashes = _peer->m_expectedHashes - c_chainReorgSize;
transition(_peer, SyncState::Hashes); transition(_peer, SyncState::Hashes);
} }
} }
@ -573,7 +580,9 @@ void PV60Sync::noteNeedsSyncing(std::shared_ptr<EthereumPeer> _peer)
if (isSyncing()) if (isSyncing())
{ {
clog(NetAllDetail) << "Sync in progress: Just set to help out."; clog(NetAllDetail) << "Sync in progress: Just set to help out.";
if (m_state == SyncState::Blocks) if (m_state == SyncState::Hashes && _peer->m_asking == Asking::Nothing)
requestSubchain(_peer);
else if (m_state == SyncState::Blocks)
requestBlocks(_peer); requestBlocks(_peer);
} }
else else
@ -649,20 +658,45 @@ void PV60Sync::noteDoneBlocks(std::shared_ptr<EthereumPeer> _peer, bool _clemenc
} }
resetSync(); resetSync();
downloadMan().reset(); downloadMan().reset();
}
_peer->m_sub.doneFetch(); _peer->m_sub.doneFetch();
}
void PV60Sync::syncHashes(std::shared_ptr<EthereumPeer> _peer)
{
if (m_state == SyncState::Idle)
{
if (isSyncing(_peer))
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
m_syncingLatestHash = _peer->m_latestHash;
m_syncingTotalDifficulty = _peer->m_totalDifficulty;
setState(_peer, SyncState::Hashes, true);
_peer->requestHashes(m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash);
}
else if (m_state == SyncState::Hashes)
{
if (!isSyncing(_peer))
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
setState(_peer, SyncState::Hashes, true);
_peer->requestHashes(m_syncingLastReceivedHash);
} }
} }
void PV60Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes) void PV60Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
_peer->setIdle();
if (!isSyncing(_peer)) if (!isSyncing(_peer))
{ {
clog(NetMessageSummary) << "Ignoring hashes since not syncing"; clog(NetMessageSummary) << "Ignoring hashes since not syncing";
return; return;
} }
if (_peer->m_syncHash != (m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash))
{
clog(NetMessageSummary) << "Ignoring unexpected hashes";
return;
}
if (_hashes.size() == 0) if (_hashes.size() == 0)
{ {
transition(_peer, SyncState::Blocks); transition(_peer, SyncState::Blocks);
@ -684,7 +718,8 @@ void PV60Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
else if (status == QueueStatus::Bad) else if (status == QueueStatus::Bad)
{ {
cwarn << "block hash bad!" << h << ". Bailing..."; cwarn << "block hash bad!" << h << ". Bailing...";
transition(_peer, SyncState::Idle); _peer->disable("Bad blocks");
restartSync();
return; return;
} }
else if (status == QueueStatus::Unknown) else if (status == QueueStatus::Unknown)
@ -711,7 +746,6 @@ void PV60Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
void PV60Sync::onPeerNewHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes) void PV60Sync::onPeerNewHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
if (isSyncing() && (m_state != SyncState::NewBlocks || isSyncing(_peer))) if (isSyncing() && (m_state != SyncState::NewBlocks || isSyncing(_peer)))
{ {
clog(NetMessageSummary) << "Ignoring since we're already downloading."; clog(NetMessageSummary) << "Ignoring since we're already downloading.";
@ -769,8 +803,33 @@ void PV60Sync::onPeerNewHashes(std::shared_ptr<EthereumPeer> _peer, h256s const&
void PV60Sync::abortSync() void PV60Sync::abortSync()
{ {
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet. // Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
host().foreachPeer([this](std::shared_ptr<EthereumPeer> _p) { _p->setIdle(); return true; }); bool continueSync = false;
setState(std::shared_ptr<EthereumPeer>(), SyncState::Idle, false, true); if (m_state == SyncState::Blocks)
{
// Main syncer aborted, try to find a replacement
host().foreachPeer([&](std::shared_ptr<EthereumPeer> _p)
{
if (_p->m_asking == Asking::Blocks)
{
setState(_p, SyncState::Blocks, true, true); // will kick off other peers to help if available.
continueSync = true;
return false;
}
if (_p->m_asking == Asking::Nothing && shouldGrabBlocks(_p))
{
transition(_p, SyncState::Blocks);
clog(NetMessageDetail) << "New sync peer selected";
continueSync = true;
return false;
}
return true;
});
}
if (!continueSync)
{
// Just set to idle. Hashchain is keept, Sync will be continued if there are more peers to sync with
setState(std::shared_ptr<EthereumPeer>(), SyncState::Idle, false, true);
}
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
} }
@ -778,45 +837,366 @@ void PV60Sync::onPeerAborting()
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet. // Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
if (m_syncer.expired()) if (m_syncer.expired() && m_state != SyncState::Idle)
{
clog(NetWarn) << "Syncing peer disconnected, restarting sync";
m_syncer.reset();
abortSync(); abortSync();
}
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
} }
bool PV60Sync::invariants() const bool PV60Sync::invariants() const
{ {
if (m_state == SyncState::Idle && isSyncing()) if (m_state == SyncState::Idle && isSyncing())
return false; BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Idle while peer syncing"));
if (m_state != SyncState::Idle && !isSyncing()) if (m_state != SyncState::Idle && !isSyncing())
return false; BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Active while peer not syncing"));
if (m_state == SyncState::Hashes) if (m_state == SyncState::Hashes)
{ {
bool hashes = false;
host().foreachPeer([&](std::shared_ptr<EthereumPeer> _p) { if (_p->m_asking == Asking::Hashes) hashes = true; return !hashes; });
if (!hashes)
return false;
if (!m_syncingLatestHash) if (!m_syncingLatestHash)
return false; BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("m_syncingLatestHash is not set while downloading hashes"));
if (m_syncingNeededBlocks.empty() != (!m_syncingLastReceivedHash)) if (m_syncingNeededBlocks.empty() != (!m_syncingLastReceivedHash))
return false; BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Received hashes but the hashes list is empty (or the other way around)"));
} }
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks) if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks)
{ {
bool blocks = false;
host().foreachPeer([&](std::shared_ptr<EthereumPeer> _p) { if (_p->m_asking == Asking::Blocks) blocks = true; return !blocks; });
if (!blocks)
return false;
if (downloadMan().isComplete()) if (downloadMan().isComplete())
return false; BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Block download complete but the state is still Blocks"));
}
if (m_state == SyncState::Waiting && !host().bq().isActive())
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Waiting while block queue is idle"));
return true;
}
PV61Sync::PV61Sync(EthereumHost& _host):
PV60Sync(_host)
{
}
void PV61Sync::syncHashes(std::shared_ptr<EthereumPeer> _peer)
{
if (_peer->m_protocolVersion != host().protocolVersion())
{
m_readyChainMap.clear();
m_completeChainMap.clear();
m_downloadingChainMap.clear();
m_syncingBlockNumber = 0;
m_chainSyncPeers.clear();
m_knownHashes.clear();
m_hashScanComplete = false;
PV60Sync::syncHashes(_peer);
return;
} }
if (m_state == SyncState::Idle) if (m_state == SyncState::Idle)
{ {
bool busy = false; if (isSyncing(_peer))
host().foreachPeer([&](std::shared_ptr<EthereumPeer> _p) { if (_p->m_asking != Asking::Nothing && _p->m_asking != Asking::State) busy = true; return !busy; }); clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
if (busy)
return false; if (m_syncingBlockNumber == 0)
m_syncingBlockNumber = host().chain().number() + c_hashSubchainSize;
m_syncingTotalDifficulty = _peer->m_totalDifficulty;
setState(_peer, SyncState::Hashes, true);
_peer->requestHashes(m_syncingBlockNumber, 1);
} }
if (m_state == SyncState::Waiting && !host().bq().isActive()) else if (m_state == SyncState::Hashes)
return false; {
if (!isSyncing(_peer))
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
m_syncingBlockNumber += c_hashSubchainSize;
setState(_peer, SyncState::Hashes, true);
_peer->requestHashes(m_syncingBlockNumber, 1);
}
}
void PV61Sync::requestSubchain(std::shared_ptr<EthereumPeer> _peer)
{
auto syncPeer = m_chainSyncPeers.find(_peer);
if (syncPeer != m_chainSyncPeers.end())
{
// Already downoading, request next batch
h256s& d = m_downloadingChainMap.at(syncPeer->second);
_peer->requestHashes(d.back());
}
else if (needsSyncing(_peer))
{
if (!m_readyChainMap.empty())
{
clog(NetAllDetail) << "Helping with hashchin download";
h256s& d = m_readyChainMap.begin()->second;
_peer->requestHashes(d.back());
m_downloadingChainMap[m_readyChainMap.begin()->first] = move(d);
m_chainSyncPeers[_peer] = m_readyChainMap.begin()->first;
m_readyChainMap.erase(m_readyChainMap.begin());
}
else if (!m_downloadingChainMap.empty() && m_hashScanComplete)
{
// Lead syncer is done, just grab whatever we can
h256s& d = m_downloadingChainMap.begin()->second;
_peer->requestHashes(d.back());
}
}
}
void PV61Sync::requestSubchains()
{
host().foreachPeer([this](std::shared_ptr<EthereumPeer> _p)
{
if (_p->m_asking == Asking::Nothing)
requestSubchain(_p);
return true;
});
}
void PV61Sync::completeSubchain(std::shared_ptr<EthereumPeer> _peer, unsigned _n)
{
m_completeChainMap[_n] = move(m_downloadingChainMap.at(_n));
m_downloadingChainMap.erase(_n);
for (auto s = m_chainSyncPeers.begin(); s != m_chainSyncPeers.end(); ++s)
if (s->second == _n) //TODO: optimize this
{
m_chainSyncPeers.erase(s);
break;
}
_peer->m_syncHashNumber = 0;
auto syncer = m_syncer.lock();
if (!syncer)
{
restartSync();
return;
}
if (m_readyChainMap.empty() && m_downloadingChainMap.empty() && m_hashScanComplete)
{
//Done chain-get
m_syncingNeededBlocks.clear();
for (auto h = m_completeChainMap.rbegin(); h != m_completeChainMap.rend(); ++h)
m_syncingNeededBlocks.insert(m_syncingNeededBlocks.end(), h->second.begin(), h->second.end());
m_completeChainMap.clear();
m_knownHashes.clear();
m_syncingBlockNumber = 0;
transition(syncer, SyncState::Blocks);
}
else
requestSubchain(_peer);
}
void PV61Sync::restartSync()
{
m_completeChainMap.clear();
m_readyChainMap.clear();
m_downloadingChainMap.clear();
m_chainSyncPeers.clear();
m_syncingBlockNumber = 0;
m_knownHashes.clear();
m_hashScanComplete = false;
PV60Sync::restartSync();
}
void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)
{
RecursiveGuard l(x_sync);
if (m_syncingBlockNumber == 0 || (_peer == m_syncer.lock() && _peer->m_protocolVersion != host().protocolVersion()))
{
// Syncing in pv60 mode
PV60Sync::onPeerHashes(_peer, _hashes);
return;
}
if (_hashes.size() == 0)
{
if (isSyncing(_peer) && _peer->m_syncHashNumber == m_syncingBlockNumber)
{
// End of hash chain, add last chunk to download
m_readyChainMap.insert(make_pair(m_syncingBlockNumber, h256s { _peer->m_latestHash }));
m_hashScanComplete = true;
_peer->m_syncHashNumber = 0;
requestSubchain(_peer);
}
else
{
auto syncPeer = m_chainSyncPeers.find(_peer);
if (syncPeer == m_chainSyncPeers.end())
clog(NetMessageDetail) << "Hashes response from unexpected peer";
else
{
// Peer does not have request hashes, move back from downloading to ready
unsigned number = syncPeer->second;
m_chainSyncPeers.erase(_peer);
m_readyChainMap[number] = move(m_downloadingChainMap.at(number));
m_downloadingChainMap.erase(number);
resetNeedsSyncing(_peer);
requestSubchains();
}
}
return;
}
if (isSyncing(_peer) && _peer->m_syncHashNumber == m_syncingBlockNumber)
{
// Got new subchain marker
assert(_hashes.size() == 1);
m_knownHashes.insert(_hashes[0]);
m_readyChainMap.insert(make_pair(m_syncingBlockNumber, h256s { _hashes[0] }));
if ((m_readyChainMap.size() + m_downloadingChainMap.size() + m_completeChainMap.size()) * c_hashSubchainSize > _peer->m_expectedHashes)
{
_peer->disable("Too many hashes from lead peer");
restartSync();
return;
}
transition(_peer, SyncState::Hashes);
requestSubchains();
}
else
{
auto syncPeer = m_chainSyncPeers.find(_peer);
unsigned number = 0;
if (syncPeer == m_chainSyncPeers.end())
{
//check downlading peers
for (auto const& downloader: m_downloadingChainMap)
if (downloader.second.back() == _peer->m_syncHash)
{
number = downloader.first;
break;
}
}
else
number = syncPeer->second;
if (number == 0)
{
clog(NetAllDetail) << "Hashes response from unexpected/expired peer";
return;
}
auto downloadingPeer = m_downloadingChainMap.find(number);
if (downloadingPeer == m_downloadingChainMap.end() || downloadingPeer->second.back() != _peer->m_syncHash)
{
// Too late, other peer has already downloaded our hashes
m_chainSyncPeers.erase(_peer);
requestSubchain(_peer);
return;
}
h256s& hashes = downloadingPeer->second;
unsigned knowns = 0;
unsigned unknowns = 0;
for (unsigned i = 0; i < _hashes.size(); ++i)
{
auto h = _hashes[i];
auto status = host().bq().blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host().chain().isKnown(h) || !!m_knownHashes.count(h))
{
clog(NetMessageSummary) << "Subchain download complete";
m_chainSyncPeers.erase(_peer);
completeSubchain(_peer, number);
return;
}
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
_peer->disable("Bad hashes");
if (isSyncing(_peer))
restartSync();
else
{
//try with other peer
m_readyChainMap[number] = move(m_downloadingChainMap.at(number));
m_downloadingChainMap.erase(number);
m_chainSyncPeers.erase(_peer);
}
return;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
hashes.push_back(h);
}
else
knowns++;
}
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << hashes.back();
if (hashes.size() > c_hashSubchainSize)
{
_peer->disable("Too many subchain hashes");
restartSync();
return;
}
requestSubchain(_peer);
}
DEV_INVARIANT_CHECK;
}
void PV61Sync::onPeerAborting()
{
RecursiveGuard l(x_sync);
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
for (auto s = m_chainSyncPeers.begin(); s != m_chainSyncPeers.end();)
{
if (s->first.expired())
{
unsigned number = s->second;
m_readyChainMap[number] = move(m_downloadingChainMap.at(number));
m_downloadingChainMap.erase(number);
m_chainSyncPeers.erase(s++);
}
else
++s;
}
if (m_syncer.expired())
{
if (m_state == SyncState::Hashes)
{
// Main syncer aborted, other peers are probably still downloading hashes, just set one of them as syncer
host().foreachPeer([&](std::shared_ptr<EthereumPeer> _p)
{
if (_p->m_asking != Asking::Hashes)
return true;
setState(_p, SyncState::Hashes, true, true);
return false;
});
}
if (m_syncer.expired())
PV60Sync::onPeerAborting();
}
else if (isPV61Syncing() && m_state == SyncState::Hashes)
requestSubchains();
DEV_INVARIANT_CHECK;
}
SyncStatus PV61Sync::status() const
{
RecursiveGuard l(x_sync);
SyncStatus res = PV60Sync::status();
res.protocolVersion = 61;
if (m_state == SyncState::Hashes && isPV61Syncing())
{
res.hashesReceived = 0;
for (auto const& d : m_readyChainMap)
res.hashesReceived += d.second.size();
for (auto const& d : m_downloadingChainMap)
res.hashesReceived += d.second.size();
for (auto const& d : m_completeChainMap)
res.hashesReceived += d.second.size();
}
return res;
}
bool PV61Sync::isPV61Syncing() const
{
return m_syncingBlockNumber != 0;
}
bool PV61Sync::invariants() const
{
if (m_state == SyncState::Hashes)
{
if (isPV61Syncing() && !m_syncingBlockNumber)
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Syncing in PV61 with no block number set"));
}
else if (!PV60Sync::invariants())
return false;
return true; return true;
} }

59
libethereum/BlockChainSync.h

@ -24,7 +24,6 @@
#include <mutex> #include <mutex>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libdevcore/RangeMask.h>
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include <libp2p/Common.h> #include <libp2p/Common.h>
#include "CommonNet.h" #include "CommonNet.h"
@ -59,6 +58,9 @@ public:
/// @returns true is Sync is in progress /// @returns true is Sync is in progress
virtual bool isSyncing() const = 0; virtual bool isSyncing() const = 0;
/// Restart sync
virtual void restartSync() = 0;
/// Called by peer to report status /// Called by peer to report status
virtual void onPeerStatus(std::shared_ptr<EthereumPeer> _peer); virtual void onPeerStatus(std::shared_ptr<EthereumPeer> _peer);
@ -93,9 +95,6 @@ protected:
/// Resume downloading after witing state /// Resume downloading after witing state
virtual void continueSync() = 0; virtual void continueSync() = 0;
/// Restart sync
virtual void restartSync() = 0;
/// Called after all blocks have been donloaded /// Called after all blocks have been donloaded
virtual void completeSync() = 0; virtual void completeSync() = 0;
@ -115,7 +114,7 @@ protected:
void requestBlocks(std::shared_ptr<EthereumPeer> _peer); void requestBlocks(std::shared_ptr<EthereumPeer> _peer);
protected: protected:
Handler m_bqRoomAvailable; ///< Triggered once block queue Handler<> m_bqRoomAvailable; ///< Triggered once block queue
mutable RecursiveMutex x_sync; mutable RecursiveMutex x_sync;
SyncState m_state = SyncState::Idle; ///< Current sync state SyncState m_state = SyncState::Idle; ///< Current sync state
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only. unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
@ -132,8 +131,8 @@ private:
/** /**
* @brief Syncrhonization over PV60. Selects a single peer and tries to downloading hashes from it. After hash downaload is complete * @brief Syncrhonization over PV60. Selects a single peer and tries to downloading hashes from it. After hash download is complete
* Syncs to peers and keeps up to date * syncs to peers and keeps up to date
*/ */
/** /**
@ -228,7 +227,7 @@ protected:
void pauseSync() override; void pauseSync() override;
void resetSyncFor(std::shared_ptr<EthereumPeer> _peer, h256 const& _latestHash, u256 const& _td) override; void resetSyncFor(std::shared_ptr<EthereumPeer> _peer, h256 const& _latestHash, u256 const& _td) override;
private: protected:
/// Transition sync state in a particular direction. @param _peer Peer that is responsible for state tranfer /// Transition sync state in a particular direction. @param _peer Peer that is responsible for state tranfer
void transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, bool _force = false, bool _needHelp = true); void transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, bool _force = false, bool _needHelp = true);
@ -262,6 +261,12 @@ private:
/// Called when peer done downloading blocks /// Called when peer done downloading blocks
void noteDoneBlocks(std::shared_ptr<EthereumPeer> _who, bool _clemency); void noteDoneBlocks(std::shared_ptr<EthereumPeer> _who, bool _clemency);
/// Start chainhash sync
virtual void syncHashes(std::shared_ptr<EthereumPeer> _peer);
/// Request subchain, no-op for pv60
virtual void requestSubchain(std::shared_ptr<EthereumPeer> /*_peer*/) {}
/// Abort syncing /// Abort syncing
void abortSync(); void abortSync();
@ -276,5 +281,43 @@ private:
u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty of the peer we aresyncing to, as of the current sync. u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty of the peer we aresyncing to, as of the current sync.
std::weak_ptr<EthereumPeer> m_syncer; ///< Peer we are currently syncing with std::weak_ptr<EthereumPeer> m_syncer; ///< Peer we are currently syncing with
}; };
/**
* @brief Syncrhonization over PV61. Selects a single peer and requests every c_hashSubchainSize hash, splitting the hashchain into subchains and downloading each subchain in parallel.
* Syncs to peers and keeps up to date
*/
class PV61Sync: public PV60Sync
{
public:
PV61Sync(EthereumHost& _host);
protected:
void restartSync() override;
void requestSubchain(std::shared_ptr<EthereumPeer> _peer) override;
void syncHashes(std::shared_ptr<EthereumPeer> _peer) override;
void onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes) override;
void onPeerAborting() override;
SyncStatus status() const override;
bool invariants() const override;
private:
/// Called when subchain is complete. Check if if hashchain is fully downloaded and proceed to downloading blocks
void completeSubchain(std::shared_ptr<EthereumPeer> _peer, unsigned _n);
/// Find a subchain for peers to downloading
void requestSubchains();
/// Check if downloading hashes in parallel
bool isPV61Syncing() const;
std::map<unsigned, h256s> m_completeChainMap; ///< Fully downloaded subchains
std::map<unsigned, h256s> m_readyChainMap; ///< Subchains ready for download
std::map<unsigned, h256s> m_downloadingChainMap; ///< Subchains currently being downloading. In sync with m_chainSyncPeers
std::map<std::weak_ptr<EthereumPeer>, unsigned, std::owner_less<std::weak_ptr<EthereumPeer>>> m_chainSyncPeers; ///< Peers to m_downloadingSubchain number map
h256Hash m_knownHashes; ///< Subchain start markers. Used to track suchain completion
unsigned m_syncingBlockNumber = 0; ///< Current subchain marker
bool m_hashScanComplete = false; ///< True if leading peer completed hashchain scan and we have a list of subchains ready
};
std::ostream& operator<<(std::ostream& _out, SyncStatus const& _sync);
} }
} }

4
libethereum/BlockDetails.h

@ -59,7 +59,7 @@ struct BlockLogBlooms
{ {
BlockLogBlooms() {} BlockLogBlooms() {}
BlockLogBlooms(RLP const& _r) { blooms = _r.toVector<LogBloom>(); size = _r.data().size(); } BlockLogBlooms(RLP const& _r) { blooms = _r.toVector<LogBloom>(); size = _r.data().size(); }
bytes rlp() const { RLPStream s; s << blooms; size = s.out().size(); return s.out(); } bytes rlp() const { bytes r = dev::rlp(blooms); size = r.size(); return r; }
LogBlooms blooms; LogBlooms blooms;
mutable unsigned size; mutable unsigned size;
@ -69,7 +69,7 @@ struct BlocksBlooms
{ {
BlocksBlooms() {} BlocksBlooms() {}
BlocksBlooms(RLP const& _r) { blooms = _r.toArray<LogBloom, c_bloomIndexSize>(); size = _r.data().size(); } BlocksBlooms(RLP const& _r) { blooms = _r.toArray<LogBloom, c_bloomIndexSize>(); size = _r.data().size(); }
bytes rlp() const { RLPStream s; s << blooms; size = s.out().size(); return s.out(); } bytes rlp() const { bytes r = dev::rlp(blooms); size = r.size(); return r; }
std::array<LogBloom, c_bloomIndexSize> blooms; std::array<LogBloom, c_bloomIndexSize> blooms;
mutable unsigned size; mutable unsigned size;

74
libethereum/BlockQueue.cpp

@ -21,6 +21,7 @@
#include "BlockQueue.h" #include "BlockQueue.h"
#include <thread> #include <thread>
#include <sstream>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
@ -114,15 +115,11 @@ void BlockQueue::verifierBody()
catch (...) catch (...)
{ {
// bad block. // bad block.
{ // has to be this order as that's how invariants() assumes.
// has to be this order as that's how invariants() assumes. WriteGuard l2(m_lock);
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification);
m_readySet.erase(work.hash);
m_knownBad.insert(work.hash);
}
unique_lock<Mutex> l(m_verification); unique_lock<Mutex> l(m_verification);
m_readySet.erase(work.hash);
m_knownBad.insert(work.hash);
for (auto it = m_verifying.begin(); it != m_verifying.end(); ++it) for (auto it = m_verifying.begin(); it != m_verifying.end(); ++it)
if (it->verified.info.mixHash == work.hash) if (it->verified.info.mixHash == work.hash)
{ {
@ -131,6 +128,7 @@ void BlockQueue::verifierBody()
} }
cwarn << "BlockQueue missing our job: was there a GM?"; cwarn << "BlockQueue missing our job: was there a GM?";
OK1:; OK1:;
drainVerified_WITH_BOTH_LOCKS();
continue; continue;
} }
@ -149,17 +147,8 @@ void BlockQueue::verifierBody()
} }
else else
m_verified.emplace_back(move(res)); m_verified.emplace_back(move(res));
while (m_verifying.size() && !m_verifying.front().blockData.empty())
{ drainVerified_WITH_BOTH_LOCKS();
if (m_knownBad.count(m_verifying.front().verified.info.parentHash))
{
m_readySet.erase(m_verifying.front().verified.info.hash());
m_knownBad.insert(res.verified.info.hash());
}
else
m_verified.emplace_back(move(m_verifying.front()));
m_verifying.pop_front();
}
ready = true; ready = true;
} }
else else
@ -179,8 +168,24 @@ void BlockQueue::verifierBody()
} }
} }
void BlockQueue::drainVerified_WITH_BOTH_LOCKS()
{
while (!m_verifying.empty() && !m_verifying.front().blockData.empty())
{
if (m_knownBad.count(m_verifying.front().verified.info.parentHash))
{
m_readySet.erase(m_verifying.front().verified.info.hash());
m_knownBad.insert(m_verifying.front().verified.info.hash());
}
else
m_verified.emplace_back(move(m_verifying.front()));
m_verifying.pop_front();
}
}
ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs) ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs)
{ {
clog(BlockQueueTraceChannel) << std::this_thread::get_id();
// Check if we already know this block. // Check if we already know this block.
h256 h = BlockInfo::headerHash(_block); h256 h = BlockInfo::headerHash(_block);
@ -210,8 +215,10 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
return ImportResult::Malformed; return ImportResult::Malformed;
} }
clog(BlockQueueTraceChannel) << "Block" << h << "is" << bi.number << "parent is" << bi.parentHash;
// Check block doesn't already exist first! // Check block doesn't already exist first!
if (_bc.details(h)) if (_bc.isKnown(h))
{ {
cblockq << "Already known in chain."; cblockq << "Already known in chain.";
return ImportResult::AlreadyInChain; return ImportResult::AlreadyInChain;
@ -242,7 +249,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (m_knownBad.count(bi.parentHash)) if (m_knownBad.count(bi.parentHash))
{ {
m_knownBad.insert(bi.hash()); m_knownBad.insert(bi.hash());
updateBad(bi.hash()); updateBad_WITH_LOCK(bi.hash());
// bad parent; this is bad too, note it as such // bad parent; this is bad too, note it as such
return ImportResult::BadChain; return ImportResult::BadChain;
} }
@ -277,12 +284,12 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
} }
} }
void BlockQueue::updateBad(h256 const& _bad) void BlockQueue::updateBad_WITH_LOCK(h256 const& _bad)
{ {
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
DEV_GUARDED(m_verification) DEV_GUARDED(m_verification)
{ {
collectUnknownBad(_bad); collectUnknownBad_WITH_BOTH_LOCKS(_bad);
bool moreBad = true; bool moreBad = true;
while (moreBad) while (moreBad)
{ {
@ -294,7 +301,7 @@ void BlockQueue::updateBad(h256 const& _bad)
{ {
m_knownBad.insert(b.verified.info.hash()); m_knownBad.insert(b.verified.info.hash());
m_readySet.erase(b.verified.info.hash()); m_readySet.erase(b.verified.info.hash());
collectUnknownBad(b.verified.info.hash()); collectUnknownBad_WITH_BOTH_LOCKS(b.verified.info.hash());
moreBad = true; moreBad = true;
} }
else else
@ -307,7 +314,7 @@ void BlockQueue::updateBad(h256 const& _bad)
{ {
m_knownBad.insert(b.hash); m_knownBad.insert(b.hash);
m_readySet.erase(b.hash); m_readySet.erase(b.hash);
collectUnknownBad(b.hash); collectUnknownBad_WITH_BOTH_LOCKS(b.hash);
moreBad = true; moreBad = true;
} }
else else
@ -321,17 +328,16 @@ void BlockQueue::updateBad(h256 const& _bad)
h256 const& h = b.blockData.size() != 0 ? b.verified.info.hash() : b.verified.info.mixHash; h256 const& h = b.blockData.size() != 0 ? b.verified.info.hash() : b.verified.info.mixHash;
m_knownBad.insert(h); m_knownBad.insert(h);
m_readySet.erase(h); m_readySet.erase(h);
collectUnknownBad(h); collectUnknownBad_WITH_BOTH_LOCKS(h);
moreBad = true; moreBad = true;
} }
else else
m_verifying.push_back(std::move(b)); m_verifying.push_back(std::move(b));
} }
} }
DEV_INVARIANT_CHECK;
} }
void BlockQueue::collectUnknownBad(h256 const& _bad) void BlockQueue::collectUnknownBad_WITH_BOTH_LOCKS(h256 const& _bad)
{ {
list<h256> badQueue(1, _bad); list<h256> badQueue(1, _bad);
while (!badQueue.empty()) while (!badQueue.empty())
@ -349,7 +355,6 @@ void BlockQueue::collectUnknownBad(h256 const& _bad)
} }
m_unknown.erase(r.first, r.second); m_unknown.erase(r.first, r.second);
} }
} }
bool BlockQueue::doneDrain(h256s const& _bad) bool BlockQueue::doneDrain(h256s const& _bad)
@ -364,7 +369,7 @@ bool BlockQueue::doneDrain(h256s const& _bad)
// at least one of them was bad. // at least one of them was bad.
m_knownBad += _bad; m_knownBad += _bad;
for (h256 const& b : _bad) for (h256 const& b : _bad)
updateBad(b); updateBad_WITH_LOCK(b);
} }
return !m_readySet.empty(); return !m_readySet.empty();
} }
@ -471,7 +476,13 @@ void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
bool BlockQueue::invariants() const bool BlockQueue::invariants() const
{ {
Guard l(m_verification); Guard l(m_verification);
return m_readySet.size() == m_verified.size() + m_unverified.size() + m_verifying.size(); if (!(m_readySet.size() == m_verified.size() + m_unverified.size() + m_verifying.size()))
{
std::stringstream s;
s << "Failed BlockQueue invariant: m_readySet: " << m_readySet.size() << " m_verified: " << m_verified.size() << " m_unverified: " << m_unverified.size() << " m_verifying" << m_verifying.size();
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment(s.str()));
}
return true;
} }
void BlockQueue::noteReady_WITH_LOCK(h256 const& _good) void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
@ -501,7 +512,6 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
} }
if (notify) if (notify)
m_moreToVerify.notify_all(); m_moreToVerify.notify_all();
DEV_INVARIANT_CHECK;
} }
void BlockQueue::retryAllUnknown() void BlockQueue::retryAllUnknown()

13
libethereum/BlockQueue.h

@ -111,8 +111,8 @@ public:
/// Get some infomration on the given block's status regarding us. /// Get some infomration on the given block's status regarding us.
QueueStatus blockStatus(h256 const& _h) const; QueueStatus blockStatus(h256 const& _h) const;
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); } template <class T> Handler<> onReady(T const& _t) { return m_onReady.add(_t); }
template <class T> Handler onRoomAvailable(T const& _t) { return m_onRoomAvailable.add(_t); } template <class T> Handler<> onRoomAvailable(T const& _t) { return m_onRoomAvailable.add(_t); }
template <class T> void setOnBad(T const& _t) { m_onBad = _t; } template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
@ -134,8 +134,9 @@ private:
bool invariants() const override; bool invariants() const override;
void verifierBody(); void verifierBody();
void collectUnknownBad(h256 const& _bad); void collectUnknownBad_WITH_BOTH_LOCKS(h256 const& _bad);
void updateBad(h256 const& _bad); void updateBad_WITH_LOCK(h256 const& _bad);
void drainVerified_WITH_BOTH_LOCKS();
mutable boost::shared_mutex m_lock; ///< General lock for the sets, m_future and m_unknown. mutable boost::shared_mutex m_lock; ///< General lock for the sets, m_future and m_unknown.
h256Hash m_drainingSet; ///< All blocks being imported. h256Hash m_drainingSet; ///< All blocks being imported.
@ -144,8 +145,8 @@ private:
std::unordered_multimap<h256, std::pair<h256, bytes>> m_unknown; ///< For blocks that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears. std::unordered_multimap<h256, std::pair<h256, bytes>> m_unknown; ///< For blocks that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears.
h256Hash m_knownBad; ///< Set of blocks that we know will never be valid. h256Hash m_knownBad; ///< Set of blocks that we know will never be valid.
std::multimap<unsigned, std::pair<h256, bytes>> m_future; ///< Set of blocks that are not yet valid. Ordered by timestamp std::multimap<unsigned, std::pair<h256, bytes>> m_future; ///< Set of blocks that are not yet valid. Ordered by timestamp
Signal m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast. Signal<> m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast.
Signal m_onRoomAvailable; ///< Called when space for new blocks becomes availabe after a drain. Be nice and exit fast. Signal<> m_onRoomAvailable; ///< Called when space for new blocks becomes availabe after a drain. Be nice and exit fast.
mutable Mutex m_verification; ///< Mutex that allows writing to m_verified, m_verifying and m_unverified. mutable Mutex m_verification; ///< Mutex that allows writing to m_verified, m_verifying and m_unverified.
std::condition_variable m_moreToVerify; ///< Signaled when m_unverified has a new entry. std::condition_variable m_moreToVerify; ///< Signaled when m_unverified has a new entry.

268
libethereum/Client.cpp

@ -24,7 +24,6 @@
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/math/distributions/normal.hpp>
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
#include <jsonrpccpp/client.h> #include <jsonrpccpp/client.h>
#include <jsonrpccpp/client/connectors/httpclient.h> #include <jsonrpccpp/client/connectors/httpclient.h>
@ -38,64 +37,97 @@
#include "Defaults.h" #include "Defaults.h"
#include "Executive.h" #include "Executive.h"
#include "EthereumHost.h" #include "EthereumHost.h"
#include "Utility.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace p2p; using namespace p2p;
VersionChecker::VersionChecker(string const& _dbPath): std::ostream& dev::eth::operator<<(std::ostream& _out, ActivityReport const& _r)
m_path(_dbPath.size() ? _dbPath : Defaults::dbPath())
{ {
bytes statusBytes = contents(m_path + "/status"); _out << "Since " << toString(_r.since) << " (" << std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - _r.since).count();
RLP status(statusBytes); _out << "): " << _r.ticks << "ticks";
try return _out;
{ }
auto protocolVersion = (unsigned)status[0];
(void)protocolVersion; #ifdef _WIN32
auto minorProtocolVersion = (unsigned)status[1]; const char* ClientNote::name() { return EthTeal "^" EthBlue " i"; }
auto databaseVersion = (unsigned)status[2]; const char* ClientChat::name() { return EthTeal "^" EthWhite " o"; }
h256 ourGenesisHash = CanonBlockChain::genesis().hash(); const char* ClientTrace::name() { return EthTeal "^" EthGray " O"; }
auto genesisHash = status.itemCount() > 3 ? (h256)status[3] : ourGenesisHash; const char* ClientDetail::name() { return EthTeal "^" EthCoal " 0"; }
#else
m_action = const char* ClientNote::name() { return EthTeal "" EthBlue ""; }
databaseVersion != c_databaseVersion || genesisHash != ourGenesisHash ? const char* ClientChat::name() { return EthTeal "" EthWhite ""; }
WithExisting::Kill const char* ClientTrace::name() { return EthTeal "" EthGray ""; }
: minorProtocolVersion != eth::c_minorProtocolVersion ? const char* ClientDetail::name() { return EthTeal "" EthCoal ""; }
WithExisting::Verify #endif
:
WithExisting::Trust; static const Addresses c_canaries =
} {
catch (...) Address("4bb7e8ae99b645c2b7860b8f3a2328aae28bd80a"), // gav
{ Address("1baf27b88c48dd02b744999cf3522766929d2b2a"), // vitalik
m_action = WithExisting::Kill; Address("a8edb1ac2c86d3d9d78f96cd18001f60df29e52c"), // jeff
} Address("ace7813896a84d3f5f80223916a5353ab16e46e6") // christoph
};
VersionChecker::VersionChecker(string const& _dbPath)
{
upgradeDatabase(_dbPath);
} }
void VersionChecker::setOk() Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Client(_extNet, make_shared<TrivialGasPricer>(), _dbPath, _forceAction, _networkId)
{ {
if (m_action != WithExisting::Trust) startWorking();
{ }
try
{ Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
boost::filesystem::create_directory(m_path); Worker("eth", 0),
} m_vc(_dbPath),
catch (...) m_bc(_dbPath, _forceAction, [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
{ m_gp(_gp),
cwarn << "Unhandled exception! Failed to create directory: " << m_path << "\n" << boost::current_exception_diagnostic_information(); m_stateDB(State::openDB(_dbPath, _forceAction)),
} m_preMine(m_stateDB, BaseState::CanonGenesis),
writeFile(m_path + "/status", rlpList(eth::c_protocolVersion, eth::c_minorProtocolVersion, c_databaseVersion, CanonBlockChain::genesis().hash())); m_postMine(m_stateDB)
} {
if (_forceAction == WithExisting::Rescue)
m_bc.rescue(m_stateDB);
m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30);
m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue);
m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue);
m_bq.setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
m_bc.setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); });
m_gp->update(m_bc);
auto host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
m_host = host;
_extNet->addCapability(host, EthereumHost::staticName(), EthereumHost::c_oldProtocolVersion); //TODO: remove this one v61+ protocol is common
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
doWork();
startWorking();
}
Client::~Client()
{
stopWorking();
} }
ImportResult Client::queueBlock(bytes const& _block, bool _isSafe) ImportResult Client::queueBlock(bytes const& _block, bool _isSafe)
{ {
if (m_bq.status().verified + m_bq.status().verifying + m_bq.status().unverified > 30000) if (m_bq.status().verified + m_bq.status().verifying + m_bq.status().unverified > 10000)
this_thread::sleep_for(std::chrono::milliseconds(500)); this_thread::sleep_for(std::chrono::milliseconds(500));
return m_bq.import(&_block, bc(), _isSafe); return m_bq.import(&_block, bc(), _isSafe);
} }
tuple<ImportRoute, bool, unsigned> Client::syncQueue(unsigned _max) tuple<ImportRoute, bool, unsigned> Client::syncQueue(unsigned _max)
{ {
stopWorking();
return m_bc.sync(m_bq, m_stateDB, _max); return m_bc.sync(m_bq, m_stateDB, _max);
} }
@ -210,144 +242,18 @@ void Client::onBadBlock(Exception& _ex) const
#endif #endif
} }
void BasicGasPricer::update(BlockChain const& _bc)
{
unsigned c = 0;
h256 p = _bc.currentHash();
m_gasPerBlock = _bc.info(p).gasLimit;
map<u256, u256> dist;
u256 total = 0;
// make gasPrice versus gasUsed distribution for the last 1000 blocks
while (c < 1000 && p)
{
BlockInfo bi = _bc.info(p);
if (bi.transactionsRoot != EmptyTrie)
{
auto bb = _bc.block(p);
RLP r(bb);
BlockReceipts brs(_bc.receipts(bi.hash()));
size_t i = 0;
for (auto const& tr: r[1])
{
Transaction tx(tr.data(), CheckTransaction::None);
u256 gu = brs.receipts[i].gasUsed();
dist[tx.gasPrice()] += gu;
total += gu;
i++;
}
}
p = bi.parentHash;
++c;
}
// fill m_octiles with weighted gasPrices
if (total > 0)
{
m_octiles[0] = dist.begin()->first;
// calc mean
u256 mean = 0;
for (auto const& i: dist)
mean += i.first * i.second;
mean /= total;
// calc standard deviation
u256 sdSquared = 0;
for (auto const& i: dist)
sdSquared += i.second * (i.first - mean) * (i.first - mean);
sdSquared /= total;
if (sdSquared)
{
long double sd = sqrt(sdSquared.convert_to<long double>());
long double normalizedSd = sd / mean.convert_to<long double>();
// calc octiles normalized to gaussian distribution
boost::math::normal gauss(1.0, (normalizedSd > 0.01) ? normalizedSd : 0.01);
for (size_t i = 1; i < 8; i++)
m_octiles[i] = u256(mean.convert_to<long double>() * boost::math::quantile(gauss, i / 8.0));
m_octiles[8] = dist.rbegin()->first;
}
else
{
for (size_t i = 0; i < 9; i++)
m_octiles[i] = (i + 1) * mean / 5;
}
}
}
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;
}
#ifdef _WIN32
const char* ClientNote::name() { return EthTeal "^" EthBlue " i"; }
const char* ClientChat::name() { return EthTeal "^" EthWhite " o"; }
const char* ClientTrace::name() { return EthTeal "^" EthGray " O"; }
const char* ClientDetail::name() { return EthTeal "^" EthCoal " 0"; }
#else
const char* ClientNote::name() { return EthTeal "" EthBlue ""; }
const char* ClientChat::name() { return EthTeal "" EthWhite ""; }
const char* ClientTrace::name() { return EthTeal "" EthGray ""; }
const char* ClientDetail::name() { return EthTeal "" EthCoal ""; }
#endif
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Client(_extNet, make_shared<TrivialGasPricer>(), _dbPath, _forceAction, _networkId)
{
startWorking();
}
Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Worker("eth", 0),
m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(_gp),
m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(m_stateDB, BaseState::CanonGenesis),
m_postMine(m_stateDB)
{
m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30);
m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue);
m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue);
m_bq.setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
m_bc.setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); });
m_gp->update(m_bc);
auto host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
m_host = host;
_extNet->addCapability(host, EthereumHost::staticName(), EthereumHost::c_oldProtocolVersion); //TODO: remove this one v61+ protocol is common
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
m_vc.setOk();
doWork();
startWorking();
}
Client::~Client()
{
stopWorking();
}
static const Address c_canary("0x");
bool Client::isChainBad() const bool Client::isChainBad() const
{ {
return stateAt(c_canary, 0) != 0; unsigned numberBad = 0;
for (auto const& a: c_canaries)
if (!!stateAt(a, 0))
numberBad++;
return numberBad >= 2;
} }
bool Client::isUpgradeNeeded() const bool Client::isUpgradeNeeded() const
{ {
return stateAt(c_canary, 0) == 2; return stateAt(c_canaries[0], 0) == 2;
} }
void Client::setNetworkId(u256 _n) void Client::setNetworkId(u256 _n)
@ -701,7 +607,7 @@ void Client::onDeadBlocks(h256s const& _blocks, h256Hash& io_changed)
for (auto const& t: m_bc.transactions(h)) for (auto const& t: m_bc.transactions(h))
{ {
clog(ClientTrace) << "Resubmitting dead-block transaction " << Transaction(t, CheckTransaction::None); clog(ClientTrace) << "Resubmitting dead-block transaction " << Transaction(t, CheckTransaction::None);
m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry); m_tq.import(t, IfDropped::Retry);
} }
} }
@ -713,14 +619,7 @@ void Client::onNewBlocks(h256s const& _blocks, h256Hash& io_changed)
{ {
// remove transactions from m_tq nicely rather than relying on out of date nonce later on. // remove transactions from m_tq nicely rather than relying on out of date nonce later on.
for (auto const& h: _blocks) for (auto const& h: _blocks)
{
clog(ClientTrace) << "Live block:" << h; clog(ClientTrace) << "Live block:" << h;
for (auto const& th: m_bc.transactionHashes(h))
{
clog(ClientTrace) << "Safely dropping transaction " << th;
m_tq.drop(th);
}
}
if (auto h = m_host.lock()) if (auto h = m_host.lock())
h->noteNewBlocks(); h->noteNewBlocks();
@ -756,7 +655,7 @@ void Client::restartMining()
for (auto const& t: m_postMine.pending()) for (auto const& t: m_postMine.pending())
{ {
clog(ClientTrace) << "Resubmitting post-mine transaction " << t; clog(ClientTrace) << "Resubmitting post-mine transaction " << t;
auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry); auto ir = m_tq.import(t, IfDropped::Retry);
if (ir != ImportResult::Success) if (ir != ImportResult::Success)
onTransactionQueueReady(); onTransactionQueueReady();
} }
@ -776,6 +675,11 @@ void Client::onChainChanged(ImportRoute const& _ir)
{ {
h256Hash changeds; h256Hash changeds;
onDeadBlocks(_ir.deadBlocks, changeds); onDeadBlocks(_ir.deadBlocks, changeds);
for (auto const& t: _ir.goodTranactions)
{
clog(ClientTrace) << "Safely dropping transaction " << t.sha3();
m_tq.dropGood(t);
}
onNewBlocks(_ir.liveBlocks, changeds); onNewBlocks(_ir.liveBlocks, changeds);
restartMining(); restartMining();
noteChanged(changeds); noteChanged(changeds);
@ -805,7 +709,7 @@ void Client::rejigMining()
{ {
clog(ClientTrace) << "Rejigging mining..."; clog(ClientTrace) << "Rejigging mining...";
DEV_WRITE_GUARDED(x_working) DEV_WRITE_GUARDED(x_working)
m_working.commitToMine(m_bc); m_working.commitToMine(m_bc, m_extraData);
DEV_READ_GUARDED(x_working) DEV_READ_GUARDED(x_working)
{ {
DEV_WRITE_GUARDED(x_postMine) DEV_WRITE_GUARDED(x_postMine)

40
libethereum/Client.h

@ -64,33 +64,6 @@ class VersionChecker
{ {
public: public:
VersionChecker(std::string const& _dbPath); VersionChecker(std::string const& _dbPath);
void setOk();
WithExisting action() const { return m_action; }
private:
WithExisting m_action;
std::string m_path;
};
class BasicGasPricer: public GasPricer
{
public:
explicit BasicGasPricer(u256 _weiPerRef, u256 _refsPerBlock): m_weiPerRef(_weiPerRef), m_refsPerBlock(_refsPerBlock) {}
void setRefPrice(u256 _weiPerRef) { if ((bigint)m_refsPerBlock * _weiPerRef > std::numeric_limits<u256>::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_weiPerRef = _weiPerRef; }
void setRefBlockFees(u256 _refsPerBlock) { if ((bigint)m_weiPerRef * _refsPerBlock > std::numeric_limits<u256>::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_refsPerBlock = _refsPerBlock; }
u256 ask(State const&) const override { return m_weiPerRef * m_refsPerBlock / m_gasPerBlock; }
u256 bid(TransactionPriority _p = TransactionPriority::Medium) const override { return m_octiles[(int)_p] > 0 ? m_octiles[(int)_p] : (m_weiPerRef * m_refsPerBlock / m_gasPerBlock); }
void update(BlockChain const& _bc) override;
private:
u256 m_weiPerRef;
u256 m_refsPerBlock;
u256 m_gasPerBlock = 3141592;
std::array<u256, 9> m_octiles;
}; };
struct ClientNote: public LogChannel { static const char* name(); static const int verbosity = 2; }; struct ClientNote: public LogChannel { static const char* name(); static const int verbosity = 2; };
@ -240,6 +213,12 @@ public:
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; } ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
/// Set a JSONRPC server to which we can report bad blocks. /// Set a JSONRPC server to which we can report bad blocks.
void setSentinel(std::string const& _server) { m_sentinel = _server; } void setSentinel(std::string const& _server) { m_sentinel = _server; }
/// Get the JSONRPC server to which we report bad blocks.
std::string const& sentinel() const { return m_sentinel; }
/// Set the extra data that goes into mined blocks.
void setExtraData(bytes const& _extraData) { m_extraData = _extraData; }
/// Rewind to a prior head.
void rewind(unsigned _n) { m_bc.rewind(_n); }
protected: protected:
/// InterfaceStub methods /// InterfaceStub methods
@ -342,10 +321,10 @@ private:
GenericFarm<ProofOfWork> m_farm; ///< Our mining farm. GenericFarm<ProofOfWork> m_farm; ///< Our mining farm.
Handler m_tqReady; Handler<> m_tqReady;
Handler m_bqReady; Handler<> m_bqReady;
bool m_wouldMine = false; ///< True if we /should/ be mining. bool m_wouldMine = false; ///< True if we /should/ be mining.
bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping. bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping.
bool m_forceMining = false; ///< Mine even when there are no transactions pending? bool m_forceMining = false; ///< Mine even when there are no transactions pending?
bool m_mineOnBadChain = false; ///< Mine even when the canary says it's a bad chain. bool m_mineOnBadChain = false; ///< Mine even when the canary says it's a bad chain.
@ -366,6 +345,7 @@ private:
std::atomic<bool> m_syncBlockQueue = {false}; std::atomic<bool> m_syncBlockQueue = {false};
std::string m_sentinel; std::string m_sentinel;
bytes m_extraData;
}; };
} }

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save