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(TESTS)
eth_format_option(NOBOOST)
eth_format_option(ROCKSDB)
eth_format_option(TOOLS)
eth_format_option(ETHKEY)
eth_format_option(ETHASHCL)
eth_format_option(JSCONSOLE)
eth_format_option_on_decent_platform(SERPENT)
@ -234,6 +236,15 @@ elseif (BUNDLE STREQUAL "full")
set(TOOLS ON)
set(TESTS 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")
set(SERPENT OFF)
set(SOLIDITY ON)
@ -312,6 +323,7 @@ message("-- Hardware identification support ${CPUID_FO
message("-- HTTP Request support ${CURL_FOUND}")
message("-- VMTRACE VM execution tracing ${VMTRACE}")
message("-- PROFILING Profiling support ${PROFILING}")
message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}")
message("-- FATDB Full database exploring ${FATDB}")
message("-- JSONRPC JSON-RPC support ${JSONRPC}")
message("-- USENPM Javascript source building ${USENPM}")
@ -325,7 +337,6 @@ message("-- SERPENT Build Serpent language components ${SERPENT}
message("-- GUI Build GUI components ${GUI}")
message("-- NCURSES Build NCurses components ${NCURSES}")
message("-- TESTS Build tests ${TESTS}")
message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}")
message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}")
message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}")
message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}")
@ -362,8 +373,6 @@ else ()
set(GENERAL 0)
endif ()
message("GENERAL ${GENERAL}")
add_subdirectory(libdevcore)
if (GENERAL)
add_subdirectory(libevmcore)
@ -398,6 +407,7 @@ if (JSCONSOLE)
endif ()
if (NOT WIN32)
add_definitions(-DETH_HAVE_SECP256K1)
add_subdirectory(secp256k1)
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:
- 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.
- 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.
@ -226,4 +226,3 @@ a. Includes should go in order of lower level (STL -> boost -> libdevcore -> lib
#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.

5
alethzero/CMakeLists.txt

@ -7,6 +7,11 @@ if (${CMAKE_MAJOR_VERSION} GREATER 2)
cmake_policy(SET CMP0043 OLD)
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)
aux_source_directory(. SRC_LIST)

198
alethzero/DownloadView.cpp

@ -30,56 +30,176 @@ using namespace std;
using namespace dev;
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_man || m_man->chainEmpty() || !m_man->subCount())
if (!m_client || !isVisible() || !rect().width() || !rect().height())
return;
double ratio = (double)rect().width() / rect().height();
if (ratio < 1)
ratio = 1 / ratio;
double n = min(16.0, min(rect().width(), rect().height()) / ceil(sqrt(m_man->chainSize() / ratio)));
DownloadMan const* man = m_client->downloadMan();
BlockQueueStatus bqs = m_client->blockQueueStatus();
SyncStatus sync = m_client->syncStatus();
// QSizeF area(rect().width() / floor(rect().width() / n), rect().height() / floor(rect().height() / n));
QSizeF area(n, n);
QPointF pos(0, 0);
unsigned syncFrom = m_client->numberFromHash(PendingBlockHash);
unsigned syncImported = syncFrom;
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();
unsigned subCount = m_man->subCount();
if (subCount == 0)
return;
unsigned dh = 360 / subCount;
for (unsigned i = bg.all().first, ei = bg.all().second; i < ei; ++i)
// best effort guess. assumes there's no forks.
unsigned downloadFrom = sync.state == SyncState::Idle ? m_lastSyncFrom : m_client->numberFromHash(m_client->isKnown(man->firstBlock()) ? man->firstBlock() : PendingBlockHash);
unsigned downloadCount = sync.state == SyncState::Idle ? m_lastSyncCount : sync.blocksTotal;
unsigned downloadDone = downloadFrom + (sync.state == SyncState::Idle ? m_lastSyncCount : sync.blocksReceived);
unsigned downloadPoint = downloadFrom + (sync.state == SyncState::Idle ? m_lastSyncCount : man->overview().lastComplete);
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;
if (bg.contains(i))
s = -1;
else
case SyncState::Hashes:
if (!syncCount || !sync.hashesEstimated)
{
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_man->foreachSub([&](DownloadSub const& sub)
{
if (sub.askedContains(i))
s = h;
h++;
});
m_lastSyncFrom = (unsigned)-1;
m_lastSyncCount = 0;
labelBack = QColor::fromHsv(0, 0, 200);
labelFore = Qt::white;
labelText = "Idle";
}
if (s == -2)
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);
else
p.fillRect(QRectF(QPointF(pos) + QPointF(1 * area.width() / 8, 1 * area.height() / 8), area * 3 / 4), QColor::fromHsv(s * dh, 64, 128));
pos.setX(pos.x() + n);
if (pos.x() >= rect().width() - n)
pos = QPoint(0, pos.y() + n);
default: break;
}
unsigned from = min(min(hashFrom, downloadFrom), min(syncFrom, m_lastSyncFrom));
unsigned count = max(max(hashFrom + hashCount, downloadFrom + downloadCount), max(syncFrom + syncCount, m_lastSyncFrom + m_lastSyncCount)) - from;
/* cnote << "Range " << from << "-" << (from + count) << "(" << hashFrom << "+" << hashCount << "," << downloadFrom << "+" << downloadCount << "," << syncFrom << "+" << syncCount << ")";
auto r = [&](unsigned u) {
return toString((u - from) * 100 / count) + "%";
};
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
namespace dev { namespace eth {
class DownloadMan;
class Client;
}}
class DownloadView: public QWidget
class SyncView: public QWidget
{
Q_OBJECT
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:
virtual void paintEvent(QPaintEvent*);
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->accounts->setEnabled(true);
ui->contracts->setEnabled(true);
for (auto i: ethereum()->addresses(m_block))
try
{
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));
for (auto i: ethereum()->addresses(m_block))
{
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>
</property>
<property name="windowTitle">
<string>AlethZero Ethereum Client</string>
<string>AlethZero ++Ethereum</string>
</property>
<property name="dockNestingEnabled">
<bool>true</bool>
@ -44,14 +44,14 @@
<string>0 bytes used</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="syncStatus">
<property name="text">
<string></string>
</property>
</widget>
</item>
</item>
<item>
<widget class="QLabel" name="syncStatus">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="chainStatus">
<property name="text">
@ -192,10 +192,14 @@
<addaction name="injectBlock"/>
<addaction name="forceMining"/>
<addaction name="separator"/>
<addaction name="vmInterpreter"/>
<addaction name="vmJIT"/>
<addaction name="vmSmart"/>
<addaction name="separator"/>
<addaction name="usePrivate"/>
<addaction name="jitvm"/>
<addaction name="retryUnknown"/>
<addaction name="confirm"/>
<addaction name="rewindChain"/>
</widget>
<widget class="QMenu" name="menu_View">
<property name="title">
@ -224,6 +228,7 @@
<string>&amp;Config</string>
</property>
<addaction name="gasPrices"/>
<addaction name="sentinel"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_View"/>
@ -703,7 +708,7 @@
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_8">
<widget class="QDockWidget" name="blockChainDockWidget">
<property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set>
</property>
@ -1145,7 +1150,7 @@ font-size: 14pt</string>
<number>0</number>
</property>
<item>
<widget class="DownloadView" name="downloadView" native="true"/>
<widget class="SyncView" name="downloadView" native="true"/>
</item>
</layout>
</widget>
@ -1696,17 +1701,6 @@ font-size: 14pt</string>
<string>&amp;Clear Pending</string>
</property>
</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">
<property name="text">
<string>&amp;Kill Account</string>
@ -1782,6 +1776,40 @@ font-size: 14pt</string>
<string>&amp;Gas Prices...</string>
</property>
</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>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
@ -1792,7 +1820,7 @@ font-size: 14pt</string>
<container>1</container>
</customwidget>
<customwidget>
<class>DownloadView</class>
<class>SyncView</class>
<extends>QWidget</extends>
<header>DownloadView.h</header>
<container>1</container>

85
alethzero/MainWin.cpp

@ -136,6 +136,12 @@ Main::Main(QWidget *parent) :
QtWebEngine::initialize();
setWindowFlags(Qt::Window);
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)
{
simpleDebugOut(s, c);
@ -239,18 +245,31 @@ Main::Main(QWidget *parent) :
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();
m_transact = new Transact(this, this);
m_transact->setWindowFlags(Qt::Dialog);
m_transact->setWindowModality(Qt::WindowModal);
connect(ui->blockChainDockWidget, &QDockWidget::visibilityChanged, [=]() { refreshBlockChain(); });
#if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts);
#endif
#if !ETH_EVMJIT
ui->jitvm->setEnabled(false);
ui->jitvm->setChecked(false);
#endif
installWatches();
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()
{
KeyPair kp = KeyPair::create();
@ -723,7 +750,8 @@ void Main::writeSettings()
s.setValue("url", ui->urlEdit->text());
s.setValue("privateChain", m_privateChain);
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();
if (!d.empty())
@ -814,8 +842,28 @@ void Main::readSettings(bool _skipGeometry)
m_privateChain = s.value("privateChain", "").toString();
ui->usePrivate->setChecked(m_privateChain.size());
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
on_urlEdit_returnPressed();
@ -992,10 +1040,19 @@ void Main::on_usePrivate_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();
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
bool ok;
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()
@ -1252,7 +1309,7 @@ void Main::refreshBlockCount()
auto d = ethereum()->blockChain().details();
BlockQueueStatus b = ethereum()->blockQueueStatus();
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)
syncStatus += QString(": %1/%2%3").arg(sync.hashesReceived).arg(sync.hashesEstimated ? "~" : "").arg(sync.hashesTotal);
if (sync.state == SyncState::Blocks || sync.state == SyncState::NewBlocks)
@ -1269,7 +1326,7 @@ void Main::on_turboMining_triggered()
void Main::refreshBlockChain()
{
if (!ui->blocks->isVisible() && isVisible())
if (!(ui->blockChainDockWidget->isVisible() || !tabifiedDockWidgets(ui->blockChainDockWidget).isEmpty()))
return;
DEV_TIMED_FUNCTION_ABOVE(500);
@ -1947,12 +2004,12 @@ void Main::on_net_triggered()
web3()->setNetworkPreferences(netPrefs(), ui->dropPeers->isChecked());
ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : h256());
web3()->startNetwork();
ui->downloadView->setDownloadMan(ethereum()->downloadMan());
ui->downloadView->setEthereum(ethereum());
ui->enode->setText(QString::fromStdString(web3()->enode()));
}
else
{
ui->downloadView->setDownloadMan(nullptr);
ui->downloadView->setEthereum(nullptr);
writeSettings();
web3()->stopNetwork();
}

11
alethzero/MainWin.h

@ -44,6 +44,7 @@
#include "Connect.h"
class QListWidgetItem;
class QActionGroup;
namespace Ui {
class Main;
@ -182,8 +183,11 @@ private slots:
void on_forceMining_triggered();
void on_usePrivate_triggered();
void on_turboMining_triggered();
void on_jitvm_triggered();
void on_retryUnknown_triggered();
void on_vmInterpreter_triggered();
void on_vmJIT_triggered();
void on_vmSmart_triggered();
void on_rewindChain_triggered();
// Debugger
void on_debugCurrent_triggered();
@ -196,6 +200,7 @@ private slots:
// Config
void on_gasPrices_triggered();
void on_sentinel_triggered();
void refreshWhisper();
void refreshBlockChain();
@ -271,6 +276,8 @@ private:
dev::Address m_nameReg;
dev::Address m_beneficiary;
QActionGroup* m_vmSelectionGroup = nullptr;
QList<QPair<QString, QString>> m_consoleHistory;
QMutex m_logLock;
QString m_logHistory;
@ -286,6 +293,6 @@ private:
std::unique_ptr<DappHost> m_dappHost;
DappLoader* m_dappLoader;
QWebEnginePage* m_webPage;
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!");
}
void OurAccountHolder::authenticate(TransactionSkeleton const& _t)
h256 OurAccountHolder::authenticate(TransactionSkeleton const& _t)
{
Guard l(x_queued);
m_queued.push(_t);
return h256();
}
void OurAccountHolder::doValidations()
@ -130,7 +131,7 @@ void OurAccountHolder::doValidations()
else
// sign and submit.
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();
virtual dev::AddressHash realAccounts() const override;
// 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:
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)
# disable empty object file warning
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
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
if (NOT ETH_STATIC)

28
cmake/Findv8.cmake

@ -31,12 +31,32 @@ set(V8_LIBRARIES ${V8_LIBRARY})
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
find_library(
V8_LIBRARY_DEBUG
NAMES v8d
DOC "v8 debug library"
V8_LIBRARY
NAMES v8_base
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()

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")
add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)

171
ethkey/KeyAux.h

@ -32,6 +32,7 @@
#include <libdevcore/FileSystem.h>
#include <libethcore/KeyManager.h>
#include <libethcore/ICAP.h>
#include <libethcore/Transaction.h>
#include "BuildInfo.h"
using namespace std;
using namespace dev;
@ -105,7 +106,9 @@ public:
ImportWithAddress,
Export,
Recode,
Kill
Kill,
SignTx,
DecodeTx,
};
KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {}
@ -131,8 +134,13 @@ public:
auto v = argv[++i];
m_kdfParams[n] = v;
}
else if (arg == "--new-bare")
m_mode = OperationMode::NewBare;
else if (arg == "--sign-tx" && i + 1 < argc)
{
m_mode = OperationMode::SignTx;
m_signKey = argv[++i];
}
else if (arg == "--decode-tx")
m_mode = OperationMode::DecodeTx;
else if (arg == "--import-bare")
m_mode = OperationMode::ImportBare;
else if (arg == "--list-bare")
@ -173,7 +181,7 @@ public:
m_mode = OperationMode::Recode;
else if (arg == "--no-icap")
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);
else
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)
{
SecretStore store(m_secretsPath);
@ -297,17 +426,7 @@ public:
{
KeyManager wallet(m_walletPath, m_secretsPath);
if (wallet.exists())
while (true)
{
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: ");
}
openWallet(wallet);
else
{
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
<< " -m, --master <password> Specify wallet (master) password." << 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
<< " --kdf <kdfname> Specify KDF to use when encrypting (default: sc rypt)" << endl
<< " --kdf-param <name> <value> Specify a parameter for the KDF." << endl
@ -445,6 +568,21 @@ public:
}
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; }
/// Operating mode.
@ -468,6 +606,9 @@ private:
/// Importing
strings m_inputs;
/// Signing
string m_signKey;
string m_kdf = "scrypt";
map<string, string> m_kdfParams;
// string m_cipher;

4
ethminer/CMakeLists.txt

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

77
ethminer/MinerAux.h

@ -40,6 +40,9 @@
#include <libethcore/ProofOfWork.h>
#include <libethcore/EthashAux.h>
#include <libethcore/Farm.h>
#if ETH_ETHASHCL || !ETH_TRUE
#include <libethash-cl/ethash_cl_miner.h>
#endif
#if ETH_JSONRPC || !ETH_TRUE
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
@ -78,6 +81,12 @@ inline std::string credits()
}
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
{
@ -128,6 +137,35 @@ public:
cerr << "Bad " << arg << " option: " << argv[i] << endl;
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")
m_shouldListDevices = true;
else if (arg == "--allow-opencl-cpu")
@ -217,7 +255,7 @@ public:
auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i]));
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;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
@ -265,18 +303,23 @@ public:
ProofOfWork::CPUMiner::setNumInstances(m_miningThreads);
else if (m_minerType == MinerType::GPU)
{
#if ETH_ETHASHCL || !ETH_TRUE
if (!ProofOfWork::GPUMiner::configureGPU(
m_localWorkSize,
m_globalWorkSizeMultiplier,
m_msPerBatch,
m_openclPlatform,
m_openclDevice,
m_clAllowCPU,
m_extraGPUMemory,
m_currentBlock
))
{
cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl;
exit(1);
}
ProofOfWork::GPUMiner::setNumInstances(m_miningThreads);
#else
cerr << "Selected GPU mining without having compiled with -DETHASHCL=1" << endl;
exit(1);
#endif
}
if (mode == OperationMode::DAGInit)
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
<< " --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
#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-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)
{
if (current)
cnote << "Mining on PoWhash" << current.headerHash << ": " << f.miningProgress();
minelog << "Mining on PoWhash" << current.headerHash << ": " << f.miningProgress();
else
cnote << "Getting work package...";
minelog << "Getting work package...";
Json::Value v = rpc.eth_getWork();
h256 hh(v[0].asString());
h256 newSeedHash(v[1].asString());
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; })))
BOOST_THROW_EXCEPTION(DAGCreationFailure());
if (m_precompute)
@ -459,10 +507,10 @@ private:
current.headerHash = hh;
current.seedHash = newSeedHash;
current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight);
cnote << "Got work package:";
cnote << " Header-hash:" << current.headerHash.hex();
cnote << " Seedhash:" << current.seedHash.hex();
cnote << " Target: " << h256(current.boundary).hex();
minelog << "Got work package:";
minelog << " Header-hash:" << current.headerHash.hex();
minelog << " Seedhash:" << current.seedHash.hex();
minelog << " Target: " << h256(current.boundary).hex();
f.setWork(current);
}
this_thread::sleep_for(chrono::milliseconds(_recheckPeriod));
@ -506,7 +554,12 @@ private:
unsigned m_miningThreads = UINT_MAX;
bool m_shouldListDevices = 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.)
unsigned m_extraGPUMemory = 350000000;

22
ethvm/main.cpp

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

4
evmjit/CMakeLists.txt

@ -29,7 +29,3 @@ add_subdirectory(libevmjit)
if(EVMJIT_CPP)
add_subdirectory(libevmjit-cpp)
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
#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 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
{
@ -23,14 +151,26 @@ public:
/// Returns `true` if the EVM code has been compiled and loaded into memory.
/// In this case the code can be executed without overhead.
/// \param _codeHash The Keccak hash of the EVM code.
static bool isCodeReady(h256 _codeHash);
EXPORT static bool isCodeReady(h256 const& _codeHash);
private:
friend class dev::eth::jit::ExecutionEngine;
/// Compile the given EVM code to machine code and make available for execution.
EXPORT static void compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash);
static uint64_t getCode(h256 _codeHash);
static void mapCode(h256 _codeHash, uint64_t _funcAddr);
EXPORT static ReturnCode exec(ExecutionContext& _context);
};
}
}
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 <libevmcore/Params.h>
#include <libevm/ExtVMFace.h>
#include <evmjit/DataTypes.h>
#include "Utils.h"
@ -21,15 +20,15 @@ extern "C"
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
*o_value = eth2llvm(value);
*o_value = eth2jit(value);
}
EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value)
{
auto index = llvm2eth(*_index);
auto value = llvm2eth(*_value);
auto index = jit2eth(*_index);
auto value = jit2eth(*_value);
if (value == 0 && _env->store(index) != 0) // If delete
_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)
{
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)
{
*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)
{
auto endowment = llvm2eth(*_endowment);
auto endowment = jit2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
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)
{
CallParameters params;
params.value = llvm2eth(*_value);
params.value = jit2eth(*_value);
params.senderAddress = _env->myAddress;
params.receiveAddress = right160(*_receiveAddress);
params.codeAddress = right160(*_codeAddress);

41
evmjit/libevmjit-cpp/JitVM.cpp

@ -7,7 +7,6 @@
#include <libdevcore/SHA3.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <evmjit/libevmjit/ExecutionEngine.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)
{
using namespace jit;
auto rejected = false;
// 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)
@ -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.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size();
m_data.address = eth2llvm(fromAddress(_ext.myAddress));
m_data.caller = eth2llvm(fromAddress(_ext.caller));
m_data.origin = eth2llvm(fromAddress(_ext.origin));
m_data.callValue = eth2llvm(_ext.value);
m_data.coinBase = eth2llvm(fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.difficulty = eth2llvm(_ext.currentBlock.difficulty);
m_data.gasLimit = eth2llvm(_ext.currentBlock.gasLimit);
m_data.address = eth2jit(fromAddress(_ext.myAddress));
m_data.caller = eth2jit(fromAddress(_ext.caller));
m_data.origin = eth2jit(fromAddress(_ext.origin));
m_data.callValue = eth2jit(_ext.value);
m_data.coinBase = eth2jit(fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.difficulty = eth2jit(_ext.currentBlock.difficulty);
m_data.gasLimit = eth2jit(_ext.currentBlock.gasLimit);
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.code = _ext.code.data();
m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2llvm(_ext.codeHash);
m_data.codeHash = eth2jit(_ext.codeHash);
auto env = reinterpret_cast<Env*>(&_ext);
auto exitCode = m_engine.run(&m_data, env);
// Pass pointer to ExtVMFace casted to evmjit::Env* opaque type.
// 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)
{
case ReturnCode::Suicide:
_ext.suicide(right160(llvm2eth(m_data.address)));
case evmjit::ReturnCode::Suicide:
_ext.suicide(right160(jit2eth(m_data.address)));
break;
case ReturnCode::BadJumpDestination:
case evmjit::ReturnCode::BadJumpDestination:
BOOST_THROW_EXCEPTION(BadJumpDestination());
case ReturnCode::OutOfGas:
case evmjit::ReturnCode::OutOfGas:
BOOST_THROW_EXCEPTION(OutOfGas());
case ReturnCode::StackUnderflow:
case evmjit::ReturnCode::StackUnderflow: // FIXME: Remove support for detail errors
BOOST_THROW_EXCEPTION(StackUnderflow());
case ReturnCode::BadInstruction:
case evmjit::ReturnCode::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
break;
default:
@ -77,7 +76,7 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
}
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
#include <libevm/VMFace.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
#include <evmjit/JIT.h>
namespace dev
{
@ -14,8 +14,8 @@ public:
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final;
private:
jit::RuntimeData m_data;
jit::ExecutionEngine m_engine;
evmjit::RuntimeData m_data;
evmjit::ExecutionContext m_context;
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
#include <evmjit/DataTypes.h>
#include <evmjit/JIT.h>
namespace dev
{
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;
u |= _i.d;
u256 u = _i.words[3];
u <<= 64;
u |= _i.c;
u |= _i.words[2];
u <<= 64;
u |= _i.b;
u |= _i.words[1];
u <<= 64;
u |= _i.a;
u |= _i.words[0];
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;
u256 mask = 0xFFFFFFFFFFFFFFFF;
i.a = static_cast<uint64_t>(_u & mask);
i.words[0] = static_cast<uint64_t>(_u);
_u >>= 64;
i.b = static_cast<uint64_t>(_u & mask);
i.words[1] = static_cast<uint64_t>(_u);
_u >>= 64;
i.c = static_cast<uint64_t>(_u & mask);
i.words[2] = static_cast<uint64_t>(_u);
_u >>= 64;
i.d = static_cast<uint64_t>(_u & mask);
i.words[3] = static_cast<uint64_t>(_u);
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
return *(evmjit::h256*)&_u;

674
evmjit/libevmjit/Arith256.cpp

@ -4,6 +4,7 @@
#include <iomanip>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.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)});
}
llvm::Function* Arith256::getMulFunc()
llvm::Function* Arith256::getMulFunc(llvm::Module& _module)
{
auto& func = m_mul;
if (!func)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
static const auto funcName = "evm.mul.i256";
if (auto func = _module.getFunction(funcName))
return func;
llvm::Type* argTypes[] = {Type::Word, Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, 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 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();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
llvm::Function* Arith256::getMul512Func(llvm::Module& _module)
{
static const auto funcName = "evm.mul.i512";
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;
}
llvm::Function* Arith256::getMul512Func()
namespace
{
auto& func = m_mul512;
if (!func)
{
auto i512 = m_builder.getIntNTy(512);
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName)
{
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder
auto retType = llvm::VectorType::get(_type, 2);
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
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();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
return func;
}
}
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;
}
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)
{
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder
auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
llvm::Type* argTypes[] = {_type, _type};
auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef<llvm::Type*>{argTypes});
auto funcName = _type == Type::Word ? "div" : "div512";
func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto zero = llvm::ConstantInt::get(_type, 0);
auto one = llvm::ConstantInt::get(_type, 1);
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
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();
x->setName("x");
auto yArg = x->getNextNode();
yArg->setName("y");
return func;
}
}
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);
auto mainBB = llvm::BasicBlock::Create(m_builder.getContext(), "Main", func);
auto loopBB = llvm::BasicBlock::Create(m_builder.getContext(), "Loop", func);
auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
llvm::Function* Arith256::getURem512Func(llvm::Module& _module)
{
static const auto funcName = "evm.urem.i512";
if (auto func = _module.getFunction(funcName))
return func;
return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName);
}
m_builder.SetInsertPoint(entryBB);
auto yNonZero = m_builder.CreateICmpNE(yArg, zero);
auto yLEx = m_builder.CreateICmpULE(yArg, x);
auto r0 = m_builder.CreateSelect(yNonZero, x, zero, "r0");
m_builder.CreateCondBr(m_builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB);
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);
llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.sdivrem.i256";
if (auto func = _module.getFunction(funcName))
return func;
m_builder.SetInsertPoint(continueBB);
auto i2 = m_builder.CreateNUWSub(iPhi, one);
auto q2 = m_builder.CreateShl(q1, one);
auto y2 = m_builder.CreateLShr(yPhi, one);
m_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);
auto udivremFunc = getUDivRem256Func(_module);
auto retType = llvm::VectorType::get(Type::Word, 2);
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {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 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;
}
@ -260,14 +439,15 @@ llvm::Function* Arith256::getExpFunc()
m_builder.CreateCondBr(eOdd, updateBB, continueBB);
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.SetInsertPoint(continueBB);
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1");
r1->addIncoming(r, bodyBB);
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");
m_builder.CreateBr(headerBB);
@ -284,137 +464,6 @@ llvm::Function* Arith256::getExpFunc()
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)
{
// while (e != 0) {
@ -445,49 +494,6 @@ llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _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:
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* 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);
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:
llvm::Function* getMulFunc();
llvm::Function* getMul512Func();
llvm::Function* getDivFunc(llvm::Type* _type);
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_addmod = nullptr;
llvm::Function* m_mulmod = nullptr;
llvm::Function* m_debug = nullptr;
};

3
evmjit/libevmjit/Array.cpp

@ -6,7 +6,6 @@
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Runtime.h"
#include "Utils.h"
namespace dev
@ -17,7 +16,6 @@ namespace jit
{
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)
{
@ -56,7 +54,6 @@ llvm::Function* Array::createArrayPushFunc()
m_builder.SetInsertPoint(reallocBB);
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 bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes");
auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes");

2
evmjit/libevmjit/BasicBlock.h

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

8
evmjit/libevmjit/CMakeLists.txt

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

27
evmjit/libevmjit/Cache.cpp

@ -12,15 +12,13 @@
#include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "ExecutionEngine.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev
{
namespace eth
{
namespace jit
namespace evmjit
{
namespace
@ -29,7 +27,7 @@ namespace
std::mutex x_cacheMutex;
CacheMode g_mode;
std::unique_ptr<llvm::MemoryBuffer> g_lastObject;
ExecutionEngineListener* g_listener;
JITListener* g_listener;
static const size_t c_versionStampLength = 32;
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};
g_mode = _mode;
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()
@ -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 eth
namespace evmjit
{
namespace jit
{
class ExecutionEngineListener;
class JITListener;
enum class CacheMode
{
@ -47,7 +45,7 @@ public:
class Cache
{
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);
/// Clears cache storage
@ -59,4 +57,3 @@ public:
}
}
}

39
evmjit/libevmjit/Common.h

@ -1,53 +1,16 @@
#pragma once
#include <tuple>
#include <cstdint>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev
{
namespace eth
{
namespace jit
namespace evmjit
{
using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
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)
}
}
}

152
evmjit/libevmjit/Compiler.cpp

@ -49,6 +49,11 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
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
bool nextJumpDest = false;
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
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();
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)
{
@ -178,7 +183,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
m_builder.SetInsertPoint(m_stopBB);
runtimeManager.exit(ReturnCode::Stop);
m_builder.SetInsertPoint(abortBB);
m_builder.SetInsertPoint(m_abortBB);
runtimeManager.exit(ReturnCode::OutOfGas);
removeDeadBlocks();
@ -270,44 +275,96 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.mul(lhs, rhs);
auto res = m_builder.CreateMul(lhs, rhs);
stack.push(res);
break;
}
case Instruction::DIV:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.div(lhs, rhs);
stack.push(res.first);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
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;
}
case Instruction::SDIV:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.sdiv(lhs, rhs);
stack.push(res.first);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
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;
}
case Instruction::MOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.div(lhs, rhs);
stack.push(res.second);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
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;
}
case Instruction::SMOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.sdiv(lhs, rhs);
stack.push(res.second);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
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;
}
@ -417,48 +474,29 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::BYTE:
{
const auto byteNum = stack.pop();
auto value = stack.pop();
const auto idx = 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 safeByteNum = m_builder.CreateZExt(m_builder.CreateTrunc(byteNum, m_builder.getIntNTy(5)), Type::lowPrecision); // Trim index, large values can crash
auto byte = m_builder.CreateExtractElement(bytes, safeByteNum, "byte");
// TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access.
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);
auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32));
value = m_builder.CreateSelect(byteNumValid, value, Constant::get(0));
value = m_builder.CreateSelect(idxValid, value, Constant::get(0));
stack.push(value);
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:
{
auto idx = stack.pop();
auto word = stack.pop();
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");
// test for word >> (k * 8 + 7)
@ -495,11 +533,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::POP:
{
auto val = 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");
stack.pop();
break;
}
@ -656,7 +690,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
}
case Instruction::CODESIZE:
// TODO: Use constant
stack.push(_runtimeManager.getCodeSize());
break;
@ -729,8 +762,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::CALLDATALOAD:
{
auto index = stack.pop();
auto value = _ext.calldataload(index);
auto idx = stack.pop();
auto value = _ext.calldataload(idx);
stack.push(value);
break;
}
@ -797,7 +830,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::STOP:
{
m_builder.CreateRet(Constant::get(ReturnCode::Stop));
m_builder.CreateBr(m_stopBB);
break;
}
@ -824,7 +857,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
}
default: // Invalid instruction - abort
m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction));
m_builder.CreateBr(m_abortBB);
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
#include "Common.h"
#include "BasicBlock.h"
namespace dev
@ -65,6 +64,9 @@ private:
/// Stop basic block - terminates execution with STOP code (0)
llvm::BasicBlock* m_stopBB = nullptr;
/// Abort basic block - terminates execution with OOG-like state
llvm::BasicBlock* m_abortBB = nullptr;
/// Block with a jump table.
std::unique_ptr<BasicBlock> m_jumpTableBlock;

25
evmjit/libevmjit/CompilerHelper.h

@ -37,7 +37,6 @@ protected:
friend class RuntimeHelper;
};
/// Compiler helper that depends on runtime data
class RuntimeHelper : public CompilerHelper
{
@ -50,29 +49,7 @@ private:
RuntimeManager& m_runtimeManager;
};
/// 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;
};
using InsertPointGuard = llvm::IRBuilderBase::InsertPointGuard;
}
}

5
evmjit/libevmjit/ExecStats.cpp

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

35
evmjit/libevmjit/ExecStats.h

@ -1,19 +1,43 @@
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <chrono>
#include "ExecutionEngine.h"
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:
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_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
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;
@ -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
}
llvm::Value* Ext::calldataload(llvm::Value* _index)
llvm::Value* Ext::calldataload(llvm::Value* _idx)
{
auto ret = getArgAlloca();
createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret});
ret = m_builder.CreateLoad(ret);
return Endianness::toNative(m_builder, ret);
auto result = m_builder.CreateBitCast(ret, Type::BytePtr);
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)

2
evmjit/libevmjit/Ext.h

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

1
evmjit/libevmjit/GasMeter.h

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

5
evmjit/libevmjit/Instruction.cpp

@ -6,9 +6,7 @@
namespace dev
{
namespace eth
{
namespace jit
namespace evmjit
{
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 eth
{
namespace jit
namespace evmjit
{
/// 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 <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 evmjit
{
using namespace eth::jit;
namespace
{
using ExecFunc = ReturnCode(*)(ExecutionContext*);
class JITImpl: JIT
std::string hash2str(i256 const& _hash)
{
public:
std::unordered_map<h256, uint64_t> codeMap;
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");
}
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 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
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 it = codeMap.find(_codeHash);
if (it != codeMap.end())
return it->second;
return 0;
auto& jit = JITImpl::instance();
auto execFunc = jit.compile(_code, _codeSize, _codeHash);
if (execFunc) // FIXME: What with error?
jit.mapExecFunc(_codeHash, execFunc);
}
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 "Type.h"
#include "Runtime.h"
#include "GasMeter.h"
#include "Endianness.h"
#include "RuntimeManager.h"
@ -191,8 +190,7 @@ llvm::Value* Memory::getSize()
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(), idx, "ptr");
return m_builder.CreateGEP(getData(), _index, "ptr");
}
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 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 dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx);
auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx);

97
evmjit/libevmjit/Optimizer.cpp

@ -1,11 +1,16 @@
#include "Optimizer.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/IPO.h>
#include "preprocessor/llvm_includes_end.h"
#include "Arith256.h"
#include "Type.h"
namespace dev
{
namespace eth
@ -16,7 +21,7 @@ namespace jit
bool optimize(llvm::Module& _module)
{
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::createInstructionCombiningPass());
pm.add(llvm::createAggressiveDCEPass());
@ -24,6 +29,96 @@ bool optimize(llvm::Module& _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 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)
{
default: return "data";
case RuntimeData::Address: return "address";
case RuntimeData::Caller: return "caller";
case RuntimeData::Origin: return "origin";
case RuntimeData::CallValue: return "callvalue";
case RuntimeData::GasPrice: return "gasprice";
case RuntimeData::CoinBase: return "coinbase";
case RuntimeData::Difficulty: return "difficulty";
case RuntimeData::GasLimit: return "gaslimit";
case RuntimeData::CallData: return "callData";
case RuntimeData::Code: return "code";
case RuntimeData::CodeSize: return "code";
case RuntimeData::CallDataSize: return "callDataSize";
case RuntimeData::Gas: return "gas";
case RuntimeData::Number: return "number";
case RuntimeData::Timestamp: return "timestamp";
default: return "";
case RuntimeData::Gas: return "msg.gas";
case RuntimeData::GasPrice: return "tx.gasprice";
case RuntimeData::CallData: return "msg.data.ptr";
case RuntimeData::CallDataSize: return "msg.data.size";
case RuntimeData::Address: return "this.address";
case RuntimeData::Caller: return "msg.caller";
case RuntimeData::Origin: return "tx.origin";
case RuntimeData::CallValue: return "msg.value";
case RuntimeData::CoinBase: return "block.coinbase";
case RuntimeData::Difficulty: return "block.difficulty";
case RuntimeData::GasLimit: return "block.gaslimit";
case RuntimeData::Number: return "block.number";
case RuntimeData::Timestamp: return "block.timestamp";
case RuntimeData::Code: return "code.ptr";
case RuntimeData::CodeSize: return "code.size";
}
}
}
@ -93,10 +93,8 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
// Unpack data
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);
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");
assert(m_memPtr->getType() == Array::getType()->getPointerTo());
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_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};
m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule());
m_checkStackLimit->setDoesNotThrow();
@ -180,7 +185,7 @@ llvm::Value* RuntimeManager::getPtr(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)
@ -194,8 +199,7 @@ void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size
{
auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo());
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, idx);
auto returnDataPtr = getBuilder().CreateGEP(mem, _offset);
set(RuntimeData::ReturnData, returnDataPtr);
auto size64 = getBuilder().CreateTrunc(_size, Type::Size);
@ -212,6 +216,8 @@ void RuntimeManager::exit(ReturnCode _returnCode)
if (m_stack)
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));
}
@ -265,9 +271,7 @@ llvm::Value* RuntimeManager::getCallDataSize()
llvm::Value* RuntimeManager::getGas()
{
auto gas = get(RuntimeData::Gas);
assert(gas->getType() == Type::Gas);
return gas;
return getBuilder().CreateLoad(getGasPtr(), "gas");
}
llvm::Value* RuntimeManager::getGasPtr()
@ -285,7 +289,7 @@ llvm::Value* RuntimeManager::getMem()
void RuntimeManager::setGas(llvm::Value* _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
#include <array>
#include "CompilerHelper.h"
#include "Type.h"
#include "RuntimeData.h"
#include "Instruction.h"
namespace dev
@ -11,6 +12,7 @@ namespace eth
{
namespace jit
{
using namespace evmjit;
class Stack;
class RuntimeManager: public CompilerHelper
@ -61,6 +63,8 @@ private:
llvm::Value* m_memPtr = nullptr;
llvm::Value* m_envPtr = nullptr;
std::array<llvm::Value*, RuntimeData::numElements> m_dataElts;
llvm::Value* m_stackSize = nullptr;
llvm::Function* m_checkStackLimit = nullptr;

120
evmjit/libevmjit/Stack.cpp

@ -5,7 +5,6 @@
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Runtime.h"
#include "Utils.h"
#include <set> // DEBUG only
@ -23,96 +22,6 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
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()
{
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(); }
private:
llvm::Function* getPopFunc();
llvm::Function* getPushFunc();
llvm::Function* getGetFunc();
llvm::Function* getSetFunc();
RuntimeManager& m_runtimeManager;
llvm::Function* m_pop = nullptr;
llvm::Function* m_push = nullptr;
llvm::Function* m_get = nullptr;
llvm::Function* m_set = nullptr;
Array m_stack;
};

3
evmjit/libevmjit/Type.cpp

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

9
evmjit/libevmjit/Type.h

@ -4,9 +4,9 @@
#include <llvm/IR/Type.h>
#include <llvm/IR/Constants.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
{
@ -14,16 +14,13 @@ namespace eth
{
namespace jit
{
using namespace evmjit;
struct Type
{
static llvm::IntegerType* Word;
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* Size;
static llvm::IntegerType* Gas;

25
evmjit/libevmjit/interface.cpp

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

12
exp/CMakeLists.txt

@ -6,6 +6,9 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${DB_INCLUDE_DIRS})
if (ETHASHCL)
include_directories(${OpenCL_INCLUDE_DIRS})
endif ()
set(EXECUTABLE exp)
@ -27,11 +30,6 @@ target_link_libraries(${EXECUTABLE} p2p)
if (ETHASHCL)
target_link_libraries(${EXECUTABLE} ethash-cl)
target_link_libraries(${EXECUTABLE} ethash)
target_link_libraries(${EXECUTABLE} OpenCL)
target_link_libraries(${EXECUTABLE} ${OpenCL_LIBRARIES})
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 s1(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((s1.nextFetch(2) == h256Set{(u256)5, (u256)6}));
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 qt 5.4.1
call :download miniupnpc 1.9
call :download v8 3.15.9
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.
#include "Base64.h"
using namespace std;
using namespace dev;
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('/');
}
std::string dev::toBase64(bytesConstRef _in)
string dev::toBase64(bytesConstRef _in)
{
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string ret;
string ret;
int i = 0;
int j = 0;
byte char_array_3[3];
@ -60,15 +62,17 @@ std::string dev::toBase64(bytesConstRef _in)
auto buf = _in.data();
auto bufLen = _in.size();
while (bufLen--) {
while (bufLen--)
{
char_array_3[i++] = *(buf++);
if (i == 3) {
if (i == 3)
{
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[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
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]];
i = 0;
}
@ -76,7 +80,7 @@ std::string dev::toBase64(bytesConstRef _in)
if (i)
{
for(j = i; j < 3; j++)
for (j = i; j < 3; j++)
char_array_3[j] = '\0';
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[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]];
while((i++ < 3))
while (i++ < 3)
ret += '=';
}
return ret;
}
bytes dev::fromBase64(std::string const& encoded_string)
bytes dev::fromBase64(string const& encoded_string)
{
auto in_len = encoded_string.size();
int i = 0;
int j = 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;
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_++;
if (i == 4) {
if (i == 4)
{
for (i = 0; i < 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++)
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[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;

11
libdevcore/Common.cpp

@ -28,14 +28,17 @@ using namespace dev;
namespace dev
{
char const* Version = "0.9.27";
char const* Version = "0.9.29";
const u256 UndefinedU256 = ~(u256)0;
void HasInvariants::checkInvariants() const
void InvariantChecker::checkInvariants() const
{
if (!invariants())
BOOST_THROW_EXCEPTION(FailedInvariant());
if (!m_this->invariants())
{
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; };

20
libdevcore/Common.h

@ -41,6 +41,7 @@
#include <functional>
#include <string>
#include <chrono>
#include <boost/current_function.hpp>
#include <boost/functional/hash.hpp>
#pragma warning(push)
#pragma GCC diagnostic push
@ -63,6 +64,8 @@ using byte = uint8_t;
#define DEV_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
#define DEV_IF_NO_ELSE(X) if(!(X)){}else
namespace dev
{
@ -163,10 +166,6 @@ private:
class HasInvariants
{
public:
/// Check invariants are met, throw if not.
void checkInvariants() const;
protected:
/// Reimplement to specify the invariants.
virtual bool invariants() const = 0;
};
@ -175,16 +174,22 @@ protected:
class InvariantChecker
{
public:
InvariantChecker(HasInvariants* _this): m_this(_this) { m_this->checkInvariants(); }
~InvariantChecker() { 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() { checkInvariants(); }
private:
/// Check invariants are met, throw if not.
void checkInvariants() const;
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.
#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
#define DEV_INVARIANT_CHECK (void)0;
#endif
@ -235,6 +240,7 @@ enum class WithExisting: int
{
Trust = 0,
Verify,
Rescue,
Kill
};

99
libdevcore/CommonData.h

@ -50,8 +50,8 @@ enum class HexPrefix
/// 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.
/// @example toHex("A\x69") == "4169"
template <class _T>
std::string toHex(_T const& _data, int _w = 2, HexPrefix _prefix = HexPrefix::DontAdd)
template <class T>
std::string toHex(T const& _data, int _w = 2, HexPrefix _prefix = HexPrefix::DontAdd)
{
std::ostringstream ret;
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.
/// 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.
/// @a _Out will typically be either std::string or bytes.
/// @a _T will typically by unsigned, u160, u256 or bigint.
template <class _T, class _Out>
inline void toBigEndian(_T _val, _Out& o_out)
/// @a Out will typically be either std::string or bytes.
/// @a T will typically by unsigned, u160, u256 or bigint.
template <class T, class Out>
inline void toBigEndian(T _val, Out& o_out)
{
for (auto i = o_out.size(); i != 0; _val >>= 8, i--)
{
_T v = _val & (_T)0xff;
o_out[i - 1] = (typename _Out::value_type)(uint8_t)v;
T v = _val & (T)0xff;
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.
/// @a _In will typically be either std::string or bytes.
/// @a _T will typically by unsigned, u160, u256 or bigint.
template <class _T, class _In>
inline _T fromBigEndian(_In const& _bytes)
/// @a T will typically by unsigned, u160, u256 or bigint.
template <class T, class _In>
inline T fromBigEndian(_In const& _bytes)
{
_T ret = (_T)0;
T ret = (T)0;
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;
}
@ -131,11 +131,11 @@ inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); ret
/// Convenience function for toBigEndian.
/// @returns a byte array just big enough to represent @a _val.
template <class _T>
inline bytes toCompactBigEndian(_T _val, unsigned _min = 0)
template <class T>
inline bytes toCompactBigEndian(T _val, unsigned _min = 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);
toBigEndian(_val, ret);
return ret;
@ -147,11 +147,11 @@ inline bytes toCompactBigEndian(byte _val, unsigned _min = 0)
/// Convenience function for toBigEndian.
/// @returns a string just big enough to represent @a _val.
template <class _T>
inline std::string toCompactBigEndianString(_T _val, unsigned _min = 0)
template <class T>
inline std::string toCompactBigEndianString(T _val, unsigned _min = 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');
toBigEndian(_val, 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.
/// @returns the number of elements both @a _t and @a _u share, in order, at the beginning.
/// @example commonPrefix("Hello world!", "Hello, world!") == 5
template <class _T, class _U>
unsigned commonPrefix(_T const& _t, _U const& _u)
template <class T, class _U>
unsigned commonPrefix(T const& _t, _U const& _u)
{
unsigned s = std::min<unsigned>(_t.size(), _u.size());
for (unsigned i = 0;; ++i)
@ -196,8 +196,8 @@ std::string randomWord();
// General datatype convenience functions.
/// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero.
template <class _T>
inline unsigned bytesRequired(_T _i)
template <class T>
inline unsigned bytesRequired(T _i)
{
unsigned i = 0;
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.
/// Only works for POD element types.
template <class _T>
void trimFront(_T& _t, unsigned _elements)
template <class T>
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]));
_t.resize(_t.size() - _elements);
}
/// Pushes an element on to the front of a collection.
/// Only works for POD element types.
template <class _T, class _U>
void pushFront(_T& _t, _U _e)
template <class T, class _U>
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);
memmove(_t.data() + 1, _t.data(), (_t.size() - 1) * sizeof(_e));
_t[0] = _e;
}
/// Concatenate two vectors of elements of POD types.
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)
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)
{
auto s = _a.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;
}
/// Concatenate two vectors of elements.
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)
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)
{
_a.reserve(_a.size() + _b.size());
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.
template <class _T>
inline std::vector<_T> operator+(std::vector<_T> const& _a, std::vector<_T> const& _b)
template <class T>
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;
}
/// Merge two sets of elements.
template <class _T>
inline std::set<_T>& operator+=(std::set<_T>& _a, std::set<_T> const& _b)
template <class T>
inline std::set<T>& operator+=(std::set<T>& _a, std::set<T> const& _b)
{
for (auto& i: _b)
_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.
template <class _T>
inline std::set<_T> operator+(std::set<_T> const& _a, std::set<_T> const& _b)
template <class T>
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;
}
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.
std::string toString(string32 const& _s);

29
libdevcore/FixedHash.h

@ -24,6 +24,7 @@
#pragma once
#include <array>
#include <cstdint>
#include <random>
#include <algorithm>
#include "CommonData.h"
@ -31,6 +32,10 @@
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;
/// 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); }
/// 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.
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~() 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; }
/// @returns a particular byte from the hash.
@ -146,7 +154,7 @@ public:
{
FixedHash ret;
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;
}
@ -171,18 +179,21 @@ public:
template <unsigned P, unsigned M> inline FixedHash<M> bloomPart() const
{
static_assert((M & (M - 1)) == 0, "M must be power-of-two");
static const unsigned c_bloomBits = M * 8;
unsigned mask = c_bloomBits - 1;
unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8;
unsigned const c_bloomBits = M * 8;
unsigned const c_mask = c_bloomBits - 1;
unsigned const c_bloomBytes = (StaticLog2<c_bloomBits>::result + 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;
byte const* p = data();
for (unsigned i = 0; i < P; ++i)
{
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 &= mask;
index &= c_mask;
ret[M - 1 - index / 8] |= (1 << (index % 8));
}
return ret;

2
libdevcore/Guards.h

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

84
libdevcore/RLP.cpp

@ -49,12 +49,12 @@ RLP::iterator& RLP::iterator::operator++()
{
if (m_remaining)
{
m_lastItem.retarget(m_lastItem.next().data(), m_remaining);
m_lastItem = m_lastItem.cropped(0, RLP(m_lastItem, ThrowOnFail | FailIfTooSmall).actualSize());
m_remaining -= std::min<unsigned>(m_remaining, m_lastItem.size());
m_currentItem.retarget(m_currentItem.next().data(), m_remaining);
m_currentItem = m_currentItem.cropped(0, sizeAsEncoded(m_currentItem));
m_remaining -= std::min<size_t>(m_remaining, m_currentItem.size());
}
else
m_lastItem.retarget(m_lastItem.next().data(), 0);
m_currentItem.retarget(m_currentItem.next().data(), 0);
return *this;
}
@ -63,28 +63,28 @@ RLP::iterator::iterator(RLP const& _parent, bool _begin)
if (_begin && _parent.isList())
{
auto pl = _parent.payload();
m_lastItem = pl.cropped(0, RLP(pl, ThrowOnFail | FailIfTooSmall).actualSize());
m_remaining = pl.size() - m_lastItem.size();
m_currentItem = pl.cropped(0, sizeAsEncoded(pl));
m_remaining = pl.size() - m_currentItem.size();
}
else
{
m_lastItem = _parent.data().cropped(_parent.data().size());
m_currentItem = _parent.data().cropped(_parent.data().size());
m_remaining = 0;
}
}
RLP RLP::operator[](unsigned _i) const
RLP RLP::operator[](size_t _i) const
{
if (_i < m_lastIndex)
{
m_lastEnd = RLP(payload(), ThrowOnFail | FailIfTooSmall).actualSize();
m_lastEnd = sizeAsEncoded(payload());
m_lastItem = payload().cropped(0, m_lastEnd);
m_lastIndex = 0;
}
for (; m_lastIndex < _i && m_lastItem.size(); ++m_lastIndex)
{
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();
}
return RLP(m_lastItem, ThrowOnFail | FailIfTooSmall);
@ -100,7 +100,7 @@ RLPs RLP::toList() const
return ret;
}
unsigned RLP::actualSize() const
size_t RLP::actualSize() const
{
if (isNull())
return 0;
@ -142,7 +142,7 @@ bool RLP::isInt() const
}
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());
return m_data[1 + n - c_rlpDataIndLenZero] != 0;
}
@ -151,63 +151,75 @@ bool RLP::isInt() const
return false;
}
unsigned RLP::length() const
size_t RLP::length() const
{
if (isNull())
return 0;
requireGood();
unsigned ret = 0;
byte n = m_data[0];
size_t ret = 0;
byte const n = m_data[0];
if (n < c_rlpDataImmLenStart)
return 1;
else if (n <= c_rlpDataIndLenZero)
return n - c_rlpDataImmLenStart;
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());
if ((int)m_data.size() > 1)
if (m_data.size() > 1)
if (m_data[1] == 0)
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];
}
else if (n <= c_rlpListIndLenZero)
return n - c_rlpListStart;
else
{
if ((int)m_data.size() <= n - c_rlpListIndLenZero)
unsigned lengthSize = n - c_rlpListIndLenZero;
if (m_data.size() <= lengthSize)
BOOST_THROW_EXCEPTION(BadRLP());
if ((int)m_data.size() > 1)
if (m_data.size() > 1)
if (m_data[1] == 0)
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];
}
// 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;
}
unsigned RLP::items() const
size_t RLP::items() const
{
if (isList())
{
bytesConstRef d = payload().cropped(0, length());
unsigned i = 0;
size_t i = 0;
for (; d.size(); ++i)
d = d.cropped(RLP(d, ThrowOnFail | FailIfTooSmall).actualSize());
d = d.cropped(sizeAsEncoded(d));
return i;
}
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());
noteAppended(_itemCount);
return *this;
}
void RLPStream::noteAppended(unsigned _itemCount)
void RLPStream::noteAppended(size_t _itemCount)
{
if (!_itemCount)
return;
@ -223,7 +235,7 @@ void RLPStream::noteAppended(unsigned _itemCount)
{
auto p = m_listStack.back().second;
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);
unsigned encodeSize = s < c_rlpListImmLenCount ? 1 : (1 + 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);
if (s < c_rlpListImmLenCount)
m_out[p] = (byte)(c_rlpListStart + s);
else
else if (c_rlpListIndLenZero + brs <= 0xff)
{
m_out[p] = (byte)(c_rlpListIndLenZero + brs);
byte* b = &(m_out[p + brs]);
for (; s; s >>= 8)
*(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.
}
}
RLPStream& RLPStream::appendList(unsigned _items)
RLPStream& RLPStream::appendList(size_t _items)
{
// cdebug << "appendList(" << _items << ")";
if (_items)
@ -266,10 +280,10 @@ RLPStream& RLPStream::appendList(bytesConstRef _rlp)
RLPStream& RLPStream::append(bytesConstRef _s, bool _compact)
{
unsigned s = _s.size();
size_t s = _s.size();
byte const* d = _s.data();
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)
m_out.push_back(*d);
@ -299,6 +313,8 @@ RLPStream& RLPStream::append(bigint _i)
else
{
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));
pushInt(br, brbr);
}
@ -308,9 +324,11 @@ RLPStream& RLPStream::append(bigint _i)
return *this;
}
void RLPStream::pushCount(unsigned _count, byte _base)
void RLPStream::pushCount(size_t _count, byte _base)
{
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.
pushInt(_count, br);
}
@ -320,7 +338,7 @@ std::ostream& dev::operator<<(std::ostream& _out, RLP const& _d)
if (_d.isNull())
_out << "null";
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())
_out << escaped(_d.toString(), false);
else if (_d.isList())

68
libdevcore/RLP.h

@ -71,7 +71,7 @@ public:
FailIfTooSmall = 16,
Strict = ThrowOnFail | FailIfTooBig,
VeryStrict = ThrowOnFail | FailIfTooBig | FailIfTooSmall,
LaisezFaire = AllowNonCanon
LaissezFaire = AllowNonCanon
};
using Strictness = int;
@ -113,12 +113,12 @@ public:
bool isInt() const;
/// @returns the number of items in the list, or zero if it isn't a list.
unsigned itemCount() const { return isList() ? items() : 0; }
unsigned itemCountStrict() const { if (!isList()) BOOST_THROW_EXCEPTION(BadCast()); return items(); }
size_t itemCount() const { return isList() ? items() : 0; }
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.
unsigned size() const { return isData() ? length() : 0; }
unsigned sizeStrict() const { if (!isData()) BOOST_THROW_EXCEPTION(BadCast()); return length(); }
size_t size() const { return isData() ? length() : 0; }
size_t sizeStrict() const { if (!isData()) BOOST_THROW_EXCEPTION(BadCast()); return length(); }
/// Equality operators; does best-effort conversion and checks for equality.
bool operator==(char const* _s) const { return isData() && toString() == _s; }
@ -137,7 +137,7 @@ public:
/// Subscript operator.
/// @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.
RLP operator[](unsigned _i) const;
RLP operator[](size_t _i) const;
using element_type = RLP;
@ -152,16 +152,16 @@ public:
iterator& operator++();
iterator operator++(int) { auto ret = *this; operator++(); return ret; }
RLP operator*() const { return RLP(m_lastItem); }
bool operator==(iterator const& _cmp) const { return m_lastItem == _cmp.m_lastItem; }
RLP operator*() const { return RLP(m_currentItem); }
bool operator==(iterator const& _cmp) const { return m_currentItem == _cmp.m_currentItem; }
bool operator!=(iterator const& _cmp) const { return !operator==(_cmp); }
private:
iterator() {}
iterator(RLP const& _parent, bool _begin);
unsigned m_remaining = 0;
bytesConstRef m_lastItem;
size_t m_remaining = 0;
bytesConstRef m_currentItem;
};
/// @brief Iterator into beginning of sub-item list (valid only if we are a list).
@ -247,7 +247,7 @@ public:
if (itemCount() != N || !isList())
BOOST_THROW_EXCEPTION(BadCast());
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);
}
@ -259,19 +259,21 @@ public:
{
requireGood();
if ((!isInt() && !(_flags & AllowNonCanon)) || isList() || isNull())
{
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
else
return 0;
else {}
}
auto p = payload();
if (p.size() > intTraits<_T>::maxSize && (_flags & FailIfTooBig))
{
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
else
return 0;
else {}
}
return fromBigEndian<_T>(p);
}
@ -280,14 +282,15 @@ public:
{
requireGood();
if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)) || (length() < _N::size && (_flags & FailIfTooSmall)))
{
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
else
return _N();
else{}
}
_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);
return ret;
}
@ -298,9 +301,9 @@ public:
/// @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); }
/// @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.
unsigned actualSize() const;
size_t actualSize() const;
private:
/// 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; }
/// @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.
unsigned payloadOffset() const { return isSingleByte() ? 0 : (1 + lengthSize()); }
size_t payloadOffset() const { return isSingleByte() ? 0 : (1 + lengthSize()); }
/// @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.
bytesConstRef m_data;
/// The list-indexing cache.
mutable unsigned m_lastIndex = (unsigned)-1;
mutable unsigned m_lastEnd = 0;
mutable size_t m_lastIndex = (size_t)-1;
mutable size_t m_lastEnd = 0;
mutable bytesConstRef m_lastItem;
};
@ -343,7 +349,7 @@ public:
RLPStream() {}
/// Initializes the RLPStream as a list of @a _listItems items.
explicit RLPStream(unsigned _listItems) { appendList(_listItems); }
explicit RLPStream(size_t _listItems) { appendList(_listItems); }
~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); }
/// 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.
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; }
/// Appends a list.
RLPStream& appendList(unsigned _items);
RLPStream& appendList(size_t _items);
RLPStream& appendList(bytesConstRef _rlp);
RLPStream& appendList(bytes const& _rlp) { return appendList(&_rlp); }
RLPStream& appendList(RLPStream const& _s) { return appendList(&_s.out()); }
/// Appends raw (pre-serialised) RLP data. Use with caution.
RLPStream& appendRaw(bytesConstRef _rlp, unsigned _itemCount = 1);
RLPStream& appendRaw(bytes const& _rlp, unsigned _itemCount = 1) { return appendRaw(&_rlp, _itemCount); }
RLPStream& appendRaw(bytesConstRef _rlp, size_t _itemCount = 1);
RLPStream& appendRaw(bytes const& _rlp, size_t _itemCount = 1) { return appendRaw(&_rlp, _itemCount); }
/// Shift operators for appending data items.
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); }
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.
/// @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.
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);
byte* b = &m_out.back();
@ -410,7 +416,7 @@ private:
/// Our output byte stream.
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; }

160
libdevcore/RangeMask.h

@ -24,6 +24,7 @@
#include <map>
#include <utility>
#include <vector>
#include <iterator>
#include <iostream>
#include <assert.h>
@ -35,6 +36,12 @@ class RLPStream;
using UnsignedRange = std::pair<unsigned, unsigned>;
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>
class RangeMask
{
@ -44,14 +51,19 @@ public:
using Range = std::pair<T, T>;
using Ranges = std::vector<Range>;
/// Constructs an empty range mask with empty ground range.
RangeMask(): m_all(0, 0) {}
/// Constructs an empty range mask with ground range [_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) {}
/// @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 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);
for (auto i = m_ranges.begin(); i != m_ranges.end() && _items; ++i)
@ -59,8 +71,10 @@ public:
return ret;
}
/// @returns the complement of the range mask relative to the ground range.
RangeMask operator~() const { return inverted(); }
/// @returns a copy of this range mask representing the complement relative to the ground range.
RangeMask inverted() const
{
RangeMask ret(m_all);
@ -76,6 +90,8 @@ public:
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(); }
template <class S> RangeMask operator-(S const& _m) const { auto ret = *this; return ret -= _m; }
@ -92,61 +108,13 @@ public:
return *this;
}
RangeMask& operator+=(Range const& _m) { return unionWith(_m); }
RangeMask& unionWith(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.
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;
}
/// Modifies this range mask to also include the range _m, which has to be a subset of
/// the ground range.
RangeMask& unionWith(Range const& _m);
/// Adds the single element _i to the range mask.
RangeMask& operator+=(T _m) { return unionWith(_m); }
/// Adds the single element _i to the range mask.
RangeMask& unionWith(T _i)
{
return operator+=(Range(_i, _i + 1));
@ -181,10 +149,12 @@ public:
m_all = std::make_pair(0, 0);
}
/// @returns the ground range.
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)); }
class const_iterator
class const_iterator: public std::iterator<std::forward_iterator_tag, T>
{
friend class RangeMask;
@ -208,6 +178,8 @@ public:
const_iterator begin() const { return const_iterator(*this, false); }
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++;
@ -219,6 +191,7 @@ public:
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 c = 0;
@ -227,8 +200,24 @@ public:
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:
/// The ground range.
UnsignedRange m_all;
/// Mapping begin -> end containing the ranges.
std::map<T, T> m_ranges;
};
@ -241,4 +230,65 @@ template <class T> inline std::ostream& operator<<(std::ostream& _out, RangeMask
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);
}
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;
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;
}
/**
* Nibble-based view on a bytesConstRef.
*/
struct NibbleSlice
{
bytesConstRef data;
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); }
unsigned size() const { return data.size() * 2 - offset; }
bool empty() const { return !size(); }
NibbleSlice mid(unsigned _index) const { return NibbleSlice(data, offset + _index); }
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(); }
/// @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()); }
/**
* @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
{
unsigned i;
for (i = 0; i < _k.size() && i < size(); ++i)
unsigned i = 0;
for (; i < _k.size() && i < size(); ++i)
if (operator[](i) < _k[i]) // Byte is lower - we're earlier..
return true;
else if (operator[](i) > _k[i]) // Byte is higher - we're not earlier.

162
libdevcore/TrieDB.h

@ -66,7 +66,7 @@ class GenericTrieDB
public:
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() {}
@ -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).
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 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);
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
}
/// Used for debugging, scans the whole trie.
void descendEntry(RLP const& _r, h256Hash& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const
{
if (_r.isData() && _r.size() == 32)
@ -118,6 +180,7 @@ public:
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
{
if (_r.isList() && _r.itemCount() == 2 && (!_wasExt || _out))
@ -139,6 +202,7 @@ public:
BOOST_THROW_EXCEPTION(InvalidTrie());
}
/// Used for debugging, scans the whole trie.
h256Hash leftOvers(std::ostream* _out = nullptr) const
{
h256Hash k = m_db->keys();
@ -146,11 +210,14 @@ public:
return k;
}
/// Used for debugging, scans the whole trie.
void debugStructure(std::ostream& _out) const
{
leftOvers(&_out);
}
/// Used for debugging, scans the whole trie.
/// @param _requireNoLeftOvers if true, requires that all keys are reachable.
bool check(bool _requireNoLeftOvers) const
{
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:
DB* db() const { return m_db; }
@ -279,12 +286,12 @@ private:
bool isTwoItemNode(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.
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 forceKillNode(h256 _h) { m_db->kill(_h); }
void forceInsertNode(h256 const& _h, bytesConstRef _v) { m_db->insert(_h, _v); }
void forceKillNode(h256 const& _h) { m_db->kill(_h); }
// 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
@ -305,6 +312,9 @@ std::ostream& operator<<(std::ostream& _out, GenericTrieDB<DB> const& _db)
return _out;
}
/**
* Different view on a GenericTrieDB that can use different key types.
*/
template <class Generic, class _KeyType>
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);
#endif
std::string rv = node(m_root);
assert(rv.size());
bytes b = mergeAt(RLP(rv), m_root, NibbleSlice(_key), _value);
std::string rootValue = node(m_root);
assert(rootValue.size());
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
// 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.
if (rv.size() < 32)
if (rootValue.size() < 32)
forceKillNode(m_root);
m_root = forceInsertNode(&b);
}
@ -1066,11 +1076,11 @@ template <class DB> bytes GenericTrieDB<DB>::place(RLP const& _orig, NibbleSlice
killNode(_orig);
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));
if (_orig.itemCount() == 2)
return (RLPStream(2) << _orig[0] << _s).out();
return rlpList(_orig[0], _s);
auto s = RLPStream(17);
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);
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 =
// std::cout << keyOf(_orig) << " ++ " << keyOf(n) << " == " << keyOf(RLP(ret)) << std::endl;
// return ret;
@ -1201,11 +1211,7 @@ template <class DB> bytes GenericTrieDB<DB>::branch(RLP const& _orig)
for (unsigned i = 0; i < 16; ++i)
if (i == b)
if (isLeaf(_orig) || k.size() > 1)
{
RLPStream bottom(2);
bottom << hexPrefixEncode(k.mid(1), isLeaf(_orig)) << _orig[1];
streamNode(r, bottom.out());
}
streamNode(r, rlpList(hexPrefixEncode(k.mid(1), isLeaf(_orig)), _orig[1]));
else
r << _orig[1];
else

42
libdevcrypto/Common.cpp

@ -22,6 +22,7 @@
#include "Common.h"
#include <random>
#include <cstdint>
#include <chrono>
#include <thread>
#include <mutex>
@ -29,6 +30,7 @@
#include <libdevcore/Guards.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/FileSystem.h>
#include <libdevcore/RLP.h>
#if ETH_HAVE_SECP256K1
#include <secp256k1/secp256k1.h>
#endif
@ -46,7 +48,6 @@ struct Secp256k1Context
~Secp256k1Context() { secp256k1_stop(); }
};
static Secp256k1Context s_secp256k1;
void dev::crypto::secp256k1Init() { (void)s_secp256k1; }
#endif
static Secp256k1PP s_secp256k1pp;
@ -91,6 +92,11 @@ Address dev::toAddress(Secret const& _secret)
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)
{
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)
{
#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());
#endif
}
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);
#endif
}
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);
#endif
}
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()
{
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)
{
KeyPair ret(FixedHash<32>::random(*s_eng.get()));
KeyPair ret(FixedHash<32>::random());
if (ret.address())
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::uniform_int_distribution<uint16_t> d(0, 255);
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)
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.
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.
void encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
@ -177,8 +180,6 @@ namespace crypto
{
struct InvalidState: public dev::Exception {};
void secp256k1Init();
/// Key derivation
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
// 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
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)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${OpenCL_INCLUDE_DIRS})
include_directories(..)
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})

76
libethash-cl/ethash_cl_miner.cpp

@ -50,6 +50,10 @@
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
#define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl
// Types of OpenCL devices we are interested in
@ -140,15 +144,21 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
bool ethash_cl_miner::configureGPU(
unsigned _platformId,
unsigned _localWorkSize,
unsigned _globalWorkSize,
unsigned _msPerBatch,
bool _allowCPU,
unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock
uint64_t _currentBlock
)
{
s_workgroupSize = _localWorkSize;
s_initialGlobalWorkSize = _globalWorkSize;
s_msPerBatch = _msPerBatch;
s_allowCPU = _allowCPU;
s_extraRequiredGPUMem = _extraGPUMemory;
// 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;
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;
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)
{
@ -254,7 +267,6 @@ void ethash_cl_miner::finish()
bool ethash_cl_miner::init(
uint8_t const* _dag,
uint64_t _dagSize,
unsigned _workgroupSize,
unsigned _platformId,
unsigned _deviceId
)
@ -299,14 +311,18 @@ bool ethash_cl_miner::init(
m_context = cl::Context(vector<cl::Device>(&device, &device + 1));
m_queue = cl::CommandQueue(m_context, device);
// use requested workgroup size, but we require multiple of 8
m_workgroupSize = ((_workgroupSize + 7) / 8) * 8;
// make sure that global work size is evenly divisible by the local workgroup size
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
// note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled
// into a byte array by bin2h.cmake. There is no need to load the file by hand in runtime
string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
addDefinition(code, "GROUP_SIZE", m_workgroupSize);
addDefinition(code, "GROUP_SIZE", s_workgroupSize);
addDefinition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
addDefinition(code, "ACCESSES", ETHASH_ACCESSES);
addDefinition(code, "MAX_OUTPUTS", c_maxSearchResults);
@ -323,7 +339,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG("Printing program log");
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());
return false;
@ -415,9 +431,8 @@ bool ethash_cl_miner::init(
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
{
struct pending_batch
@ -454,10 +469,9 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
unsigned buf = 0;
random_device 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
m_searchKernel.setArg(0, m_searchBuffer[buf]);
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);
// 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 });
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);
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)
break;
@ -497,19 +511,31 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
pending.pop();
}
/* chrono::high_resolution_clock::duration d = chrono::high_resolution_clock::now() - t;
if (d > chrono::milliseconds(_msPerBatch * 10 / 9))
// adjust global work size depending on last search time
if (s_msPerBatch)
{
cerr << "Batch of" << m_batchSize << "took" << chrono::duration_cast<chrono::milliseconds>(d).count() << "ms, >>" << _msPerBatch << "ms.";
m_batchSize = max<unsigned>(128, m_batchSize * 9 / 10);
cerr << "New batch size" << m_batchSize;
// Global work size must be:
// - less than or equal to 2 ^ DEVICE_BITS - 1
// - 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

31
libethash-cl/ethash_cl_miner.h

@ -1,6 +1,6 @@
#pragma once
#define __CL_ENABLE_EXCEPTIONS
#define __CL_ENABLE_EXCEPTIONS
#define CL_USE_DEPRECATED_OPENCL_2_0_APIS
#if defined(__clang__)
@ -12,7 +12,6 @@
#include "cl.hpp"
#endif
#include <boost/optional.hpp>
#include <time.h>
#include <functional>
#include <libethash/ethash.h>
@ -20,7 +19,7 @@
class ethash_cl_miner
{
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:
struct search_hook
@ -45,24 +44,34 @@ public:
static void listDevices();
static bool configureGPU(
unsigned _platformId,
unsigned _localWorkSize,
unsigned _globalWorkSize,
unsigned _msPerBatch,
bool _allowCPU,
unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock
uint64_t _currentBlock
);
bool init(
uint8_t const* _dag,
uint64_t _dagSize,
unsigned _workgroupSize = 64,
unsigned _platformId = 0,
unsigned _deviceId = 0
);
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 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:
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_hashBuffer[c_bufferCount];
cl::Buffer m_searchBuffer[c_bufferCount];
unsigned m_workgroupSize;
unsigned m_batchSize = c_searchBatchSize;
unsigned m_globalWorkSize;
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
static bool s_allowCPU;
/// 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} devcrypto)
#target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} evmcore)
if (ETHASHCL)
target_link_libraries(${EXECUTABLE} ethash-cl)

24
libethcore/Common.h

@ -97,10 +97,13 @@ enum class RelativeBlock: BlockNumber
Pending = PendingBlock
};
class Transaction;
struct ImportRoute
{
h256s deadBlocks;
h256s liveBlocks;
std::vector<Transaction> goodTranactions;
};
enum class ImportResult
@ -129,10 +132,10 @@ struct ImportRequirements
};
/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight.
class Signal
template<typename... Args> class Signal
{
public:
using Callback = std::function<void()>;
using Callback = std::function<void(Args...)>;
class HandlerAux
{
@ -141,7 +144,7 @@ public:
public:
~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; }
void reset() { m_s = nullptr; }
void fire() { m_h(); }
void fire(Args&&... _args) { m_h(std::forward<Args>(_args)...); }
private:
HandlerAux(unsigned _i, Signal* _s, Callback const& _h): m_i(_i), m_s(_s), m_h(_h) {}
@ -154,7 +157,8 @@ public:
~Signal()
{
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)
@ -165,13 +169,18 @@ public:
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:
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
{
@ -182,6 +191,7 @@ struct TransactionSkeleton
bytes data;
u256 gas = UndefinedU256;
u256 gasPrice = UndefinedU256;
u256 nonce = UndefinedU256;
};
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));
}
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);
@ -409,16 +409,39 @@ void Ethash::GPUMiner::listDevices()
}
bool Ethash::GPUMiner::configureGPU(
unsigned _localWorkSize,
unsigned _globalWorkSizeMultiplier,
unsigned _msPerBatch,
unsigned _platformId,
unsigned _deviceId,
bool _allowCPU,
unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock
uint64_t _currentBlock
)
{
s_platformId = _platformId;
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

7
libethcore/Ethash.h

@ -88,7 +88,7 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); }
static std::string platformInfo();
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()); }
protected:
void kickOff() override
@ -118,11 +118,14 @@ public:
static unsigned getNumDevices();
static void listDevices();
static bool configureGPU(
unsigned _localWorkSize,
unsigned _globalWorkSizeMultiplier,
unsigned _msPerBatch,
unsigned _platformId,
unsigned _deviceId,
bool _allowCPU,
unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock
uint64_t _currentBlock
);
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; }
/// 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 const& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); }
void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = std::move(_code); changed(); }
/// @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(); }
@ -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
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.
m_cacheUsage.resize(c_collectionQueueSize);
@ -137,8 +137,7 @@ BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, WithExisti
m_genesisBlock = _genesisBlock;
m_genesisHash = sha3(RLP(m_genesisBlock)[0].data());
open(_path, _we);
if (_we == WithExisting::Verify)
if (open(_path, _we) != c_minorProtocolVersion)
rebuild(_path, _p);
}
@ -147,24 +146,41 @@ BlockChain::~BlockChain()
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;
boost::filesystem::create_directories(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);
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)
{
boost::filesystem::remove_all(path + "/blocks");
boost::filesystem::remove_all(path + "/details");
cnote << "Killing blockchain & extras database (WithExisting::Kill).";
boost::filesystem::remove_all(chainPath + "/blocks");
boost::filesystem::remove_all(extrasPath + "/extras");
}
ldb::Options o;
o.create_if_missing = true;
o.max_open_files = 256;
ldb::DB::Open(o, path + "/blocks", &m_blocksDB);
ldb::DB::Open(o, path + "/details", &m_extrasDB);
ldb::DB::Open(o, chainPath + "/blocks", &m_blocksDB);
ldb::DB::Open(o, extrasPath + "/extras", &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.";
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))
{
// 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_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()
@ -208,11 +227,11 @@ void BlockChain::close()
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)
{
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
ProfilerStart("BlockChain_rebuild.log");
@ -220,16 +239,21 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
unsigned originalNumber = m_lastBlockNumber;
///////////////////////////////
// TODO
// - KILL ALL STATE/CHAIN
// - REINSERT ALL BLOCKS
///////////////////////////////
// Keep extras DB around, but under a temp name
delete m_extrasDB;
m_extrasDB = nullptr;
IGNORE_EXCEPTIONS(boost::filesystem::remove_all(path + "/details.old"));
boost::filesystem::rename(path + "/details", path + "/details.old");
boost::filesystem::rename(path + "/details", path + "/extras.old");
ldb::DB* oldExtrasDB;
ldb::Options o;
o.create_if_missing = true;
ldb::DB::Open(o, path + "/details.old", &oldExtrasDB);
ldb::DB::Open(o, path + "/details", &m_extrasDB);
ldb::DB::Open(o, extrasPath + "/extras.old", &oldExtrasDB);
ldb::DB::Open(o, extrasPath + "/extras", &m_extrasDB);
// Open a fresh state DB
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;
Timer t;
for (unsigned d = 1; d < originalNumber; ++d)
for (unsigned d = 1; d <= originalNumber; ++d)
{
if (!(d % 1000))
{
@ -289,7 +313,7 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
#endif
delete oldExtrasDB;
boost::filesystem::remove_all(path + "/details.old");
boost::filesystem::remove_all(path + "/extras.old");
}
LastHashes BlockChain::lastHashes(unsigned _n) const
@ -315,6 +339,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
h256s fresh;
h256s dead;
h256s badBlocks;
Transactions goodTransactions;
unsigned count = 0;
for (VerifiedBlock const& block: blocks)
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.
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);
fresh += r.liveBlocks;
dead += r.deadBlocks;
goodTransactions += r.goodTranactions;
++count;
}
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());
}
}
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
@ -475,6 +501,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
BlockReceipts br;
u256 td;
Transactions goodTransactions;
#if ETH_CATCH
try
#endif
@ -488,6 +515,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
{
blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i));
goodTransactions.push_back(s.pending()[i]);
}
s.cleanup(true);
@ -728,7 +756,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
dead.push_back(h);
else
fresh.push_back(h);
return ImportRoute{dead, fresh};
return ImportRoute{dead, fresh, move(goodTransactions)};
}
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
{
// cdebug << "treeRoute" << _from << "..." << _to;
@ -845,33 +942,21 @@ template <class T> static unsigned getHashSize(unordered_map<h256, T> const& _ma
void BlockChain::updateStats() const
{
{
ReadGuard l(x_blocks);
m_lastStats.memBlocks = 0;
m_lastStats.memBlocks = 0;
DEV_READ_GUARDED(x_blocks)
for (auto const& i: m_blocks)
m_lastStats.memBlocks += i.second.size() + 64;
}
{
ReadGuard l(x_details);
DEV_READ_GUARDED(x_details)
m_lastStats.memDetails = getHashSize(m_details);
}
{
ReadGuard l1(x_logBlooms);
ReadGuard l2(x_blocksBlooms);
m_lastStats.memLogBlooms = getHashSize(m_logBlooms) + getHashSize(m_blocksBlooms);
}
{
ReadGuard l(x_receipts);
DEV_READ_GUARDED(x_logBlooms)
DEV_READ_GUARDED(x_blocksBlooms)
m_lastStats.memLogBlooms = getHashSize(m_logBlooms) + getHashSize(m_blocksBlooms);
DEV_READ_GUARDED(x_receipts)
m_lastStats.memReceipts = getHashSize(m_receipts);
}
{
ReadGuard l(x_blockHashes);
DEV_READ_GUARDED(x_blockHashes)
m_lastStats.memBlockHashes = getHashSize(m_blockHashes);
}
{
ReadGuard l(x_transactionAddresses);
DEV_READ_GUARDED(x_transactionAddresses)
m_lastStats.memTransactionAddresses = getHashSize(m_transactionAddresses);
}
}
void BlockChain::garbageCollect(bool _force)
@ -928,10 +1013,8 @@ void BlockChain::garbageCollect(bool _force)
void BlockChain::checkConsistency()
{
{
WriteGuard l(x_details);
DEV_WRITE_GUARDED(x_details)
m_details.clear();
}
ldb::Iterator* it = m_blocksDB->NewIterator(m_readOptions);
for (it->SeekToFirst(); it->Valid(); it->Next())
if (it->key().size() == 32)
@ -943,13 +1026,9 @@ void BlockChain::checkConsistency()
{
auto dp = details(p);
if (asserts(contains(dp.children, h)))
{
cnote << "Apparently the database is corrupt. Not much we can do at this stage...";
}
if (assertsEqual(dp.number, dh.number - 1))
{
cnote << "Apparently the database is corrupt. Not much we can do at this stage...";
}
}
}
delete it;
@ -1062,7 +1141,8 @@ bool BlockChain::isKnown(h256 const& _hash) const
if (d.empty())
return false;
}
return true;
// return true;
return details(_hash).number <= m_lastBlockNumber; // to allow rewind functionality.
}
bytes BlockChain::block(h256 const& _hash) const

13
libethereum/BlockChain.h

@ -94,7 +94,7 @@ using ProgressCallback = std::function<void(unsigned, unsigned)>;
class BlockChain
{
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();
/// 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() 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.
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()); }
@ -212,6 +215,12 @@ public:
/// 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);
/// 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:
* - 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;
@ -264,7 +273,7 @@ public:
private:
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();
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 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):
m_host(_host)
@ -68,22 +80,25 @@ DownloadMan& BlockChainSync::downloadMan()
void BlockChainSync::abortSync()
{
downloadMan().resetToChain(h256s());
downloadMan().reset();
}
void BlockChainSync::onPeerStatus(std::shared_ptr<EthereumPeer> _peer)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
std::shared_ptr<Session> session = _peer->session();
if (!session)
return; // Expired
if (_peer->m_genesisHash != host().chain().genesisHash())
_peer->disable("Invalid genesis hash");
else if (_peer->m_protocolVersion != host().protocolVersion() && _peer->m_protocolVersion != EthereumHost::c_oldProtocolVersion)
_peer->disable("Invalid protocol version.");
else if (_peer->m_networkId != host().networkId())
_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.");
else if (host().isBanned(_peer->session()->id()))
else if (host().isBanned(session->id()))
_peer->disable("Peer banned for previous bad behaviour.");
else
{
@ -91,7 +106,6 @@ void BlockChainSync::onPeerStatus(std::shared_ptr<EthereumPeer> _peer)
_peer->m_expectedHashes = hashes;
onNewPeer(_peer);
}
DEV_INVARIANT_CHECK;
}
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";
pauseSync();
_peer->setIdle();
return;
}
_peer->requestBlocks();
@ -137,13 +150,14 @@ void BlockChainSync::logNewBlock(h256 const& _h)
void BlockChainSync::onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const& _r)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
_peer->setIdle();
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)
{
clog(NetAllDetail) << "Ignored blocks while waiting";
@ -184,6 +198,7 @@ void BlockChainSync::onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const
case ImportResult::BadChain:
logNewBlock(h);
_peer->disable("Malformed block received.");
restartSync();
return;
case ImportResult::FutureTimeKnown:
@ -281,17 +296,22 @@ void BlockChainSync::onPeerNewBlock(std::shared_ptr<EthereumPeer> _peer, RLP con
case ImportResult::FutureTimeUnknown:
case ImportResult::UnknownParent:
{
logNewBlock(h);
clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
resetSyncFor(_peer, h, _r[1].toInt<u256>());
u256 totalDifficulty = _r[1].toInt<u256>();
if (totalDifficulty > _peer->m_totalDifficulty)
{
clog(NetMessageDetail) << "Received block with no known parent. Resyncing...";
resetSyncFor(_peer, h, totalDifficulty);
}
break;
}
default:;
}
DEV_GUARDED(_peer->x_knownBlocks)
_peer->m_knownBlocks.insert(h);
}
DEV_INVARIANT_CHECK;
}
PV60Sync::PV60Sync(EthereumHost& _host):
@ -305,6 +325,7 @@ SyncStatus PV60Sync::status() const
RecursiveGuard l(x_sync);
SyncStatus res;
res.state = m_state;
res.protocolVersion = EthereumHost::c_oldProtocolVersion;
if (m_state == SyncState::Hashes)
{
res.hashesTotal = m_estimatedHashes;
@ -344,26 +365,30 @@ void PV60Sync::restartSync()
{
resetSync();
host().bq().clear();
if (isSyncing())
transition(m_syncer.lock(), SyncState::Idle);
std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
if (syncer)
transition(syncer, SyncState::Idle);
}
void PV60Sync::completeSync()
{
if (isSyncing())
transition(m_syncer.lock(), SyncState::Idle);
std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
if (syncer)
transition(syncer, SyncState::Idle);
}
void PV60Sync::pauseSync()
{
if (isSyncing())
setState(m_syncer.lock(), SyncState::Waiting, true);
std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
if (syncer)
transition(syncer, SyncState::Waiting, true);
}
void PV60Sync::continueSync()
{
if (isSyncing())
transition(m_syncer.lock(), SyncState::Blocks);
std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
if (syncer)
transition(syncer, SyncState::Blocks);
}
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)
_peer->m_requireTransactions = true;
RLPStream s;
if (_s == SyncState::Hashes)
{
if (m_state == SyncState::Idle)
if (m_state == SyncState::Idle || m_state == SyncState::Hashes)
{
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, _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;
m_estimatedHashes = _peer->m_expectedHashes - c_chainReorgSize;
syncHashes(_peer);
return;
}
}
@ -462,7 +470,6 @@ void PV60Sync::transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, boo
}
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)
{
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.
_peer->m_sub.doneFetch();
_peer->setIdle();
setState(_peer, SyncState::Idle, false);
}
else if (m_state == SyncState::Hashes)
@ -502,7 +508,9 @@ void PV60Sync::setNeedsSyncing(std::shared_ptr<EthereumPeer> _peer, h256 const&
if (_peer->m_latestHash)
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
@ -562,7 +570,6 @@ void PV60Sync::attemptSync(std::shared_ptr<EthereumPeer> _peer)
else
{
clog(NetAllDetail) << "Yes. Their chain is better.";
m_estimatedHashes = _peer->m_expectedHashes - c_chainReorgSize;
transition(_peer, SyncState::Hashes);
}
}
@ -573,7 +580,9 @@ void PV60Sync::noteNeedsSyncing(std::shared_ptr<EthereumPeer> _peer)
if (isSyncing())
{
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);
}
else
@ -649,20 +658,45 @@ void PV60Sync::noteDoneBlocks(std::shared_ptr<EthereumPeer> _peer, bool _clemenc
}
resetSync();
downloadMan().reset();
}
_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)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
_peer->setIdle();
if (!isSyncing(_peer))
{
clog(NetMessageSummary) << "Ignoring hashes since not syncing";
return;
}
if (_peer->m_syncHash != (m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash))
{
clog(NetMessageSummary) << "Ignoring unexpected hashes";
return;
}
if (_hashes.size() == 0)
{
transition(_peer, SyncState::Blocks);
@ -684,7 +718,8 @@ void PV60Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
transition(_peer, SyncState::Idle);
_peer->disable("Bad blocks");
restartSync();
return;
}
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)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
if (isSyncing() && (m_state != SyncState::NewBlocks || isSyncing(_peer)))
{
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()
{
// 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; });
setState(std::shared_ptr<EthereumPeer>(), SyncState::Idle, false, true);
bool continueSync = false;
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;
}
@ -778,45 +837,366 @@ void PV60Sync::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.
if (m_syncer.expired())
if (m_syncer.expired() && m_state != SyncState::Idle)
{
clog(NetWarn) << "Syncing peer disconnected, restarting sync";
m_syncer.reset();
abortSync();
}
DEV_INVARIANT_CHECK;
}
bool PV60Sync::invariants() const
{
if (m_state == SyncState::Idle && isSyncing())
return false;
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Idle while peer syncing"));
if (m_state != SyncState::Idle && !isSyncing())
return false;
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Active while peer not syncing"));
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)
return false;
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("m_syncingLatestHash is not set while downloading hashes"));
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)
{
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())
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)
{
bool busy = false;
host().foreachPeer([&](std::shared_ptr<EthereumPeer> _p) { if (_p->m_asking != Asking::Nothing && _p->m_asking != Asking::State) busy = true; return !busy; });
if (busy)
return false;
if (isSyncing(_peer))
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
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())
return false;
else if (m_state == SyncState::Hashes)
{
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;
}

59
libethereum/BlockChainSync.h

@ -24,7 +24,6 @@
#include <mutex>
#include <libdevcore/Guards.h>
#include <libdevcore/RangeMask.h>
#include <libethcore/Common.h>
#include <libp2p/Common.h>
#include "CommonNet.h"
@ -59,6 +58,9 @@ public:
/// @returns true is Sync is in progress
virtual bool isSyncing() const = 0;
/// Restart sync
virtual void restartSync() = 0;
/// Called by peer to report status
virtual void onPeerStatus(std::shared_ptr<EthereumPeer> _peer);
@ -93,9 +95,6 @@ protected:
/// Resume downloading after witing state
virtual void continueSync() = 0;
/// Restart sync
virtual void restartSync() = 0;
/// Called after all blocks have been donloaded
virtual void completeSync() = 0;
@ -115,7 +114,7 @@ protected:
void requestBlocks(std::shared_ptr<EthereumPeer> _peer);
protected:
Handler m_bqRoomAvailable; ///< Triggered once block queue
Handler<> m_bqRoomAvailable; ///< Triggered once block queue
mutable RecursiveMutex x_sync;
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.
@ -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
* Syncs to peers and keeps up to date
* @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
*/
/**
@ -228,7 +227,7 @@ protected:
void pauseSync() 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
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
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
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.
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(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;
mutable unsigned size;
@ -69,7 +69,7 @@ struct BlocksBlooms
{
BlocksBlooms() {}
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;
mutable unsigned size;

74
libethereum/BlockQueue.cpp

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

13
libethereum/BlockQueue.h

@ -111,8 +111,8 @@ public:
/// Get some infomration on the given block's status regarding us.
QueueStatus blockStatus(h256 const& _h) const;
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
template <class T> Handler onRoomAvailable(T const& _t) { return m_onRoomAvailable.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> void setOnBad(T const& _t) { m_onBad = _t; }
@ -134,8 +134,9 @@ private:
bool invariants() const override;
void verifierBody();
void collectUnknownBad(h256 const& _bad);
void updateBad(h256 const& _bad);
void collectUnknownBad_WITH_BOTH_LOCKS(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.
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.
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
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_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.
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.

268
libethereum/Client.cpp

@ -24,7 +24,6 @@
#include <chrono>
#include <thread>
#include <boost/filesystem.hpp>
#include <boost/math/distributions/normal.hpp>
#if ETH_JSONRPC || !ETH_TRUE
#include <jsonrpccpp/client.h>
#include <jsonrpccpp/client/connectors/httpclient.h>
@ -38,64 +37,97 @@
#include "Defaults.h"
#include "Executive.h"
#include "EthereumHost.h"
#include "Utility.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace p2p;
VersionChecker::VersionChecker(string const& _dbPath):
m_path(_dbPath.size() ? _dbPath : Defaults::dbPath())
std::ostream& dev::eth::operator<<(std::ostream& _out, ActivityReport const& _r)
{
bytes statusBytes = contents(m_path + "/status");
RLP status(statusBytes);
try
{
auto protocolVersion = (unsigned)status[0];
(void)protocolVersion;
auto minorProtocolVersion = (unsigned)status[1];
auto databaseVersion = (unsigned)status[2];
h256 ourGenesisHash = CanonBlockChain::genesis().hash();
auto genesisHash = status.itemCount() > 3 ? (h256)status[3] : ourGenesisHash;
m_action =
databaseVersion != c_databaseVersion || genesisHash != ourGenesisHash ?
WithExisting::Kill
: minorProtocolVersion != eth::c_minorProtocolVersion ?
WithExisting::Verify
:
WithExisting::Trust;
}
catch (...)
{
m_action = WithExisting::Kill;
}
_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
static const Addresses c_canaries =
{
Address("4bb7e8ae99b645c2b7860b8f3a2328aae28bd80a"), // gav
Address("1baf27b88c48dd02b744999cf3522766929d2b2a"), // vitalik
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)
{
try
{
boost::filesystem::create_directory(m_path);
}
catch (...)
{
cwarn << "Unhandled exception! Failed to create directory: " << m_path << "\n" << boost::current_exception_diagnostic_information();
}
writeFile(m_path + "/status", rlpList(eth::c_protocolVersion, eth::c_minorProtocolVersion, c_databaseVersion, CanonBlockChain::genesis().hash()));
}
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, _forceAction, [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(_gp),
m_stateDB(State::openDB(_dbPath, _forceAction)),
m_preMine(m_stateDB, BaseState::CanonGenesis),
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)
{
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));
return m_bq.import(&_block, bc(), _isSafe);
}
tuple<ImportRoute, bool, unsigned> Client::syncQueue(unsigned _max)
{
stopWorking();
return m_bc.sync(m_bq, m_stateDB, _max);
}
@ -210,144 +242,18 @@ void Client::onBadBlock(Exception& _ex) const
#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
{
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
{
return stateAt(c_canary, 0) == 2;
return stateAt(c_canaries[0], 0) == 2;
}
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))
{
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.
for (auto const& h: _blocks)
{
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())
h->noteNewBlocks();
@ -756,7 +655,7 @@ void Client::restartMining()
for (auto const& t: m_postMine.pending())
{
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)
onTransactionQueueReady();
}
@ -776,6 +675,11 @@ void Client::onChainChanged(ImportRoute const& _ir)
{
h256Hash 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);
restartMining();
noteChanged(changeds);
@ -805,7 +709,7 @@ void Client::rejigMining()
{
clog(ClientTrace) << "Rejigging mining...";
DEV_WRITE_GUARDED(x_working)
m_working.commitToMine(m_bc);
m_working.commitToMine(m_bc, m_extraData);
DEV_READ_GUARDED(x_working)
{
DEV_WRITE_GUARDED(x_postMine)

40
libethereum/Client.h

@ -64,33 +64,6 @@ class VersionChecker
{
public:
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; };
@ -240,6 +213,12 @@ public:
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
/// Set a JSONRPC server to which we can report bad blocks.
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:
/// InterfaceStub methods
@ -342,10 +321,10 @@ private:
GenericFarm<ProofOfWork> m_farm; ///< Our mining farm.
Handler m_tqReady;
Handler m_bqReady;
Handler<> m_tqReady;
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_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.
@ -366,6 +345,7 @@ private:
std::atomic<bool> m_syncBlockQueue = {false};
std::string m_sentinel;
bytes m_extraData;
};
}

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

Loading…
Cancel
Save