Browse Source

Merge remote-tracking branch 'up/develop' into designDebugPanel

Conflicts:
	mix/MixClient.cpp
cl-refactor
yann300 10 years ago
parent
commit
5eecec74eb
  1. 3
      alethzero/CMakeLists.txt
  2. 18
      alethzero/Context.cpp
  3. 4
      alethzero/Context.h
  4. 4
      alethzero/Debugger.cpp
  5. 181
      alethzero/GasPricing.ui
  6. 12
      alethzero/Main.ui
  7. 55
      alethzero/MainWin.cpp
  8. 7
      alethzero/MainWin.h
  9. 15
      alethzero/Transact.cpp
  10. 1
      alethzero/Transact.h
  11. 2
      alethzero/Transact.ui
  12. 85
      eth/main.cpp
  13. 39
      ethkey/KeyAux.h
  14. 19
      ethminer/MinerAux.h
  15. 2
      ethminer/farm.json
  16. 10
      evmjit/libevmjit-cpp/Env.cpp
  17. 16
      evmjit/libevmjit-cpp/JitVM.cpp
  18. 10
      evmjit/libevmjit-cpp/JitVM.h
  19. 2
      libdevcore/Common.cpp
  20. 52
      libdevcore/CommonData.cpp
  21. 2
      libdevcore/CommonData.h
  22. 29
      libdevcore/Log.cpp
  23. 31
      libdevcore/Log.h
  24. 18
      libdevcore/TransientDirectory.cpp
  25. 2
      libdevcrypto/Common.cpp
  26. 2
      libdevcrypto/Common.h
  27. 2
      libdevcrypto/SecretStore.cpp
  28. 463
      libethash-cl/ethash_cl_miner.cpp
  29. 17
      libethash-cl/ethash_cl_miner.h
  30. 142
      libethash-cl/ethash_cl_miner_kernel.cl
  31. 13
      libethash/internal.c
  32. 21
      libethash/io.c
  33. 17
      libethash/io.h
  34. 6
      libethcore/Common.cpp
  35. 8
      libethcore/Common.h
  36. 8
      libethcore/Ethash.cpp
  37. 5
      libethcore/Ethash.h
  38. 5
      libethcore/EthashAux.cpp
  39. 23
      libethcore/KeyManager.cpp
  40. 11
      libethcore/KeyManager.h
  41. 3
      libethcore/Params.cpp
  42. 14
      libethereum/BlockQueue.cpp
  43. 2
      libethereum/BlockQueue.h
  44. 26
      libethereum/CanonBlockChain.cpp
  45. 28
      libethereum/CanonBlockChain.h
  46. 17
      libethereum/Client.cpp
  47. 1
      libethereum/Client.h
  48. 6
      libethereum/CommonNet.h
  49. 163
      libethereum/EthereumHost.cpp
  50. 13
      libethereum/EthereumHost.h
  51. 24
      libethereum/EthereumPeer.cpp
  52. 146
      libethereum/Executive.cpp
  53. 25
      libethereum/Executive.h
  54. 5
      libethereum/ExtVM.cpp
  55. 55
      libethereum/Precompiled.cpp
  56. 2
      libethereum/Precompiled.h
  57. 24
      libethereum/State.cpp
  58. 17
      libethereum/State.h
  59. 17
      libethereum/Transaction.h
  60. 2
      libevm/ExtVMFace.h
  61. 10
      libevm/SmartVM.cpp
  62. 8
      libevm/SmartVM.h
  63. 42
      libevm/VM.cpp
  64. 15
      libevm/VM.h
  65. 19
      libevm/VMFace.h
  66. 14
      libevm/VMFactory.cpp
  67. 4
      libevm/VMFactory.h
  68. 14
      libevmasm/Assembly.cpp
  69. 9
      libevmasm/Assembly.h
  70. 13
      libevmasm/CommonSubexpressionEliminator.cpp
  71. 3
      libevmasm/CommonSubexpressionEliminator.h
  72. 225
      libevmasm/ConstantOptimiser.cpp
  73. 147
      libevmasm/ConstantOptimiser.h
  74. 16
      libevmasm/ExpressionClasses.cpp
  75. 7
      libevmasm/GasMeter.cpp
  76. 4
      libevmasm/GasMeter.h
  77. 1
      libevmasm/KnownState.h
  78. 4
      libevmcore/Instruction.cpp
  79. 6
      libp2p/HostCapability.cpp
  80. 4
      libp2p/HostCapability.h
  81. 28
      libp2p/RLPxHandshake.cpp
  82. 16
      libp2p/Session.cpp
  83. 2
      libp2p/UDP.h
  84. 30
      libsolidity/AST.cpp
  85. 27
      libsolidity/AST.h
  86. 55
      libsolidity/ArrayUtils.cpp
  87. 26
      libsolidity/Compiler.cpp
  88. 17
      libsolidity/Compiler.h
  89. 5
      libsolidity/CompilerContext.h
  90. 6
      libsolidity/CompilerStack.cpp
  91. 2
      libsolidity/CompilerStack.h
  92. 15
      libsolidity/CompilerUtils.cpp
  93. 63
      libsolidity/ExpressionCompiler.cpp
  94. 26
      libsolidity/LValue.cpp
  95. 47
      libsolidity/NameAndTypeResolver.cpp
  96. 105
      libsolidity/Parser.cpp
  97. 9
      libsolidity/Parser.h
  98. 3
      libsolidity/Token.h
  99. 64
      libsolidity/Types.cpp
  100. 41
      libsolidity/Types.h

3
alethzero/CMakeLists.txt

@ -24,6 +24,7 @@ qt5_wrap_ui(ui_Debugger.h Debugger.ui)
qt5_wrap_ui(ui_Transact.h Transact.ui) qt5_wrap_ui(ui_Transact.h Transact.ui)
qt5_wrap_ui(ui_ExportState.h ExportState.ui) qt5_wrap_ui(ui_ExportState.h ExportState.ui)
qt5_wrap_ui(ui_GetPassword.h GetPassword.ui) qt5_wrap_ui(ui_GetPassword.h GetPassword.ui)
qt5_wrap_ui(ui_GasPricing.h GasPricing.ui)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
@ -36,7 +37,7 @@ endif ()
# eth_add_executable is defined in cmake/EthExecutableHelper.cmake # eth_add_executable is defined in cmake/EthExecutableHelper.cmake
eth_add_executable(${EXECUTABLE} eth_add_executable(${EXECUTABLE}
ICON alethzero ICON alethzero
UI_RESOURCES alethzero.icns Main.ui Connect.ui Debugger.ui Transact.ui ExportState.ui GetPassword.ui UI_RESOURCES alethzero.icns Main.ui Connect.ui Debugger.ui Transact.ui ExportState.ui GetPassword.ui GasPricing.ui
WIN_RESOURCES alethzero.rc WIN_RESOURCES alethzero.rc
) )

18
alethzero/Context.cpp

@ -21,6 +21,7 @@
#include "Context.h" #include "Context.h"
#include <QComboBox> #include <QComboBox>
#include <QSpinBox>
#include <libethcore/Common.h> #include <libethcore/Common.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -34,6 +35,23 @@ Context::~Context()
{ {
} }
void setValueUnits(QComboBox* _units, QSpinBox* _value, u256 _v)
{
initUnits(_units);
_units->setCurrentIndex(0);
while (_v > 50000 && _units->currentIndex() < (int)(units().size() - 2))
{
_v /= 1000;
_units->setCurrentIndex(_units->currentIndex() + 1);
}
_value->setValue((unsigned)_v);
}
u256 fromValueUnits(QComboBox* _units, QSpinBox* _value)
{
return _value->value() * units()[units().size() - 1 - _units->currentIndex()].first;
}
void initUnits(QComboBox* _b) void initUnits(QComboBox* _b)
{ {
for (auto n = (unsigned)units().size(); n-- != 0; ) for (auto n = (unsigned)units().size(); n-- != 0; )

4
alethzero/Context.h

@ -28,6 +28,7 @@
#include <libethcore/Common.h> #include <libethcore/Common.h>
class QComboBox; class QComboBox;
class QSpinBox;
namespace dev { namespace eth { struct StateDiff; class KeyManager; } } namespace dev { namespace eth { struct StateDiff; class KeyManager; } }
@ -37,6 +38,8 @@ namespace dev { namespace eth { struct StateDiff; class KeyManager; } }
#define Span(S) "<span style=\"" S "\">" #define Span(S) "<span style=\"" S "\">"
void initUnits(QComboBox* _b); void initUnits(QComboBox* _b);
void setValueUnits(QComboBox* _units, QSpinBox* _value, dev::u256 _v);
dev::u256 fromValueUnits(QComboBox* _units, QSpinBox* _value);
std::vector<dev::KeyPair> keysAsVector(QList<dev::KeyPair> const& _keys); std::vector<dev::KeyPair> keysAsVector(QList<dev::KeyPair> const& _keys);
@ -67,5 +70,6 @@ public:
virtual dev::Secret retrieveSecret(dev::Address const& _a) const = 0; virtual dev::Secret retrieveSecret(dev::Address const& _a) const = 0;
virtual dev::eth::KeyManager& keyManager() = 0; virtual dev::eth::KeyManager& keyManager() = 0;
virtual dev::u256 gasPrice() const = 0;
}; };

4
alethzero/Debugger.cpp

@ -82,7 +82,7 @@ bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transacti
bytesConstRef lastData; bytesConstRef lastData;
h256 lastHash; h256 lastHash;
h256 lastDataHash; h256 lastDataHash;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, VM* voidVM, ExtVMFace const* voidExt) auto onOp = [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{ {
VM& vm = *voidVM; VM& vm = *voidVM;
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt); ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
@ -104,7 +104,7 @@ bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transacti
levels.push_back(&history.back()); levels.push_back(&history.back());
else else
levels.resize(ext.depth); levels.resize(ext.depth);
history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, static_cast<u256>(gas), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
}; };
_executive.go(onOp); _executive.go(onOp);
_executive.finalize(); _executive.finalize();

181
alethzero/GasPricing.ui

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GasPricing</class>
<widget class="QDialog" name="GasPricing">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>416</width>
<height>286</height>
</rect>
</property>
<property name="windowTitle">
<string>Gas Pricing</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>398</width>
<height>45</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="label5_3">
<property name="text">
<string>&amp;Bid: The minimum grice of gas that is accepting into a block which we mine.</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>bidValue</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="askValue">
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="10" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="close">
<property name="text">
<string>Close</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label5_2">
<property name="text">
<string>&amp;Ask: The minimum grice of gas that is accepting into a block which we mine.</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>askValue</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="askUnits"/>
</item>
<item row="5" column="0">
<widget class="QSpinBox" name="bidValue">
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="bidUnits"/>
</item>
<item row="7" column="0" colspan="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="9" column="0" colspan="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="8" column="0" colspan="2">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>close</sender>
<signal>clicked()</signal>
<receiver>GasPricing</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>387</x>
<y>264</y>
</hint>
<hint type="destinationlabel">
<x>240</x>
<y>262</y>
</hint>
</hints>
</connection>
</connections>
</ui>

12
alethzero/Main.ui

@ -212,12 +212,19 @@
<addaction name="debugDumpState"/> <addaction name="debugDumpState"/>
<addaction name="debugDumpStatePre"/> <addaction name="debugDumpStatePre"/>
</widget> </widget>
<widget class="QMenu" name="menu_Config">
<property name="title">
<string>&amp;Config</string>
</property>
<addaction name="gasPrices"/>
</widget>
<addaction name="menu_File"/> <addaction name="menu_File"/>
<addaction name="menu_View"/> <addaction name="menu_View"/>
<addaction name="menu_Network"/> <addaction name="menu_Network"/>
<addaction name="menu_Tools"/> <addaction name="menu_Tools"/>
<addaction name="menuWhispe"/> <addaction name="menuWhispe"/>
<addaction name="menuDebug"/> <addaction name="menuDebug"/>
<addaction name="menu_Config"/>
<addaction name="menu_Help"/> <addaction name="menu_Help"/>
<addaction name="menu_Debug"/> <addaction name="menu_Debug"/>
</widget> </widget>
@ -1763,6 +1770,11 @@ font-size: 14pt</string>
<string>Re-Encrypt All Keys...</string> <string>Re-Encrypt All Keys...</string>
</property> </property>
</action> </action>
<action name="gasPrices">
<property name="text">
<string>&amp;Gas Prices...</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>

55
alethzero/MainWin.cpp

@ -76,6 +76,7 @@
#include "ExportState.h" #include "ExportState.h"
#include "ui_Main.h" #include "ui_Main.h"
#include "ui_GetPassword.h" #include "ui_GetPassword.h"
#include "ui_GasPricing.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
@ -128,7 +129,7 @@ static QString filterOutTerminal(QString _s)
Main::Main(QWidget *parent) : Main::Main(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::Main), ui(new Ui::Main),
m_transact(this, this), m_transact(nullptr),
m_dappLoader(nullptr), m_dappLoader(nullptr),
m_webPage(nullptr) m_webPage(nullptr)
{ {
@ -232,6 +233,11 @@ Main::Main(QWidget *parent) :
// inspector->setPage(page); // inspector->setPage(page);
setBeneficiary(*m_keyManager.accounts().begin()); setBeneficiary(*m_keyManager.accounts().begin());
readSettings(); readSettings();
m_transact = new Transact(this, this);
m_transact->setWindowFlags(Qt::Dialog);
m_transact->setWindowModality(Qt::WindowModal);
#if !ETH_FATDB #if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts); removeDockWidget(ui->dockWidget_accounts);
#endif #endif
@ -262,6 +268,23 @@ bool Main::confirm() const
return ui->natSpec->isChecked(); return ui->natSpec->isChecked();
} }
void Main::on_gasPrices_triggered()
{
QDialog d;
Ui_GasPricing gp;
gp.setupUi(&d);
d.setWindowTitle("Gas Pricing");
setValueUnits(gp.bidUnits, gp.bidValue, static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->bid());
setValueUnits(gp.askUnits, gp.askValue, static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->ask());
if (d.exec() == QDialog::Accepted)
{
static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->setBid(fromValueUnits(gp.bidUnits, gp.bidValue));
static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->setAsk(fromValueUnits(gp.askUnits, gp.askValue));
m_transact->resetGasPrice();
}
}
void Main::on_newIdentity_triggered() void Main::on_newIdentity_triggered()
{ {
KeyPair kp = KeyPair::create(); KeyPair kp = KeyPair::create();
@ -467,10 +490,8 @@ void Main::load(QString _s)
void Main::on_newTransaction_triggered() void Main::on_newTransaction_triggered()
{ {
m_transact.setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB); m_transact->setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB);
m_transact.setWindowFlags(Qt::Dialog); m_transact->show();
m_transact.setWindowModality(Qt::WindowModal);
m_transact.show();
} }
void Main::on_loadJS_triggered() void Main::on_loadJS_triggered()
@ -653,6 +674,11 @@ void Main::on_paranoia_triggered()
ethereum()->setParanoia(ui->paranoia->isChecked()); ethereum()->setParanoia(ui->paranoia->isChecked());
} }
dev::u256 Main::gasPrice() const
{
return ethereum()->gasPricer()->bid();
}
void Main::writeSettings() void Main::writeSettings()
{ {
QSettings s("ethereum", "alethzero"); QSettings s("ethereum", "alethzero");
@ -669,6 +695,8 @@ void Main::writeSettings()
s.setValue("identities", b); s.setValue("identities", b);
} }
s.setValue("askPrice", QString::fromStdString(toString(static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->ask())));
s.setValue("bidPrice", QString::fromStdString(toString(static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->bid())));
s.setValue("upnp", ui->upnp->isChecked()); s.setValue("upnp", ui->upnp->isChecked());
s.setValue("forceAddress", ui->forcePublicIP->text()); s.setValue("forceAddress", ui->forcePublicIP->text());
s.setValue("forceMining", ui->forceMining->isChecked()); s.setValue("forceMining", ui->forceMining->isChecked());
@ -752,6 +780,9 @@ void Main::readSettings(bool _skipGeometry)
} }
} }
static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->setAsk(u256(s.value("askPrice", "500000000000").toString().toStdString()));
static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->setBid(u256(s.value("bidPrice", "500000000000").toString().toStdString()));
ui->upnp->setChecked(s.value("upnp", true).toBool()); ui->upnp->setChecked(s.value("upnp", true).toBool());
ui->forcePublicIP->setText(s.value("forceAddress", "").toString()); ui->forcePublicIP->setText(s.value("forceAddress", "").toString());
ui->dropPeers->setChecked(false); ui->dropPeers->setChecked(false);
@ -1803,7 +1834,7 @@ void Main::on_accounts_doubleClicked()
} }
} }
static shh::FullTopic topicFromText(QString _s) static shh::Topics topicFromText(QString _s)
{ {
shh::BuildTopic ret; shh::BuildTopic ret;
while (_s.size()) while (_s.size())
@ -2083,14 +2114,14 @@ void Main::on_reencryptKey_triggered()
auto pw = [&](){ auto pw = [&](){
auto p = QInputDialog::getText(this, "Re-Encrypt Key", "Enter the original password for this key.\nHint: " + QString::fromStdString(m_keyManager.hint(a)), QLineEdit::Password, QString()).toStdString(); auto p = QInputDialog::getText(this, "Re-Encrypt Key", "Enter the original password for this key.\nHint: " + QString::fromStdString(m_keyManager.hint(a)), QLineEdit::Password, QString()).toStdString();
if (p.empty()) if (p.empty())
throw UnknownPassword(); throw PasswordUnknown();
return p; return p;
}; };
while (!(password.empty() ? m_keyManager.recode(a, SemanticPassword::Master, pw, kdf) : m_keyManager.recode(a, password, hint, pw, kdf))) while (!(password.empty() ? m_keyManager.recode(a, SemanticPassword::Master, pw, kdf) : m_keyManager.recode(a, password, hint, pw, kdf)))
if (QMessageBox::question(this, "Re-Encrypt Key", "Password given is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) if (QMessageBox::question(this, "Re-Encrypt Key", "Password given is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel)
return; return;
} }
catch (UnknownPassword&) {} catch (PasswordUnknown&) {}
} }
} }
@ -2106,13 +2137,13 @@ void Main::on_reencryptAll_triggered()
while (!m_keyManager.recode(a, SemanticPassword::Existing, [&](){ while (!m_keyManager.recode(a, SemanticPassword::Existing, [&](){
auto p = QInputDialog::getText(nullptr, "Re-Encrypt Key", QString("Enter the original password for key %1.\nHint: %2").arg(QString::fromStdString(pretty(a))).arg(QString::fromStdString(m_keyManager.hint(a))), QLineEdit::Password, QString()).toStdString(); auto p = QInputDialog::getText(nullptr, "Re-Encrypt Key", QString("Enter the original password for key %1.\nHint: %2").arg(QString::fromStdString(pretty(a))).arg(QString::fromStdString(m_keyManager.hint(a))), QLineEdit::Password, QString()).toStdString();
if (p.empty()) if (p.empty())
throw UnknownPassword(); throw PasswordUnknown();
return p; return p;
}, (KDF)kdfs.indexOf(kdf))) }, (KDF)kdfs.indexOf(kdf)))
if (QMessageBox::question(this, "Re-Encrypt Key", "Password given is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) if (QMessageBox::question(this, "Re-Encrypt Key", "Password given is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel)
return; return;
} }
catch (UnknownPassword&) {} catch (PasswordUnknown&) {}
} }
void Main::on_go_triggered() void Main::on_go_triggered()
@ -2187,10 +2218,10 @@ void Main::refreshWhispers()
shh::Envelope const& e = w.second; shh::Envelope const& e = w.second;
shh::Message m; shh::Message m;
for (pair<Public, Secret> const& i: m_server->ids()) for (pair<Public, Secret> const& i: m_server->ids())
if (!!(m = e.open(shh::FullTopic(), i.second))) if (!!(m = e.open(shh::Topics(), i.second)))
break; break;
if (!m) if (!m)
m = e.open(shh::FullTopic()); m = e.open(shh::Topics());
QString msg; QString msg;
if (m.from()) if (m.from())

7
alethzero/MainWin.h

@ -91,7 +91,7 @@ public:
QList<dev::KeyPair> owned() const { return m_myIdentities; } QList<dev::KeyPair> owned() const { return m_myIdentities; }
dev::u256 gasPrice() const { return 10 * dev::eth::szabo; } dev::u256 gasPrice() const override;
dev::eth::KeyManager& keyManager() override { return m_keyManager; } dev::eth::KeyManager& keyManager() override { return m_keyManager; }
bool doConfirm(); bool doConfirm();
@ -194,6 +194,9 @@ private slots:
void on_newIdentity_triggered(); void on_newIdentity_triggered();
void on_post_clicked(); void on_post_clicked();
// Config
void on_gasPrices_triggered();
void refreshWhisper(); void refreshWhisper();
void refreshBlockChain(); void refreshBlockChain();
void addNewId(QString _ids); void addNewId(QString _ids);
@ -279,7 +282,7 @@ private:
static std::string fromRaw(dev::h256 _n, unsigned* _inc = nullptr); static std::string fromRaw(dev::h256 _n, unsigned* _inc = nullptr);
NatspecHandler m_natSpecDB; NatspecHandler m_natSpecDB;
Transact m_transact; Transact* m_transact;
std::unique_ptr<DappHost> m_dappHost; std::unique_ptr<DappHost> m_dappHost;
DappLoader* m_dappLoader; DappLoader* m_dappLoader;
QWebEnginePage* m_webPage; QWebEnginePage* m_webPage;

15
alethzero/Transact.cpp

@ -58,11 +58,9 @@ Transact::Transact(Context* _c, QWidget* _parent):
{ {
ui->setupUi(this); ui->setupUi(this);
initUnits(ui->gasPriceUnits); resetGasPrice();
initUnits(ui->valueUnits); setValueUnits(ui->valueUnits, ui->value, 0);
ui->valueUnits->setCurrentIndex(6);
ui->gasPriceUnits->setCurrentIndex(4);
ui->gasPrice->setValue(10);
on_destination_currentTextChanged(QString()); on_destination_currentTextChanged(QString());
} }
@ -92,6 +90,11 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e
ui->from->setCurrentIndex(0); ui->from->setCurrentIndex(0);
} }
void Transact::resetGasPrice()
{
setValueUnits(ui->gasPriceUnits, ui->gasPrice, m_context->gasPrice());
}
bool Transact::isCreation() const bool Transact::isCreation() const
{ {
return ui->destination->currentText().isEmpty() || ui->destination->currentText() == "(Create Contract)"; return ui->destination->currentText().isEmpty() || ui->destination->currentText() == "(Create Contract)";
@ -357,7 +360,7 @@ void Transact::rejigData()
return; return;
} }
else else
gasNeeded = (qint64)min<bigint>(ethereum()->gasLimitRemaining(), ((b - value()) / gasPrice())); gasNeeded = (qint64)min<bigint>(ethereum()->gasLimitRemaining(), ((b - value()) / max<u256>(gasPrice(), 1)));
// Dry-run execution to determine gas requirement and any execution errors // Dry-run execution to determine gas requirement and any execution errors
Address to; Address to;

1
alethzero/Transact.h

@ -41,6 +41,7 @@ public:
explicit Transact(Context* _context, QWidget* _parent = 0); explicit Transact(Context* _context, QWidget* _parent = 0);
~Transact(); ~Transact();
void resetGasPrice();
void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB);
private slots: private slots:

2
alethzero/Transact.ui

@ -159,7 +159,7 @@
<string>@ </string> <string>@ </string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>1</number> <number>0</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>430000000</number> <number>430000000</number>

85
eth/main.cpp

@ -66,6 +66,8 @@ using namespace dev::eth;
using namespace boost::algorithm; using namespace boost::algorithm;
using dev::eth::Instruction; using dev::eth::Instruction;
static bool g_silence = false;
void interactiveHelp() void interactiveHelp()
{ {
cout cout
@ -116,6 +118,7 @@ void help()
#endif #endif
<< " -K,--kill First kill the blockchain." << endl << " -K,--kill First kill the blockchain." << endl
<< " -R,--rebuild Rebuild the blockchain from the existing database." << endl << " -R,--rebuild Rebuild the blockchain from the existing database." << endl
<< " --genesis-nonce <nonce> Set the Genesis Nonce to the given hex nonce." << endl
<< " -s,--import-secret <secret> Import a secret key into the key store and use as the default." << endl << " -s,--import-secret <secret> Import a secret key into the key store and use as the default." << endl
<< " -S,--import-session-secret <secret> Import a secret key into the key store and use as the default for this session only." << endl << " -S,--import-session-secret <secret> Import a secret key into the key store and use as the default for this session only." << endl
<< " --sign-key <address> Sign all transactions with the key of the given address." << endl << " --sign-key <address> Sign all transactions with the key of the given address." << endl
@ -124,9 +127,11 @@ void help()
<< " --password <password> Give a password for a private key." << endl << " --password <password> Give a password for a private key." << endl
<< endl << endl
<< "Client transacting:" << endl << "Client transacting:" << endl
<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl /*<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (default: 30.679)." << endl << " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (default: 30.679)." << endl
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl << " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl*/
<< " --ask <wei> Set the minimum ask gas price under which no transactions will be mined (default 500000000000)." << endl
<< " --bid <wei> Set the bid gas price for to pay for transactions (default 500000000000)." << endl
<< endl << endl
<< "Client mining:" << endl << "Client mining:" << endl
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl << " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl
@ -299,8 +304,10 @@ int main(int argc, char** argv)
/// Transaction params /// Transaction params
TransactionPriority priority = TransactionPriority::Medium; TransactionPriority priority = TransactionPriority::Medium;
double etherPrice = 30.679; // double etherPrice = 30.679;
double blockFees = 15.0; // double blockFees = 15.0;
u256 askPrice("500000000000");
u256 bidPrice("500000000000");
// javascript console // javascript console
bool useConsole = false; bool useConsole = false;
@ -464,7 +471,19 @@ int main(int argc, char** argv)
} }
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i]; dbPath = argv[++i];
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) else if (arg == "--genesis-nonce" && i + 1 < argc)
{
try
{
CanonBlockChain::setGenesisNonce(Nonce(argv[++i]));
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}
/* else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{ {
try try
{ {
@ -487,6 +506,30 @@ int main(int argc, char** argv)
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1; return -1;
} }
}*/
else if (arg == "--ask" && i + 1 < argc)
{
try
{
askPrice = u256(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}
else if (arg == "--bid" && i + 1 < argc)
{
try
{
bidPrice = u256(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
} }
else if ((arg == "-P" || arg == "--priority") && i + 1 < argc) else if ((arg == "-P" || arg == "--priority") && i + 1 < argc)
{ {
@ -595,21 +638,20 @@ int main(int argc, char** argv)
clientName += "/"; clientName += "/";
string logbuf; string logbuf;
bool silence = false;
std::string additional; std::string additional;
g_logPost = [&](std::string const& a, char const*){ g_logPost = [&](std::string const& a, char const*){
if (silence) if (g_silence)
logbuf += a + "\n"; logbuf += a + "\n";
else else
cout << "\r \r" << a << endl << additional << flush; cout << "\r \r" << a << endl << additional << flush;
}; };
auto getPassword = [&](string const& prompt){ auto getPassword = [&](string const& prompt){
auto s = silence; auto s = g_silence;
silence = true; g_silence = true;
cout << endl; cout << endl;
string ret = dev::getPassword(prompt); string ret = dev::getPassword(prompt);
silence = s; g_silence = s;
return ret; return ret;
}; };
auto getAccountPassword = [&](Address const& a){ auto getAccountPassword = [&](Address const& a){
@ -730,7 +772,8 @@ int main(int argc, char** argv)
cout << ethCredits(); cout << ethCredits();
web3.setIdealPeerCount(peers); web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); // std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
std::shared_ptr<eth::TrivialGasPricer> gasPricer = make_shared<eth::TrivialGasPricer>(askPrice, bidPrice);
eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr; eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr;
StructuredLogger::starting(clientImplString, dev::Version); StructuredLogger::starting(clientImplString, dev::Version);
if (c) if (c)
@ -774,11 +817,11 @@ int main(int argc, char** argv)
string l; string l;
while (!g_exit) while (!g_exit)
{ {
silence = false; g_silence = false;
cout << logbuf << "Press Enter" << flush; cout << logbuf << "Press Enter" << flush;
std::getline(cin, l); std::getline(cin, l);
logbuf.clear(); logbuf.clear();
silence = true; g_silence = true;
#if ETH_READLINE #if ETH_READLINE
if (l.size()) if (l.size())
@ -829,7 +872,7 @@ int main(int argc, char** argv)
iss >> enable; iss >> enable;
c->setForceMining(isTrue(enable)); c->setForceMining(isTrue(enable));
} }
else if (c && cmd == "setblockfees") /* else if (c && cmd == "setblockfees")
{ {
iss >> blockFees; iss >> blockFees;
try try
@ -884,7 +927,7 @@ int main(int argc, char** argv)
cerr << "Unknown priority: " << m << endl; cerr << "Unknown priority: " << m << endl;
} }
cout << "Priority: " << (int)priority << "/8" << endl; cout << "Priority: " << (int)priority << "/8" << endl;
} }*/
else if (cmd == "verbosity") else if (cmd == "verbosity")
{ {
if (iss.peek() != -1) if (iss.peek() != -1)
@ -1187,7 +1230,7 @@ int main(int argc, char** argv)
{ {
OnOpFunc oof; OnOpFunc oof;
if (format == "pretty") if (format == "pretty")
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{ {
dev::eth::VM* vm = vvm; dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM); dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
@ -1198,24 +1241,24 @@ int main(int argc, char** argv)
f << " STORAGE" << endl; f << " STORAGE" << endl;
for (auto const& i: ext->state().storage(ext->myAddress)) for (auto const& i: ext->state().storage(ext->myAddress))
f << showbase << hex << i.first << ": " << i.second << endl; f << showbase << hex << i.first << ": " << i.second << endl;
f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << vm->gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32"; f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << gas << " | -" << dec << gasCost << " | " << newMemSize << "x32";
}; };
else if (format == "standard") else if (format == "standard")
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{ {
dev::eth::VM* vm = vvm; dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM); dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl; f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl;
}; };
else if (format == "standard+") else if (format == "standard+")
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{ {
dev::eth::VM* vm = vvm; dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM); dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE) if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE)
for (auto const& i: ext->state().storage(ext->myAddress)) for (auto const& i: ext->state().storage(ext->myAddress))
f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl; f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl;
}; };
e.initialize(t); e.initialize(t);
if (!e.execute()) if (!e.execute())

39
ethkey/KeyAux.h

@ -102,6 +102,7 @@ public:
List, List,
New, New,
Import, Import,
ImportWithAddress,
Export, Export,
Recode, Recode,
Kill Kill
@ -159,6 +160,13 @@ public:
m_inputs = strings(1, argv[++i]); m_inputs = strings(1, argv[++i]);
m_name = argv[++i]; m_name = argv[++i];
} }
else if ((arg == "-i" || arg == "--import-with-address") && i + 3 < argc)
{
m_mode = OperationMode::ImportWithAddress;
m_inputs = strings(1, argv[++i]);
m_address = Address(argv[++i]);
m_name = argv[++i];
}
else if (arg == "--export") else if (arg == "--export")
m_mode = OperationMode::Export; m_mode = OperationMode::Export;
else if (arg == "--recode") else if (arg == "--recode")
@ -314,6 +322,33 @@ public:
cout << " ICAP: " << ICAP(k.address()).encoded() << endl; cout << " ICAP: " << ICAP(k.address()).encoded() << endl;
break; break;
} }
case OperationMode::ImportWithAddress:
{
string const& i = m_inputs[0];
h128 u;
bytes b;
b = fromHex(i);
if (b.size() != 32)
{
std::string s = contentsString(i);
b = fromHex(s);
if (b.size() != 32)
u = wallet.store().importKey(i);
}
if (!u && b.size() == 32)
u = wallet.store().importSecret(b, lockPassword(toAddress(Secret(b)).abridged()));
if (!u)
{
cerr << "Cannot import " << i << " not a file or secret." << endl;
break;
}
wallet.importExisting(u, m_name, m_address);
cout << "Successfully imported " << i << ":" << endl;
cout << " Name: " << m_name << endl;
cout << " Address: " << m_address << endl;
cout << " UUID: " << toUUID(u) << endl;
break;
}
case OperationMode::List: case OperationMode::List:
{ {
vector<u128> bare; vector<u128> bare;
@ -369,6 +404,7 @@ public:
<< " -l,--list List all keys available in wallet." << endl << " -l,--list List all keys available in wallet." << endl
<< " -n,--new <name> Create a new key with given name and add it in the wallet." << endl << " -n,--new <name> Create a new key with given name and add it in the wallet." << endl
<< " -i,--import [<uuid>|<file>|<secret-hex>] <name> Import keys from given source and place in wallet." << endl << " -i,--import [<uuid>|<file>|<secret-hex>] <name> Import keys from given source and place in wallet." << endl
<< " --import-with-address [<uuid>|<file>|<secret-hex>] <address> <name> Import keys from given source with given address and place in wallet." << endl
<< " -e,--export [ <address>|<uuid> , ... ] Export given keys." << endl << " -e,--export [ <address>|<uuid> , ... ] Export given keys." << endl
<< " -r,--recode [ <address>|<uuid>|<file> , ... ] Decrypt and re-encrypt given keys." << endl << " -r,--recode [ <address>|<uuid>|<file> , ... ] Decrypt and re-encrypt given keys." << endl
<< "Wallet configuration:" << endl << "Wallet configuration:" << endl
@ -418,8 +454,9 @@ private:
string m_lockHint; string m_lockHint;
bool m_icap = true; bool m_icap = true;
/// Creating /// Creating/importing
string m_name; string m_name;
Address m_address;
/// Importing /// Importing
strings m_inputs; strings m_inputs;

19
ethminer/MinerAux.h

@ -127,6 +127,10 @@ public:
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument(); throw BadArgument();
} }
else if (arg == "--use-chunks")
{
dagChunks = 4;
}
else if (arg == "--phone-home" && i + 1 < argc) else if (arg == "--phone-home" && i + 1 < argc)
{ {
string m = argv[++i]; string m = argv[++i];
@ -171,8 +175,16 @@ public:
m_minerType = MinerType::CPU; m_minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--opencl") else if (arg == "-G" || arg == "--opencl")
{ {
m_minerType = MinerType::GPU; if (!ProofOfWork::GPUMiner::haveSufficientGPUMemory())
miningThreads = 1; {
cout << "No GPU device with sufficient memory was found. Defaulting to CPU" << endl;
m_minerType = MinerType::CPU;
}
else
{
m_minerType = MinerType::GPU;
miningThreads = 1;
}
} }
else if (arg == "--no-precompute") else if (arg == "--no-precompute")
{ {
@ -256,6 +268,7 @@ public:
ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform); ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform);
ProofOfWork::GPUMiner::setDefaultDevice(openclDevice); ProofOfWork::GPUMiner::setDefaultDevice(openclDevice);
ProofOfWork::GPUMiner::setNumInstances(miningThreads); ProofOfWork::GPUMiner::setNumInstances(miningThreads);
ProofOfWork::GPUMiner::setDagChunks(dagChunks);
} }
if (mode == OperationMode::DAGInit) if (mode == OperationMode::DAGInit)
doInitDAG(initDAG); doInitDAG(initDAG);
@ -293,6 +306,7 @@ public:
<< " --opencl-platform <n> When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl << " --opencl-platform <n> When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl
<< " --opencl-device <n> When mining using -G/--opencl use OpenCL device n (default: 0)." << endl << " --opencl-device <n> When mining using -G/--opencl use OpenCL device n (default: 0)." << endl
<< " -t, --mining-threads <n> Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl << " -t, --mining-threads <n> Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl
<< " --use-chunks When using GPU mining upload the DAG to the GPU in 4 chunks. " << endl
; ;
} }
@ -480,6 +494,7 @@ private:
unsigned openclPlatform = 0; unsigned openclPlatform = 0;
unsigned openclDevice = 0; unsigned openclDevice = 0;
unsigned miningThreads = UINT_MAX; unsigned miningThreads = UINT_MAX;
unsigned dagChunks = 1;
/// DAG initialisation param. /// DAG initialisation param.
unsigned initDAG = 0; unsigned initDAG = 0;

2
ethminer/farm.json

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

10
evmjit/libevmjit-cpp/Env.cpp

@ -54,7 +54,7 @@ extern "C"
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{ {
u256 gas = *io_gas; u256 gas = *io_gas;
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight); h256 address(_env->create(endowment, gas, {_initBeg, (size_t)_initSize}, {}), h256::AlignRight);
*io_gas = static_cast<int64_t>(gas); *io_gas = static_cast<int64_t>(gas);
*o_address = address; *o_address = address;
} }
@ -69,8 +69,8 @@ extern "C"
params.senderAddress = _env->myAddress; params.senderAddress = _env->myAddress;
params.receiveAddress = right160(*_receiveAddress); params.receiveAddress = right160(*_receiveAddress);
params.codeAddress = right160(*_codeAddress); params.codeAddress = right160(*_codeAddress);
params.data = {_inBeg, _inSize}; params.data = {_inBeg, (size_t)_inSize};
params.out = {_outBeg, _outSize}; params.out = {_outBeg, (size_t)_outSize};
params.onOp = {}; params.onOp = {};
const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL
@ -102,7 +102,7 @@ extern "C"
EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash) EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash)
{ {
auto hash = sha3({_begin, _size}); auto hash = sha3({_begin, (size_t)_size});
*o_hash = hash; *o_hash = hash;
} }
@ -130,7 +130,7 @@ extern "C"
if (_topic4) if (_topic4)
topics.push_back(*_topic4); topics.push_back(*_topic4);
_env->log(std::move(topics), {_beg, _size}); _env->log(std::move(topics), {_beg, (size_t)_size});
} }
} }

16
evmjit/libevmjit-cpp/JitVM.cpp

@ -18,27 +18,25 @@ namespace eth
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{ {
using namespace jit; using namespace jit;
auto rejected = false; auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope // TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= m_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max) rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max(); rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max(); rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max(); rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max();
if (rejected) if (rejected)
{ {
cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter"; cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter";
m_fallbackVM = VMFactory::create(VMKind::Interpreter, m_gas); m_fallbackVM = VMFactory::create(VMKind::Interpreter);
auto&& output = m_fallbackVM->go(_ext, _onOp, _step); return m_fallbackVM->execImpl(io_gas, _ext, _onOp);
m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it
return output;
} }
m_data.gas = static_cast<decltype(m_data.gas)>(m_gas); m_data.gas = static_cast<decltype(m_data.gas)>(io_gas);
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice); m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
m_data.callData = _ext.data.data(); m_data.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size(); m_data.callDataSize = _ext.data.size();
@ -78,7 +76,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
break; break;
} }
m_gas = m_data.gas; // TODO: Remove m_gas field io_gas = m_data.gas;
return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)}; return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)};
} }

10
evmjit/libevmjit-cpp/JitVM.h

@ -10,16 +10,10 @@ namespace eth
class JitVM: public VMFace class JitVM: public VMFace
{ {
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; public:
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final;
virtual u256 gas() const noexcept { return m_gas; }
virtual void reset(u256 const& _gas = 0) noexcept { m_gas = _gas; }
private: private:
friend class VMFactory;
explicit JitVM(u256 _gas = 0): m_gas(_gas) {}
u256 m_gas;
jit::RuntimeData m_data; jit::RuntimeData m_data;
jit::ExecutionEngine m_engine; jit::ExecutionEngine m_engine;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT

2
libdevcore/Common.cpp

@ -28,7 +28,7 @@ using namespace dev;
namespace dev namespace dev
{ {
char const* Version = "0.9.23"; char const* Version = "0.9.24";
const u256 UndefinedU256 = ~(u256)0; const u256 UndefinedU256 = ~(u256)0;

52
libdevcore/CommonData.cpp

@ -67,7 +67,7 @@ std::string dev::randomWord()
return ret; return ret;
} }
int dev::fromHex(char _i) int dev::fromHex(char _i, WhenError _throw)
{ {
if (_i >= '0' && _i <= '9') if (_i >= '0' && _i <= '9')
return _i - '0'; return _i - '0';
@ -75,7 +75,10 @@ int dev::fromHex(char _i)
return _i - 'a' + 10; return _i - 'a' + 10;
if (_i >= 'A' && _i <= 'F') if (_i >= 'A' && _i <= 'F')
return _i - 'A' + 10; return _i - 'A' + 10;
BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i)); if (_throw == WhenError::Throw)
BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i));
else
return -1;
} }
bytes dev::fromHex(std::string const& _s, WhenError _throw) bytes dev::fromHex(std::string const& _s, WhenError _throw)
@ -85,33 +88,26 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw)
ret.reserve((_s.size() - s + 1) / 2); ret.reserve((_s.size() - s + 1) / 2);
if (_s.size() % 2) if (_s.size() % 2)
try {
{ int h = fromHex(_s[s++], WhenError::DontThrow);
ret.push_back(fromHex(_s[s++])); if (h != -1)
} ret.push_back(h);
catch (...) else if (_throw == WhenError::Throw)
{ throw BadHexCharacter();
ret.push_back(0); else
// msvc does not support it return bytes();
#ifndef BOOST_NO_EXCEPTIONS }
cwarn << boost::current_exception_diagnostic_information();
#endif
if (_throw == WhenError::Throw)
throw;
}
for (unsigned i = s; i < _s.size(); i += 2) for (unsigned i = s; i < _s.size(); i += 2)
try {
{ int h = fromHex(_s[i], WhenError::DontThrow);
ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); int l = fromHex(_s[i + 1], WhenError::DontThrow);
} if (h != -1 && l != -1)
catch (...){ ret.push_back((byte)(h * 16 + l));
ret.push_back(0); else if (_throw == WhenError::Throw)
#ifndef BOOST_NO_EXCEPTIONS throw BadHexCharacter();
cwarn << boost::current_exception_diagnostic_information(); else
#endif return bytes();
if (_throw == WhenError::Throw) }
throw;
}
return ret; return ret;
} }

2
libdevcore/CommonData.h

@ -61,7 +61,7 @@ std::string toHex(_T const& _data, int _w = 2, HexPrefix _prefix = HexPrefix::Do
/// Converts a (printable) ASCII hex character into the correspnding integer value. /// Converts a (printable) ASCII hex character into the correspnding integer value.
/// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5 /// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5
int fromHex(char _i); int fromHex(char _i, WhenError _throw);
/// Converts a (printable) ASCII hex string into the corresponding byte stream. /// Converts a (printable) ASCII hex string into the corresponding byte stream.
/// @example fromHex("41626261") == asBytes("Abba") /// @example fromHex("41626261") == asBytes("Abba")

29
libdevcore/Log.cpp

@ -33,7 +33,29 @@ using namespace dev;
// Logging // Logging
int dev::g_logVerbosity = 5; int dev::g_logVerbosity = 5;
map<type_info const*, bool> dev::g_logOverride; mutex x_logOverride;
/// Map of Log Channel types to bool, false forces the channel to be disabled, true forces it to be enabled.
/// If a channel has no entry, then it will output as long as its verbosity (LogChannel::verbosity) is less than
/// or equal to the currently output verbosity (g_logVerbosity).
static map<type_info const*, bool> s_logOverride;
LogOverrideAux::LogOverrideAux(std::type_info const* _ch, bool _value):
m_ch(_ch)
{
Guard l(x_logOverride);
m_old = s_logOverride.count(_ch) ? (int)s_logOverride[_ch] : c_null;
s_logOverride[m_ch] = _value;
}
LogOverrideAux::~LogOverrideAux()
{
Guard l(x_logOverride);
if (m_old == c_null)
s_logOverride.erase(m_ch);
else
s_logOverride[m_ch] = (bool)m_old;
}
#ifdef _WIN32 #ifdef _WIN32
const char* LogChannel::name() { return EthGray "..."; } const char* LogChannel::name() { return EthGray "..."; }
@ -55,8 +77,9 @@ LogOutputStreamBase::LogOutputStreamBase(char const* _id, std::type_info const*
m_autospacing(_autospacing), m_autospacing(_autospacing),
m_verbosity(_v) m_verbosity(_v)
{ {
auto it = g_logOverride.find(_info); Guard l(x_logOverride);
if ((it != g_logOverride.end() && it->second == true) || (it == g_logOverride.end() && (int)_v <= g_logVerbosity)) auto it = s_logOverride.find(_info);
if ((it != s_logOverride.end() && it->second == true) || (it == s_logOverride.end() && (int)_v <= g_logVerbosity))
{ {
time_t rawTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); time_t rawTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
char buf[24]; char buf[24];

31
libdevcore/Log.h

@ -54,10 +54,33 @@ extern int g_logVerbosity;
/// The current method that the logging system uses to output the log messages. Defaults to simpleDebugOut(). /// The current method that the logging system uses to output the log messages. Defaults to simpleDebugOut().
extern std::function<void(std::string const&, char const*)> g_logPost; extern std::function<void(std::string const&, char const*)> g_logPost;
/// Map of Log Channel types to bool, false forces the channel to be disabled, true forces it to be enabled. class LogOverrideAux
/// If a channel has no entry, then it will output as long as its verbosity (LogChannel::verbosity) is less than {
/// or equal to the currently output verbosity (g_logVerbosity). protected:
extern std::map<std::type_info const*, bool> g_logOverride; LogOverrideAux(std::type_info const* _ch, bool _value);
~LogOverrideAux();
private:
std::type_info const* m_ch;
static const int c_null = -1;
int m_old;
};
template <class Channel>
class LogOverride: LogOverrideAux
{
public:
LogOverride(bool _value): LogOverrideAux(&typeid(Channel), _value) {}
};
/// Temporary changes system's verbosity for specific function. Restores the old verbosity when function returns.
/// Not thread-safe, use with caution!
struct VerbosityHolder
{
VerbosityHolder(int _temporaryValue): oldLogVerbosity(g_logVerbosity) { g_logVerbosity = _temporaryValue; }
~VerbosityHolder() { g_logVerbosity = oldLogVerbosity; }
int oldLogVerbosity;
};
#define ETH_THREAD_CONTEXT(name) for (std::pair<dev::ThreadContext, bool> __eth_thread_context(name, true); p.second; p.second = false) #define ETH_THREAD_CONTEXT(name) for (std::pair<dev::ThreadContext, bool> __eth_thread_context(name, true); p.second; p.second = false)

18
libdevcore/TransientDirectory.cpp

@ -19,10 +19,12 @@
* @date 2015 * @date 2015
*/ */
#include <thread>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include "Exceptions.h" #include "Exceptions.h"
#include "TransientDirectory.h" #include "TransientDirectory.h"
#include "CommonIO.h" #include "CommonIO.h"
#include "Log.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -42,5 +44,19 @@ TransientDirectory::TransientDirectory(std::string const& _path):
TransientDirectory::~TransientDirectory() TransientDirectory::~TransientDirectory()
{ {
boost::filesystem::remove_all(m_path); boost::system::error_code ec;
boost::filesystem::remove_all(m_path, ec);
if (!ec)
return;
// In some cases, antivirus runnig on Windows will scan all the newly created directories.
// As a consequence, directory is locked and can not be deleted immediately.
// Retry after 10 milliseconds usually is successful.
// This will help our tests run smoothly in such environment.
this_thread::sleep_for(chrono::milliseconds(10));
ec.clear();
boost::filesystem::remove_all(m_path, ec);
if (!ec)
cwarn << "Failed to delete directory '" << m_path << "': " << ec.message();
} }

2
libdevcrypto/Common.cpp

@ -37,7 +37,7 @@ using namespace dev::crypto;
static Secp256k1 s_secp256k1; static Secp256k1 s_secp256k1;
bool dev::SignatureStruct::isValid() const bool dev::SignatureStruct::isValid() const noexcept
{ {
if (v > 1 || if (v > 1 ||
r >= h256("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") || r >= h256("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") ||

2
libdevcrypto/Common.h

@ -51,7 +51,7 @@ struct SignatureStruct
operator Signature() const { return *(h520 const*)this; } operator Signature() const { return *(h520 const*)this; }
/// @returns true if r,s,v values are valid, otherwise false /// @returns true if r,s,v values are valid, otherwise false
bool isValid() const; bool isValid() const noexcept;
h256 r; h256 r;
h256 s; h256 s;

2
libdevcrypto/SecretStore.cpp

@ -164,7 +164,7 @@ void SecretStore::load(std::string const& _keysPath)
h128 SecretStore::readKey(std::string const& _file, bool _deleteFile) h128 SecretStore::readKey(std::string const& _file, bool _deleteFile)
{ {
cdebug << "Reading" << _file; cnote << "Reading" << _file;
return readKeyContent(contentsString(_file), _deleteFile ? _file : string()); return readKeyContent(contentsString(_file), _deleteFile ? _file : string());
} }

463
libethash-cl/ethash_cl_miner.cpp

@ -24,6 +24,7 @@
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <fstream>
#include <iostream> #include <iostream>
#include <assert.h> #include <assert.h>
#include <queue> #include <queue>
@ -35,6 +36,7 @@
#include "ethash_cl_miner_kernel.h" #include "ethash_cl_miner_kernel.h"
#define ETHASH_BYTES 32 #define ETHASH_BYTES 32
#define ETHASH_CL_MINIMUM_MEMORY 2000000000
// workaround lame platforms // workaround lame platforms
#if !CL_VERSION_1_2 #if !CL_VERSION_1_2
@ -47,6 +49,9 @@
using namespace std; using namespace std;
// 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
static void add_definition(std::string& source, char const* id, unsigned value) static void add_definition(std::string& source, char const* id, unsigned value)
{ {
char buf[256]; char buf[256];
@ -72,7 +77,7 @@ std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _devic
cl::Platform::get(&platforms); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
cout << "No OpenCL platforms found." << endl; ETHCL_LOG("No OpenCL platforms found.");
return std::string(); return std::string();
} }
@ -82,7 +87,7 @@ std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _devic
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty()) if (devices.empty())
{ {
cout << "No OpenCL devices found." << endl; ETHCL_LOG("No OpenCL devices found.");
return std::string(); return std::string();
} }
@ -107,7 +112,7 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
cl::Platform::get(&platforms); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
cout << "No OpenCL platforms found." << endl; ETHCL_LOG("No OpenCL platforms found.");
return 0; return 0;
} }
@ -116,276 +121,302 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty()) if (devices.empty())
{ {
cout << "No OpenCL devices found." << endl; ETHCL_LOG("No OpenCL devices found.");
return 0; return 0;
} }
return devices.size(); return devices.size();
} }
void ethash_cl_miner::finish() bool ethash_cl_miner::haveSufficientGPUMemory(unsigned _platformId)
{
if (m_queue())
m_queue.finish();
}
bool ethash_cl_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned workgroup_size, unsigned _platformId, unsigned _deviceId)
{ {
// get all platforms
std::vector<cl::Platform> platforms; std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
cout << "No OpenCL platforms found." << endl; ETHCL_LOG("No OpenCL platforms found.");
return false; return false;
} }
// use selected platform
_platformId = std::min<unsigned>(_platformId, platforms.size() - 1);
cout << "Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str() << endl;
// get GPU device of the default platform
std::vector<cl::Device> devices; std::vector<cl::Device> devices;
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices); unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty()) if (devices.empty())
{ {
cout << "No OpenCL devices found." << endl; ETHCL_LOG("No OpenCL devices found.");
return false; return false;
} }
// use selected device for (cl::Device const& device: devices)
cl::Device& device = devices[std::min<unsigned>(_deviceId, devices.size() - 1)];
std::string device_version = device.getInfo<CL_DEVICE_VERSION>();
cout << "Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")" << endl;
if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0)
{ {
cout << "OpenCL 1.0 is not supported." << endl; cl_ulong result;
return false; device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
if (result >= ETHASH_CL_MINIMUM_MEMORY)
{
ETHCL_LOG(
"Found suitable OpenCL device [" << device.getInfo<CL_DEVICE_NAME>()
<< "] with " << result << " bytes of GPU memory"
);
return true;
}
else
ETHCL_LOG(
"OpenCL device " << device.getInfo<CL_DEVICE_NAME>()
<< " has insufficient GPU memory." << result <<
" bytes of memory found < " << ETHASH_CL_MINIMUM_MEMORY << " bytes of memory required"
);
} }
if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0) return false;
m_opencl_1_1 = true; }
// create context
m_context = cl::Context(std::vector<cl::Device>(&device, &device + 1));
m_queue = cl::CommandQueue(m_context, device);
// use requested workgroup size, but we require multiple of 8
m_workgroup_size = ((workgroup_size + 7) / 8) * 8;
// patch source code void ethash_cl_miner::finish()
std::string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE); {
add_definition(code, "GROUP_SIZE", m_workgroup_size); if (m_queue())
add_definition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES)); m_queue.finish();
add_definition(code, "ACCESSES", ETHASH_ACCESSES); }
add_definition(code, "MAX_OUTPUTS", c_max_search_results);
//debugf("%s", code.c_str());
// create miner OpenCL program bool ethash_cl_miner::init(
cl::Program::Sources sources; uint8_t const* _dag,
sources.push_back({code.c_str(), code.size()}); uint64_t _dagSize,
unsigned workgroup_size,
unsigned _platformId,
unsigned _deviceId,
unsigned _dagChunksNum
)
{
// for now due to the .cl kernels we can only have either 1 big chunk or 4 chunks
assert(_dagChunksNum == 1 || _dagChunksNum == 4);
// now create the number of chunk buffers
m_dagChunksNum = _dagChunksNum;
cl::Program program(m_context, sources); // get all platforms
try try
{ {
program.build({device}); std::vector<cl::Platform> platforms;
} cl::Platform::get(&platforms);
catch (cl::Error err) if (platforms.empty())
{ {
cout << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str(); ETHCL_LOG("No OpenCL platforms found.");
return false; return false;
} }
m_hash_kernel = cl::Kernel(program, "ethash_hash");
m_search_kernel = cl::Kernel(program, "ethash_search");
// create buffer for dag
m_dag = cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize);
// create buffer for header
m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32);
// compute dag on CPU
try {
m_queue.enqueueWriteBuffer(m_dag, CL_TRUE, 0, _dagSize, _dag);
}
catch (...)
{
// didn't work. shitty driver. try allocating in CPU RAM and manually memcpying it.
void* dag_ptr = m_queue.enqueueMapBuffer(m_dag, true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, _dagSize);
memcpy(dag_ptr, _dag, _dagSize);
m_queue.enqueueUnmapMemObject(m_dag, dag_ptr);
}
// create mining buffers // use selected platform
for (unsigned i = 0; i != c_num_buffers; ++i) _platformId = std::min<unsigned>(_platformId, platforms.size() - 1);
{ ETHCL_LOG("Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str());
m_hash_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY | (!m_opencl_1_1 ? CL_MEM_HOST_READ_ONLY : 0), 32*c_hash_batch_size);
m_search_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_max_search_results + 1) * sizeof(uint32_t));
}
return true;
}
void ethash_cl_miner::hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count) // get GPU device of the default platform
{ std::vector<cl::Device> devices;
struct pending_batch platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
{ if (devices.empty())
unsigned base;
unsigned count;
unsigned buf;
};
std::queue<pending_batch> pending;
// update header constant buffer
m_queue.enqueueWriteBuffer(m_header, true, 0, 32, header);
/*
__kernel void ethash_combined_hash(
__global hash32_t* g_hashes,
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
ulong start_nonce,
uint isolate
)
*/
m_hash_kernel.setArg(1, m_header);
m_hash_kernel.setArg(2, m_dag);
m_hash_kernel.setArg(3, nonce);
m_hash_kernel.setArg(4, ~0u); // have to pass this to stop the compile unrolling the loop
unsigned buf = 0;
for (unsigned i = 0; i < count || !pending.empty(); )
{
// how many this batch
if (i < count)
{ {
unsigned const this_count = std::min<unsigned>(count - i, c_hash_batch_size); ETHCL_LOG("No OpenCL devices found.");
unsigned const batch_count = std::max<unsigned>(this_count, m_workgroup_size); return false;
}
// supply output hash buffer to kernel // use selected device
m_hash_kernel.setArg(0, m_hash_buf[buf]); cl::Device& device = devices[std::min<unsigned>(_deviceId, devices.size() - 1)];
std::string device_version = device.getInfo<CL_DEVICE_VERSION>();
ETHCL_LOG("Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")");
// execute it! if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0)
m_queue.enqueueNDRangeKernel( {
m_hash_kernel, ETHCL_LOG("OpenCL 1.0 is not supported.");
cl::NullRange, return false;
cl::NDRange(batch_count), }
cl::NDRange(m_workgroup_size) if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0)
); m_opencl_1_1 = true;
m_queue.flush();
// create context
pending.push({i, this_count, buf}); m_context = cl::Context(std::vector<cl::Device>(&device, &device + 1));
i += this_count; m_queue = cl::CommandQueue(m_context, device);
buf = (buf + 1) % c_num_buffers;
// use requested workgroup size, but we require multiple of 8
m_workgroup_size = ((workgroup_size + 7) / 8) * 8;
// patch source code
// note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled
// into a byte array by bin2h.cmake. There is no need to load the file by hand in runtime
std::string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
add_definition(code, "GROUP_SIZE", m_workgroup_size);
add_definition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
add_definition(code, "ACCESSES", ETHASH_ACCESSES);
add_definition(code, "MAX_OUTPUTS", c_max_search_results);
//debugf("%s", code.c_str());
// create miner OpenCL program
cl::Program::Sources sources;
sources.push_back({ code.c_str(), code.size() });
cl::Program program(m_context, sources);
try
{
program.build({ device });
ETHCL_LOG("Printing program log");
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
}
catch (cl::Error err)
{
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
return false;
}
if (_dagChunksNum == 1)
{
ETHCL_LOG("Loading single big chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash");
m_search_kernel = cl::Kernel(program, "ethash_search");
}
else
{
ETHCL_LOG("Loading chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash_chunks");
m_search_kernel = cl::Kernel(program, "ethash_search_chunks");
} }
// read results // create buffer for dag
if (i == count || pending.size() == c_num_buffers) if (_dagChunksNum == 1)
{ {
pending_batch const& batch = pending.front(); ETHCL_LOG("Creating one big buffer");
m_dagChunks.push_back(cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize));
}
else
for (unsigned i = 0; i < _dagChunksNum; i++)
{
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
ETHCL_LOG("Creating buffer for chunk " << i);
m_dagChunks.push_back(cl::Buffer(
m_context,
CL_MEM_READ_ONLY,
(i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7
));
}
// create buffer for header
ETHCL_LOG("Creating buffer for header.");
m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32);
// could use pinned host pointer instead, but this path isn't that important. if (_dagChunksNum == 1)
uint8_t* hashes = (uint8_t*)m_queue.enqueueMapBuffer(m_hash_buf[batch.buf], true, CL_MAP_READ, 0, batch.count * ETHASH_BYTES); {
memcpy(ret + batch.base*ETHASH_BYTES, hashes, batch.count*ETHASH_BYTES); ETHCL_LOG("Mapping one big chunk.");
m_queue.enqueueUnmapMemObject(m_hash_buf[batch.buf], hashes); m_queue.enqueueWriteBuffer(m_dagChunks[0], CL_TRUE, 0, _dagSize, _dag);
}
else
{
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
void* dag_ptr[4];
for (unsigned i = 0; i < _dagChunksNum; i++)
{
ETHCL_LOG("Mapping chunk " << i);
dag_ptr[i] = m_queue.enqueueMapBuffer(m_dagChunks[i], true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
}
for (unsigned i = 0; i < _dagChunksNum; i++)
{
memcpy(dag_ptr[i], (char *)_dag + i*((_dagSize >> 9) << 7), (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
m_queue.enqueueUnmapMemObject(m_dagChunks[i], dag_ptr[i]);
}
}
pending.pop(); // create mining buffers
for (unsigned i = 0; i != c_num_buffers; ++i)
{
ETHCL_LOG("Creating mining buffer " << i);
m_hash_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY | (!m_opencl_1_1 ? CL_MEM_HOST_READ_ONLY : 0), 32 * c_hash_batch_size);
m_search_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_max_search_results + 1) * sizeof(uint32_t));
} }
} }
catch (cl::Error err)
{
ETHCL_LOG(err.what() << "(" << err.err() << ")");
return false;
}
return true;
} }
void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook) void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook)
{ {
struct pending_batch try
{ {
uint64_t start_nonce; struct pending_batch
unsigned buf; {
}; uint64_t start_nonce;
std::queue<pending_batch> pending; unsigned buf;
};
std::queue<pending_batch> pending;
uint32_t const c_zero = 0; static uint32_t const c_zero = 0;
// update header constant buffer // update header constant buffer
m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header); m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header);
for (unsigned i = 0; i != c_num_buffers; ++i) for (unsigned i = 0; i != c_num_buffers; ++i)
m_queue.enqueueWriteBuffer(m_search_buf[i], false, 0, 4, &c_zero); m_queue.enqueueWriteBuffer(m_search_buf[i], false, 0, 4, &c_zero);
#if CL_VERSION_1_2 && 0 #if CL_VERSION_1_2 && 0
cl::Event pre_return_event; cl::Event pre_return_event;
if (!m_opencl_1_1) if (!m_opencl_1_1)
m_queue.enqueueBarrierWithWaitList(NULL, &pre_return_event); m_queue.enqueueBarrierWithWaitList(NULL, &pre_return_event);
else else
#endif #endif
m_queue.finish(); m_queue.finish();
/* unsigned argPos = 2;
__kernel void ethash_combined_search( m_search_kernel.setArg(1, m_header);
__global hash32_t* g_hashes, // 0 for (unsigned i = 0; i < m_dagChunksNum; ++i, ++argPos)
__constant hash32_t const* g_header, // 1 m_search_kernel.setArg(argPos, m_dagChunks[i]);
__global hash128_t const* g_dag, // 2 // pass these to stop the compiler unrolling the loops
ulong start_nonce, // 3 m_search_kernel.setArg(argPos + 1, target);
ulong target, // 4 m_search_kernel.setArg(argPos + 2, ~0u);
uint isolate // 5
) unsigned buf = 0;
*/ std::random_device engine;
m_search_kernel.setArg(1, m_header); uint64_t start_nonce = std::uniform_int_distribution<uint64_t>()(engine);
m_search_kernel.setArg(2, m_dag); for (;; start_nonce += c_search_batch_size)
// pass these to stop the compiler unrolling the loops
m_search_kernel.setArg(4, target);
m_search_kernel.setArg(5, ~0u);
unsigned buf = 0;
std::random_device engine;
uint64_t start_nonce = std::uniform_int_distribution<uint64_t>()(engine);
for (; ; start_nonce += c_search_batch_size)
{
// supply output buffer to kernel
m_search_kernel.setArg(0, m_search_buf[buf]);
m_search_kernel.setArg(3, start_nonce);
// execute it!
m_queue.enqueueNDRangeKernel(m_search_kernel, cl::NullRange, c_search_batch_size, m_workgroup_size);
pending.push({start_nonce, buf});
buf = (buf + 1) % c_num_buffers;
// read results
if (pending.size() == c_num_buffers)
{ {
pending_batch const& batch = pending.front(); // supply output buffer to kernel
m_search_kernel.setArg(0, m_search_buf[buf]);
if (m_dagChunksNum == 1)
m_search_kernel.setArg(3, start_nonce);
else
m_search_kernel.setArg(6, start_nonce);
// could use pinned host pointer instead // execute it!
uint32_t* results = (uint32_t*)m_queue.enqueueMapBuffer(m_search_buf[batch.buf], true, CL_MAP_READ, 0, (1+c_max_search_results) * sizeof(uint32_t)); m_queue.enqueueNDRangeKernel(m_search_kernel, cl::NullRange, c_search_batch_size, m_workgroup_size);
unsigned num_found = std::min<unsigned>(results[0], c_max_search_results);
uint64_t nonces[c_max_search_results]; pending.push({ start_nonce, buf });
for (unsigned i = 0; i != num_found; ++i) buf = (buf + 1) % c_num_buffers;
// read results
if (pending.size() == c_num_buffers)
{ {
nonces[i] = batch.start_nonce + results[i+1]; pending_batch const& batch = pending.front();
}
// could use pinned host pointer instead
uint32_t* results = (uint32_t*)m_queue.enqueueMapBuffer(m_search_buf[batch.buf], true, CL_MAP_READ, 0, (1 + c_max_search_results) * sizeof(uint32_t));
unsigned num_found = std::min<unsigned>(results[0], c_max_search_results);
m_queue.enqueueUnmapMemObject(m_search_buf[batch.buf], results); uint64_t nonces[c_max_search_results];
for (unsigned i = 0; i != num_found; ++i)
bool exit = num_found && hook.found(nonces, num_found); nonces[i] = batch.start_nonce + results[i + 1];
exit |= hook.searched(batch.start_nonce, c_search_batch_size); // always report searched before exit
if (exit)
break;
// reset search buffer if we're still going m_queue.enqueueUnmapMemObject(m_search_buf[batch.buf], results);
if (num_found) bool exit = num_found && hook.found(nonces, num_found);
m_queue.enqueueWriteBuffer(m_search_buf[batch.buf], true, 0, 4, &c_zero); exit |= hook.searched(batch.start_nonce, c_search_batch_size); // always report searched before exit
if (exit)
break;
pending.pop(); // reset search buffer if we're still going
if (num_found)
m_queue.enqueueWriteBuffer(m_search_buf[batch.buf], true, 0, 4, &c_zero);
pending.pop();
}
} }
}
// not safe to return until this is ready // not safe to return until this is ready
#if CL_VERSION_1_2 && 0 #if CL_VERSION_1_2 && 0
if (!m_opencl_1_1) if (!m_opencl_1_1)
pre_return_event.wait(); pre_return_event.wait();
#endif #endif
}
catch (cl::Error err)
{
ETHCL_LOG(err.what() << "(" << err.err() << ")");
}
} }

17
libethash-cl/ethash_cl_miner.h

@ -35,12 +35,22 @@ public:
static unsigned get_num_platforms(); static unsigned get_num_platforms();
static unsigned get_num_devices(unsigned _platformId = 0); static unsigned get_num_devices(unsigned _platformId = 0);
static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0); static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0);
static bool haveSufficientGPUMemory(unsigned _platformId = 0);
bool init(uint8_t const* _dag, uint64_t _dagSize, unsigned workgroup_size = 64, unsigned _platformId = 0, unsigned _deviceId = 0); bool init(
uint8_t const* _dag,
uint64_t _dagSize,
unsigned workgroup_size = 64,
unsigned _platformId = 0,
unsigned _deviceId = 0,
unsigned _dagChunksNum = 1
);
void finish(); void finish();
void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count);
void search(uint8_t const* header, uint64_t target, search_hook& hook); 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);
private: private:
enum { c_max_search_results = 63, c_num_buffers = 2, c_hash_batch_size = 1024, c_search_batch_size = 1024*256 }; enum { c_max_search_results = 63, c_num_buffers = 2, c_hash_batch_size = 1024, c_search_batch_size = 1024*256 };
@ -48,7 +58,8 @@ private:
cl::CommandQueue m_queue; cl::CommandQueue m_queue;
cl::Kernel m_hash_kernel; cl::Kernel m_hash_kernel;
cl::Kernel m_search_kernel; cl::Kernel m_search_kernel;
cl::Buffer m_dag; unsigned m_dagChunksNum;
std::vector<cl::Buffer> m_dagChunks;
cl::Buffer m_header; cl::Buffer m_header;
cl::Buffer m_hash_buf[c_num_buffers]; cl::Buffer m_hash_buf[c_num_buffers];
cl::Buffer m_search_buf[c_num_buffers]; cl::Buffer m_search_buf[c_num_buffers];

142
libethash-cl/ethash_cl_miner_kernel.cl

@ -179,13 +179,13 @@ void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate)
// much we try and help the compiler save VGPRs because it seems to throw // much we try and help the compiler save VGPRs because it seems to throw
// that information away, hence the implementation of keccak here // that information away, hence the implementation of keccak here
// doesn't bother. // doesn't bother.
if (isolate) if (isolate)
{ {
keccak_f1600_round((uint2*)a, r++, 25); keccak_f1600_round((uint2*)a, r++, 25);
} }
} }
while (r < 23); while (r < 23);
// final round optimised for digest size // final round optimised for digest size
keccak_f1600_round((uint2*)a, r++, out_size); keccak_f1600_round((uint2*)a, r++, out_size);
} }
@ -232,7 +232,7 @@ hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate)
hash64_t init; hash64_t init;
uint const init_size = countof(init.ulongs); uint const init_size = countof(init.ulongs);
uint const hash_size = countof(header->ulongs); uint const hash_size = countof(header->ulongs);
// sha3_512(header .. nonce) // sha3_512(header .. nonce)
ulong state[25]; ulong state[25];
copy(state, header->ulongs, hash_size); copy(state, header->ulongs, hash_size);
@ -243,6 +243,40 @@ hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate)
return init; return init;
} }
uint inner_loop_chunks(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, __global hash128_t const* g_dag1, __global hash128_t const* g_dag2, __global hash128_t const* g_dag3, uint isolate)
{
uint4 mix = init;
// share init0
if (thread_id == 0)
*share = mix.x;
barrier(CLK_LOCAL_MEM_FENCE);
uint init0 = *share;
uint a = 0;
do
{
bool update_share = thread_id == (a/4) % THREADS_PER_HASH;
#pragma unroll
for (uint i = 0; i != 4; ++i)
{
if (update_share)
{
uint m[4] = { mix.x, mix.y, mix.z, mix.w };
*share = fnv(init0 ^ (a+i), m[i]) % DAG_SIZE;
}
barrier(CLK_LOCAL_MEM_FENCE);
mix = fnv4(mix, *share>=3 * DAG_SIZE / 4 ? g_dag3[*share - 3 * DAG_SIZE / 4].uint4s[thread_id] : *share>=DAG_SIZE / 2 ? g_dag2[*share - DAG_SIZE / 2].uint4s[thread_id] : *share>=DAG_SIZE / 4 ? g_dag1[*share - DAG_SIZE / 4].uint4s[thread_id]:g_dag[*share].uint4s[thread_id]);
}
} while ((a += 4) != (ACCESSES & isolate));
return fnv_reduce(mix);
}
uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate) uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate)
{ {
uint4 mix = init; uint4 mix = init;
@ -276,6 +310,7 @@ uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash12
return fnv_reduce(mix); return fnv_reduce(mix);
} }
hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate) hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate)
{ {
ulong state[25]; ulong state[25];
@ -309,7 +344,7 @@ hash32_t compute_hash_simple(
{ {
mix.uint4s[i] = init.uint4s[i % countof(init.uint4s)]; mix.uint4s[i] = init.uint4s[i % countof(init.uint4s)];
} }
uint mix_val = mix.uints[0]; uint mix_val = mix.uints[0];
uint init0 = mix.uints[0]; uint init0 = mix.uints[0];
uint a = 0; uint a = 0;
@ -333,7 +368,7 @@ hash32_t compute_hash_simple(
{ {
fnv_mix.uints[i] = fnv_reduce(mix.uint4s[i]); fnv_mix.uints[i] = fnv_reduce(mix.uint4s[i]);
} }
return final_hash(&init, &fnv_mix, isolate); return final_hash(&init, &fnv_mix, isolate);
} }
@ -347,6 +382,7 @@ typedef union
hash32_t mix; hash32_t mix;
} compute_hash_share; } compute_hash_share;
hash32_t compute_hash( hash32_t compute_hash(
__local compute_hash_share* share, __local compute_hash_share* share,
__constant hash32_t const* g_header, __constant hash32_t const* g_header,
@ -390,6 +426,53 @@ hash32_t compute_hash(
return final_hash(&init, &mix, isolate); return final_hash(&init, &mix, isolate);
} }
hash32_t compute_hash_chunks(
__local compute_hash_share* share,
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
__global hash128_t const* g_dag1,
__global hash128_t const* g_dag2,
__global hash128_t const* g_dag3,
ulong nonce,
uint isolate
)
{
uint const gid = get_global_id(0);
// Compute one init hash per work item.
hash64_t init = init_hash(g_header, nonce, isolate);
// Threads work together in this phase in groups of 8.
uint const thread_id = gid % THREADS_PER_HASH;
uint const hash_id = (gid % GROUP_SIZE) / THREADS_PER_HASH;
hash32_t mix;
uint i = 0;
do
{
// share init with other threads
if (i == thread_id)
share[hash_id].init = init;
barrier(CLK_LOCAL_MEM_FENCE);
uint4 thread_init = share[hash_id].init.uint4s[thread_id % (64 / sizeof(uint4))];
barrier(CLK_LOCAL_MEM_FENCE);
uint thread_mix = inner_loop_chunks(thread_init, thread_id, share[hash_id].mix.uints, g_dag, g_dag1, g_dag2, g_dag3, isolate);
share[hash_id].mix.uints[thread_id] = thread_mix;
barrier(CLK_LOCAL_MEM_FENCE);
if (i == thread_id)
mix = share[hash_id].mix;
barrier(CLK_LOCAL_MEM_FENCE);
}
while (++i != (THREADS_PER_HASH & isolate));
return final_hash(&init, &mix, isolate);
}
__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) __attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1)))
__kernel void ethash_hash_simple( __kernel void ethash_hash_simple(
__global hash32_t* g_hashes, __global hash32_t* g_hashes,
@ -415,13 +498,15 @@ __kernel void ethash_search_simple(
{ {
uint const gid = get_global_id(0); uint const gid = get_global_id(0);
hash32_t hash = compute_hash_simple(g_header, g_dag, start_nonce + gid, isolate); hash32_t hash = compute_hash_simple(g_header, g_dag, start_nonce + gid, isolate);
if (as_ulong(as_uchar8(hash.ulongs[0]).s76543210) < target)
if (hash.ulongs[countof(hash.ulongs)-1] < target)
{ {
uint slot = min(MAX_OUTPUTS, atomic_inc(&g_output[0]) + 1); uint slot = min(convert_uint(MAX_OUTPUTS), convert_uint(atomic_inc(&g_output[0]) + 1));
g_output[slot] = gid; g_output[slot] = gid;
} }
} }
__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) __attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1)))
__kernel void ethash_hash( __kernel void ethash_hash(
__global hash32_t* g_hashes, __global hash32_t* g_hashes,
@ -458,3 +543,46 @@ __kernel void ethash_search(
g_output[slot] = gid; g_output[slot] = gid;
} }
} }
__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1)))
__kernel void ethash_hash_chunks(
__global hash32_t* g_hashes,
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
__global hash128_t const* g_dag1,
__global hash128_t const* g_dag2,
__global hash128_t const* g_dag3,
ulong start_nonce,
uint isolate
)
{
__local compute_hash_share share[HASHES_PER_LOOP];
uint const gid = get_global_id(0);
g_hashes[gid] = compute_hash_chunks(share, g_header, g_dag, g_dag1, g_dag2, g_dag3,start_nonce + gid, isolate);
}
__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1)))
__kernel void ethash_search_chunks(
__global volatile uint* restrict g_output,
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
__global hash128_t const* g_dag1,
__global hash128_t const* g_dag2,
__global hash128_t const* g_dag3,
ulong start_nonce,
ulong target,
uint isolate
)
{
__local compute_hash_share share[HASHES_PER_LOOP];
uint const gid = get_global_id(0);
hash32_t hash = compute_hash_chunks(share, g_header, g_dag, g_dag1, g_dag2, g_dag3, start_nonce + gid, isolate);
if (as_ulong(as_uchar8(hash.ulongs[0]).s76543210) < target)
{
uint slot = min(convert_uint(MAX_OUTPUTS), convert_uint(atomic_inc(&g_output[0]) + 1));
g_output[slot] = gid;
}
}

13
libethash/internal.c

@ -364,6 +364,7 @@ static bool ethash_mmap(struct ethash_full* ret, FILE* f)
{ {
int fd; int fd;
char* mmapped_data; char* mmapped_data;
errno = 0;
ret->file = f; ret->file = f;
if ((fd = ethash_fileno(ret->file)) == -1) { if ((fd = ethash_fileno(ret->file)) == -1) {
return false; return false;
@ -400,38 +401,48 @@ ethash_full_t ethash_full_new_internal(
ret->file_size = (size_t)full_size; ret->file_size = (size_t)full_size;
switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) { switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) {
case ETHASH_IO_FAIL: case ETHASH_IO_FAIL:
// ethash_io_prepare will do all ETHASH_CRITICAL() logging in fail case
goto fail_free_full; goto fail_free_full;
case ETHASH_IO_MEMO_MATCH: case ETHASH_IO_MEMO_MATCH:
if (!ethash_mmap(ret, f)) { if (!ethash_mmap(ret, f)) {
ETHASH_CRITICAL("mmap failure()");
goto fail_close_file; goto fail_close_file;
} }
return ret; return ret;
case ETHASH_IO_MEMO_SIZE_MISMATCH: case ETHASH_IO_MEMO_SIZE_MISMATCH:
// if a DAG of same filename but unexpected size is found, silently force new file creation // if a DAG of same filename but unexpected size is found, silently force new file creation
if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) { if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) {
ETHASH_CRITICAL("Could not recreate DAG file after finding existing DAG with unexpected size.");
goto fail_free_full; goto fail_free_full;
} }
// fallthrough to the mismatch case here, DO NOT go through match // fallthrough to the mismatch case here, DO NOT go through match
case ETHASH_IO_MEMO_MISMATCH: case ETHASH_IO_MEMO_MISMATCH:
if (!ethash_mmap(ret, f)) { if (!ethash_mmap(ret, f)) {
ETHASH_CRITICAL("mmap failure()");
goto fail_close_file; goto fail_close_file;
} }
break; break;
} }
if (!ethash_compute_full_data(ret->data, full_size, light, callback)) { if (!ethash_compute_full_data(ret->data, full_size, light, callback)) {
ETHASH_CRITICAL("Failure at computing DAG data.");
goto fail_free_full_data; goto fail_free_full_data;
} }
// after the DAG has been filled then we finalize it by writting the magic number at the beginning // after the DAG has been filled then we finalize it by writting the magic number at the beginning
if (fseek(f, 0, SEEK_SET) != 0) { if (fseek(f, 0, SEEK_SET) != 0) {
ETHASH_CRITICAL("Could not seek to DAG file start to write magic number.");
goto fail_free_full_data; goto fail_free_full_data;
} }
uint64_t const magic_num = ETHASH_DAG_MAGIC_NUM; uint64_t const magic_num = ETHASH_DAG_MAGIC_NUM;
if (fwrite(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) { if (fwrite(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
ETHASH_CRITICAL("Could not write magic number to DAG's beginning.");
goto fail_free_full_data;
}
if (fflush(f) != 0) {// make sure the magic number IS there
ETHASH_CRITICAL("Could not flush memory mapped data to DAG file. Insufficient space?");
goto fail_free_full_data; goto fail_free_full_data;
} }
fflush(f); // make sure the magic number IS there
return ret; return ret;
fail_free_full_data: fail_free_full_data:

21
libethash/io.c

@ -21,6 +21,7 @@
#include "io.h" #include "io.h"
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h>
enum ethash_io_rc ethash_io_prepare( enum ethash_io_rc ethash_io_prepare(
char const* dirname, char const* dirname,
@ -32,15 +33,19 @@ enum ethash_io_rc ethash_io_prepare(
{ {
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE]; char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL; enum ethash_io_rc ret = ETHASH_IO_FAIL;
// reset errno before io calls
errno = 0;
// assert directory exists // assert directory exists
if (!ethash_mkdir(dirname)) { if (!ethash_mkdir(dirname)) {
ETHASH_CRITICAL("Could not create the ethash directory");
goto end; goto end;
} }
ethash_io_mutable_name(ETHASH_REVISION, &seedhash, mutable_name); ethash_io_mutable_name(ETHASH_REVISION, &seedhash, mutable_name);
char* tmpfile = ethash_io_create_filename(dirname, mutable_name, strlen(mutable_name)); char* tmpfile = ethash_io_create_filename(dirname, mutable_name, strlen(mutable_name));
if (!tmpfile) { if (!tmpfile) {
ETHASH_CRITICAL("Could not create the full DAG pathname");
goto end; goto end;
} }
@ -52,6 +57,7 @@ enum ethash_io_rc ethash_io_prepare(
size_t found_size; size_t found_size;
if (!ethash_file_size(f, &found_size)) { if (!ethash_file_size(f, &found_size)) {
fclose(f); fclose(f);
ETHASH_CRITICAL("Could not query size of DAG file: \"%s\"", tmpfile);
goto free_memo; goto free_memo;
} }
if (file_size != found_size - ETHASH_DAG_MAGIC_NUM_SIZE) { if (file_size != found_size - ETHASH_DAG_MAGIC_NUM_SIZE) {
@ -64,6 +70,7 @@ enum ethash_io_rc ethash_io_prepare(
if (fread(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) { if (fread(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
// I/O error // I/O error
fclose(f); fclose(f);
ETHASH_CRITICAL("Could not read from DAG file: \"%s\"", tmpfile);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH; ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo; goto free_memo;
} }
@ -80,15 +87,25 @@ enum ethash_io_rc ethash_io_prepare(
// file does not exist, will need to be created // file does not exist, will need to be created
f = ethash_fopen(tmpfile, "wb+"); f = ethash_fopen(tmpfile, "wb+");
if (!f) { if (!f) {
ETHASH_CRITICAL("Could not create DAG file: \"%s\"", tmpfile);
goto free_memo; goto free_memo;
} }
// make sure it's of the proper size // make sure it's of the proper size
if (fseek(f, (long int)(file_size + ETHASH_DAG_MAGIC_NUM_SIZE - 1), SEEK_SET) != 0) { if (fseek(f, (long int)(file_size + ETHASH_DAG_MAGIC_NUM_SIZE - 1), SEEK_SET) != 0) {
fclose(f); fclose(f);
ETHASH_CRITICAL("Could not seek to the end of DAG file: \"%s\". Insufficient space?", tmpfile);
goto free_memo;
}
if (fputc('\n', f) == EOF) {
fclose(f);
ETHASH_CRITICAL("Could not write in the end of DAG file: \"%s\". Insufficient space?", tmpfile);
goto free_memo;
}
if (fflush(f) != 0) {
fclose(f);
ETHASH_CRITICAL("Could not flush at end of DAG file: \"%s\". Insufficient space?", tmpfile);
goto free_memo; goto free_memo;
} }
fputc('\n', f);
fflush(f);
ret = ETHASH_IO_MEMO_MISMATCH; ret = ETHASH_IO_MEMO_MISMATCH;
goto set_file; goto set_file;

17
libethash/io.h

@ -54,6 +54,23 @@ enum ethash_io_rc {
#define snprintf(...) sprintf_s(__VA_ARGS__) #define snprintf(...) sprintf_s(__VA_ARGS__)
#endif #endif
/**
* Logs a critical error in important parts of ethash. Should mostly help
* figure out what kind of problem (I/O, memory e.t.c.) causes a NULL
* ethash_full_t
*/
#ifdef ETHASH_PRINT_CRITICAL_OUTPUT
#define ETHASH_CRITICAL(...) \
do \
{ \
printf("ETHASH CRITICAL ERROR: "__VA_ARGS__); \
printf("\n"); \
fflush(stdout); \
} while (0)
#else
#define ETHASH_CRITICAL(...)
#endif
/** /**
* Prepares io for ethash * Prepares io for ethash
* *

6
libethcore/Common.cpp

@ -46,6 +46,12 @@ const unsigned c_databaseBaseVersion = 9;
const unsigned c_databaseVersionModifier = 0; const unsigned c_databaseVersionModifier = 0;
#endif #endif
#if ETH_FRONTIER
Network const c_network = Network::Frontier;
#else
Network const c_network = Network::Olympic;
#endif
const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (ProofOfWork::revision() << 9); const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (ProofOfWork::revision() << 9);
vector<pair<u256, string>> const& units() vector<pair<u256, string>> const& units()

8
libethcore/Common.h

@ -43,6 +43,14 @@ extern const unsigned c_minorProtocolVersion;
/// Current database version. /// Current database version.
extern const unsigned c_databaseVersion; extern const unsigned c_databaseVersion;
/// The network id.
enum class Network
{
Olympic = 0,
Frontier = 1
};
extern const Network c_network;
/// User-friendly string representation of the amount _b in wei. /// User-friendly string representation of the amount _b in wei.
std::string formatBalance(bigint const& _b); std::string formatBalance(bigint const& _b);

8
libethcore/Ethash.cpp

@ -285,6 +285,7 @@ private:
unsigned Ethash::GPUMiner::s_platformId = 0; unsigned Ethash::GPUMiner::s_platformId = 0;
unsigned Ethash::GPUMiner::s_deviceId = 0; unsigned Ethash::GPUMiner::s_deviceId = 0;
unsigned Ethash::GPUMiner::s_numInstances = 0; unsigned Ethash::GPUMiner::s_numInstances = 0;
unsigned Ethash::GPUMiner::s_dagChunks = 1;
Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci):
Miner(_ci), Miner(_ci),
@ -345,7 +346,7 @@ void Ethash::GPUMiner::workLoop()
this_thread::sleep_for(chrono::milliseconds(500)); this_thread::sleep_for(chrono::milliseconds(500));
} }
bytesConstRef dagData = dag->data(); bytesConstRef dagData = dag->data();
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device); m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device, s_dagChunks);
} }
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
@ -363,6 +364,11 @@ void Ethash::GPUMiner::pause()
stopWorking(); stopWorking();
} }
bool Ethash::GPUMiner::haveSufficientGPUMemory()
{
return ethash_cl_miner::haveSufficientGPUMemory(s_platformId);
}
std::string Ethash::GPUMiner::platformInfo() std::string Ethash::GPUMiner::platformInfo()
{ {
return ethash_cl_miner::platform_info(s_platformId, s_deviceId); return ethash_cl_miner::platform_info(s_platformId, s_deviceId);

5
libethcore/Ethash.h

@ -87,7 +87,9 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); } static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); }
static std::string platformInfo(); static std::string platformInfo();
static bool haveSufficientGPUMemory() { return false; }
static void setDefaultPlatform(unsigned) {} static void setDefaultPlatform(unsigned) {}
static void setDagChunks(unsigned) {}
static void setDefaultDevice(unsigned) {} static void setDefaultDevice(unsigned) {}
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
protected: protected:
@ -115,10 +117,12 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : 1; } static unsigned instances() { return s_numInstances > 0 ? s_numInstances : 1; }
static std::string platformInfo(); static std::string platformInfo();
static bool haveSufficientGPUMemory();
static unsigned getNumDevices(); static unsigned getNumDevices();
static void setDefaultPlatform(unsigned _id) { s_platformId = _id; } static void setDefaultPlatform(unsigned _id) { s_platformId = _id; }
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; } static void setDefaultDevice(unsigned _id) { s_deviceId = _id; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); }
static void setDagChunks(unsigned _dagChunks) { s_dagChunks = _dagChunks; }
protected: protected:
void kickOff() override; void kickOff() override;
@ -137,6 +141,7 @@ public:
static unsigned s_platformId; static unsigned s_platformId;
static unsigned s_deviceId; static unsigned s_deviceId;
static unsigned s_numInstances; static unsigned s_numInstances;
static unsigned s_dagChunks;
}; };
#else #else
using GPUMiner = CPUMiner; using GPUMiner = CPUMiner;

5
libethcore/EthashAux.cpp

@ -137,7 +137,10 @@ EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback
full = ethash_full_new(_light, _cb); full = ethash_full_new(_light, _cb);
// cdebug << "Called OK."; // cdebug << "Called OK.";
if (!full) if (!full)
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new()")); {
clog(DAGChannel) << "DAG Generation Failure. Reason: " << strerror(errno);
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new"));
}
} }
EthashAux::FullAllocation::~FullAllocation() EthashAux::FullAllocation::~FullAllocation()

23
libethcore/KeyManager.cpp

@ -89,18 +89,18 @@ bool KeyManager::load(std::string const& _pass)
for (auto const& i: s[1]) for (auto const& i: s[1])
{ {
m_keyInfo[m_addrLookup[(Address)i[0]] = (h128)i[1]] = KeyInfo((h256)i[2], (std::string)i[3]); m_keyInfo[m_addrLookup[(Address)i[0]] = (h128)i[1]] = KeyInfo((h256)i[2], (std::string)i[3]);
cdebug << toString((Address)i[0]) << toString((h128)i[1]) << toString((h256)i[2]) << (std::string)i[3]; // cdebug << toString((Address)i[0]) << toString((h128)i[1]) << toString((h256)i[2]) << (std::string)i[3];
} }
for (auto const& i: s[2]) for (auto const& i: s[2])
m_passwordInfo[(h256)i[0]] = (std::string)i[1]; m_passwordInfo[(h256)i[0]] = (std::string)i[1];
m_password = (string)s[3]; m_password = (string)s[3];
} }
cdebug << hashPassword(m_password) << toHex(m_password); // cdebug << hashPassword(m_password) << toHex(m_password);
m_cachedPasswords[hashPassword(m_password)] = m_password; m_cachedPasswords[hashPassword(m_password)] = m_password;
cdebug << hashPassword(asString(m_key.ref())) << m_key.hex(); // cdebug << hashPassword(asString(m_key.ref())) << m_key.hex();
m_cachedPasswords[hashPassword(asString(m_key.ref()))] = asString(m_key.ref()); m_cachedPasswords[hashPassword(asString(m_key.ref()))] = asString(m_key.ref());
cdebug << hashPassword(_pass) << _pass; // cdebug << hashPassword(_pass) << _pass;
m_cachedPasswords[m_master = hashPassword(_pass)] = _pass; m_cachedPasswords[m_master = hashPassword(_pass)] = _pass;
return true; return true;
} }
@ -141,7 +141,7 @@ std::string KeyManager::getPassword(h256 const& _passHash, function<std::string(
std::string p = _pass(); std::string p = _pass();
if (p.empty()) if (p.empty())
break; break;
if (hashPassword(p) == _passHash || !_passHash) if (hashPassword(p) == _passHash || _passHash == UnknownPassword)
{ {
m_cachedPasswords[hashPassword(p)] = p; m_cachedPasswords[hashPassword(p)] = p;
return p; return p;
@ -186,12 +186,17 @@ void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, std
return; return;
Address a = KeyPair(Secret(key)).address(); Address a = KeyPair(Secret(key)).address();
auto passHash = hashPassword(_pass); auto passHash = hashPassword(_pass);
if (!m_passwordInfo.count(passHash))
m_passwordInfo[passHash] = _passInfo;
if (!m_cachedPasswords.count(passHash)) if (!m_cachedPasswords.count(passHash))
m_cachedPasswords[passHash] = _pass; m_cachedPasswords[passHash] = _pass;
m_addrLookup[a] = _uuid; importExisting(_uuid, _info, a, passHash, _passInfo);
m_keyInfo[_uuid].passHash = passHash; }
void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, Address const& _address, h256 const& _passHash, std::string const& _passInfo)
{
if (!m_passwordInfo.count(_passHash))
m_passwordInfo[_passHash] = _passInfo;
m_addrLookup[_address] = _uuid;
m_keyInfo[_uuid].passHash = _passHash;
m_keyInfo[_uuid].info = _info; m_keyInfo[_uuid].info = _info;
write(m_keysFile); write(m_keysFile);
} }

11
libethcore/KeyManager.h

@ -30,17 +30,19 @@ namespace dev
{ {
namespace eth namespace eth
{ {
class UnknownPassword: public Exception {}; class PasswordUnknown: public Exception {};
struct KeyInfo struct KeyInfo
{ {
KeyInfo() = default; KeyInfo() = default;
KeyInfo(h256 const& _passHash, std::string const& _info): passHash(_passHash), info(_info) {} KeyInfo(h256 const& _passHash, std::string const& _info): passHash(_passHash), info(_info) {}
h256 passHash;
std::string info; h256 passHash; ///< Hash of the password or h256() if unknown.
std::string info; ///< Name of the key, or JSON key info if begins with '{'.
}; };
static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::string(); }; static const h256 UnknownPassword;
static const auto DontKnowThrow = [](){ throw PasswordUnknown(); return std::string(); };
enum class SemanticPassword enum class SemanticPassword
{ {
@ -89,6 +91,7 @@ public:
SecretStore& store() { return m_store; } SecretStore& store() { return m_store; }
void importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo); void importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo);
void importExisting(h128 const& _uuid, std::string const& _info) { importExisting(_uuid, _info, defaultPassword(), std::string()); } void importExisting(h128 const& _uuid, std::string const& _info) { importExisting(_uuid, _info, defaultPassword(), std::string()); }
void importExisting(h128 const& _uuid, std::string const& _info, Address const& _addr, h256 const& _passHash = h256(), std::string const& _passInfo = std::string());
Secret secret(Address const& _address, std::function<std::string()> const& _pass = DontKnowThrow) const; Secret secret(Address const& _address, std::function<std::string()> const& _pass = DontKnowThrow) const;
Secret secret(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const; Secret secret(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const;

3
libethcore/Params.cpp

@ -20,6 +20,7 @@
*/ */
#include "Params.h" #include "Params.h"
#include "Common.h"
using namespace std; using namespace std;
namespace dev namespace dev
@ -35,7 +36,7 @@ u256 const c_minGasLimit = 125000;
u256 const c_gasLimitBoundDivisor = 1024; u256 const c_gasLimitBoundDivisor = 1024;
u256 const c_minimumDifficulty = 131072; u256 const c_minimumDifficulty = 131072;
u256 const c_difficultyBoundDivisor = 2048; u256 const c_difficultyBoundDivisor = 2048;
u256 const c_durationLimit = 8; u256 const c_durationLimit = c_network == Network::Olympic ? 8 : 12;
//--- END: AUTOGENERATED FROM /feeStructure.json //--- END: AUTOGENERATED FROM /feeStructure.json
} }

14
libethereum/BlockQueue.cpp

@ -81,7 +81,7 @@ void BlockQueue::verifierBody()
res.first.populate(res.second, CheckEverything, work.first); res.first.populate(res.second, CheckEverything, work.first);
res.first.verifyInternals(&res.second); res.first.verifyInternals(&res.second);
} }
catch (InvalidNonce&) catch (InvalidBlockNonce&)
{ {
badBlock(res.second, "Invalid block nonce"); badBlock(res.second, "Invalid block nonce");
cwarn << " Nonce:" << res.first.nonce.hex(); cwarn << " Nonce:" << res.first.nonce.hex();
@ -418,3 +418,15 @@ void BlockQueue::retryAllUnknown()
m_unknown.clear(); m_unknown.clear();
m_moreToVerify.notify_all(); m_moreToVerify.notify_all();
} }
std::ostream& dev::eth::operator<<(std::ostream& _out, BlockQueueStatus const& _bqs)
{
_out << "verified: " << _bqs.verified << endl;
_out << "verifying: " << _bqs.verifying << endl;
_out << "unverified: " << _bqs.unverified << endl;
_out << "future: " << _bqs.future << endl;
_out << "unknown: " << _bqs.unknown << endl;
_out << "bad: " << _bqs.bad << endl;
return _out;
}

2
libethereum/BlockQueue.h

@ -136,5 +136,7 @@ private:
bool m_deleting = false; ///< Exit condition for verifiers. bool m_deleting = false; ///< Exit condition for verifiers.
}; };
std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s);
} }
} }

26
libethereum/CanonBlockChain.cpp

@ -72,6 +72,7 @@ std::unordered_map<Address, Account> const& dev::eth::genesisState()
std::unique_ptr<BlockInfo> CanonBlockChain::s_genesis; std::unique_ptr<BlockInfo> CanonBlockChain::s_genesis;
boost::shared_mutex CanonBlockChain::x_genesis; boost::shared_mutex CanonBlockChain::x_genesis;
Nonce CanonBlockChain::s_nonce(u64(42));
bytes CanonBlockChain::createGenesisBlock() bytes CanonBlockChain::createGenesisBlock()
{ {
@ -87,12 +88,33 @@ bytes CanonBlockChain::createGenesisBlock()
} }
block.appendList(15) block.appendList(15)
<< h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 << string() << h256() << Nonce(u64(42)); << h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 << string() << h256() << s_nonce;
block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList);
return block.out(); return block.out();
} }
CanonBlockChain::CanonBlockChain(std::string const& _path, WithExisting _we, ProgressCallback const& _pc): BlockChain(CanonBlockChain::createGenesisBlock(), _path, _we, _pc) CanonBlockChain::CanonBlockChain(std::string const& _path, WithExisting _we, ProgressCallback const& _pc):
BlockChain(createGenesisBlock(), _path, _we, _pc)
{ {
} }
void CanonBlockChain::setGenesisNonce(Nonce const& _n)
{
WriteGuard l(x_genesis);
s_nonce = _n;
s_genesis.reset();
}
BlockInfo const& CanonBlockChain::genesis()
{
UpgradableGuard l(x_genesis);
if (!s_genesis)
{
auto gb = createGenesisBlock();
UpgradeGuard ul(l);
s_genesis.reset(new BlockInfo);
s_genesis->populate(&gb);
}
return *s_genesis;
}

28
libethereum/CanonBlockChain.h

@ -54,21 +54,27 @@ std::unordered_map<Address, Account> const& genesisState();
class CanonBlockChain: public BlockChain class CanonBlockChain: public BlockChain
{ {
public: public:
CanonBlockChain(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()): CanonBlockChain(std::string(), _we, _pc) {} CanonBlockChain(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()): CanonBlockChain(std::string(), _we, _pc) {}
CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()); CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback());
~CanonBlockChain() {} ~CanonBlockChain() {}
/// @returns the genesis block header. /// @returns the genesis block header.
static BlockInfo const& genesis() { UpgradableGuard l(x_genesis); if (!s_genesis) { auto gb = createGenesisBlock(); UpgradeGuard ul(l); s_genesis.reset(new BlockInfo); s_genesis->populate(&gb); } return *s_genesis; } static BlockInfo const& genesis();
/// @returns the genesis block as its RLP-encoded byte array. /// @returns the genesis block as its RLP-encoded byte array.
/// @note This is slow as it's constructed anew each call. Consider genesis() instead. /// @note This is slow as it's constructed anew each call. Consider genesis() instead.
static bytes createGenesisBlock(); static bytes createGenesisBlock();
/// Alter the value of the genesis block's nonce.
/// @warning Unless you're very careful, make sure you call this right at the start of the
/// program, before anything has had the chance to use this class at all.
static void setGenesisNonce(Nonce const& _n);
private: private:
/// Static genesis info and its lock. /// Static genesis info and its lock.
static boost::shared_mutex x_genesis; static boost::shared_mutex x_genesis;
static std::unique_ptr<BlockInfo> s_genesis; static std::unique_ptr<BlockInfo> s_genesis;
static Nonce s_nonce;
}; };
} }

17
libethereum/Client.cpp

@ -47,8 +47,11 @@ VersionChecker::VersionChecker(string const& _dbPath):
(void)protocolVersion; (void)protocolVersion;
auto minorProtocolVersion = (unsigned)status[1]; auto minorProtocolVersion = (unsigned)status[1];
auto databaseVersion = (unsigned)status[2]; auto databaseVersion = (unsigned)status[2];
h256 ourGenesisHash = CanonBlockChain::genesis().hash();
auto genesisHash = status.itemCount() > 3 ? (h256)status[3] : ourGenesisHash;
m_action = m_action =
databaseVersion != c_databaseVersion ? databaseVersion != c_databaseVersion || genesisHash != ourGenesisHash ?
WithExisting::Kill WithExisting::Kill
: minorProtocolVersion != eth::c_minorProtocolVersion ? : minorProtocolVersion != eth::c_minorProtocolVersion ?
WithExisting::Verify WithExisting::Verify
@ -73,7 +76,7 @@ void VersionChecker::setOk()
{ {
cwarn << "Unhandled exception! Failed to create directory: " << m_path << "\n" << boost::current_exception_diagnostic_information(); 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)); writeFile(m_path + "/status", rlpList(eth::c_protocolVersion, eth::c_minorProtocolVersion, c_databaseVersion, CanonBlockChain::genesis().hash()));
} }
} }
@ -269,9 +272,11 @@ void Client::killChain()
{ {
WriteGuard l(x_postMine); WriteGuard l(x_postMine);
WriteGuard l2(x_preMine); WriteGuard l2(x_preMine);
WriteGuard l3(x_working);
m_preMine = State(); m_preMine = State();
m_postMine = State(); m_postMine = State();
m_working = State();
m_stateDB = OverlayDB(); m_stateDB = OverlayDB();
m_stateDB = State::openDB(Defaults::dbPath(), WithExisting::Kill); m_stateDB = State::openDB(Defaults::dbPath(), WithExisting::Kill);
@ -284,6 +289,7 @@ void Client::killChain()
if (auto h = m_host.lock()) if (auto h = m_host.lock())
h->reset(); h->reset();
startedWorking();
doWork(); doWork();
startWorking(); startWorking();
@ -423,9 +429,10 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
temp = m_postMine; temp = m_postMine;
temp.addBalance(_from, _value + _gasPrice * _gas); temp.addBalance(_from, _value + _gasPrice * _gas);
Executive e(temp, LastHashes(), 0); Executive e(temp, LastHashes(), 0);
if (!e.call(_dest, _dest, _from, _value, _gasPrice, &_data, _gas, _from)) e.setResultRecipient(ret);
if (!e.call(_dest, _from, _value, _gasPrice, &_data, _gas))
e.go(); e.go();
ret = e.executionResult(); e.finalize();
} }
catch (...) catch (...)
{ {
@ -659,7 +666,7 @@ void Client::doWork()
syncBlockQueue(); syncBlockQueue();
t = true; t = true;
if (m_syncTransactionQueue.compare_exchange_strong(t, false) && !m_remoteWorking) if (m_syncTransactionQueue.compare_exchange_strong(t, false) && !m_remoteWorking && !isSyncing())
syncTransactionQueue(); syncTransactionQueue();
tick(); tick();

1
libethereum/Client.h

@ -133,6 +133,7 @@ public:
/// Resets the gas pricer to some other object. /// Resets the gas pricer to some other object.
void setGasPricer(std::shared_ptr<GasPricer> _gp) { m_gp = _gp; } void setGasPricer(std::shared_ptr<GasPricer> _gp) { m_gp = _gp; }
std::shared_ptr<GasPricer> gasPricer() const { return m_gp; }
/// Blocks until all pending transactions have been processed. /// Blocks until all pending transactions have been processed.
virtual void flushTransactions() override; virtual void flushTransactions() override;

6
libethereum/CommonNet.h

@ -38,14 +38,16 @@ namespace eth
#if ETH_DEBUG #if ETH_DEBUG
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send. static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for. static const unsigned c_maxHashesAsk = 256; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send. static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). static const unsigned c_maxBlocksAsk = 8; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
static const unsigned c_maxPayload = 262144; ///< Maximum size of packet for us to send.
#else #else
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send. static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for. static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send. static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
static const unsigned c_maxPayload = 262144; ///< Maximum size of packet for us to send.
#endif #endif
class BlockChain; class BlockChain;

163
libethereum/EthereumHost.cpp

@ -54,7 +54,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
EthereumHost::~EthereumHost() EthereumHost::~EthereumHost()
{ {
forEachPeer([](EthereumPeer* _p) { _p->abortSync(); }); foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
} }
bool EthereumHost::ensureInitialised() bool EthereumHost::ensureInitialised()
@ -74,7 +74,7 @@ bool EthereumHost::ensureInitialised()
void EthereumHost::reset() void EthereumHost::reset()
{ {
forEachPeer([](EthereumPeer* _p) { _p->abortSync(); }); foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
m_man.resetToChain(h256s()); m_man.resetToChain(h256s());
m_hashMan.reset(m_chain.number() + 1); m_hashMan.reset(m_chain.number() + 1);
m_needSyncBlocks = true; m_needSyncBlocks = true;
@ -91,7 +91,7 @@ void EthereumHost::doWork()
bool netChange = ensureInitialised(); bool netChange = ensureInitialised();
auto h = m_chain.currentHash(); auto h = m_chain.currentHash();
// If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks // If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks
if (!isSyncing() && m_chain.isKnown(m_latestBlockSent)) if (isSyncing() && m_chain.isKnown(m_latestBlockSent))
{ {
if (m_newTransactions) if (m_newTransactions)
{ {
@ -105,7 +105,7 @@ void EthereumHost::doWork()
} }
} }
forEachPeer([](EthereumPeer* _p) { _p->tick(); }); foreachPeer([](EthereumPeer* _p) { _p->tick(); });
// return netChange; // return netChange;
// TODO: Figure out what to do with netChange. // TODO: Figure out what to do with netChange.
@ -120,12 +120,13 @@ void EthereumHost::maintainTransactions()
for (auto const& i: ts) for (auto const& i: ts)
{ {
bool unsent = !m_transactionsSent.count(i.first); bool unsent = !m_transactionsSent.count(i.first);
for (auto const& p: randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }).second) auto peers = get<1>(randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }));
for (auto const& p: peers)
peerTransactions[p].push_back(i.first); peerTransactions[p].push_back(i.first);
} }
for (auto const& t: ts) for (auto const& t: ts)
m_transactionsSent.insert(t.first); m_transactionsSent.insert(t.first);
forEachPeerPtr([&](shared_ptr<EthereumPeer> _p) foreachPeerPtr([&](shared_ptr<EthereumPeer> _p)
{ {
bytes b; bytes b;
unsigned n = 0; unsigned n = 0;
@ -148,16 +149,16 @@ void EthereumHost::maintainTransactions()
}); });
} }
void EthereumHost::forEachPeer(std::function<void(EthereumPeer*)> const& _f) const void EthereumHost::foreachPeer(std::function<void(EthereumPeer*)> const& _f) const
{ {
forEachPeerPtr([&](std::shared_ptr<EthereumPeer> _p) foreachPeerPtr([&](std::shared_ptr<EthereumPeer> _p)
{ {
if (_p) if (_p)
_f(_p.get()); _f(_p.get());
}); });
} }
void EthereumHost::forEachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const void EthereumHost::foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const
{ {
for (auto s: peerSessions()) for (auto s: peerSessions())
_f(s.first->cap<EthereumPeer>()); _f(s.first->cap<EthereumPeer>());
@ -165,24 +166,32 @@ void EthereumHost::forEachPeerPtr(std::function<void(std::shared_ptr<EthereumPee
_f(s.first->cap<EthereumPeer>(c_oldProtocolVersion)); _f(s.first->cap<EthereumPeer>(c_oldProtocolVersion));
} }
pair<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow) tuple<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<Session>>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow)
{ {
pair<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>> ret; vector<shared_ptr<EthereumPeer>> chosen;
forEachPeerPtr([&](shared_ptr<EthereumPeer> _p) vector<shared_ptr<EthereumPeer>> allowed;
vector<shared_ptr<Session>> sessions;
auto const& ps = peerSessions();
allowed.reserve(ps.size());
for (auto const& j: ps)
{ {
if (_p && _allow(_p.get())) auto pp = j.first->cap<EthereumPeer>();
ret.second.push_back(_p); if (_allow(pp.get()))
}); {
allowed.push_back(move(pp));
sessions.push_back(move(j.first));
}
}
size_t size = (ret.second.size() * _percent + 99) / 100; chosen.reserve((ps.size() * _percent + 99) / 100);
ret.second.reserve(size); for (unsigned i = (ps.size() * _percent + 99) / 100; i-- && allowed.size();)
for (unsigned i = size; i-- && ret.second.size();)
{ {
unsigned n = rand() % ret.second.size(); unsigned n = rand() % allowed.size();
ret.first.push_back(std::move(ret.second[n])); chosen.push_back(std::move(allowed[n]));
ret.second.erase(ret.second.begin() + n); allowed.erase(allowed.begin() + n);
} }
return ret; return make_tuple(move(chosen), move(allowed), move(sessions));
} }
void EthereumHost::maintainBlocks(h256 const& _currentHash) void EthereumHost::maintainBlocks(h256 const& _currentHash)
@ -200,7 +209,7 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
h256s blocks = get<0>(m_chain.treeRoute(m_latestBlockSent, _currentHash, false, false, true)); h256s blocks = get<0>(m_chain.treeRoute(m_latestBlockSent, _currentHash, false, false, true));
auto s = randomSelection(25, [&](EthereumPeer* p){ DEV_GUARDED(p->x_knownBlocks) return !p->m_knownBlocks.count(_currentHash); return false; }); auto s = randomSelection(25, [&](EthereumPeer* p){ DEV_GUARDED(p->x_knownBlocks) return !p->m_knownBlocks.count(_currentHash); return false; });
for (shared_ptr<EthereumPeer> const& p: s.first) for (shared_ptr<EthereumPeer> const& p: get<0>(s))
for (auto const& b: blocks) for (auto const& b: blocks)
{ {
RLPStream ts; RLPStream ts;
@ -210,7 +219,7 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
p->sealAndSend(ts); p->sealAndSend(ts);
p->m_knownBlocks.clear(); p->m_knownBlocks.clear();
} }
for (shared_ptr<EthereumPeer> const& p: s.second) for (shared_ptr<EthereumPeer> const& p: get<1>(s))
{ {
RLPStream ts; RLPStream ts;
p->prep(ts, NewBlockHashesPacket, blocks.size()); p->prep(ts, NewBlockHashesPacket, blocks.size());
@ -241,11 +250,12 @@ void EthereumHost::onPeerStatus(EthereumPeer* _peer)
_peer->disable("Peer banned for previous bad behaviour."); _peer->disable("Peer banned for previous bad behaviour.");
else else
{ {
_peer->m_protocolVersion = EthereumHost::c_oldProtocolVersion; //force V60 for now
if (_peer->m_protocolVersion != protocolVersion()) if (_peer->m_protocolVersion != protocolVersion())
estimatePeerHashes(_peer); estimatePeerHashes(_peer);
else if (_peer->m_latestBlockNumber > m_chain.number()) else if (_peer->m_latestBlockNumber > m_chain.number())
_peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number(); _peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number() + 1000;
else
_peer->m_expectedHashes = 1000;
if (m_hashMan.chainSize() < _peer->m_expectedHashes) if (m_hashMan.chainSize() < _peer->m_expectedHashes)
m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes); m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes);
continueSync(_peer); continueSync(_peer);
@ -283,6 +293,7 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
unsigned knowns = 0; unsigned knowns = 0;
unsigned unknowns = 0; unsigned unknowns = 0;
h256s neededBlocks; h256s neededBlocks;
bool syncByNumber = !m_syncingLatestHash;
for (unsigned i = 0; i < _hashes.size(); ++i) for (unsigned i = 0; i < _hashes.size(); ++i)
{ {
_peer->addRating(1); _peer->addRating(1);
@ -290,10 +301,14 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
auto status = m_bq.blockStatus(h); auto status = m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || m_chain.isKnown(h)) if (status == QueueStatus::Importing || status == QueueStatus::Ready || m_chain.isKnown(h))
{ {
clog(NetMessageSummary) << "block hash ready:" << h << ". Start blocks download..."; clog(NetMessageSummary) << "Block hash already known:" << h;
m_hashes += neededBlocks; if (!syncByNumber)
onPeerDoneHashes(_peer, true); {
return; m_hashes += neededBlocks;
clog(NetMessageSummary) << "Start blocks download...";
onPeerDoneHashes(_peer, true);
return;
}
} }
else if (status == QueueStatus::Bad) else if (status == QueueStatus::Bad)
{ {
@ -308,65 +323,25 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
} }
else else
knowns++; knowns++;
m_syncingLatestHash = h; if (!syncByNumber)
} m_syncingLatestHash = h;
m_hashes += neededBlocks;
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLatestHash;
if (_complete)
{
m_needSyncBlocks = true;
continueSync(_peer);
} }
else if (m_hashes.size() > _peer->m_expectedHashes) if (syncByNumber)
{ {
_peer->disable("Too many hashes"); m_man.appendToChain(neededBlocks); // Append to download manager immediatelly
m_hashes.clear(); clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns";
m_syncingLatestHash = h256();
continueSync(); ///Try with some other peer, keep the chain
} }
else else
continueSync(_peer); /// Grab next hashes
}
void EthereumHost::onPeerHashes(EthereumPeer* _peer, unsigned /*_index*/, h256s const& _hashes)
{
Guard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
if (_hashes.empty())
{ {
onPeerDoneHashes(_peer, true); m_hashes += neededBlocks; // Append to local list
return; clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLatestHash;
} }
unsigned knowns = 0; if (_complete)
unsigned unknowns = 0;
h256s neededBlocks;
for (unsigned i = 0; i < _hashes.size(); ++i)
{ {
_peer->addRating(1); m_needSyncBlocks = true;
auto h = _hashes[i]; continueSync(_peer);
auto status = m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || m_chain.isKnown(h))
{
clog(NetWarn) << "block hash already known:" << h;
}
else if (status == QueueStatus::Bad)
{
clog(NetWarn) << "block hash bad!" << h << ". Bailing...";
_peer->setIdle();
return;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
neededBlocks.push_back(h);
}
else
knowns++;
} }
m_man.appendToChain(neededBlocks); else if (syncByNumber && m_hashMan.isComplete())
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns";
if (m_hashMan.isComplete())
{ {
// Done our chain-get. // Done our chain-get.
m_needSyncHashes = false; m_needSyncHashes = false;
@ -376,8 +351,15 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, unsigned /*_index*/, h256s
m_hashMan.reset(m_chain.number() + 1); m_hashMan.reset(m_chain.number() + 1);
continueSync(); continueSync();
} }
else if (m_hashes.size() > _peer->m_expectedHashes)
{
_peer->disable("Too many hashes");
m_hashes.clear();
m_syncingLatestHash = h256();
continueSync(); ///Try with some other peer, keep the chain
}
else else
continueSync(_peer); continueSync(_peer); /// Grab next hashes
} }
void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain) void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
@ -471,7 +453,7 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{ {
Guard l(x_sync); Guard l(x_sync);
if (_peer->m_asking != Asking::Nothing) if (isSyncing_UNSAFE())
{ {
clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading."; clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading.";
return; return;
@ -483,7 +465,7 @@ void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{ {
Guard l(x_sync); Guard l(x_sync);
if (_peer->m_asking != Asking::Nothing) if (isSyncing_UNSAFE())
{ {
clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading."; clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading.";
return; return;
@ -525,7 +507,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
_peer->m_totalDifficulty = difficulty; _peer->m_totalDifficulty = difficulty;
m_needSyncHashes = true; m_needSyncHashes = true;
m_needSyncBlocks = true; m_needSyncBlocks = true;
m_syncingLatestHash = _peer->m_latestHash; m_syncingLatestHash = h;
sync = true; sync = true;
} }
} }
@ -572,7 +554,7 @@ void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
void EthereumHost::continueSync() void EthereumHost::continueSync()
{ {
clog(NetAllDetail) << "Getting help with downloading hashes and blocks"; clog(NetAllDetail) << "Getting help with downloading hashes and blocks";
forEachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p->m_asking == Asking::Nothing) if (_p->m_asking == Asking::Nothing)
continueSync(_p); continueSync(_p);
@ -585,7 +567,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
bool otherPeerSync = false; bool otherPeerSync = false;
if (m_needSyncHashes && peerShouldGrabChain(_peer)) if (m_needSyncHashes && peerShouldGrabChain(_peer))
{ {
forEachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p != _peer && _p->m_asking == Asking::Hashes && _p->m_protocolVersion != protocolVersion()) if (_p != _peer && _p->m_asking == Asking::Hashes && _p->m_protocolVersion != protocolVersion())
otherPeerSync = true; // Already have a peer downloading hash chain with old protocol, do nothing otherPeerSync = true; // Already have a peer downloading hash chain with old protocol, do nothing
@ -646,11 +628,12 @@ bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
} }
} }
bool EthereumHost::isSyncing() const bool EthereumHost::isSyncing_UNSAFE() const
{ {
Guard l(x_sync); /// We need actual peer information here to handle the case when we are the first ever peer on the network to mine.
/// I.e. on a new private network the first node mining has noone to sync with and should start block propogation immediately.
bool syncing = false; bool syncing = false;
forEachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p->m_asking != Asking::Nothing) if (_p->m_asking != Asking::Nothing)
syncing = true; syncing = true;

13
libethereum/EthereumHost.h

@ -70,8 +70,7 @@ public:
void reset(); void reset();
DownloadMan const& downloadMan() const { return m_man; } DownloadMan const& downloadMan() const { return m_man; }
bool isSyncing() const; bool isSyncing() const { Guard l(x_sync); return isSyncing_UNSAFE(); }
bool isBanned(p2p::NodeId _id) const { return !!m_banned.count(_id); } bool isBanned(p2p::NodeId _id) const { return !!m_banned.count(_id); }
void noteNewTransactions() { m_newTransactions = true; } void noteNewTransactions() { m_newTransactions = true; }
@ -82,7 +81,6 @@ public:
void onPeerNewBlock(EthereumPeer* _peer, RLP const& _r); ///< Called by peer once it has new blocks void onPeerNewBlock(EthereumPeer* _peer, RLP const& _r); ///< Called by peer once it has new blocks
void onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has new hashes void onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has new hashes
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has another sequential block of hashes during sync void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has another sequential block of hashes during sync
void onPeerHashes(EthereumPeer* _peer, unsigned _index, h256s const& _hashes); ///< Called by peer once it has a new ordered block of hashes starting with a particular number
void onPeerTransactions(EthereumPeer* _peer, RLP const& _r); ///< Called by peer when it has new transactions void onPeerTransactions(EthereumPeer* _peer, RLP const& _r); ///< Called by peer when it has new transactions
DownloadMan& downloadMan() { return m_man; } DownloadMan& downloadMan() { return m_man; }
@ -90,10 +88,13 @@ public:
BlockChain const& chain() { return m_chain; } BlockChain const& chain() { return m_chain; }
static unsigned const c_oldProtocolVersion; static unsigned const c_oldProtocolVersion;
private: private:
std::pair<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; }); std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
void forEachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void forEachPeer(std::function<void(EthereumPeer*)> const& _f) const; void foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void foreachPeer(std::function<void(EthereumPeer*)> const& _f) const;
bool isSyncing_UNSAFE() const;
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
void doWork(); void doWork();

24
libethereum/EthereumPeer.cpp

@ -40,7 +40,6 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, Cap
m_hashSub(host()->hashDownloadMan()), m_hashSub(host()->hashDownloadMan()),
m_peerCapabilityVersion(_cap.second) m_peerCapabilityVersion(_cap.second)
{ {
m_peerCapabilityVersion = EthereumHost::c_oldProtocolVersion;
m_syncHashNumber = host()->chain().number() + 1; m_syncHashNumber = host()->chain().number() + 1;
requestStatus(); requestStatus();
} }
@ -78,7 +77,6 @@ string toString(Asking _a)
return "?"; return "?";
} }
void EthereumPeer::setIdle() void EthereumPeer::setIdle()
{ {
m_sub.doneFetch(); m_sub.doneFetch();
@ -88,8 +86,7 @@ void EthereumPeer::setIdle()
void EthereumPeer::requestStatus() void EthereumPeer::requestStatus()
{ {
if (m_asking != Asking::Nothing) assert(m_asking == Asking::Nothing);
clog(NetWarn) << "Bad state: requesting state should be the first action";
setAsking(Asking::State); setAsking(Asking::State);
RLPStream s; RLPStream s;
bool latest = m_peerCapabilityVersion == host()->protocolVersion(); bool latest = m_peerCapabilityVersion == host()->protocolVersion();
@ -106,22 +103,22 @@ void EthereumPeer::requestStatus()
void EthereumPeer::requestHashes() void EthereumPeer::requestHashes()
{ {
if (m_asking == Asking::Blocks) assert(m_asking == Asking::Nothing);
return;
m_syncHashNumber = m_hashSub.nextFetch(c_maxHashesAsk); m_syncHashNumber = m_hashSub.nextFetch(c_maxHashesAsk);
setAsking(Asking::Hashes); setAsking(Asking::Hashes);
RLPStream s; RLPStream s;
prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << c_maxHashesAsk; prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << c_maxHashesAsk;
clog(NetMessageDetail) << "Requesting block hashes for numbers " << m_syncHashNumber << "-" << m_syncHashNumber + c_maxHashesAsk - 1;
sealAndSend(s); sealAndSend(s);
} }
void EthereumPeer::requestHashes(h256 const& _lastHash) void EthereumPeer::requestHashes(h256 const& _lastHash)
{ {
if (m_asking == Asking::Blocks) assert(m_asking == Asking::Nothing);
return;
setAsking(Asking::Hashes); setAsking(Asking::Hashes);
RLPStream s; RLPStream s;
prep(s, GetBlockHashesPacket, 2) << _lastHash << c_maxHashesAsk; prep(s, GetBlockHashesPacket, 2) << _lastHash << c_maxHashesAsk;
clog(NetMessageDetail) << "Requesting block hashes staring from " << _lastHash;
sealAndSend(s); sealAndSend(s);
} }
@ -212,7 +209,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
u256 number256 = _r[0].toInt<u256>(); u256 number256 = _r[0].toInt<u256>();
unsigned number = (unsigned) number256; unsigned number = (unsigned) number256;
unsigned limit = _r[1].toInt<unsigned>(); unsigned limit = _r[1].toInt<unsigned>();
clog(NetMessageSummary) << "GetBlockHashesByNumber (" << number << "-" << number + limit << ")"; clog(NetMessageSummary) << "GetBlockHashesByNumber (" << number << "-" << number + limit - 1 << ")";
RLPStream s; RLPStream s;
if (number <= host()->chain().number()) if (number <= host()->chain().number())
{ {
@ -248,11 +245,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
m_hashSub.noteHash(m_syncHashNumber + i, 1); m_hashSub.noteHash(m_syncHashNumber + i, 1);
} }
if (m_protocolVersion == host()->protocolVersion())
host()->onPeerHashes(this, m_syncHashNumber, hashes); // V61+, report hashes by number
else
host()->onPeerHashes(this, hashes);
m_syncHashNumber += itemCount; m_syncHashNumber += itemCount;
host()->onPeerHashes(this, hashes);
break; break;
} }
case GetBlocksPacket: case GetBlocksPacket:
@ -269,7 +263,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
// return the requested blocks. // return the requested blocks.
bytes rlp; bytes rlp;
unsigned n = 0; unsigned n = 0;
for (unsigned i = 0; i < min(count, c_maxBlocks); ++i) for (unsigned i = 0; i < min(count, c_maxBlocks) && rlp.size() < c_maxPayload; ++i)
{ {
auto h = _r[i].toHash<h256>(); auto h = _r[i].toHash<h256>();
if (host()->chain().isKnown(h)) if (host()->chain().isKnown(h))
@ -292,7 +286,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case BlocksPacket: case BlocksPacket:
{ {
if (m_asking != Asking::Blocks) if (m_asking != Asking::Blocks)
clog(NetWarn) << "Peer giving us blocks when we didn't ask for them."; clog(NetImpolite) << "Peer giving us blocks when we didn't ask for them.";
else else
{ {
setAsking(Asking::Nothing); setAsking(Asking::Nothing);

146
libethereum/Executive.cpp

@ -42,12 +42,7 @@ Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
u256 Executive::gasUsed() const u256 Executive::gasUsed() const
{ {
return m_t.gas() - m_endGas; return m_t.gas() - m_gas;
}
ExecutionResult Executive::executionResult() const
{
return ExecutionResult(gasUsed(), m_excepted, m_newAddress, m_out, m_codeDeposit, m_ext ? m_ext->sub.refunds : 0, m_depositSize, m_gasForDeposit);
} }
void Executive::accrueSubState(SubState& _parentContext) void Executive::accrueSubState(SubState& _parentContext)
@ -101,9 +96,9 @@ void Executive::initialize(Transaction const& _transaction)
m_totalCost = m_t.value() + m_gasCost; m_totalCost = m_t.value() + m_gasCost;
if (m_s.balance(m_t.sender()) < m_totalCost) if (m_s.balance(m_t.sender()) < m_totalCost)
{ {
clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender()); clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender()) << "for sender: " << m_t.sender();
m_excepted = TransactionException::NotEnoughCash; m_excepted = TransactionException::NotEnoughCash;
BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(m_totalCost, (bigint)m_s.balance(m_t.sender()))); BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(m_totalCost, (bigint)m_s.balance(m_t.sender())) << errinfo_comment(m_t.sender().abridged()));
} }
} }
@ -121,75 +116,44 @@ bool Executive::execute()
if (m_t.isCreation()) if (m_t.isCreation())
return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_t.gasRequired(), &m_t.data(), m_t.sender()); return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_t.gasRequired(), &m_t.data(), m_t.sender());
else else
return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired(), m_t.sender()); return call(m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired());
} }
bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas)
{ {
m_isCreation = false; CallParameters params{_senderAddress, _receiveAddress, _receiveAddress, _gas, _value, _data, {}, {}};
// cnote << "Transferring" << formatBalance(_value) << "to receiver."; return call(params, _gasPrice, _senderAddress);
auto it = !(_codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_codeAddress) : precompiled().end();
if (it != precompiled().end())
{
bigint g = it->second.gas(_data);
if (_gas < g)
{
m_endGas = 0;
m_excepted = TransactionException::OutOfGasBase;
// Bail from exception.
return true; // true actually means "all finished - nothing more to be done regarding go().
}
else
{
m_endGas = (u256)(_gas - g);
m_precompiledOut = it->second.exec(_data);
m_out = &m_precompiledOut;
}
}
else if (m_s.addressHasCode(_codeAddress))
{
m_vm = VMFactory::create(_gas);
bytes const& c = m_s.code(_codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth);
}
else
m_endGas = _gas;
m_s.transferBalance(_senderAddress, _receiveAddress, _value);
return !m_ext;
} }
bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address const& _origin) bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address const& _origin)
{ {
m_isCreation = false; m_isCreation = false;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
auto it = !(_p.codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_p.codeAddress) : precompiled().end(); auto it = !(_p.codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_p.codeAddress) : precompiled().end();
if (it != precompiled().end()) if (it != precompiled().end())
{ {
bigint g = it->second.gas(_p.data); bigint g = it->second.gas(_p.data);
if (_p.gas < g) if (_p.gas < g)
{ {
m_endGas = 0;
m_excepted = TransactionException::OutOfGasBase; m_excepted = TransactionException::OutOfGasBase;
// Bail from exception. // Bail from exception.
return true; // true actually means "all finished - nothing more to be done regarding go(). return true; // true actually means "all finished - nothing more to be done regarding go().
} }
else else
{ {
m_endGas = (u256)(_p.gas - g); m_gas = (u256)(_p.gas - g);
m_precompiledOut = it->second.exec(_p.data); it->second.exec(_p.data, _p.out);
m_out = &m_precompiledOut;
} }
} }
else if (m_s.addressHasCode(_p.codeAddress)) else
{ {
m_vm = VMFactory::create(_p.gas); m_gas = _p.gas;
bytes const& c = m_s.code(_p.codeAddress); if (m_s.addressHasCode(_p.codeAddress))
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, m_depth); {
m_outRef = _p.out; // Save ref to expected output buffer to be used in go()
bytes const& c = m_s.code(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, m_depth);
}
} }
else
m_endGas = _p.gas;
m_s.transferBalance(_p.senderAddress, _p.receiveAddress, _p.value); m_s.transferBalance(_p.senderAddress, _p.receiveAddress, _p.value);
@ -203,29 +167,24 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
// We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since // We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since
// we delete it explicitly if we decide we need to revert. // we delete it explicitly if we decide we need to revert.
m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1))); m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));
m_gas = _gas;
// Execute _init. // Execute _init.
if (!_init.empty()) if (!_init.empty())
{
m_vm = VMFactory::create(_gas);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth); m_ext = make_shared<ExtVM>(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth);
}
m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress), Account::ContractConception); m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress), Account::ContractConception);
m_s.transferBalance(_sender, m_newAddress, _endowment); m_s.transferBalance(_sender, m_newAddress, _endowment);
if (_init.empty()) if (_init.empty())
{
m_s.m_cache[m_newAddress].setCode({}); m_s.m_cache[m_newAddress].setCode({});
m_endGas = _gas;
}
return !m_ext; return !m_ext;
} }
OnOpFunc Executive::simpleTrace() OnOpFunc Executive::simpleTrace()
{ {
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt) return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{ {
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt); ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM; VM& vm = *voidVM;
@ -239,13 +198,13 @@ OnOpFunc Executive::simpleTrace()
for (auto const& i: ext.state().storage(ext.myAddress)) for (auto const& i: ext.state().storage(ext.myAddress))
o << showbase << hex << i.first << ": " << i.second << endl; o << showbase << hex << i.first << ": " << i.second << endl;
dev::LogOutputStream<VMTraceChannel, false>() << o.str(); dev::LogOutputStream<VMTraceChannel, false>() << o.str();
dev::LogOutputStream<VMTraceChannel, false>() << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << vm.gas() << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >"; dev::LogOutputStream<VMTraceChannel, false>() << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
}; };
} }
OnOpFunc Executive::standardTrace(ostream& o_output) OnOpFunc Executive::standardTrace(ostream& o_output)
{ {
return [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt) return [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{ {
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt); ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM; VM& vm = *voidVM;
@ -257,48 +216,59 @@ OnOpFunc Executive::standardTrace(ostream& o_output)
o_output << " STORAGE" << endl; o_output << " STORAGE" << endl;
for (auto const& i: ext.state().storage(ext.myAddress)) for (auto const& i: ext.state().storage(ext.myAddress))
o_output << showbase << hex << i.first << ": " << i.second << endl; o_output << showbase << hex << i.first << ": " << i.second << endl;
o_output << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << vm.gas() << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >"; o_output << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
}; };
} }
bool Executive::go(OnOpFunc const& _onOp) bool Executive::go(OnOpFunc const& _onOp)
{ {
if (m_vm) if (m_ext)
{ {
#if ETH_TIMED_EXECUTIONS #if ETH_TIMED_EXECUTIONS
boost::timer t; boost::timer t;
#endif #endif
try try
{ {
m_out = m_vm->go(*m_ext, _onOp); auto vm = VMFactory::create();
m_endGas = m_vm->gas();
if (m_isCreation) if (m_isCreation)
{ {
m_gasForDeposit = m_endGas; auto out = vm->exec(m_gas, *m_ext, _onOp);
m_depositSize = m_out.size(); if (m_res)
if (m_out.size() * c_createDataGas <= m_endGas) {
m_res->gasForDeposit = m_gas;
m_res->depositSize = out.size();
}
if (out.size() * c_createDataGas <= m_gas)
{ {
m_codeDeposit = CodeDeposit::Success; if (m_res)
m_endGas -= m_out.size() * c_createDataGas; m_res->codeDeposit = CodeDeposit::Success;
m_gas -= out.size() * c_createDataGas;
} }
else else
{ {
if (m_res)
m_codeDeposit = CodeDeposit::Failed; m_res->codeDeposit = CodeDeposit::Failed;
m_out.reset(); out.clear();
} }
m_s.m_cache[m_newAddress].setCode(m_out.toBytes()); if (m_res)
m_res->output = out; // copy output to execution result
m_s.m_cache[m_newAddress].setCode(std::move(out)); // FIXME: Set only if Success?
}
else
{
if (m_res)
{
m_res->output = vm->exec(m_gas, *m_ext, _onOp); // take full output
bytesConstRef{&m_res->output}.copyTo(m_outRef);
}
else
vm->exec(m_gas, *m_ext, m_outRef, _onOp); // take only expected output
} }
}
catch (StepsDone const&)
{
return false;
} }
catch (VMException const& _e) catch (VMException const& _e)
{ {
clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e); clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e);
m_endGas = 0; m_gas = 0;
m_excepted = toTransactionException(_e); m_excepted = toTransactionException(_e);
m_ext->revert(); m_ext->revert();
@ -331,12 +301,12 @@ void Executive::finalize()
// SSTORE refunds... // SSTORE refunds...
// must be done before the miner gets the fees. // must be done before the miner gets the fees.
if (m_ext) if (m_ext)
m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds); m_gas += min((m_t.gas() - m_gas) / 2, m_ext->sub.refunds);
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")"; // cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_t.sender(), m_endGas * m_t.gasPrice()); m_s.addBalance(m_t.sender(), m_gas * m_t.gasPrice());
u256 feesEarned = (m_t.gas() - m_endGas) * m_t.gasPrice(); u256 feesEarned = (m_t.gas() - m_gas) * m_t.gasPrice();
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned); m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);
// Suicides... // Suicides...
@ -347,4 +317,12 @@ void Executive::finalize()
// Logs.. // Logs..
if (m_ext) if (m_ext)
m_logs = m_ext->sub.logs; m_logs = m_ext->sub.logs;
if (m_res) // Collect results
{
m_res->gasUsed = gasUsed();
m_res->excepted = m_excepted; // TODO: m_except is used only in ExtVM::call
m_res->newAddress = m_newAddress;
m_res->gasRefunded = m_ext ? m_ext->sub.refunds : 0;
}
} }

25
libethereum/Executive.h

@ -95,13 +95,13 @@ public:
bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress); bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
/// Set up the executive for evaluating a bare CALL (message call) operation. /// Set up the executive for evaluating a bare CALL (message call) operation.
/// @returns false iff go() must be called (and thus a VM execution in required). /// @returns false iff go() must be called (and thus a VM execution in required).
bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress); bool call(Address _receiveAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas);
bool call(CallParameters const& _cp, u256 const& _gasPrice, Address const& _origin); bool call(CallParameters const& _cp, u256 const& _gasPrice, Address const& _origin);
/// Finalise an operation through accruing the substate into the parent context. /// Finalise an operation through accruing the substate into the parent context.
void accrueSubState(SubState& _parentContext); void accrueSubState(SubState& _parentContext);
/// Executes (or continues execution of) the VM. /// Executes (or continues execution of) the VM.
/// @returns false iff go() must be called again to finish the transction. /// @returns false iff go() must be called again to finish the transaction.
bool go(OnOpFunc const& _onOp = OnOpFunc()); bool go(OnOpFunc const& _onOp = OnOpFunc());
/// Operation function for providing a simple trace of the VM execution. /// Operation function for providing a simple trace of the VM execution.
@ -110,34 +110,29 @@ public:
/// Operation function for providing a simple trace of the VM execution. /// Operation function for providing a simple trace of the VM execution.
static OnOpFunc standardTrace(std::ostream& o_output); static OnOpFunc standardTrace(std::ostream& o_output);
/// @returns gas remaining after the transaction/operation. /// @returns gas remaining after the transaction/operation. Valid after the transaction has been executed.
u256 endGas() const { return m_endGas; } u256 gas() const { return m_gas; }
/// @returns output data of the transaction/operation.
bytesConstRef out() const { return m_out; }
/// @returns the new address for the created contract in the CREATE operation. /// @returns the new address for the created contract in the CREATE operation.
h160 newAddress() const { return m_newAddress; } h160 newAddress() const { return m_newAddress; }
/// @returns true iff the operation ended with a VM exception. /// @returns true iff the operation ended with a VM exception.
bool excepted() const { return m_excepted != TransactionException::None; } bool excepted() const { return m_excepted != TransactionException::None; }
/// Get the above in an amalgamated fashion. /// Collect execution results in the result storage provided.
ExecutionResult executionResult() const; void setResultRecipient(ExecutionResult& _res) { m_res = &_res; }
private: private:
State& m_s; ///< The state to which this operation/transaction is applied. State& m_s; ///< The state to which this operation/transaction is applied.
LastHashes m_lastHashes; LastHashes m_lastHashes;
std::shared_ptr<ExtVM> m_ext; ///< The VM externality object for the VM execution or null if no VM is required. std::shared_ptr<ExtVM> m_ext; ///< The VM externality object for the VM execution or null if no VM is required.
std::unique_ptr<VMFace> m_vm; ///< The VM object or null if no VM is required. bytesRef m_outRef; ///< Reference to "expected output" buffer.
bytes m_precompiledOut; ///< Used for the output when there is no VM for a contract (i.e. precompiled). ExecutionResult* m_res = nullptr; ///< Optional storage for execution results.
bytesConstRef m_out; ///< The copyable output.
Address m_newAddress; ///< The address of the created contract in the case of create() being called. Address m_newAddress; ///< The address of the created contract in the case of create() being called.
unsigned m_depth = 0; ///< The context's call-depth. unsigned m_depth = 0; ///< The context's call-depth.
bool m_isCreation = false; ///< True if the transaction creates a contract, or if create() is called. bool m_isCreation = false; ///< True if the transaction creates a contract, or if create() is called.
unsigned m_depositSize = 0; ///< Amount of code of the creation's attempted deposit.
u256 m_gasForDeposit; ///< Amount of gas remaining for the code deposit phase.
CodeDeposit m_codeDeposit = CodeDeposit::None; ///< True if an attempted deposit failed due to lack of gas.
TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception. TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception.
u256 m_endGas; ///< The final amount of gas for the transaction. u256 m_gas = 0; ///< The gas for EVM code execution. Initial amount before go() execution, final amount after go() execution.
Transaction m_t; ///< The original transaction. Set by setup(). Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize(). LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize().

5
libethereum/ExtVM.cpp

@ -34,8 +34,7 @@ bool ExtVM::call(CallParameters& _p)
e.go(_p.onOp); e.go(_p.onOp);
e.accrueSubState(sub); e.accrueSubState(sub);
} }
_p.gas = e.endGas(); _p.gas = e.gas();
e.out().copyTo(_p.out);
return !e.excepted(); return !e.excepted();
} }
@ -51,7 +50,7 @@ h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc
e.go(_onOp); e.go(_onOp);
e.accrueSubState(sub); e.accrueSubState(sub);
} }
io_gas = e.endGas(); io_gas = e.gas();
return e.newAddress(); return e.newAddress();
} }

55
libethereum/Precompiled.cpp

@ -31,7 +31,10 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
static bytes ecrecoverCode(bytesConstRef _in) namespace
{
void ecrecoverCode(bytesConstRef _in, bytesRef _out)
{ {
struct inType struct inType
{ {
@ -44,47 +47,49 @@ static bytes ecrecoverCode(bytesConstRef _in)
memcpy(&in, _in.data(), min(_in.size(), sizeof(in))); memcpy(&in, _in.data(), min(_in.size(), sizeof(in)));
h256 ret; h256 ret;
u256 v = (u256)in.v;
if ((u256)in.v > 28) if (v >= 27 && v <= 28)
return ret.asBytes();
SignatureStruct sig(in.r, in.s, (byte)((int)(u256)in.v - 27));
if (!sig.isValid())
return ret.asBytes();
try
{ {
ret = dev::sha3(recover(sig, in.hash)); SignatureStruct sig(in.r, in.s, (byte)((int)v - 27));
if (sig.isValid())
{
try
{
ret = sha3(recover(sig, in.hash));
}
catch (...) {}
}
} }
catch (...) {}
memset(ret.data(), 0, 12); memset(ret.data(), 0, 12);
return ret.asBytes(); ret.ref().copyTo(_out);
} }
static bytes sha256Code(bytesConstRef _in) void sha256Code(bytesConstRef _in, bytesRef _out)
{ {
return sha256(_in).asBytes(); sha256(_in).ref().copyTo(_out);
} }
static bytes ripemd160Code(bytesConstRef _in) void ripemd160Code(bytesConstRef _in, bytesRef _out)
{ {
return h256(ripemd160(_in), h256::AlignRight).asBytes(); h256(ripemd160(_in), h256::AlignRight).ref().copyTo(_out);
} }
static bytes identityCode(bytesConstRef _in) void identityCode(bytesConstRef _in, bytesRef _out)
{ {
return _in.toBytes(); _in.copyTo(_out);
} }
static const std::unordered_map<unsigned, PrecompiledAddress> c_precompiled = }
{
{ 1, { [](bytesConstRef) -> bigint { return c_ecrecoverGas; }, ecrecoverCode }},
{ 2, { [](bytesConstRef i) -> bigint { return c_sha256Gas + (i.size() + 31) / 32 * c_sha256WordGas; }, sha256Code }},
{ 3, { [](bytesConstRef i) -> bigint { return c_ripemd160Gas + (i.size() + 31) / 32 * c_ripemd160WordGas; }, ripemd160Code }},
{ 4, { [](bytesConstRef i) -> bigint { return c_identityGas + (i.size() + 31) / 32 * c_identityWordGas; }, identityCode }}
};
std::unordered_map<unsigned, PrecompiledAddress> const& dev::eth::precompiled() std::unordered_map<unsigned, PrecompiledAddress> const& dev::eth::precompiled()
{ {
static const std::unordered_map<unsigned, PrecompiledAddress> c_precompiled =
{
{ 1, { [](bytesConstRef) -> bigint { return c_ecrecoverGas; }, ecrecoverCode }},
{ 2, { [](bytesConstRef i) -> bigint { return c_sha256Gas + (i.size() + 31) / 32 * c_sha256WordGas; }, sha256Code }},
{ 3, { [](bytesConstRef i) -> bigint { return c_ripemd160Gas + (i.size() + 31) / 32 * c_ripemd160WordGas; }, ripemd160Code }},
{ 4, { [](bytesConstRef i) -> bigint { return c_identityGas + (i.size() + 31) / 32 * c_identityWordGas; }, identityCode }}
};
return c_precompiled; return c_precompiled;
} }

2
libethereum/Precompiled.h

@ -34,7 +34,7 @@ namespace eth
struct PrecompiledAddress struct PrecompiledAddress
{ {
std::function<bigint(bytesConstRef)> gas; std::function<bigint(bytesConstRef)> gas;
std::function<bytes(bytesConstRef)> exec; std::function<void(bytesConstRef, bytesRef)> exec;
}; };
/// Info on precompiled contract accounts baked into the protocol. /// Info on precompiled contract accounts baked into the protocol.

24
libethereum/State.cpp

@ -46,7 +46,7 @@ using namespace dev::eth;
#define ctrace clog(StateTrace) #define ctrace clog(StateTrace)
#define ETH_TIMED_ENACTMENTS 0 #define ETH_TIMED_ENACTMENTS 0
static const u256 c_blockReward = 1500 * finney; static const u256 c_blockReward = c_network == Network::Olympic ? (1500 * finney) : (5 * ether);
const char* StateSafeExceptions::name() { return EthViolet "" EthBlue ""; } const char* StateSafeExceptions::name() { return EthViolet "" EthBlue ""; }
const char* StateDetail::name() { return EthViolet "" EthWhite ""; } const char* StateDetail::name() { return EthViolet "" EthWhite ""; }
@ -592,24 +592,6 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire
return ss.str(); return ss.str();
} }
template <class Channel>
class LogOverride
{
public:
LogOverride(bool _value): m_old(g_logOverride.count(&typeid(Channel)) ? (int)g_logOverride[&typeid(Channel)] : c_null) { g_logOverride[&typeid(Channel)] = _value; }
~LogOverride()
{
if (m_old == c_null)
g_logOverride.erase(&typeid(Channel));
else
g_logOverride[&typeid(Channel)] = (bool)m_old;
}
private:
static const int c_null = -1;
int m_old;
};
u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir) u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir)
{ {
// m_currentBlock is assumed to be prepopulated and reset. // m_currentBlock is assumed to be prepopulated and reset.
@ -1189,6 +1171,8 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
// Create and initialize the executive. This will throw fairly cheaply and quickly if the // Create and initialize the executive. This will throw fairly cheaply and quickly if the
// transaction is bad in any way. // transaction is bad in any way.
Executive e(*this, _lh, 0); Executive e(*this, _lh, 0);
ExecutionResult res;
e.setResultRecipient(res);
e.initialize(_t); e.initialize(_t);
// Uncommitting is a non-trivial operation - only do it once we've verified as much of the // Uncommitting is a non-trivial operation - only do it once we've verified as much of the
@ -1248,7 +1232,7 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
m_transactionSet.insert(e.t().sha3()); m_transactionSet.insert(e.t().sha3());
} }
return e.executionResult(); return res;
} }
State State::fromPending(unsigned _i) const State State::fromPending(unsigned _i) const

17
libethereum/State.h

@ -84,9 +84,20 @@ public:
class TrivialGasPricer: public GasPricer class TrivialGasPricer: public GasPricer
{ {
protected: public:
u256 ask(State const&) const override { return 10 * szabo; } TrivialGasPricer() = default;
u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return 10 * szabo; } TrivialGasPricer(u256 const& _ask, u256 const& _bid): m_ask(_ask), m_bid(_bid) {}
void setAsk(u256 const& _ask) { m_ask = _ask; }
void setBid(u256 const& _bid) { m_bid = _bid; }
u256 ask() const { return m_ask; }
u256 ask(State const&) const override { return m_ask; }
u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return m_bid; }
private:
u256 m_ask = 10 * szabo;
u256 m_bid = 10 * szabo;
}; };
enum class Permanence enum class Permanence

17
libethereum/Transaction.h

@ -74,25 +74,14 @@ TransactionException toTransactionException(VMException const& _e);
/// Description of the result of executing a transaction. /// Description of the result of executing a transaction.
struct ExecutionResult struct ExecutionResult
{ {
ExecutionResult() = default;
ExecutionResult(u256 const& _gasUsed, TransactionException _excepted, Address const& _newAddress, bytesConstRef _output, CodeDeposit _codeDeposit, u256 const& _gasRefund, unsigned _depositSize, u256 const& _gasForDeposit):
gasUsed(_gasUsed),
excepted(_excepted),
newAddress(_newAddress),
output(_output.toBytes()),
codeDeposit(_codeDeposit),
gasRefunded(_gasRefund),
depositSize(_depositSize),
gasForDeposit(_gasForDeposit)
{}
u256 gasUsed = 0; u256 gasUsed = 0;
TransactionException excepted = TransactionException::Unknown; TransactionException excepted = TransactionException::Unknown;
Address newAddress; Address newAddress;
bytes output; bytes output;
CodeDeposit codeDeposit = CodeDeposit::None; CodeDeposit codeDeposit = CodeDeposit::None; ///< Failed if an attempted deposit failed due to lack of gas.
u256 gasRefunded = 0; u256 gasRefunded = 0;
unsigned depositSize = 0; unsigned depositSize = 0; ///< Amount of code of the creation's attempted deposit.
u256 gasForDeposit; u256 gasForDeposit; ///< Amount of gas remaining for the code deposit phase.
}; };
std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er); std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er);

2
libevm/ExtVMFace.h

@ -106,7 +106,7 @@ class VM;
using LastHashes = std::vector<h256>; using LastHashes = std::vector<h256>;
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, VM*, ExtVMFace const*)>; using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, bigint /*gas*/, VM*, ExtVMFace const*)>;
struct CallParameters struct CallParameters
{ {

10
libevm/SmartVM.cpp

@ -41,7 +41,7 @@ namespace
} }
} }
bytesConstRef SmartVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) bytesConstRef SmartVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{ {
auto codeHash = sha3(_ext.code); auto codeHash = sha3(_ext.code);
auto vmKind = VMKind::Interpreter; // default VM auto vmKind = VMKind::Interpreter; // default VM
@ -66,11 +66,9 @@ bytesConstRef SmartVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step
} }
// TODO: Selected VM must be kept only because it returns reference to its internal memory. // TODO: Selected VM must be kept only because it returns reference to its internal memory.
// VM implementations should be stateless, without gas counter and escaping memory reference. // VM implementations should be stateless, without escaping memory reference.
m_selectedVM = VMFactory::create(vmKind, gas()); m_selectedVM = VMFactory::create(vmKind);
auto out = m_selectedVM->go(_ext, _onOp, _steps); return m_selectedVM->execImpl(io_gas, _ext, _onOp);
m_gas = m_selectedVM->gas();
return out;
} }
} }

8
libevm/SmartVM.h

@ -31,16 +31,10 @@ namespace eth
class SmartVM: public VMFace class SmartVM: public VMFace
{ {
public: public:
SmartVM(u256 const& _gas): m_gas(_gas) {} virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final;
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void reset(u256 const& _gas = 0) noexcept override { m_gas = _gas; }
u256 gas() const noexcept override { return (u256)m_gas; }
private: private:
std::unique_ptr<VMFace> m_selectedVM; std::unique_ptr<VMFace> m_selectedVM;
bigint m_gas;
}; };
} }

42
libevm/VM.cpp

@ -25,13 +25,6 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
void VM::reset(u256 const& _gas) noexcept
{
m_gas = _gas;
m_curPC = 0;
m_jumpDests.clear();
}
struct InstructionMetric struct InstructionMetric
{ {
int gasPriceTier; int gasPriceTier;
@ -52,8 +45,12 @@ static array<InstructionMetric, 256> metrics()
return s_ret; return s_ret;
} }
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{ {
// Reset leftovers from possible previous run
m_curPC = 0;
m_jumpDests.clear();
m_stack.reserve((unsigned)c_stackLimit); m_stack.reserve((unsigned)c_stackLimit);
unique_ptr<CallParameters> callParams; unique_ptr<CallParameters> callParams;
@ -76,8 +73,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1; i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1;
} }
u256 nextPC = m_curPC + 1; u256 nextPC = m_curPC + 1;
auto osteps = _steps; for (uint64_t steps = 0; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++steps)
for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1)
{ {
// INSTRUCTION... // INSTRUCTION...
Instruction inst = (Instruction)_ext.getCode(m_curPC); Instruction inst = (Instruction)_ext.getCode(m_curPC);
@ -100,7 +96,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
auto onOperation = [&]() auto onOperation = [&]()
{ {
if (_onOp) if (_onOp)
_onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext); _onOp(steps, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext);
}; };
switch (inst) switch (inst)
@ -198,17 +194,11 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
runGas += c_copyGas * ((copySize + 31) / 32); runGas += c_copyGas * ((copySize + 31) / 32);
onOperation(); onOperation();
// if (_onOp)
// _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext);
if (m_gas < runGas) if (io_gas < runGas)
{
// Out of gas!
m_gas = 0;
BOOST_THROW_EXCEPTION(OutOfGas()); BOOST_THROW_EXCEPTION(OutOfGas());
}
m_gas -= runGas; io_gas -= (u256)runGas;
if (newTempSize > m_temp.size()) if (newTempSize > m_temp.size())
m_temp.resize((size_t)newTempSize); m_temp.resize((size_t)newTempSize);
@ -567,7 +557,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.push_back(m_temp.size()); m_stack.push_back(m_temp.size());
break; break;
case Instruction::GAS: case Instruction::GAS:
m_stack.push_back((u256)m_gas); m_stack.push_back(io_gas);
break; break;
case Instruction::JUMPDEST: case Instruction::JUMPDEST:
break; break;
@ -616,11 +606,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.pop_back(); m_stack.pop_back();
if (_ext.balance(_ext.myAddress) >= endowment && _ext.depth < 1024) if (_ext.balance(_ext.myAddress) >= endowment && _ext.depth < 1024)
{ m_stack.push_back((u160)_ext.create(endowment, io_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
u256 g(m_gas);
m_stack.push_back((u160)_ext.create(endowment, g, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
m_gas = g;
}
else else
m_stack.push_back(0); m_stack.push_back(0);
break; break;
@ -661,7 +647,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
else else
m_stack.push_back(0); m_stack.push_back(0);
m_gas += callParams->gas; io_gas += callParams->gas;
break; break;
} }
case Instruction::RETURN: case Instruction::RETURN:
@ -670,7 +656,6 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.pop_back(); m_stack.pop_back();
unsigned s = (unsigned)m_stack.back(); unsigned s = (unsigned)m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
return bytesConstRef(m_temp.data() + b, s); return bytesConstRef(m_temp.data() + b, s);
} }
case Instruction::SUICIDE: case Instruction::SUICIDE:
@ -683,7 +668,6 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
return bytesConstRef(); return bytesConstRef();
} }
} }
if (_steps == (uint64_t)-1)
BOOST_THROW_EXCEPTION(StepsDone());
return bytesConstRef(); return bytesConstRef();
} }

15
libevm/VM.h

@ -52,31 +52,22 @@ inline u256 fromAddress(Address _a)
class VM: public VMFace class VM: public VMFace
{ {
public: public:
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final;
void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 curPC() const { return m_curPC; } u256 curPC() const { return m_curPC; }
bytes const& memory() const { return m_temp; } bytes const& memory() const { return m_temp; }
u256s const& stack() const { return m_stack; } u256s const& stack() const { return m_stack; }
void reset(u256 const& _gas = 0) noexcept override;
u256 gas() const noexcept override { return (u256)m_gas; }
private: private:
friend class VMFactory; void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
/// Construct VM object.
explicit VM(u256 _gas): m_gas(_gas) {}
u256 m_curPC = 0; u256 m_curPC = 0;
bytes m_temp; bytes m_temp;
u256s m_stack; u256s m_stack;
std::vector<uint64_t> m_jumpDests; std::vector<uint64_t> m_jumpDests;
std::function<void()> m_onFail; std::function<void()> m_onFail;
bigint m_gas = 0;
}; };
} }

19
libevm/VMFace.h

@ -26,7 +26,6 @@ namespace eth
{ {
struct VMException: virtual Exception {}; struct VMException: virtual Exception {};
struct StepsDone: virtual VMException {};
struct BreakPointHit: virtual VMException {}; struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {}; struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {}; struct BadJumpDestination: virtual VMException {};
@ -43,10 +42,22 @@ public:
VMFace(VMFace const&) = delete; VMFace(VMFace const&) = delete;
VMFace& operator=(VMFace const&) = delete; VMFace& operator=(VMFace const&) = delete;
virtual void reset(u256 const& _gas = 0) noexcept = 0; /// Execute EVM code by VM.
virtual u256 gas() const noexcept = 0; ///
/// @param _out Expected output
void exec(u256& io_gas, ExtVMFace& _ext, bytesRef _out, OnOpFunc const& _onOp = {})
{
execImpl(io_gas, _ext, _onOp).copyTo(_out);
}
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0; /// The same as above but returns a copy of full output.
bytes exec(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {})
{
return execImpl(io_gas, _ext, _onOp).toVector();
}
/// VM implementation
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) = 0;
}; };
} }

14
libevm/VMFactory.cpp

@ -38,27 +38,27 @@ void VMFactory::setKind(VMKind _kind)
g_kind = _kind; g_kind = _kind;
} }
std::unique_ptr<VMFace> VMFactory::create(u256 _gas) std::unique_ptr<VMFace> VMFactory::create()
{ {
return create(g_kind, _gas); return create(g_kind);
} }
std::unique_ptr<VMFace> VMFactory::create(VMKind _kind, u256 _gas) std::unique_ptr<VMFace> VMFactory::create(VMKind _kind)
{ {
#if ETH_EVMJIT #if ETH_EVMJIT
switch (_kind) switch (_kind)
{ {
default: default:
case VMKind::Interpreter: case VMKind::Interpreter:
return std::unique_ptr<VMFace>(new VM(_gas)); return std::unique_ptr<VMFace>(new VM);
case VMKind::JIT: case VMKind::JIT:
return std::unique_ptr<VMFace>(new JitVM(_gas)); return std::unique_ptr<VMFace>(new JitVM);
case VMKind::Smart: case VMKind::Smart:
return std::unique_ptr<VMFace>(new SmartVM(_gas)); return std::unique_ptr<VMFace>(new SmartVM);
} }
#else #else
asserts(_kind == VMKind::Interpreter && "JIT disabled in build configuration"); asserts(_kind == VMKind::Interpreter && "JIT disabled in build configuration");
return std::unique_ptr<VMFace>(new VM(_gas)); return std::unique_ptr<VMFace>(new VM);
#endif #endif
} }

4
libevm/VMFactory.h

@ -36,10 +36,10 @@ public:
VMFactory() = delete; VMFactory() = delete;
/// Creates a VM instance of global kind (controlled by setKind() function). /// Creates a VM instance of global kind (controlled by setKind() function).
static std::unique_ptr<VMFace> create(u256 _gas); static std::unique_ptr<VMFace> create();
/// Creates a VM instance of kind provided. /// Creates a VM instance of kind provided.
static std::unique_ptr<VMFace> create(VMKind _kind, u256 _gas); static std::unique_ptr<VMFace> create(VMKind _kind);
/// Set global VM kind /// Set global VM kind
static void setKind(VMKind _kind); static void setKind(VMKind _kind);

14
libevmasm/Assembly.cpp

@ -22,9 +22,12 @@
#include "Assembly.h" #include "Assembly.h"
#include <fstream> #include <fstream>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libevmcore/Params.h>
#include <libevmasm/CommonSubexpressionEliminator.h> #include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/ControlFlowGraph.h> #include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/BlockDeduplicator.h> #include <libevmasm/BlockDeduplicator.h>
#include <libevmasm/ConstantOptimiser.h>
#include <libevmasm/GasMeter.h>
#include <json/json.h> #include <json/json.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -302,7 +305,7 @@ inline bool matches(AssemblyItemsConstRef _a, AssemblyItemsConstRef _b)
struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; }; struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; };
#define copt dev::LogOutputStream<OptimiserChannel, true>() #define copt dev::LogOutputStream<OptimiserChannel, true>()
Assembly& Assembly::optimise(bool _enable) Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
{ {
if (!_enable) if (!_enable)
return *this; return *this;
@ -364,10 +367,17 @@ Assembly& Assembly::optimise(bool _enable)
} }
} }
total += ConstantOptimisationMethod::optimiseConstants(
_isCreation,
_isCreation ? 1 : _runs,
*this,
m_items
);
copt << total << " optimisations done."; copt << total << " optimisations done.";
for (auto& sub: m_subs) for (auto& sub: m_subs)
sub.optimise(true); sub.optimise(true, false, _runs);
return *this; return *this;
} }

9
libevmasm/Assembly.h

@ -49,6 +49,7 @@ public:
AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); } AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); }
AssemblyItem newData(bytes const& _data) { h256 h = (u256)std::hash<std::string>()(asString(_data)); m_data[h] = _data; return AssemblyItem(PushData, h); } AssemblyItem newData(bytes const& _data) { h256 h = (u256)std::hash<std::string>()(asString(_data)); m_data[h] = _data; return AssemblyItem(PushData, h); }
AssemblyItem newSub(Assembly const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); } AssemblyItem newSub(Assembly const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
Assembly const& getSub(size_t _sub) const { return m_subs.at(_sub); }
AssemblyItem newPushString(std::string const& _data) { h256 h = (u256)std::hash<std::string>()(_data); m_strings[h] = _data; return AssemblyItem(PushString, h); } AssemblyItem newPushString(std::string const& _data) { h256 h = (u256)std::hash<std::string>()(_data); m_strings[h] = _data; return AssemblyItem(PushString, h); }
AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
@ -92,7 +93,13 @@ public:
void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; } void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; }
bytes assemble() const; bytes assemble() const;
Assembly& optimise(bool _enable); bytes const& data(h256 const& _i) const { return m_data[_i]; }
/// Modify (if @a _enable is set) and return the current assembly such that creation and
/// execution gas usage is optimised. @a _isCreation should be true for the top-level assembly.
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200);
Json::Value stream( Json::Value stream(
std::ostream& _out, std::ostream& _out,
std::string const& _prefix = "", std::string const& _prefix = "",

13
libevmasm/CommonSubexpressionEliminator.cpp

@ -46,6 +46,7 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
targetStackContents[height] = m_state.stackElement(height, SourceLocation()); targetStackContents[height] = m_state.stackElement(height, SourceLocation());
AssemblyItems items = CSECodeGenerator(m_state.expressionClasses(), m_storeOperations).generateCode( AssemblyItems items = CSECodeGenerator(m_state.expressionClasses(), m_storeOperations).generateCode(
m_initialState.sequenceNumber(),
m_initialState.stackHeight(), m_initialState.stackHeight(),
initialStackContents, initialStackContents,
targetStackContents targetStackContents
@ -112,6 +113,7 @@ CSECodeGenerator::CSECodeGenerator(
} }
AssemblyItems CSECodeGenerator::generateCode( AssemblyItems CSECodeGenerator::generateCode(
unsigned _initialSequenceNumber,
int _initialStackHeight, int _initialStackHeight,
map<int, Id> const& _initialStack, map<int, Id> const& _initialStack,
map<int, Id> const& _targetStackContents map<int, Id> const& _targetStackContents
@ -137,7 +139,14 @@ AssemblyItems CSECodeGenerator::generateCode(
for (auto const& p: m_neededBy) for (auto const& p: m_neededBy)
for (auto id: {p.first, p.second}) for (auto id: {p.first, p.second})
if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber) if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber)
{
if (seqNr < _initialSequenceNumber)
// Invalid sequenced operation.
// @todo quick fix for now. Proper fix needs to choose representative with higher
// sequence number during dependency analyis.
BOOST_THROW_EXCEPTION(StackTooDeepException());
sequencedExpressions.insert(make_pair(seqNr, id)); sequencedExpressions.insert(make_pair(seqNr, id));
}
// Perform all operations on storage and memory in order, if they are needed. // Perform all operations on storage and memory in order, if they are needed.
for (auto const& seqAndId: sequencedExpressions) for (auto const& seqAndId: sequencedExpressions)
@ -428,7 +437,7 @@ void CSECodeGenerator::appendDup(int _fromPosition, SourceLocation const& _locat
{ {
assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
int instructionNum = 1 + m_stackHeight - _fromPosition; int instructionNum = 1 + m_stackHeight - _fromPosition;
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep."); assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep, try removing local variables.");
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
appendItem(AssemblyItem(dupInstruction(instructionNum), _location)); appendItem(AssemblyItem(dupInstruction(instructionNum), _location));
m_stack[m_stackHeight] = m_stack[_fromPosition]; m_stack[m_stackHeight] = m_stack[_fromPosition];
@ -441,7 +450,7 @@ void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation cons
if (_fromPosition == m_stackHeight) if (_fromPosition == m_stackHeight)
return; return;
int instructionNum = m_stackHeight - _fromPosition; int instructionNum = m_stackHeight - _fromPosition;
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep."); assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep, try removing local variables.");
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
appendItem(AssemblyItem(swapInstruction(instructionNum), _location)); appendItem(AssemblyItem(swapInstruction(instructionNum), _location));

3
libevmasm/CommonSubexpressionEliminator.h

@ -105,10 +105,13 @@ public:
CSECodeGenerator(ExpressionClasses& _expressionClasses, StoreOperations const& _storeOperations); CSECodeGenerator(ExpressionClasses& _expressionClasses, StoreOperations const& _storeOperations);
/// @returns the assembly items generated from the given requirements /// @returns the assembly items generated from the given requirements
/// @param _initialSequenceNumber starting sequence number, do not generate sequenced operations
/// before this number.
/// @param _initialStack current contents of the stack (up to stack height of zero) /// @param _initialStack current contents of the stack (up to stack height of zero)
/// @param _targetStackContents final contents of the stack, by stack height relative to initial /// @param _targetStackContents final contents of the stack, by stack height relative to initial
/// @note should only be called once on each object. /// @note should only be called once on each object.
AssemblyItems generateCode( AssemblyItems generateCode(
unsigned _initialSequenceNumber,
int _initialStackHeight, int _initialStackHeight,
std::map<int, Id> const& _initialStack, std::map<int, Id> const& _initialStack,
std::map<int, Id> const& _targetStackContents std::map<int, Id> const& _targetStackContents

225
libevmasm/ConstantOptimiser.cpp

@ -0,0 +1,225 @@
/*
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 ConstantOptimiser.cpp
* @author Christian <c@ethdev.com>
* @date 2015
*/
#include "libevmasm/ConstantOptimiser.h"
#include <libevmasm/Assembly.h>
#include <libevmasm/GasMeter.h>
#include <libevmcore/Params.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
unsigned ConstantOptimisationMethod::optimiseConstants(
bool _isCreation,
size_t _runs,
Assembly& _assembly,
AssemblyItems& _items
)
{
unsigned optimisations = 0;
map<AssemblyItem, size_t> pushes;
for (AssemblyItem const& item: _items)
if (item.type() == Push)
pushes[item]++;
for (auto it: pushes)
{
AssemblyItem const& item = it.first;
if (item.data() < 0x100)
continue;
Params params;
params.multiplicity = it.second;
params.isCreation = _isCreation;
params.runs = _runs;
LiteralMethod lit(params, item.data());
bigint literalGas = lit.gasNeeded();
CodeCopyMethod copy(params, item.data());
bigint copyGas = copy.gasNeeded();
ComputeMethod compute(params, item.data());
bigint computeGas = compute.gasNeeded();
if (copyGas < literalGas && copyGas < computeGas)
{
copy.execute(_assembly, _items);
optimisations++;
}
else if (computeGas < literalGas && computeGas < copyGas)
{
compute.execute(_assembly, _items);
optimisations++;
}
}
return optimisations;
}
bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
{
bigint gas = 0;
for (AssemblyItem const& item: _items)
if (item.type() == Push)
gas += GasMeter::runGas(Instruction::PUSH1);
else if (item.type() == Operation)
gas += GasMeter::runGas(item.instruction());
return gas;
}
bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
{
if (m_params.isCreation)
{
bigint gas;
for (auto b: _data)
gas += b ? c_txDataNonZeroGas : c_txDataZeroGas;
return gas;
}
else
return c_createDataGas * dataSize();
}
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
{
size_t size = 0;
for (AssemblyItem const& item: _items)
size += item.bytesRequired(3); // assume 3 byte addresses
return size;
}
void ConstantOptimisationMethod::replaceConstants(
AssemblyItems& _items,
AssemblyItems const& _replacement
) const
{
assertThrow(_items.size() > 0, OptimizerException, "");
for (size_t i = 0; i < _items.size(); ++i)
{
if (_items.at(i) != AssemblyItem(m_value))
continue;
_items[i] = _replacement[0];
_items.insert(_items.begin() + i + 1, _replacement.begin() + 1, _replacement.end());
i += _replacement.size() - 1;
}
}
bigint LiteralMethod::gasNeeded()
{
return combineGas(
simpleRunGas({Instruction::PUSH1}),
// PUSHX plus data
(m_params.isCreation ? c_txDataNonZeroGas : c_createDataGas) + dataGas(),
0
);
}
CodeCopyMethod::CodeCopyMethod(Params const& _params, u256 const& _value):
ConstantOptimisationMethod(_params, _value)
{
m_copyRoutine = AssemblyItems{
u256(0),
Instruction::DUP1,
Instruction::MLOAD, // back up memory
u256(32),
AssemblyItem(PushData, u256(1) << 16), // has to be replaced
Instruction::DUP4,
Instruction::CODECOPY,
Instruction::DUP2,
Instruction::MLOAD,
Instruction::SWAP2,
Instruction::MSTORE
};
}
bigint CodeCopyMethod::gasNeeded()
{
return combineGas(
// Run gas: we ignore memory increase costs
simpleRunGas(m_copyRoutine) + c_copyGas,
// Data gas for copy routines: Some bytes are zero, but we ignore them.
bytesRequired(m_copyRoutine) * (m_params.isCreation ? c_txDataNonZeroGas : c_createDataGas),
// Data gas for data itself
dataGas(toBigEndian(m_value))
);
}
void CodeCopyMethod::execute(Assembly& _assembly, AssemblyItems& _items)
{
bytes data = toBigEndian(m_value);
m_copyRoutine[4] = _assembly.newData(data);
replaceConstants(_items, m_copyRoutine);
}
AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
{
if (_value < 0x10000)
// Very small value, not worth computing
return AssemblyItems{_value};
else if (dev::bytesRequired(~_value) < dev::bytesRequired(_value))
// Negated is shorter to represent
return findRepresentation(~_value) + AssemblyItems{Instruction::NOT};
else
{
// Decompose value into a * 2**k + b where abs(b) << 2**k
// Is not always better, try literal and decomposition method.
AssemblyItems routine{u256(_value)};
bigint bestGas = gasNeeded(routine);
for (unsigned bits = 255; bits > 8; --bits)
{
unsigned gapDetector = unsigned(_value >> (bits - 8)) & 0x1ff;
if (gapDetector != 0xff && gapDetector != 0x100)
continue;
u256 powerOfTwo = u256(1) << bits;
u256 upperPart = _value >> bits;
bigint lowerPart = _value & (powerOfTwo - 1);
if (abs(powerOfTwo - lowerPart) < lowerPart)
lowerPart = lowerPart - powerOfTwo; // make it negative
if (abs(lowerPart) >= (powerOfTwo >> 8))
continue;
AssemblyItems newRoutine;
if (lowerPart != 0)
newRoutine += findRepresentation(u256(abs(lowerPart)));
newRoutine += AssemblyItems{u256(bits), u256(2), Instruction::EXP};
if (upperPart != 1 && upperPart != 0)
newRoutine += findRepresentation(upperPart) + AssemblyItems{Instruction::MUL};
if (lowerPart > 0)
newRoutine += AssemblyItems{Instruction::ADD};
else if (lowerPart < 0)
newRoutine.push_back(Instruction::SUB);
bigint newGas = gasNeeded(newRoutine);
if (newGas < bestGas)
{
bestGas = move(newGas);
routine = move(newRoutine);
}
}
return routine;
}
}
bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine)
{
size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP);
return combineGas(
simpleRunGas(_routine) + numExps * (c_expGas + c_expByteGas),
// Data gas for routine: Some bytes are zero, but we ignore them.
bytesRequired(_routine) * (m_params.isCreation ? c_txDataNonZeroGas : c_createDataGas),
0
);
}

147
libevmasm/ConstantOptimiser.h

@ -0,0 +1,147 @@
/*
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 ConstantOptimiser.cpp
* @author Christian <c@ethdev.com>
* @date 2015
*/
#pragma once
#include <vector>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
namespace dev
{
namespace eth
{
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
class Assembly;
/**
* Abstract base class for one way to change how constants are represented in the code.
*/
class ConstantOptimisationMethod
{
public:
/// Tries to optimised how constants are represented in the source code and modifies
/// @a _assembly and its @a _items.
/// @returns zero if no optimisations could be performed.
static unsigned optimiseConstants(
bool _isCreation,
size_t _runs,
Assembly& _assembly,
AssemblyItems& _items
);
struct Params
{
bool isCreation; ///< Whether this is called during contract creation or runtime.
size_t runs; ///< Estimated number of calls per opcode oven the lifetime of the contract.
size_t multiplicity; ///< Number of times the constant appears in the code.
};
explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):
m_params(_params), m_value(_value) {}
virtual bigint gasNeeded() = 0;
virtual void execute(Assembly& _assembly, AssemblyItems& _items) = 0;
protected:
size_t dataSize() const { return std::max<size_t>(1, dev::bytesRequired(m_value)); }
/// @returns the run gas for the given items ignoring special gas costs
static bigint simpleRunGas(AssemblyItems const& _items);
/// @returns the gas needed to store the given data literally
bigint dataGas(bytes const& _data) const;
/// @returns the gas needed to store the value literally
bigint dataGas() const { return dataGas(toCompactBigEndian(m_value, 1)); }
static size_t bytesRequired(AssemblyItems const& _items);
/// @returns the combined estimated gas usage taking @a m_params into account.
bigint combineGas(
bigint const& _runGas,
bigint const& _repeatedDataGas,
bigint const& _uniqueDataGas
)
{
// _runGas is not multiplied by _multiplicity because the runs are "per opcode"
return m_params.runs * _runGas + m_params.multiplicity * _repeatedDataGas + _uniqueDataGas;
}
/// Replaces the constant by the code given in @a _replacement.
void replaceConstants(AssemblyItems& _items, AssemblyItems const& _replacement) const;
Params m_params;
u256 const& m_value;
};
/**
* Optimisation method that pushes the constant to the stack literally. This is the default method,
* i.e. executing it does not alter the Assembly.
*/
class LiteralMethod: public ConstantOptimisationMethod
{
public:
explicit LiteralMethod(Params const& _params, u256 const& _value):
ConstantOptimisationMethod(_params, _value) {}
virtual bigint gasNeeded() override;
virtual void execute(Assembly&, AssemblyItems&) override {}
};
/**
* Method that stores the data in the .data section of the code and copies it to the stack.
*/
class CodeCopyMethod: public ConstantOptimisationMethod
{
public:
explicit CodeCopyMethod(Params const& _params, u256 const& _value);
virtual bigint gasNeeded() override;
virtual void execute(Assembly& _assembly, AssemblyItems& _items) override;
protected:
AssemblyItems m_copyRoutine;
};
/**
* Method that tries to compute the constant.
*/
class ComputeMethod: public ConstantOptimisationMethod
{
public:
explicit ComputeMethod(Params const& _params, u256 const& _value):
ConstantOptimisationMethod(_params, _value)
{
m_routine = findRepresentation(m_value);
}
virtual bigint gasNeeded() override { return gasNeeded(m_routine); }
virtual void execute(Assembly&, AssemblyItems& _items) override
{
replaceConstants(_items, m_routine);
}
protected:
/// Tries to recursively find a way to compute @a _value.
AssemblyItems findRepresentation(u256 const& _value);
bigint gasNeeded(AssemblyItems const& _routine);
AssemblyItems m_routine;
};
}
}

16
libevmasm/ExpressionClasses.cpp

@ -260,6 +260,22 @@ Rules::Rules()
{{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }}, {{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }},
}; };
// Double negation of opcodes with binary result
for (auto const& op: vector<Instruction>{
Instruction::EQ,
Instruction::LT,
Instruction::SLT,
Instruction::GT,
Instruction::SGT
})
m_rules.push_back({
{Instruction::ISZERO, {{Instruction::ISZERO, {{op, {X, Y}}}}}},
[=]() -> Pattern { return {op, {X, Y}}; }
});
m_rules.push_back({
{Instruction::ISZERO, {{Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}}},
[=]() -> Pattern { return {Instruction::ISZERO, {X}}; }
});
// Associative operations // Associative operations
for (auto const& opFun: vector<pair<Instruction,function<u256(u256 const&,u256 const&)>>>{ for (auto const& opFun: vector<pair<Instruction,function<u256(u256 const&,u256 const&)>>>{
{Instruction::ADD, plus<u256>()}, {Instruction::ADD, plus<u256>()},

7
libevmasm/GasMeter.cpp

@ -201,13 +201,14 @@ GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosS
})); }));
} }
GasMeter::GasConsumption GasMeter::runGas(Instruction _instruction) u256 GasMeter::runGas(Instruction _instruction)
{ {
if (_instruction == Instruction::JUMPDEST) if (_instruction == Instruction::JUMPDEST)
return GasConsumption(1); return 1;
int tier = instructionInfo(_instruction).gasPriceTier; int tier = instructionInfo(_instruction).gasPriceTier;
return tier == InvalidTier ? GasConsumption::infinite() : c_tierStepGas[tier]; assertThrow(tier != InvalidTier, OptimizerException, "Invalid gas tier.");
return c_tierStepGas[tier];
} }

4
libevmasm/GasMeter.h

@ -66,6 +66,8 @@ public:
u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; } u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }
static u256 runGas(Instruction _instruction);
private: private:
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise. /// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.
GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value); GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value);
@ -76,8 +78,6 @@ private:
/// given as values on the stack at the given relative positions. /// given as values on the stack at the given relative positions.
GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize); GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize);
static GasConsumption runGas(Instruction _instruction);
std::shared_ptr<KnownState> m_state; std::shared_ptr<KnownState> m_state;
/// Largest point where memory was accessed since the creation of this object. /// Largest point where memory was accessed since the creation of this object.
u256 m_largestMemoryAccess; u256 m_largestMemoryAccess;

1
libevmasm/KnownState.h

@ -94,6 +94,7 @@ public:
/// Resets any knowledge. /// Resets any knowledge.
void reset() { resetStorage(); resetMemory(); resetStack(); } void reset() { resetStorage(); resetMemory(); resetStack(); }
unsigned sequenceNumber() const { return m_sequenceNumber; }
/// Manually increments the storage and memory sequence number. /// Manually increments the storage and memory sequence number.
void incrementSequenceNumber() { m_sequenceNumber += 2; } void incrementSequenceNumber() { m_sequenceNumber += 2; }

4
libevmcore/Instruction.cpp

@ -300,7 +300,7 @@ void dev::eth::eachInstruction(
function<void(Instruction,u256 const&)> const& _onInstruction function<void(Instruction,u256 const&)> const& _onInstruction
) )
{ {
for (auto it = _mem.begin(); it != _mem.end(); ++it) for (auto it = _mem.begin(); it < _mem.end(); ++it)
{ {
Instruction instr = Instruction(*it); Instruction instr = Instruction(*it);
size_t additional = 0; size_t additional = 0;
@ -310,7 +310,7 @@ void dev::eth::eachInstruction(
for (size_t i = 0; i < additional; ++i) for (size_t i = 0; i < additional; ++i)
{ {
data <<= 8; data <<= 8;
if (it != _mem.end() && ++it != _mem.end()) if (++it < _mem.end())
data |= *it; data |= *it;
} }
_onInstruction(instr, data); _onInstruction(instr, data);

6
libp2p/HostCapability.cpp

@ -27,15 +27,15 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions() const std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions() const
{ {
return peerSessions(version()); return peerSessions(version());
} }
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions(u256 const& _version) const std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions(u256 const& _version) const
{ {
RecursiveGuard l(m_host->x_sessions); RecursiveGuard l(m_host->x_sessions);
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> ret; std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> ret;
for (auto const& i: m_host->m_sessions) for (auto const& i: m_host->m_sessions)
if (std::shared_ptr<Session> s = i.second.lock()) if (std::shared_ptr<Session> s = i.second.lock())
if (s->m_capabilities.count(std::make_pair(name(), _version))) if (s->m_capabilities.count(std::make_pair(name(), _version)))

4
libp2p/HostCapability.h

@ -45,8 +45,8 @@ public:
Host* host() const { return m_host; } Host* host() const { return m_host; }
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> peerSessions() const; std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> peerSessions() const;
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> peerSessions(u256 const& _version) const; std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> peerSessions(u256 const& _version) const;
protected: protected:
virtual std::string name() const = 0; virtual std::string name() const = 0;

28
libp2p/RLPxHandshake.cpp

@ -184,7 +184,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
// old packet format // old packet format
// 5 arguments, HelloPacket // 5 arguments, HelloPacket
RLPStream s; RLPStream s;
s.append((unsigned)0).appendList(5) s.append((unsigned)HelloPacket).appendList(5)
<< dev::p2p::c_protocolVersion << dev::p2p::c_protocolVersion
<< m_host->m_clientVersion << m_host->m_clientVersion
<< m_host->caps() << m_host->caps()
@ -205,15 +205,16 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
m_nextState = StartSession; m_nextState = StartSession;
// read frame header // read frame header
m_handshakeInBuffer.resize(h256::size); unsigned const handshakeSize = 32;
ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, h256::size), [this, self](boost::system::error_code ec, std::size_t) m_handshakeInBuffer.resize(handshakeSize);
ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, handshakeSize), [this, self](boost::system::error_code ec, std::size_t)
{ {
if (ec) if (ec)
transition(ec); transition(ec);
else else
{ {
/// authenticate and decrypt header /// authenticate and decrypt header
if (!m_io->authAndDecryptHeader(bytesRef(m_handshakeInBuffer.data(), h256::size))) if (!m_io->authAndDecryptHeader(bytesRef(m_handshakeInBuffer.data(), m_handshakeInBuffer.size())))
{ {
m_nextState = Error; m_nextState = Error;
transition(); transition();
@ -235,7 +236,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
} }
/// rlp of header has protocol-type, sequence-id[, total-packet-size] /// rlp of header has protocol-type, sequence-id[, total-packet-size]
bytes headerRLP(header.size() - 3 - h128::size); bytes headerRLP(header.size() - 3 - h128::size); // this is always 32 - 3 - 16 = 13. wtf?
bytesConstRef(&header).cropped(3).copyTo(&headerRLP); bytesConstRef(&header).cropped(3).copyTo(&headerRLP);
/// read padded frame and mac /// read padded frame and mac
@ -255,8 +256,8 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
return; return;
} }
PacketType packetType = (PacketType)(frame[0] == 0x80 ? 0x0 : frame[0]); PacketType packetType = frame[0] == 0x80 ? HelloPacket : (PacketType)frame[0];
if (packetType != 0) if (packetType != HelloPacket)
{ {
clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type"; clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type";
m_nextState = Error; m_nextState = Error;
@ -265,8 +266,17 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
} }
clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session."; clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session.";
RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall); try
m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); {
RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall);
m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint());
}
catch (std::exception const& _e)
{
clog(NetWarn) << "Handshake causing an exception:" << _e.what();
m_nextState = Error;
transition();
}
} }
}); });
} }

16
libp2p/Session.cpp

@ -319,10 +319,14 @@ void Session::send(bytes&& _msg)
void Session::write() void Session::write()
{ {
const bytes& bytes = m_writeQueue[0]; bytes const* out;
m_io->writeSingleFramePacket(&bytes, m_writeQueue[0]); DEV_GUARDED(x_writeQueue)
{
m_io->writeSingleFramePacket(&m_writeQueue[0], m_writeQueue[0]);
out = &m_writeQueue[0];
}
auto self(shared_from_this()); auto self(shared_from_this());
ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/) ba::async_write(m_socket, ba::buffer(*out), [this, self](boost::system::error_code ec, std::size_t /*length*/)
{ {
ThreadContext tc(info().id.abridged()); ThreadContext tc(info().id.abridged());
ThreadContext tc2(info().clientVersion); ThreadContext tc2(info().clientVersion);
@ -443,8 +447,12 @@ void Session::doRead()
clog(NetWarn) << "Error reading: " << ec.message(); clog(NetWarn) << "Error reading: " << ec.message();
drop(TCPError); drop(TCPError);
} }
else if (ec && length == 0) else if (ec && length < tlen)
{
clog(NetWarn) << "Error reading - Abrupt peer disconnect: " << ec.message();
drop(TCPError);
return; return;
}
else else
{ {
if (!m_io->authAndDecryptFrame(bytesRef(m_data.data(), tlen))) if (!m_io->authAndDecryptFrame(bytesRef(m_data.data(), tlen)))

2
libp2p/UDP.h

@ -99,7 +99,7 @@ struct UDPSocketFace
*/ */
struct UDPSocketEvents struct UDPSocketEvents
{ {
virtual void onDisconnected(UDPSocketFace*) {}; virtual void onDisconnected(UDPSocketFace*) {}
virtual void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packetData) = 0; virtual void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packetData) = 0;
}; };

30
libsolidity/AST.cpp

@ -528,6 +528,17 @@ void VariableDeclaration::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables."));
} }
bool VariableDeclaration::isFunctionParameter() const
{
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
if (!function)
return false;
for (auto const& variable: function->getParameters() + function->getReturnParameters())
if (variable.get() == this)
return true;
return false;
}
bool VariableDeclaration::isExternalFunctionParameter() const bool VariableDeclaration::isExternalFunctionParameter() const
{ {
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope()); auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
@ -686,9 +697,14 @@ void Expression::expectType(Type const& _expectedType)
checkTypeRequirements(nullptr); checkTypeRequirements(nullptr);
Type const& type = *getType(); Type const& type = *getType();
if (!type.isImplicitlyConvertibleTo(_expectedType)) if (!type.isImplicitlyConvertibleTo(_expectedType))
BOOST_THROW_EXCEPTION(createTypeError("Type " + type.toString() + BOOST_THROW_EXCEPTION(createTypeError(
" not implicitly convertible to expected type " "Type " +
+ _expectedType.toString() + ".")); type.toString() +
" is not implicitly convertible to expected type " +
_expectedType.toString() +
"."
)
);
} }
void Expression::requireLValue() void Expression::requireLValue()
@ -874,7 +890,7 @@ void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
{ {
auto const& arrayType(dynamic_cast<ArrayType const&>(type)); auto const& arrayType(dynamic_cast<ArrayType const&>(type));
m_isLValue = (*m_memberName == "length" && m_isLValue = (*m_memberName == "length" &&
arrayType.getLocation() != ArrayType::Location::CallData && arrayType.isDynamicallySized()); arrayType.location() != ReferenceType::Location::CallData && arrayType.isDynamicallySized());
} }
else else
m_isLValue = false; m_isLValue = false;
@ -897,7 +913,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
m_type = make_shared<FixedBytesType>(1); m_type = make_shared<FixedBytesType>(1);
else else
m_type = type.getBaseType(); m_type = type.getBaseType();
m_isLValue = type.getLocation() != ArrayType::Location::CallData; m_isLValue = type.location() != ReferenceType::Location::CallData;
break; break;
} }
case Type::Category::Mapping: case Type::Category::Mapping:
@ -914,7 +930,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
{ {
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType()); TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
if (!m_index) if (!m_index)
m_type = make_shared<TypeType>(make_shared<ArrayType>(ArrayType::Location::Memory, type.getActualType())); m_type = make_shared<TypeType>(make_shared<ArrayType>(ReferenceType::Location::Memory, type.getActualType()));
else else
{ {
m_index->checkTypeRequirements(nullptr); m_index->checkTypeRequirements(nullptr);
@ -922,7 +938,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
if (!length) if (!length)
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected.")); BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
m_type = make_shared<TypeType>(make_shared<ArrayType>( m_type = make_shared<TypeType>(make_shared<ArrayType>(
ArrayType::Location::Memory, type.getActualType(), length->literalValue(nullptr))); ReferenceType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
} }
break; break;
} }

27
libsolidity/AST.h

@ -474,22 +474,26 @@ private:
class VariableDeclaration: public Declaration class VariableDeclaration: public Declaration
{ {
public: public:
enum Location { Default, Storage, Memory };
VariableDeclaration( VariableDeclaration(
SourceLocation const& _location, SourceLocation const& _sourceLocation,
ASTPointer<TypeName> const& _type, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<Expression> _value, ASTPointer<Expression> _value,
Visibility _visibility, Visibility _visibility,
bool _isStateVar = false, bool _isStateVar = false,
bool _isIndexed = false, bool _isIndexed = false,
bool _isConstant = false bool _isConstant = false,
Location _referenceLocation = Location::Default
): ):
Declaration(_location, _name, _visibility), Declaration(_sourceLocation, _name, _visibility),
m_typeName(_type), m_typeName(_type),
m_value(_value), m_value(_value),
m_isStateVariable(_isStateVar), m_isStateVariable(_isStateVar),
m_isIndexed(_isIndexed), m_isIndexed(_isIndexed),
m_isConstant(_isConstant){} m_isConstant(_isConstant),
m_location(_referenceLocation) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
@ -507,20 +511,25 @@ public:
void checkTypeRequirements(); void checkTypeRequirements();
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); } bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
/// @returns true if this variable is a parameter or return parameter of a function.
bool isFunctionParameter() const;
/// @returns true if this variable is a parameter (not return parameter) of an external function.
bool isExternalFunctionParameter() const; bool isExternalFunctionParameter() const;
bool isStateVariable() const { return m_isStateVariable; } bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; } bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; } bool isConstant() const { return m_isConstant; }
Location referenceLocation() const { return m_location; }
protected: protected:
Visibility getDefaultVisibility() const override { return Visibility::Internal; } Visibility getDefaultVisibility() const override { return Visibility::Internal; }
private: private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var") ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
ASTPointer<Expression> m_value; ///< the assigned value, can be missing ASTPointer<Expression> m_value; ///< the assigned value, can be missing
bool m_isStateVariable; ///< Whether or not this is a contract state variable bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events). bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
bool m_isConstant; ///< Whether the variable is a compile-time constant. bool m_isConstant; ///< Whether the variable is a compile-time constant.
Location m_location; ///< Location of the variable if it is of reference type.
std::shared_ptr<Type const> m_type; ///< derived type, initially empty std::shared_ptr<Type const> m_type; ///< derived type, initially empty
}; };

55
libsolidity/ArrayUtils.cpp

@ -38,10 +38,10 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// need to leave "target_ref target_byte_off" on the stack at the end // need to leave "target_ref target_byte_off" on the stack at the end
// stack layout: [source_ref] [source_byte_off] [source length] target_ref target_byte_off (top) // stack layout: [source_ref] [source_byte_off] [source length] target_ref target_byte_off (top)
solAssert(_targetType.getLocation() == ArrayType::Location::Storage, ""); solAssert(_targetType.location() == ReferenceType::Location::Storage, "");
solAssert( solAssert(
_sourceType.getLocation() == ArrayType::Location::CallData || _sourceType.location() == ReferenceType::Location::CallData ||
_sourceType.getLocation() == ArrayType::Location::Storage, _sourceType.location() == ReferenceType::Location::Storage,
"Given array location not implemented." "Given array location not implemented."
); );
@ -51,7 +51,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// TODO unroll loop for small sizes // TODO unroll loop for small sizes
bool sourceIsStorage = _sourceType.getLocation() == ArrayType::Location::Storage; bool sourceIsStorage = _sourceType.location() == ReferenceType::Location::Storage;
bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType; bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16; bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16;
bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16; bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16;
@ -69,7 +69,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
// stack: target_ref source_ref [source_length] // stack: target_ref source_ref [source_length]
// retrieve source length // retrieve source length
if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized()) if (_sourceType.location() != ReferenceType::Location::CallData || !_sourceType.isDynamicallySized())
retrieveLength(_sourceType); // otherwise, length is already there retrieveLength(_sourceType); // otherwise, length is already there
// stack: target_ref source_ref source_length // stack: target_ref source_ref source_length
m_context << eth::Instruction::DUP3; m_context << eth::Instruction::DUP3;
@ -82,7 +82,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
if (sourceBaseType->getCategory() == Type::Category::Mapping) if (sourceBaseType->getCategory() == Type::Category::Mapping)
{ {
solAssert(targetBaseType->getCategory() == Type::Category::Mapping, ""); solAssert(targetBaseType->getCategory() == Type::Category::Mapping, "");
solAssert(_sourceType.getLocation() == ArrayType::Location::Storage, ""); solAssert(_sourceType.location() == ReferenceType::Location::Storage, "");
// nothing to copy // nothing to copy
m_context m_context
<< eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP
@ -106,7 +106,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag(); eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset); m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized()) if (_sourceType.location() == ReferenceType::Location::Storage && _sourceType.isDynamicallySized())
CompilerUtils(m_context).computeHashStatic(); CompilerUtils(m_context).computeHashStatic();
// stack: target_ref target_data_end source_length target_data_pos source_data_pos // stack: target_ref target_data_end source_length target_data_pos source_data_pos
m_context << eth::Instruction::SWAP2; m_context << eth::Instruction::SWAP2;
@ -155,7 +155,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// checking is easier. // checking is easier.
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
m_context << eth::dupInstruction(3 + byteOffsetSize); m_context << eth::dupInstruction(3 + byteOffsetSize);
if (_sourceType.getLocation() == ArrayType::Location::Storage) if (_sourceType.location() == ReferenceType::Location::Storage)
{ {
if (haveByteOffsetSource) if (haveByteOffsetSource)
m_context << eth::Instruction::DUP2; m_context << eth::Instruction::DUP2;
@ -168,7 +168,10 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
else else
solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString()); solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString());
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>... // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>...
solAssert(2 + byteOffsetSize + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep."); solAssert(
2 + byteOffsetSize + sourceBaseType->getSizeOnStack() <= 16,
"Stack too deep, try removing local variables."
);
// fetch target storage reference // fetch target storage reference
m_context << eth::dupInstruction(2 + byteOffsetSize + sourceBaseType->getSizeOnStack()); m_context << eth::dupInstruction(2 + byteOffsetSize + sourceBaseType->getSizeOnStack());
if (haveByteOffsetTarget) if (haveByteOffsetTarget)
@ -228,7 +231,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
void ArrayUtils::clearArray(ArrayType const& _type) const void ArrayUtils::clearArray(ArrayType const& _type) const
{ {
unsigned stackHeightStart = m_context.getStackHeight(); unsigned stackHeightStart = m_context.getStackHeight();
solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.location() == ReferenceType::Location::Storage, "");
if (_type.getBaseType()->getStorageBytes() < 32) if (_type.getBaseType()->getStorageBytes() < 32)
{ {
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type."); solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
@ -283,7 +286,7 @@ void ArrayUtils::clearArray(ArrayType const& _type) const
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
{ {
solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.isDynamicallySized(), ""); solAssert(_type.isDynamicallySized(), "");
unsigned stackHeightStart = m_context.getStackHeight(); unsigned stackHeightStart = m_context.getStackHeight();
@ -311,7 +314,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
{ {
solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.isDynamicallySized(), ""); solAssert(_type.isDynamicallySized(), "");
if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32) if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32)
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type."); solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
@ -396,7 +399,7 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const
{ {
if (_arrayType.getLocation() == ArrayType::Location::Storage) if (_arrayType.location() == ReferenceType::Location::Storage)
{ {
if (_arrayType.getBaseType()->getStorageSize() <= 1) if (_arrayType.getBaseType()->getStorageSize() <= 1)
{ {
@ -432,15 +435,15 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
else else
{ {
m_context << eth::Instruction::DUP1; m_context << eth::Instruction::DUP1;
switch (_arrayType.getLocation()) switch (_arrayType.location())
{ {
case ArrayType::Location::CallData: case ReferenceType::Location::CallData:
// length is stored on the stack // length is stored on the stack
break; break;
case ArrayType::Location::Memory: case ReferenceType::Location::Memory:
m_context << eth::Instruction::MLOAD; m_context << eth::Instruction::MLOAD;
break; break;
case ArrayType::Location::Storage: case ReferenceType::Location::Storage:
m_context << eth::Instruction::SLOAD; m_context << eth::Instruction::SLOAD;
break; break;
} }
@ -449,16 +452,16 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
{ {
ArrayType::Location location = _arrayType.getLocation(); ReferenceType::Location location = _arrayType.location();
eth::Instruction load = eth::Instruction load =
location == ArrayType::Location::Storage ? eth::Instruction::SLOAD : location == ReferenceType::Location::Storage ? eth::Instruction::SLOAD :
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD : location == ReferenceType::Location::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD; eth::Instruction::CALLDATALOAD;
// retrieve length // retrieve length
if (!_arrayType.isDynamicallySized()) if (!_arrayType.isDynamicallySized())
m_context << _arrayType.getLength(); m_context << _arrayType.getLength();
else if (location == ArrayType::Location::CallData) else if (location == ReferenceType::Location::CallData)
// length is stored on the stack // length is stored on the stack
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
else else
@ -473,15 +476,15 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
if (_arrayType.isDynamicallySized()) if (_arrayType.isDynamicallySized())
{ {
if (location == ArrayType::Location::Storage) if (location == ReferenceType::Location::Storage)
CompilerUtils(m_context).computeHashStatic(); CompilerUtils(m_context).computeHashStatic();
else if (location == ArrayType::Location::Memory) else if (location == ReferenceType::Location::Memory)
m_context << u256(32) << eth::Instruction::ADD; m_context << u256(32) << eth::Instruction::ADD;
} }
// stack: <index> <data_ref> // stack: <index> <data_ref>
switch (location) switch (location)
{ {
case ArrayType::Location::CallData: case ReferenceType::Location::CallData:
if (!_arrayType.isByteArray()) if (!_arrayType.isByteArray())
m_context m_context
<< eth::Instruction::SWAP1 << eth::Instruction::SWAP1
@ -496,7 +499,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
false false
); );
break; break;
case ArrayType::Location::Storage: case ReferenceType::Location::Storage:
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
if (_arrayType.getBaseType()->getStorageBytes() <= 16) if (_arrayType.getBaseType()->getStorageBytes() <= 16)
{ {
@ -524,7 +527,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::ADD << u256(0); m_context << eth::Instruction::ADD << u256(0);
} }
break; break;
case ArrayType::Location::Memory: case ReferenceType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented."); solAssert(false, "Memory lvalues not yet implemented.");
} }
} }

26
libsolidity/Compiler.cpp

@ -69,6 +69,8 @@ void Compiler::compileContract(ContractDefinition const& _contract,
swap(m_context, m_runtimeContext); swap(m_context, m_runtimeContext);
initializeContext(_contract, _contracts); initializeContext(_contract, _contracts);
packIntoContractCreator(_contract, m_runtimeContext); packIntoContractCreator(_contract, m_runtimeContext);
if (m_optimize)
m_context.optimise(m_optimizeRuns);
} }
eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _function) const eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _function) const
@ -120,9 +122,11 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
else if (auto c = m_context.getNextConstructor(_contract)) else if (auto c = m_context.getNextConstructor(_contract))
appendBaseConstructor(*c); appendBaseConstructor(*c);
eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly()); eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.getAssembly());
solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
m_runtimeSub = size_t(runtimeSub.data());
// stack contains sub size // stack contains sub size
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY; m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN; m_context << u256(0) << eth::Instruction::RETURN;
// note that we have to include the functions again because of absolute jump labels // note that we have to include the functions again because of absolute jump labels
@ -174,6 +178,16 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions(); map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints; map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
FunctionDefinition const* fallback = _contract.getFallbackFunction();
eth::AssemblyItem notFound = m_context.newTag();
// shortcut messages without data if we have many functions in order to be able to receive
// ether with constant gas
if (interfaceFunctions.size() > 5 || fallback)
{
m_context << eth::Instruction::CALLDATASIZE << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(notFound);
}
// retrieve the function signature hash from the calldata // retrieve the function signature hash from the calldata
if (!interfaceFunctions.empty()) if (!interfaceFunctions.empty())
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true); CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
@ -185,7 +199,10 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ; m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
} }
if (FunctionDefinition const* fallback = _contract.getFallbackFunction()) m_context.appendJumpTo(notFound);
m_context << notFound;
if (fallback)
{ {
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this); fallback->accept(*this);
@ -194,6 +211,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
} }
else else
m_context << eth::Instruction::STOP; // function not found m_context << eth::Instruction::STOP; // function not found
for (auto const& it: interfaceFunctions) for (auto const& it: interfaceFunctions)
{ {
FunctionTypePointer const& functionType = it.second; FunctionTypePointer const& functionType = it.second;
@ -349,7 +367,7 @@ bool Compiler::visit(FunctionDefinition const& _function)
stackLayout.push_back(i); stackLayout.push_back(i);
stackLayout += vector<int>(c_localVariablesSize, -1); stackLayout += vector<int>(c_localVariablesSize, -1);
solAssert(stackLayout.size() <= 17, "Stack too deep."); solAssert(stackLayout.size() <= 17, "Stack too deep, try removing local variables.");
while (stackLayout.back() != int(stackLayout.size() - 1)) while (stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0) if (stackLayout.back() < 0)
{ {

17
libsolidity/Compiler.h

@ -34,13 +34,18 @@ namespace solidity {
class Compiler: private ASTConstVisitor class Compiler: private ASTConstVisitor
{ {
public: public:
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(), explicit Compiler(bool _optimize = false, unsigned _runs = 200):
m_returnTag(m_context.newTag()) {} m_optimize(_optimize),
m_optimizeRuns(_runs),
m_context(),
m_returnTag(m_context.newTag())
{
}
void compileContract(ContractDefinition const& _contract, void compileContract(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts); std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } bytes getAssembledBytecode() { return m_context.getAssembledBytecode(); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);} bytes getRuntimeBytecode() { return m_context.getAssembledRuntimeBytecode(m_runtimeSub); }
/// @arg _sourceCodes is the map of input files to source code strings /// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFromat shows whether the out should be in Json format /// @arg _inJsonFromat shows whether the out should be in Json format
Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
@ -50,7 +55,7 @@ public:
/// @returns Assembly items of the normal compiler context /// @returns Assembly items of the normal compiler context
eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); } eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); }
/// @returns Assembly items of the runtime compiler context /// @returns Assembly items of the runtime compiler context
eth::AssemblyItems const& getRuntimeAssemblyItems() const { return m_runtimeContext.getAssembly().getItems(); } eth::AssemblyItems const& getRuntimeAssemblyItems() const { return m_context.getAssembly().getSub(m_runtimeSub).getItems(); }
/// @returns the entry label of the given function. Might return an AssemblyItem of type /// @returns the entry label of the given function. Might return an AssemblyItem of type
/// UndefinedItem if it does not exist yet. /// UndefinedItem if it does not exist yet.
@ -93,7 +98,9 @@ private:
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
bool const m_optimize; bool const m_optimize;
unsigned const m_optimizeRuns;
CompilerContext m_context; CompilerContext m_context;
size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly
CompilerContext m_runtimeContext; CompilerContext m_runtimeContext;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement

5
libsolidity/CompilerContext.h

@ -126,6 +126,8 @@ public:
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; } CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; } CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
void optimise(unsigned _runs = 200) { m_asm.optimise(true, true, _runs); }
eth::Assembly const& getAssembly() const { return m_asm; } eth::Assembly const& getAssembly() const { return m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings /// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFormat shows whether the out should be in Json format /// @arg _inJsonFormat shows whether the out should be in Json format
@ -134,7 +136,8 @@ public:
return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat); return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
} }
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); } bytes getAssembledBytecode() { return m_asm.assemble(); }
bytes getAssembledRuntimeBytecode(size_t _subIndex) { m_asm.assemble(); return m_asm.data(u256(_subIndex)); }
/** /**
* Helper class to pop the visited nodes stack when a scope closes * Helper class to pop the visited nodes stack when a scope closes

6
libsolidity/CompilerStack.cpp

@ -145,7 +145,7 @@ vector<string> CompilerStack::getContractNames() const
} }
void CompilerStack::compile(bool _optimize) void CompilerStack::compile(bool _optimize, unsigned _runs)
{ {
if (!m_parseSuccessful) if (!m_parseSuccessful)
parse(); parse();
@ -157,9 +157,9 @@ void CompilerStack::compile(bool _optimize)
{ {
if (!contract->isFullyImplemented()) if (!contract->isFullyImplemented())
continue; continue;
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize); shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
compiler->compileContract(*contract, contractBytecode); compiler->compileContract(*contract, contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()]; Contract& compiledContract = m_contracts.at(contract->getName());
compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode(); compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler); compiledContract.compiler = move(compiler);

2
libsolidity/CompilerStack.h

@ -90,7 +90,7 @@ public:
std::string defaultContractName() const; std::string defaultContractName() const;
/// Compiles the source units that were previously added and parsed. /// Compiles the source units that were previously added and parsed.
void compile(bool _optimize = false); void compile(bool _optimize = false, unsigned _runs = 200);
/// Parses and compiles the given source code. /// Parses and compiles the given source code.
/// @returns the compiled bytecode /// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false); bytes const& compile(std::string const& _sourceCode, bool _optimize = false);

15
libsolidity/CompilerUtils.cpp

@ -81,7 +81,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
auto const& type = dynamic_cast<ArrayType const&>(_type); auto const& type = dynamic_cast<ArrayType const&>(_type);
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here."); solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
if (type.getLocation() == ArrayType::Location::CallData) if (type.location() == ReferenceType::Location::CallData)
{ {
// stack: target source_offset source_len // stack: target source_offset source_len
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5 m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
@ -92,7 +92,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
} }
else else
{ {
solAssert(type.getLocation() == ArrayType::Location::Storage, "Memory arrays not yet implemented."); solAssert(type.location() == ReferenceType::Location::Storage, "Memory arrays not yet implemented.");
m_context << eth::Instruction::POP; // remove offset, arrays always start new slot m_context << eth::Instruction::POP; // remove offset, arrays always start new slot
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes // stack here: memory_offset storage_offset length_bytes
@ -142,22 +142,25 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
solAssert(stackPosition >= size, "Variable size and position mismatch."); solAssert(stackPosition >= size, "Variable size and position mismatch.");
// move variable starting from its top end in the stack // move variable starting from its top end in the stack
if (stackPosition - size + 1 > 16) if (stackPosition - size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation()) BOOST_THROW_EXCEPTION(
<< errinfo_comment("Stack too deep.")); CompilerError() <<
errinfo_sourceLocation(_variable.getLocation()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < size; ++i) for (unsigned i = 0; i < size; ++i)
m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP; m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP;
} }
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize) void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
{ {
solAssert(_stackDepth <= 16, "Stack too deep."); solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
for (unsigned i = 0; i < _itemSize; ++i) for (unsigned i = 0; i < _itemSize; ++i)
m_context << eth::dupInstruction(_stackDepth); m_context << eth::dupInstruction(_stackDepth);
} }
void CompilerUtils::moveToStackTop(unsigned _stackDepth) void CompilerUtils::moveToStackTop(unsigned _stackDepth)
{ {
solAssert(_stackDepth <= 15, "Stack too deep."); solAssert(_stackDepth <= 15, "Stack too deep, try removing local variables.");
for (unsigned i = 0; i < _stackDepth; ++i) for (unsigned i = 0; i < _stackDepth; ++i)
m_context << eth::swapInstruction(1 + i); m_context << eth::swapInstruction(1 + i);
} }

63
libsolidity/ExpressionCompiler.cpp

@ -23,6 +23,7 @@
#include <utility> #include <utility>
#include <numeric> #include <numeric>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Params.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/SHA3.h> #include <libdevcore/SHA3.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
@ -262,7 +263,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
if (lvalueSize > 0) if (lvalueSize > 0)
{ {
solAssert(itemSize + lvalueSize <= 16, "Stack too deep."); solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables.");
// value [lvalue_ref] updated_value // value [lvalue_ref] updated_value
for (unsigned i = 0; i < itemSize; ++i) for (unsigned i = 0; i < itemSize; ++i)
m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP; m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP;
@ -497,6 +498,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{ {
// stack layout: contract_address function_id [gas] [value] // stack layout: contract_address function_id [gas] [value]
_functionCall.getExpression().accept(*this); _functionCall.getExpression().accept(*this);
arguments.front()->accept(*this); arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), IntegerType(256), true); appendTypeConversion(*arguments.front()->getType(), IntegerType(256), true);
// Note that function is not the original function, but the ".gas" function. // Note that function is not the original function, but the ".gas" function.
@ -519,7 +521,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break; break;
case Location::Send: case Location::Send:
_functionCall.getExpression().accept(*this); _functionCall.getExpression().accept(*this);
m_context << u256(0); // 0 gas, we do not want to execute code m_context << u256(0); // do not send gas (there still is the stipend)
arguments.front()->accept(*this); arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), appendTypeConversion(*arguments.front()->getType(),
*function.getParameterTypes().front(), true); *function.getParameterTypes().front(), true);
@ -769,12 +771,12 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << type.getLength(); m_context << type.getLength();
} }
else else
switch (type.getLocation()) switch (type.location())
{ {
case ArrayType::Location::CallData: case ReferenceType::Location::CallData:
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
break; break;
case ArrayType::Location::Storage: case ReferenceType::Location::Storage:
setLValue<StorageArrayLength>(_memberAccess, type); setLValue<StorageArrayLength>(_memberAccess, type);
break; break;
default: default:
@ -815,13 +817,13 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
// remove storage byte offset // remove storage byte offset
if (arrayType.getLocation() == ArrayType::Location::Storage) if (arrayType.location() == ReferenceType::Location::Storage)
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
_indexAccess.getIndexExpression()->accept(*this); _indexAccess.getIndexExpression()->accept(*this);
// stack layout: <base_ref> [<length>] <index> // stack layout: <base_ref> [<length>] <index>
ArrayUtils(m_context).accessIndex(arrayType); ArrayUtils(m_context).accessIndex(arrayType);
if (arrayType.getLocation() == ArrayType::Location::Storage) if (arrayType.location() == ReferenceType::Location::Storage)
{ {
if (arrayType.isByteArray()) if (arrayType.isByteArray())
{ {
@ -1056,10 +1058,15 @@ void ExpressionCompiler::appendExternalFunctionCall(
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize); unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1); unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
bool returnSuccessCondition =
_functionType.getLocation() == FunctionType::Location::Bare ||
_functionType.getLocation() == FunctionType::Location::BareCallCode;
//@todo only return the first return value for now //@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get(); _functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0; unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
if (returnSuccessCondition)
retSize = 0; // return value actually is success condition
m_context << u256(retSize) << u256(0); m_context << u256(retSize) << u256(0);
if (_functionType.isBareCall()) if (_functionType.isBareCall())
@ -1098,7 +1105,10 @@ void ExpressionCompiler::appendExternalFunctionCall(
else else
// send all gas except the amount needed to execute "SUB" and "CALL" // send all gas except the amount needed to execute "SUB" and "CALL"
// @todo this retains too much gas for now, needs to be fine-tuned. // @todo this retains too much gas for now, needs to be fine-tuned.
m_context << u256(50 + (_functionType.valueSet() ? 9000 : 0) + 25000) << eth::Instruction::GAS << eth::Instruction::SUB; m_context <<
u256(eth::c_callGas + 10 + (_functionType.valueSet() ? eth::c_callValueTransferGas : 0) + eth::c_callNewAccountGas) <<
eth::Instruction::GAS <<
eth::Instruction::SUB;
if ( if (
_functionType.getLocation() == FunctionType::Location::CallCode || _functionType.getLocation() == FunctionType::Location::CallCode ||
_functionType.getLocation() == FunctionType::Location::BareCallCode _functionType.getLocation() == FunctionType::Location::BareCallCode
@ -1107,19 +1117,28 @@ void ExpressionCompiler::appendExternalFunctionCall(
else else
m_context << eth::Instruction::CALL; m_context << eth::Instruction::CALL;
//Propagate error condition (if CALL pushes 0 on stack). unsigned remainsSize =
m_context << eth::Instruction::ISZERO; 1 + // contract address
m_context.appendConditionalJumpTo(m_context.errorTag()); _functionType.valueSet() +
_functionType.gasSet() +
!_functionType.isBareCall();
if (_functionType.valueSet()) if (returnSuccessCondition)
m_context << eth::Instruction::POP; m_context << eth::swapInstruction(remainsSize);
if (_functionType.gasSet()) else
m_context << eth::Instruction::POP; {
if (!_functionType.isBareCall()) //Propagate error condition (if CALL pushes 0 on stack).
m_context << eth::Instruction::POP; m_context << eth::Instruction::ISZERO;
m_context << eth::Instruction::POP; // pop contract address m_context.appendConditionalJumpTo(m_context.errorTag());
}
if (_functionType.getLocation() == FunctionType::Location::RIPEMD160) CompilerUtils(m_context).popStackSlots(remainsSize);
if (returnSuccessCondition)
{
// already there
}
else if (_functionType.getLocation() == FunctionType::Location::RIPEMD160)
{ {
// fix: built-in contract returns right-aligned data // fix: built-in contract returns right-aligned data
CompilerUtils(m_context).loadFromMemory(0, IntegerType(160), false, true); CompilerUtils(m_context).loadFromMemory(0, IntegerType(160), false, true);
@ -1165,13 +1184,13 @@ void ExpressionCompiler::appendArgumentsCopyToMemory(
auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType()); auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType());
// move memory reference to top of stack // move memory reference to top of stack
CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack()); CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack());
if (arrayType.getLocation() == ArrayType::Location::CallData) if (arrayType.location() == ReferenceType::Location::CallData)
m_context << eth::Instruction::DUP2; // length is on stack m_context << eth::Instruction::DUP2; // length is on stack
else if (arrayType.getLocation() == ArrayType::Location::Storage) else if (arrayType.location() == ReferenceType::Location::Storage)
m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD; m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
else else
{ {
solAssert(arrayType.getLocation() == ArrayType::Location::Memory, ""); solAssert(arrayType.location() == ReferenceType::Location::Memory, "");
m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD; m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
} }
appendTypeMoveToMemory(IntegerType(256), true); appendTypeMoveToMemory(IntegerType(256), true);

26
libsolidity/LValue.cpp

@ -42,8 +42,11 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
{ {
unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset); unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset);
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() BOOST_THROW_EXCEPTION(
<< errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep.")); CompilerError() <<
errinfo_sourceLocation(_location) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < m_size; ++i) for (unsigned i = 0; i < m_size; ++i)
m_context << eth::dupInstruction(stackPos + 1); m_context << eth::dupInstruction(stackPos + 1);
} }
@ -52,8 +55,11 @@ void StackVariable::storeValue(Type const&, SourceLocation const& _location, boo
{ {
unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1; unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1;
if (stackDiff > 16) if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() BOOST_THROW_EXCEPTION(
<< errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep.")); CompilerError() <<
errinfo_sourceLocation(_location) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
else if (stackDiff > 0) else if (stackDiff > 0)
for (unsigned i = 0; i < m_size; ++i) for (unsigned i = 0; i < m_size; ++i)
m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP; m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
@ -65,8 +71,11 @@ void StackVariable::setToZero(SourceLocation const& _location, bool) const
{ {
unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset); unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset);
if (stackDiff > 16) if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() BOOST_THROW_EXCEPTION(
<< errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep.")); CompilerError() <<
errinfo_sourceLocation(_location) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
solAssert(stackDiff >= m_size - 1, ""); solAssert(stackDiff >= m_size - 1, "");
for (unsigned i = 0; i < m_size; ++i) for (unsigned i = 0; i < m_size; ++i)
m_context << u256(0) << eth::swapInstruction(stackDiff + 1 - i) m_context << u256(0) << eth::swapInstruction(stackDiff + 1 - i)
@ -204,7 +213,10 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
// stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_member_ref source_member_off // stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_member_ref source_member_off
StorageItem(m_context, *memberType).retrieveValue(_location, true); StorageItem(m_context, *memberType).retrieveValue(_location, true);
// stack: source_ref source_off target_ref target_off member_offset source_value... // stack: source_ref source_off target_ref target_off member_offset source_value...
solAssert(4 + memberType->getSizeOnStack() <= 16, "Stack too deep."); solAssert(
4 + memberType->getSizeOnStack() <= 16,
"Stack too deep, try removing local varibales."
);
m_context m_context
<< eth::dupInstruction(4 + memberType->getSizeOnStack()) << eth::dupInstruction(4 + memberType->getSizeOnStack())
<< eth::dupInstruction(3 + memberType->getSizeOnStack()) << eth::Instruction::ADD << eth::dupInstruction(3 + memberType->getSizeOnStack()) << eth::Instruction::ADD

47
libsolidity/NameAndTypeResolver.cpp

@ -424,10 +424,49 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
if (_variable.getTypeName()) if (_variable.getTypeName())
{ {
TypePointer type = _variable.getTypeName()->toType(); TypePointer type = _variable.getTypeName()->toType();
// All array parameter types should point to call data using Location = VariableDeclaration::Location;
if (_variable.isExternalFunctionParameter()) Location loc = _variable.referenceLocation();
if (auto const* arrayType = dynamic_cast<ArrayType const*>(type.get())) // References are forced to calldata for external function parameters (not return)
type = arrayType->copyForLocation(ArrayType::Location::CallData); // and memory for parameters (also return) of publicly visible functions.
// They default to memory for function parameters and storage for local variables.
if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
{
if (_variable.isExternalFunctionParameter())
{
// force location of external function parameters (not return) to calldata
if (loc != Location::Default)
BOOST_THROW_EXCEPTION(_variable.createTypeError(
"Location has to be calldata for external functions "
"(remove the \"memory\" or \"storage\" keyword)."
));
type = ref->copyForLocation(ReferenceType::Location::CallData);
}
else if (_variable.isFunctionParameter() && _variable.getScope()->isPublic())
{
// force locations of public or external function (return) parameters to memory
if (loc == VariableDeclaration::Location::Storage)
BOOST_THROW_EXCEPTION(_variable.createTypeError(
"Location has to be memory for publicly visible functions "
"(remove the \"storage\" keyword)."
));
type = ref->copyForLocation(ReferenceType::Location::Memory);
}
else
{
if (loc == Location::Default)
loc = _variable.isFunctionParameter() ? Location::Memory : Location::Storage;
type = ref->copyForLocation(
loc == Location::Memory ?
ReferenceType::Location::Memory :
ReferenceType::Location::Storage
);
}
}
else if (loc != Location::Default && !ref)
BOOST_THROW_EXCEPTION(_variable.createTypeError(
"Storage location can only be given for array or struct types."
));
_variable.setType(type); _variable.setType(type);
if (!_variable.getType()) if (!_variable.getType())

105
libsolidity/Parser.cpp

@ -224,7 +224,9 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
name = make_shared<ASTString>(); // anonymous function name = make_shared<ASTString>(); // anonymous function
else else
name = expectIdentifierToken(); name = expectIdentifierToken();
ASTPointer<ParameterList> parameters(parseParameterList()); VarDeclParserOptions options;
options.allowLocationSpecifier = true;
ASTPointer<ParameterList> parameters(parseParameterList(options));
bool isDeclaredConst = false; bool isDeclaredConst = false;
Declaration::Visibility visibility(Declaration::Visibility::Default); Declaration::Visibility visibility(Declaration::Visibility::Default);
vector<ASTPointer<ModifierInvocation>> modifiers; vector<ASTPointer<ModifierInvocation>> modifiers;
@ -252,7 +254,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
{ {
bool const permitEmptyParameterList = false; bool const permitEmptyParameterList = false;
m_scanner->next(); m_scanner->next();
returnParameters = parseParameterList(permitEmptyParameterList); returnParameters = parseParameterList(options, permitEmptyParameterList);
} }
else else
returnParameters = createEmptyParameterList(); returnParameters = createEmptyParameterList();
@ -319,7 +321,9 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
} }
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
VarDeclParserOptions const& _options, ASTPointer<TypeName> const& _lookAheadArrayType) VarDeclParserOptions const& _options,
ASTPointer<TypeName> const& _lookAheadArrayType
)
{ {
ASTNodeFactory nodeFactory = _lookAheadArrayType ? ASTNodeFactory nodeFactory = _lookAheadArrayType ?
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this); ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
@ -334,20 +338,41 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
} }
bool isIndexed = false; bool isIndexed = false;
bool isDeclaredConst = false; bool isDeclaredConst = false;
ASTPointer<ASTString> identifier;
Token::Value token = m_scanner->getCurrentToken();
Declaration::Visibility visibility(Declaration::Visibility::Default); Declaration::Visibility visibility(Declaration::Visibility::Default);
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token)) VariableDeclaration::Location location = VariableDeclaration::Location::Default;
visibility = parseVisibilitySpecifier(token); ASTPointer<ASTString> identifier;
if (_options.allowIndexed && token == Token::Indexed)
{ while (true)
isIndexed = true;
m_scanner->next();
}
if (token == Token::Const)
{ {
isDeclaredConst = true; Token::Value token = m_scanner->getCurrentToken();
m_scanner->next(); if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
{
if (visibility != Declaration::Visibility::Default)
BOOST_THROW_EXCEPTION(createParserError("Visibility already specified."));
visibility = parseVisibilitySpecifier(token);
}
else
{
if (_options.allowIndexed && token == Token::Indexed)
isIndexed = true;
else if (token == Token::Const)
isDeclaredConst = true;
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
{
if (location != VariableDeclaration::Location::Default)
BOOST_THROW_EXCEPTION(createParserError("Location already specified."));
if (!type)
BOOST_THROW_EXCEPTION(createParserError("Location specifier needs explicit type name."));
location = (
token == Token::Memory ?
VariableDeclaration::Location::Memory :
VariableDeclaration::Location::Storage
);
}
else
break;
m_scanner->next();
}
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -369,9 +394,16 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
nodeFactory.setEndPositionFromNode(value); nodeFactory.setEndPositionFromNode(value);
} }
} }
return nodeFactory.createNode<VariableDeclaration>(type, identifier, value, return nodeFactory.createNode<VariableDeclaration>(
visibility, _options.isStateVariable, type,
isIndexed, isDeclaredConst); identifier,
value,
visibility,
_options.isStateVariable,
isIndexed,
isDeclaredConst,
location
);
} }
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition() ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
@ -388,7 +420,12 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
ASTPointer<ASTString> name(expectIdentifierToken()); ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters; ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LParen) if (m_scanner->getCurrentToken() == Token::LParen)
parameters = parseParameterList(); {
VarDeclParserOptions options;
options.allowIndexed = true;
options.allowLocationSpecifier = true;
parameters = parseParameterList(options);
}
else else
parameters = createEmptyParameterList(); parameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock(); ASTPointer<Block> block = parseBlock();
@ -407,7 +444,11 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
ASTPointer<ASTString> name(expectIdentifierToken()); ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters; ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LParen) if (m_scanner->getCurrentToken() == Token::LParen)
parameters = parseParameterList(true, true); {
VarDeclParserOptions options;
options.allowIndexed = true;
parameters = parseParameterList(options);
}
else else
parameters = createEmptyParameterList(); parameters = createEmptyParameterList();
bool anonymous = false; bool anonymous = false;
@ -505,12 +546,14 @@ ASTPointer<Mapping> Parser::parseMapping()
return nodeFactory.createNode<Mapping>(keyType, valueType); return nodeFactory.createNode<Mapping>(keyType, valueType);
} }
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed) ASTPointer<ParameterList> Parser::parseParameterList(
VarDeclParserOptions const& _options,
bool _allowEmpty
)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<VariableDeclaration>> parameters; vector<ASTPointer<VariableDeclaration>> parameters;
VarDeclParserOptions options; VarDeclParserOptions options(_options);
options.allowIndexed = _allowIndexed;
options.allowEmptyName = true; options.allowEmptyName = true;
expectToken(Token::LParen); expectToken(Token::LParen);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen) if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen)
@ -691,7 +734,7 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
} }
while (m_scanner->getCurrentToken() == Token::LBrack); while (m_scanner->getCurrentToken() == Token::LBrack);
if (m_scanner->getCurrentToken() == Token::Identifier) if (m_scanner->getCurrentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->getCurrentToken()))
return parseVariableDeclarationStatement(typeNameIndexAccessStructure(primary, indices)); return parseVariableDeclarationStatement(typeNameIndexAccessStructure(primary, indices));
else else
return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices)); return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices));
@ -703,6 +746,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
VarDeclParserOptions options; VarDeclParserOptions options;
options.allowVar = true; options.allowVar = true;
options.allowInitialValue = true; options.allowInitialValue = true;
options.allowLocationSpecifier = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType); ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType);
ASTNodeFactory nodeFactory(*this, variable); ASTNodeFactory nodeFactory(*this, variable);
return nodeFactory.createNode<VariableDeclarationStatement>(variable); return nodeFactory.createNode<VariableDeclarationStatement>(variable);
@ -944,11 +988,16 @@ Parser::LookAheadInfo Parser::peekStatementType() const
Token::Value token(m_scanner->getCurrentToken()); Token::Value token(m_scanner->getCurrentToken());
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier); bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
if (token == Token::Mapping || token == Token::Var || if (token == Token::Mapping || token == Token::Var)
(mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
return LookAheadInfo::VariableDeclarationStatement; return LookAheadInfo::VariableDeclarationStatement;
if (mightBeTypeName && m_scanner->peekNextToken() == Token::LBrack) if (mightBeTypeName)
return LookAheadInfo::IndexAccessStructure; {
Token::Value next = m_scanner->peekNextToken();
if (next == Token::Identifier || Token::isLocationSpecifier(next))
return LookAheadInfo::VariableDeclarationStatement;
if (m_scanner->peekNextToken() == Token::LBrack)
return LookAheadInfo::IndexAccessStructure;
}
return LookAheadInfo::ExpressionStatement; return LookAheadInfo::ExpressionStatement;
} }

9
libsolidity/Parser.h

@ -47,13 +47,15 @@ private:
/// End position of the current token /// End position of the current token
int getEndPosition() const; int getEndPosition() const;
struct VarDeclParserOptions { struct VarDeclParserOptions
{
VarDeclParserOptions() {} VarDeclParserOptions() {}
bool allowVar = false; bool allowVar = false;
bool isStateVariable = false; bool isStateVariable = false;
bool allowIndexed = false; bool allowIndexed = false;
bool allowEmptyName = false; bool allowEmptyName = false;
bool allowInitialValue = false; bool allowInitialValue = false;
bool allowLocationSpecifier = false;
}; };
///@{ ///@{
@ -74,7 +76,10 @@ private:
ASTPointer<Identifier> parseIdentifier(); ASTPointer<Identifier> parseIdentifier();
ASTPointer<TypeName> parseTypeName(bool _allowVar); ASTPointer<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping(); ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false); ASTPointer<ParameterList> parseParameterList(
VarDeclParserOptions const& _options,
bool _allowEmpty = true
);
ASTPointer<Block> parseBlock(); ASTPointer<Block> parseBlock();
ASTPointer<Statement> parseStatement(); ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement(); ASTPointer<IfStatement> parseIfStatement();

3
libsolidity/Token.h

@ -161,12 +161,14 @@ namespace solidity
K(Import, "import", 0) \ K(Import, "import", 0) \
K(Is, "is", 0) \ K(Is, "is", 0) \
K(Mapping, "mapping", 0) \ K(Mapping, "mapping", 0) \
K(Memory, "memory", 0) \
K(Modifier, "modifier", 0) \ K(Modifier, "modifier", 0) \
K(New, "new", 0) \ K(New, "new", 0) \
K(Public, "public", 0) \ K(Public, "public", 0) \
K(Private, "private", 0) \ K(Private, "private", 0) \
K(Return, "return", 0) \ K(Return, "return", 0) \
K(Returns, "returns", 0) \ K(Returns, "returns", 0) \
K(Storage, "storage", 0) \
K(Struct, "struct", 0) \ K(Struct, "struct", 0) \
K(Var, "var", 0) \ K(Var, "var", 0) \
K(While, "while", 0) \ K(While, "while", 0) \
@ -370,6 +372,7 @@ public:
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; }
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; }

64
libsolidity/Types.cpp

@ -144,9 +144,9 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
else if (_typeToken == Token::Bool) else if (_typeToken == Token::Bool)
return make_shared<BoolType>(); return make_shared<BoolType>();
else if (_typeToken == Token::Bytes) else if (_typeToken == Token::Bytes)
return make_shared<ArrayType>(ArrayType::Location::Storage); return make_shared<ArrayType>(ReferenceType::Location::Storage);
else if (_typeToken == Token::String) else if (_typeToken == Token::String)
return make_shared<ArrayType>(ArrayType::Location::Storage, true); return make_shared<ArrayType>(ReferenceType::Location::Storage, true);
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type.")); std::string(Token::toString(_typeToken)) + " to type."));
@ -196,10 +196,10 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get()); auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
if (!length) if (!length)
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length.")); BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType, length->literalValue(nullptr)); return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType, length->literalValue(nullptr));
} }
else else
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType); return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType);
} }
TypePointer Type::forLiteral(Literal const& _literal) TypePointer Type::forLiteral(Literal const& _literal)
@ -317,9 +317,9 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
const MemberList IntegerType::AddressMemberList({ const MemberList IntegerType::AddressMemberList({
{"balance", make_shared<IntegerType >(256)}, {"balance", make_shared<IntegerType >(256)},
{"call", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Bare, true)}, {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)},
{"callcode", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::BareCallCode, true)}, {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)} {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
}); });
IntegerConstantType::IntegerConstantType(Literal const& _literal) IntegerConstantType::IntegerConstantType(Literal const& _literal)
@ -361,17 +361,27 @@ IntegerConstantType::IntegerConstantType(Literal const& _literal)
bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{ {
shared_ptr<IntegerType const> integerType = getIntegerType(); if (auto targetType = dynamic_cast<IntegerType const*>(&_convertTo))
if (!integerType) {
if (m_value == 0)
return true;
int forSignBit = (targetType->isSigned() ? 1 : 0);
if (m_value > 0)
{
if (m_value <= (u256(-1) >> (256 - targetType->getNumBits() + forSignBit)))
return true;
}
else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->getNumBits() - forSignBit)))
return true;
return false; return false;
}
if (_convertTo.getCategory() == Category::FixedBytes) else if (_convertTo.getCategory() == Category::FixedBytes)
{ {
FixedBytesType const& convertTo = dynamic_cast<FixedBytesType const&>(_convertTo); FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
return convertTo.getNumBytes() * 8 >= integerType->getNumBits(); return fixedBytes.getNumBytes() * 8 >= getIntegerType()->getNumBits();
} }
else
return integerType->isImplicitlyConvertibleTo(_convertTo); return false;
} }
bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const
@ -514,9 +524,10 @@ shared_ptr<IntegerType const> IntegerConstantType::getIntegerType() const
if (value > u256(-1)) if (value > u256(-1))
return shared_ptr<IntegerType const>(); return shared_ptr<IntegerType const>();
else else
return make_shared<IntegerType>(max(bytesRequired(value), 1u) * 8, return make_shared<IntegerType>(
negative ? IntegerType::Modifier::Signed max(bytesRequired(value), 1u) * 8,
: IntegerType::Modifier::Unsigned); negative ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned
);
} }
shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal) shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal)
@ -663,7 +674,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
return false; return false;
auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo); auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
// let us not allow assignment to memory arrays for now // let us not allow assignment to memory arrays for now
if (convertTo.getLocation() != Location::Storage) if (convertTo.location() != Location::Storage)
return false; return false;
if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString()) if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
return false; return false;
@ -767,12 +778,12 @@ TypePointer ArrayType::externalType() const
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length); return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length);
} }
shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const TypePointer ArrayType::copyForLocation(ReferenceType::Location _location) const
{ {
auto copy = make_shared<ArrayType>(_location); auto copy = make_shared<ArrayType>(_location);
copy->m_arrayKind = m_arrayKind; copy->m_arrayKind = m_arrayKind;
if (m_baseType->getCategory() == Type::Category::Array) if (auto ref = dynamic_cast<ReferenceType const*>(m_baseType.get()))
copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location); copy->m_baseType = ref->copyForLocation(_location);
else else
copy->m_baseType = m_baseType; copy->m_baseType = m_baseType;
copy->m_hasDynamicLength = m_hasDynamicLength; copy->m_hasDynamicLength = m_hasDynamicLength;
@ -923,6 +934,13 @@ MemberList const& StructType::getMembers() const
return *m_members; return *m_members;
} }
TypePointer StructType::copyForLocation(ReferenceType::Location _location) const
{
auto copy = make_shared<StructType>(m_struct);
copy->m_location = _location;
return copy;
}
pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
{ {
auto const* offsets = getMembers().getMemberStorageOffset(_name); auto const* offsets = getMembers().getMemberStorageOffset(_name);
@ -1455,7 +1473,7 @@ MagicType::MagicType(MagicType::Kind _kind):
{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, {"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)}, {"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)},
{"data", make_shared<ArrayType>(ArrayType::Location::CallData)}, {"data", make_shared<ArrayType>(ReferenceType::Location::CallData)},
{"sig", make_shared<FixedBytesType>(4)} {"sig", make_shared<FixedBytesType>(4)}
})); }));
break; break;

41
libsolidity/Types.h

@ -353,6 +353,24 @@ public:
virtual TypePointer externalType() const override { return shared_from_this(); } virtual TypePointer externalType() const override { return shared_from_this(); }
}; };
/**
* Trait used by types which are not value types and can be stored either in storage, memory
* or calldata. This is currently used by arrays and structs.
*/
class ReferenceType
{
public:
enum class Location { Storage, CallData, Memory };
explicit ReferenceType(Location _location): m_location(_location) {}
Location location() const { return m_location; }
/// @returns a copy of this type with location (recursively) changed to @a _location.
virtual TypePointer copyForLocation(Location _location) const = 0;
protected:
Location m_location = Location::Storage;
};
/** /**
* The type of an array. The flavours are byte array (bytes), statically- (<type>[<length>]) * The type of an array. The flavours are byte array (bytes), statically- (<type>[<length>])
* and dynamically-sized array (<type>[]). * and dynamically-sized array (<type>[]).
@ -360,27 +378,26 @@ public:
* one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and * one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and
* thus start on their own slot. * thus start on their own slot.
*/ */
class ArrayType: public Type class ArrayType: public Type, public ReferenceType
{ {
public: public:
enum class Location { Storage, CallData, Memory };
virtual Category getCategory() const override { return Category::Array; } virtual Category getCategory() const override { return Category::Array; }
/// Constructor for a byte array ("bytes") and string. /// Constructor for a byte array ("bytes") and string.
explicit ArrayType(Location _location, bool _isString = false): explicit ArrayType(Location _location, bool _isString = false):
m_location(_location), ReferenceType(_location),
m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes), m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
m_baseType(std::make_shared<FixedBytesType>(1)) m_baseType(std::make_shared<FixedBytesType>(1))
{} {}
/// Constructor for a dynamically sized array type ("type[]") /// Constructor for a dynamically sized array type ("type[]")
ArrayType(Location _location, const TypePointer &_baseType): ArrayType(Location _location, const TypePointer &_baseType):
m_location(_location), ReferenceType(_location),
m_baseType(_baseType) m_baseType(_baseType)
{} {}
/// Constructor for a fixed-size array type ("type[20]") /// Constructor for a fixed-size array type ("type[20]")
ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length): ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length):
m_location(_location), ReferenceType(_location),
m_baseType(_baseType), m_baseType(_baseType),
m_hasDynamicLength(false), m_hasDynamicLength(false),
m_length(_length) m_length(_length)
@ -400,7 +417,6 @@ public:
} }
virtual TypePointer externalType() const override; virtual TypePointer externalType() const override;
Location getLocation() const { return m_location; }
/// @returns true if this is a byte array or a string /// @returns true if this is a byte array or a string
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; } bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
/// @returns true if this is a string /// @returns true if this is a string
@ -408,15 +424,12 @@ public:
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
u256 const& getLength() const { return m_length; } u256 const& getLength() const { return m_length; }
/// @returns a copy of this type with location changed to @a _location TypePointer copyForLocation(Location _location) const override;
/// @todo this might move as far up as Type later
std::shared_ptr<ArrayType> copyForLocation(Location _location) const;
private: private:
/// String is interpreted as a subtype of Bytes. /// String is interpreted as a subtype of Bytes.
enum class ArrayKind { Ordinary, Bytes, String }; enum class ArrayKind { Ordinary, Bytes, String };
Location m_location;
///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays. ///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
ArrayKind m_arrayKind = ArrayKind::Ordinary; ArrayKind m_arrayKind = ArrayKind::Ordinary;
TypePointer m_baseType; TypePointer m_baseType;
@ -484,11 +497,13 @@ private:
/** /**
* The type of a struct instance, there is one distinct type per struct definition. * The type of a struct instance, there is one distinct type per struct definition.
*/ */
class StructType: public Type class StructType: public Type, public ReferenceType
{ {
public: public:
virtual Category getCategory() const override { return Category::Struct; } virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct): m_struct(_struct) {} explicit StructType(StructDefinition const& _struct):
//@todo only storage until we have non-storage structs
ReferenceType(Location::Storage), m_struct(_struct) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
@ -498,6 +513,8 @@ public:
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
TypePointer copyForLocation(Location _location) const override;
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const; std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
private: private:

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

Loading…
Cancel
Save