Browse Source

Merge branch 'develop' into p2p

cl-refactor
subtly 10 years ago
parent
commit
cc937998ff
  1. 9
      README.md
  2. 2
      alethzero/CMakeLists.txt
  3. 3
      alethzero/GraphParameters.h
  4. 16
      alethzero/Grapher.h
  5. 12
      alethzero/Main.ui
  6. 25
      alethzero/MainWin.cpp
  7. 11
      alethzero/MainWin.h
  8. 5
      alethzero/MiningView.cpp
  9. 1
      alethzero/NatspecHandler.cpp
  10. 2
      alethzero/NatspecHandler.h
  11. 78
      alethzero/OurWebThreeStubServer.cpp
  12. 20
      alethzero/OurWebThreeStubServer.h
  13. 8
      alethzero/Transact.cpp
  14. 12
      alethzero/Transact.h
  15. 5
      cmake/EthDependencies.cmake
  16. 52
      cmake/EthExecutableHelper.cmake
  17. 10
      cmake/FindMHD.cmake
  18. 4
      eth/CMakeLists.txt
  19. 6
      eth/main.cpp
  20. 7
      evmjit/CMakeLists.txt
  21. 21
      evmjit/LICENSE.md
  22. 43
      evmjit/README.md
  23. 5
      evmjit/libevmjit-cpp/CMakeLists.txt
  24. 21
      evmjit/libevmjit-cpp/Env.cpp
  25. 34
      evmjit/libevmjit-cpp/JitVM.cpp
  26. 4
      evmjit/libevmjit-cpp/JitVM.h
  27. 328
      evmjit/libevmjit/Arith256.cpp
  28. 13
      evmjit/libevmjit/Arith256.h
  29. 19
      evmjit/libevmjit/BasicBlock.cpp
  30. 20
      evmjit/libevmjit/BasicBlock.h
  31. 10
      evmjit/libevmjit/BuildInfo.h.in
  32. 53
      evmjit/libevmjit/CMakeLists.txt
  33. 62
      evmjit/libevmjit/Cache.cpp
  34. 9
      evmjit/libevmjit/Cache.h
  35. 17
      evmjit/libevmjit/Common.h
  36. 94
      evmjit/libevmjit/Compiler.cpp
  37. 9
      evmjit/libevmjit/Compiler.h
  38. 4
      evmjit/libevmjit/CompilerHelper.cpp
  39. 5
      evmjit/libevmjit/CompilerHelper.h
  40. 3
      evmjit/libevmjit/Endianness.cpp
  41. 3
      evmjit/libevmjit/Endianness.h
  42. 97
      evmjit/libevmjit/ExecStats.cpp
  43. 44
      evmjit/libevmjit/ExecStats.h
  44. 131
      evmjit/libevmjit/ExecutionEngine.cpp
  45. 32
      evmjit/libevmjit/ExecutionEngine.h
  46. 31
      evmjit/libevmjit/Ext.cpp
  47. 6
      evmjit/libevmjit/Ext.h
  48. 107
      evmjit/libevmjit/GasMeter.cpp
  49. 3
      evmjit/libevmjit/GasMeter.h
  50. 8
      evmjit/libevmjit/Instruction.cpp
  51. 4
      evmjit/libevmjit/Instruction.h
  52. 93
      evmjit/libevmjit/Memory.cpp
  53. 15
      evmjit/libevmjit/Memory.h
  54. 8
      evmjit/libevmjit/Runtime.cpp
  55. 8
      evmjit/libevmjit/Runtime.h
  56. 4
      evmjit/libevmjit/RuntimeData.h
  57. 54
      evmjit/libevmjit/RuntimeManager.cpp
  58. 17
      evmjit/libevmjit/RuntimeManager.h
  59. 111
      evmjit/libevmjit/Stack.cpp
  60. 9
      evmjit/libevmjit/Stack.h
  61. 11
      evmjit/libevmjit/Type.cpp
  62. 8
      evmjit/libevmjit/Type.h
  63. 1
      evmjit/libevmjit/Utils.cpp
  64. 3
      evmjit/libevmjit/Utils.h
  65. 11
      evmjit/libevmjit/interface.cpp
  66. 7
      evmjit/libevmjit/preprocessor/llvm_includes_end.h
  67. 12
      evmjit/libevmjit/preprocessor/llvm_includes_start.h
  68. 6
      exp/main.cpp
  69. 2
      extdep/CMakeLists.txt
  70. 2
      libdevcore/Common.cpp
  71. 2
      libdevcore/FixedHash.h
  72. 4
      libdevcore/RLP.h
  73. 2
      libdevcore/Worker.h
  74. 7
      libdevcore/vector_ref.h
  75. 2
      libdevcrypto/Common.cpp
  76. 3
      libdevcrypto/Common.h
  77. 2
      libdevcrypto/CryptoPP.cpp
  78. 6
      libdevcrypto/TrieDB.h
  79. 2
      libethcore/CommonEth.cpp
  80. 2
      libethcore/ProofOfWork.h
  81. 3
      libethereum/BlockChain.cpp
  82. 3
      libethereum/BlockQueue.cpp
  83. 32
      libethereum/CanonBlockChain.cpp
  84. 2
      libethereum/Client.h
  85. 2
      libethereum/EthereumHost.h
  86. 40
      libethereum/GenesisInfo.cpp
  87. 32
      libethereum/GenesisInfo.h
  88. 4
      libethereum/State.cpp
  89. 2
      libethereum/Transaction.cpp
  90. 2
      libethereum/Transaction.h
  91. 12
      libevm/VM.cpp
  92. 2
      libevm/VMFace.h
  93. 2
      libevm/VMFactory.cpp
  94. 6
      libevmcore/Assembly.cpp
  95. 2
      libevmcore/Assembly.h
  96. 1
      libjsqrc/ethereumjs/.gitignore
  97. 2
      libjsqrc/ethereumjs/.travis.yml
  98. 8
      libjsqrc/ethereumjs/README.md
  99. 2
      libjsqrc/ethereumjs/bower.json
  100. 649
      libjsqrc/ethereumjs/dist/ethereum.js

9
README.md

@ -1,12 +1,12 @@
## Ethereum C++ Client. ## Ethereum C++ Client.
By Gav Wood, 2014. By Gav Wood et al, 2013, 2014, 2015.
| Linux | OSX | Windows | Linux | OSX | Windows
----------|---------|-----|-------- ----------|---------|-----|--------
develop | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20develop%20branch)](http://build.ethdev.com/builders/Linux%20C%2B%2B%20develop%20branch/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20develop%20branch)](http://build.ethdev.com/builders/OSX%20C%2B%2B%20develop%20branch/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Windows%20C%2B%2B%20develop%20branch)](http://build.ethdev.com/builders/Windows%20C%2B%2B%20develop%20branch/builds/-1) develop | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20develop%20branch)](https://build.ethdev.com/builders/Linux%20C%2B%2B%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20develop%20branch)](https://build.ethdev.com/builders/OSX%20C%2B%2B%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Windows%20C%2B%2B%20develop%20branch)](https://build.ethdev.com/builders/Windows%20C%2B%2B%20develop%20branch/builds/-1)
master | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20master%20branch)](http://build.ethdev.com/builders/Linux%20C%2B%2B%20master%20branch/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20master%20branch)](http://build.ethdev.com/builders/OSX%20C%2B%2B%20master%20branch/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Windows%20C%2B%2B%20master%20branch)](http://build.ethdev.com/builders/Windows%20C%2B%2B%20master%20branch/builds/-1) master | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20master%20branch)](https://build.ethdev.com/builders/Linux%20C%2B%2B%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20master%20branch)](https://build.ethdev.com/builders/OSX%20C%2B%2B%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Windows%20C%2B%2B%20master%20branch)](https://build.ethdev.com/builders/Windows%20C%2B%2B%20master%20branch/builds/-1)
evmjit | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20develop%20evmjit)](http://build.ethdev.com/builders/Linux%20C%2B%2B%20develop%20evmjit/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20develop%20evmjit)](http://build.ethdev.com/builders/OSX%20C%2B%2B%20develop%20evmjit/builds/-1) | N/A evmjit | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20develop%20evmjit)](https://build.ethdev.com/builders/Linux%20C%2B%2B%20develop%20evmjit/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20develop%20evmjit)](https://build.ethdev.com/builders/OSX%20C%2B%2B%20develop%20evmjit/builds/-1) | N/A
[![Stories in Ready](https://badge.waffle.io/ethereum/cpp-ethereum.png?label=ready&title=Ready)](http://waffle.io/ethereum/cpp-ethereum) [![Stories in Ready](https://badge.waffle.io/ethereum/cpp-ethereum.png?label=ready&title=Ready)](http://waffle.io/ethereum/cpp-ethereum)
@ -24,7 +24,6 @@ To run the tests, make sure you clone the tests repository from github.com/ether
See [TODO](https://github.com/ethereum/cpp-ethereum/wiki/TODO) See [TODO](https://github.com/ethereum/cpp-ethereum/wiki/TODO)
### License ### License
See [LICENSE](LICENSE) See [LICENSE](LICENSE)

2
alethzero/CMakeLists.txt

@ -58,5 +58,5 @@ if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
endif() endif()
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake # eth_install_executable is defined in cmake/EthExecutableHelper.cmake
eth_install_executable(${EXECUTABLE}) eth_install_executable(${EXECUTABLE} DLLS ${MHD_DLL_RELEASE})

3
alethzero/GraphParameters.h

@ -36,9 +36,12 @@ static T graphParameters(T _min, T _max, unsigned _divisions, T* o_from = 0, T*
T uMin = _min / _divisor; T uMin = _min / _divisor;
T uMax = _max / _divisor; T uMax = _max / _divisor;
if (uMax == uMin || !_divisions) if (uMax == uMin || !_divisions)
{
if (o_delta && o_from)
{ {
*o_from = 0; *o_from = 0;
*o_delta = 1; *o_delta = 1;
}
return 1; return 1;
} }
long double l10 = std::log10((uMax - uMin) / T(_divisions) * 5.5f); long double l10 = std::log10((uMax - uMin) / T(_divisions) * 5.5f);

16
alethzero/Grapher.h

@ -76,24 +76,24 @@ public:
void labelYOrderedPoints(std::map<float, float> const& _translatedData, int _maxCount = 20, float _minFactor = .01f) const; void labelYOrderedPoints(std::map<float, float> const& _translatedData, int _maxCount = 20, float _minFactor = .01f) const;
protected: protected:
QPainter* p; QPainter* p = nullptr;
QRect active; QRect active;
std::pair<float, float> xRange; std::pair<float, float> xRange;
std::pair<float, float> yRange; std::pair<float, float> yRange;
float xM; float xM = 0;
float xC; float xC = 0;
float yM; float yM = 0;
float yC; float yC = 0;
float dx; float dx = 0;
float dy; float dy = 0;
std::function<std::string(float _f)> xLabel; std::function<std::string(float _f)> xLabel;
std::function<std::string(float _f)> yLabel; std::function<std::string(float _f)> yLabel;
std::function<std::string(float _x, float _y)> pLabel; std::function<std::string(float _x, float _y)> pLabel;
float fontPixelSize; float fontPixelSize = 0;
// Translate from raw indexed data into x/y graph units. Only relevant for indexed data. // Translate from raw indexed data into x/y graph units. Only relevant for indexed data.
float xT(float _dataIndex) const { return _dataIndex * xM + xC; } float xT(float _dataIndex) const { return _dataIndex * xM + xC; }

12
alethzero/Main.ui

@ -163,6 +163,7 @@
<property name="title"> <property name="title">
<string>&amp;Special</string> <string>&amp;Special</string>
</property> </property>
<addaction name="natSpec"/>
<addaction name="paranoia"/> <addaction name="paranoia"/>
<addaction name="clearPending"/> <addaction name="clearPending"/>
<addaction name="killBlockchain"/> <addaction name="killBlockchain"/>
@ -1652,6 +1653,17 @@ font-size: 14pt</string>
<string>New &amp;Transaction...</string> <string>New &amp;Transaction...</string>
</property> </property>
</action> </action>
<action name="natSpec">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;NatSpec Enabled</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>

25
alethzero/MainWin.cpp

@ -201,6 +201,11 @@ Main::~Main()
g_logPost = simpleDebugOut; g_logPost = simpleDebugOut;
} }
bool Main::confirm() const
{
return ui->natSpec->isChecked();
}
void Main::on_newIdentity_triggered() void Main::on_newIdentity_triggered()
{ {
KeyPair kp = KeyPair::create(); KeyPair kp = KeyPair::create();
@ -651,6 +656,7 @@ void Main::writeSettings()
s.setValue("localNetworking", ui->localNetworking->isChecked()); s.setValue("localNetworking", ui->localNetworking->isChecked());
s.setValue("forceMining", ui->forceMining->isChecked()); s.setValue("forceMining", ui->forceMining->isChecked());
s.setValue("paranoia", ui->paranoia->isChecked()); s.setValue("paranoia", ui->paranoia->isChecked());
s.setValue("natSpec", ui->natSpec->isChecked());
s.setValue("showAll", ui->showAll->isChecked()); s.setValue("showAll", ui->showAll->isChecked());
s.setValue("showAllAccounts", ui->showAllAccounts->isChecked()); s.setValue("showAllAccounts", ui->showAllAccounts->isChecked());
s.setValue("clientName", ui->clientName->text()); s.setValue("clientName", ui->clientName->text());
@ -662,7 +668,7 @@ void Main::writeSettings()
s.setValue("jitvm", ui->jitvm->isChecked()); s.setValue("jitvm", ui->jitvm->isChecked());
bytes d = m_webThree->saveNetwork(); bytes d = m_webThree->saveNetwork();
if (d.size()) if (!d.empty())
m_networkConfig = QByteArray((char*)d.data(), (int)d.size()); m_networkConfig = QByteArray((char*)d.data(), (int)d.size());
s.setValue("peers", m_networkConfig); s.setValue("peers", m_networkConfig);
s.setValue("nameReg", ui->nameReg->text()); s.setValue("nameReg", ui->nameReg->text());
@ -720,6 +726,7 @@ void Main::readSettings(bool _skipGeometry)
ui->forceMining->setChecked(s.value("forceMining", false).toBool()); ui->forceMining->setChecked(s.value("forceMining", false).toBool());
on_forceMining_triggered(); on_forceMining_triggered();
ui->paranoia->setChecked(s.value("paranoia", false).toBool()); ui->paranoia->setChecked(s.value("paranoia", false).toBool());
ui->natSpec->setChecked(s.value("natSpec", true).toBool());
ui->showAll->setChecked(s.value("showAll", false).toBool()); ui->showAll->setChecked(s.value("showAll", false).toBool());
ui->showAllAccounts->setChecked(s.value("showAllAccounts", false).toBool()); ui->showAllAccounts->setChecked(s.value("showAllAccounts", false).toBool());
ui->clientName->setText(s.value("clientName", "").toString()); ui->clientName->setText(s.value("clientName", "").toString());
@ -1160,6 +1167,7 @@ void Main::timerEvent(QTimerEvent*)
interval = 0; interval = 0;
refreshNetwork(); refreshNetwork();
refreshWhispers(); refreshWhispers();
poll();
} }
else else
interval += 100; interval += 100;
@ -1450,7 +1458,7 @@ void Main::on_debugCurrent_triggered()
} }
} }
void Main::on_debugDumpState_triggered(int _add) void Main::debugDumpState(int _add)
{ {
if (auto item = ui->blocks->currentItem()) if (auto item = ui->blocks->currentItem())
{ {
@ -1471,11 +1479,6 @@ void Main::on_debugDumpState_triggered(int _add)
} }
} }
void Main::on_debugDumpStatePre_triggered()
{
on_debugDumpState_triggered(0);
}
void Main::on_contracts_currentItemChanged() void Main::on_contracts_currentItemChanged()
{ {
ui->contractInfo->clear(); ui->contractInfo->clear();
@ -1503,7 +1506,7 @@ void Main::on_contracts_currentItemChanged()
} }
} }
void Main::on_idealPeers_valueChanged() void Main::on_idealPeers_valueChanged(int)
{ {
m_webThree->setIdealPeerCount(ui->idealPeers->value()); m_webThree->setIdealPeerCount(ui->idealPeers->value());
} }
@ -1515,11 +1518,11 @@ void Main::on_ourAccounts_doubleClicked()
qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray())));
} }
void Main::on_log_doubleClicked() /*void Main::on_log_doubleClicked()
{ {
ui->log->setPlainText(""); ui->log->setPlainText("");
m_logHistory.clear(); m_logHistory.clear();
} }*/
void Main::on_accounts_doubleClicked() void Main::on_accounts_doubleClicked()
{ {
@ -1626,7 +1629,7 @@ void Main::on_net_triggered()
{ {
web3()->setIdealPeerCount(ui->idealPeers->value()); web3()->setIdealPeerCount(ui->idealPeers->value());
web3()->setNetworkPreferences(netPrefs()); web3()->setNetworkPreferences(netPrefs());
ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : 0); ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : h256());
// TODO: p2p // TODO: p2p
// if (m_networkConfig.size()/* && ui->usePast->isChecked()*/) // if (m_networkConfig.size()/* && ui->usePast->isChecked()*/)
// web3()->restoreNetwork(bytesConstRef((byte*)m_networkConfig.data(), m_networkConfig.size())); // web3()->restoreNetwork(bytesConstRef((byte*)m_networkConfig.data(), m_networkConfig.size()));

11
alethzero/MainWin.h

@ -72,6 +72,7 @@ public:
dev::eth::Client* ethereum() const { return m_webThree->ethereum(); } dev::eth::Client* ethereum() const { return m_webThree->ethereum(); }
std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); } std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); }
bool confirm() const;
NatSpecFace* natSpec() { return &m_natSpecDB; } NatSpecFace* natSpec() { return &m_natSpecDB; }
QVariant evalRaw(QString const& _js); QVariant evalRaw(QString const& _js);
@ -108,7 +109,7 @@ private slots:
void on_go_triggered(); void on_go_triggered();
void on_net_triggered(); void on_net_triggered();
void on_connect_triggered(); void on_connect_triggered();
void on_idealPeers_valueChanged(); void on_idealPeers_valueChanged(int);
// Mining // Mining
void on_mine_triggered(); void on_mine_triggered();
@ -141,7 +142,7 @@ private slots:
void on_blocks_currentItemChanged(); void on_blocks_currentItemChanged();
// Logging // Logging
void on_log_doubleClicked(); // void on_log_doubleClicked();
void on_verbosity_valueChanged(); void on_verbosity_valueChanged();
// Misc // Misc
@ -161,8 +162,8 @@ private slots:
// Debugger // Debugger
void on_debugCurrent_triggered(); void on_debugCurrent_triggered();
void on_debugDumpState_triggered(int _add = 1); void on_debugDumpState_triggered() { debugDumpState(1); }
void on_debugDumpStatePre_triggered(); void on_debugDumpStatePre_triggered() { debugDumpState(0); }
// Whisper // Whisper
void on_newIdentity_triggered(); void on_newIdentity_triggered();
@ -176,6 +177,8 @@ signals:
void poll(); void poll();
private: private:
void debugDumpState(int _add);
dev::p2p::NetworkPreferences netPrefs() const; dev::p2p::NetworkPreferences netPrefs() const;
QString lookup(QString const& _n) const; QString lookup(QString const& _n) const;

5
alethzero/MiningView.cpp

@ -48,7 +48,6 @@ string sL(float _x, float _y) { return toString(round(_x * 1000)) + "s (" + toSt
MiningView::MiningView(QWidget* _p): QWidget(_p) MiningView::MiningView(QWidget* _p): QWidget(_p)
{ {
} }
void MiningView::appendStats(list<MineInfo> const& _i, MineProgress const& _p) void MiningView::appendStats(list<MineInfo> const& _i, MineProgress const& _p)
@ -86,10 +85,10 @@ void MiningView::appendStats(list<MineInfo> const& _i, MineProgress const& _p)
for (auto& i: m_resets) for (auto& i: m_resets)
i -= o; i -= o;
remove_if(m_resets.begin(), m_resets.end(), [](int i){return i < 0;}); m_resets.erase(remove_if(m_resets.begin(), m_resets.end(), [](int i){return i < 0;}), m_resets.end());
for (auto& i: m_completes) for (auto& i: m_completes)
i -= o; i -= o;
remove_if(m_completes.begin(), m_completes.end(), [](int i){return i < 0;}); m_completes.erase(remove_if(m_completes.begin(), m_completes.end(), [](int i){return i < 0;}), m_completes.end());
m_progress = _p; m_progress = _p;
update(); update();

1
alethzero/NatspecHandler.cpp

@ -66,7 +66,6 @@ string NatspecHandler::getUserNotice(string const& json, dev::bytes const& _tran
{ {
Json::Value natspec; Json::Value natspec;
Json::Value userNotice; Json::Value userNotice;
string retStr;
m_reader.parse(json, natspec); m_reader.parse(json, natspec);
FixedHash<4> transactionFunctionHash((bytesConstRef(&_transactionData).cropped(0, 4).toBytes())); FixedHash<4> transactionFunctionHash((bytesConstRef(&_transactionData).cropped(0, 4).toBytes()));

2
alethzero/NatspecHandler.h

@ -54,6 +54,6 @@ class NatspecHandler: public NatSpecFace
private: private:
ldb::ReadOptions m_readOptions; ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions; ldb::WriteOptions m_writeOptions;
ldb::DB* m_db; ldb::DB* m_db = nullptr;
Json::Reader m_reader; Json::Reader m_reader;
}; };

78
alethzero/OurWebThreeStubServer.cpp

@ -33,9 +33,11 @@ using namespace dev;
using namespace dev::eth; using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3,
vector<KeyPair> const& _accounts, Main* main): vector<KeyPair> const& _accounts, Main* _main):
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(main) WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(_main)
{} {
connect(_main, SIGNAL(poll()), this, SLOT(doValidations()));
}
string OurWebThreeStubServer::shh_newIdentity() string OurWebThreeStubServer::shh_newIdentity()
{ {
@ -44,29 +46,44 @@ string OurWebThreeStubServer::shh_newIdentity()
return toJS(kp.pub()); return toJS(kp.pub());
} }
bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string const& _text) const bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string const& _text)
{ {
int button; if (!m_main->confirm())
QMetaObject::invokeMethod(m_main, "authenticate", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, button), Q_ARG(QString, QString::fromStdString(_title)), Q_ARG(QString, QString::fromStdString(_text))); {
return button == QMessageBox::Ok; cnote << "Skipping confirmation step for: " << _title << "\n" << _text;
return true;
}
QMessageBox userInput;
userInput.setText(QString::fromStdString(_title));
userInput.setInformativeText(QString::fromStdString(_text));
userInput.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
userInput.button(QMessageBox::Ok)->setText("Allow");
userInput.button(QMessageBox::Cancel)->setText("Reject");
userInput.setDefaultButton(QMessageBox::Cancel);
return userInput.exec() == QMessageBox::Ok;
//QMetaObject::invokeMethod(m_main, "authenticate", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, button), Q_ARG(QString, QString::fromStdString(_title)), Q_ARG(QString, QString::fromStdString(_text)));
//return button == QMessageBox::Ok;
} }
bool OurWebThreeStubServer::showCreationNotice(TransactionSkeleton const& _t) const bool OurWebThreeStubServer::showCreationNotice(TransactionSkeleton const& _t, bool _toProxy)
{ {
return showAuthenticationPopup("Contract Creation Transaction", "ÐApp is attemping to create a contract; to be endowed with " + formatBalance(_t.value) + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is <b>" + formatBalance(_t.value + _t.gas * _t.gasPrice) + "</b>."); return showAuthenticationPopup("Contract Creation Transaction", string("ÐApp is attemping to create a contract; ") + (_toProxy ? "(this transaction is not executed directly, but forwarded to another ÐApp) " : "") + "to be endowed with " + formatBalance(_t.value) + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
} }
bool OurWebThreeStubServer::showSendNotice(TransactionSkeleton const& _t) const bool OurWebThreeStubServer::showSendNotice(TransactionSkeleton const& _t, bool _toProxy)
{ {
return showAuthenticationPopup("Fund Transfer Transaction", "ÐApp is attempting to send " + formatBalance(_t.value) + " to a recipient " + m_main->pretty(_t.to).toStdString() + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is <b>" + formatBalance(_t.value + _t.gas * _t.gasPrice) + "</b>."); return showAuthenticationPopup("Fund Transfer Transaction", "ÐApp is attempting to send " + formatBalance(_t.value) + " to a recipient " + m_main->pretty(_t.to).toStdString() + (_toProxy ? " (this transaction is not executed directly, but forwarded to another ÐApp)" : "") +
", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
} }
bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t) const bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t, bool _toProxy)
{ {
return showAuthenticationPopup("DANGEROUS! Unknown Contract Transaction!", return showAuthenticationPopup("DANGEROUS! Unknown Contract Transaction!",
"ÐApp is attempting to call into an unknown contract at address " + "ÐApp is attempting to call into an unknown contract at address " +
m_main->pretty(_t.to).toStdString() + m_main->pretty(_t.to).toStdString() + ".\n\n" +
".\n\nCall involves sending " + (_toProxy ? "This transaction is not executed directly, but forwarded to another ÐApp.\n\n" : "") +
"Call involves sending " +
formatBalance(_t.value) + " to the recipient, with additional network fees of up to " + formatBalance(_t.value) + " to the recipient, with additional network fees of up to " +
formatBalance(_t.gas * _t.gasPrice) + formatBalance(_t.gas * _t.gasPrice) +
"However, this also does other stuff which we don't understand, and does so in your name.\n\n" + "However, this also does other stuff which we don't understand, and does so in your name.\n\n" +
@ -76,25 +93,43 @@ bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t)
"REJECT UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!"); "REJECT UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!");
} }
bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t) void OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t, bool _toProxy)
{
Guard l(x_queued);
m_queued.push(make_pair(_t, _toProxy));
}
void OurWebThreeStubServer::doValidations()
{
Guard l(x_queued);
while (!m_queued.empty())
{
auto q = m_queued.front();
m_queued.pop();
if (validateTransaction(q.first, q.second))
WebThreeStubServerBase::authenticate(q.first, q.second);
}
}
bool OurWebThreeStubServer::validateTransaction(TransactionSkeleton const& _t, bool _toProxy)
{ {
if (_t.creation) if (_t.creation)
{ {
// recipient has no code - nothing special about this transaction, show basic value transfer info // show notice concerning the creation code. TODO: this needs entering into natspec.
return showCreationNotice(_t); return showCreationNotice(_t, _toProxy);
} }
h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to); h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to);
if (contractCodeHash == EmptySHA3) if (contractCodeHash == EmptySHA3)
{ {
// recipient has no code - nothing special about this transaction, show basic value transfer info // recipient has no code - nothing special about this transaction, show basic value transfer info
return showSendNotice(_t); return showSendNotice(_t, _toProxy);
} }
string userNotice = m_main->natSpec()->getUserNotice(contractCodeHash, _t.data); string userNotice = m_main->natSpec()->getUserNotice(contractCodeHash, _t.data);
if (userNotice.empty()) if (userNotice.empty())
return showUnknownCallNotice(_t); return showUnknownCallNotice(_t, _toProxy);
NatspecExpressionEvaluator evaluator; NatspecExpressionEvaluator evaluator;
userNotice = evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString(); userNotice = evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString();
@ -104,11 +139,12 @@ bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t)
"ÐApp attempting to conduct contract interaction with " + "ÐApp attempting to conduct contract interaction with " +
m_main->pretty(_t.to).toStdString() + m_main->pretty(_t.to).toStdString() +
": <b>" + userNotice + "</b>.\n\n" + ": <b>" + userNotice + "</b>.\n\n" +
(_toProxy ? "This transaction is not executed directly, but forwarded to another ÐApp.\n\n" : "") +
(_t.value > 0 ? (_t.value > 0 ?
"In addition, ÐApp is attempting to send " + "In addition, ÐApp is attempting to send " +
formatBalance(_t.value) + " to said recipient, with additional network fees of up to " + formatBalance(_t.value) + " to said recipient, with additional network fees of up to " +
formatBalance(_t.gas * _t.gasPrice) + " = <b>" + formatBalance(_t.gas * _t.gasPrice) + " = " +
formatBalance(_t.value + _t.gas * _t.gasPrice) + "</b>." formatBalance(_t.value + _t.gas * _t.gasPrice) + "."
: :
"Additional network fees are at most" + "Additional network fees are at most" +
formatBalance(_t.gas * _t.gasPrice) + ".") formatBalance(_t.gas * _t.gasPrice) + ".")

20
alethzero/OurWebThreeStubServer.h

@ -19,7 +19,9 @@
* @date 2014 * @date 2014
*/ */
#include <queue>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <libdevcore/Guards.h>
#include <libethcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
@ -35,16 +37,24 @@ public:
std::vector<dev::KeyPair> const& _accounts, Main* main); std::vector<dev::KeyPair> const& _accounts, Main* main);
virtual std::string shh_newIdentity() override; virtual std::string shh_newIdentity() override;
virtual bool authenticate(dev::eth::TransactionSkeleton const& _t); virtual void authenticate(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
signals: signals:
void onNewId(QString _s); void onNewId(QString _s);
public slots:
void doValidations();
private: private:
bool showAuthenticationPopup(std::string const& _title, std::string const& _text) const; bool showAuthenticationPopup(std::string const& _title, std::string const& _text);
bool showCreationNotice(dev::eth::TransactionSkeleton const& _t) const; bool showCreationNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
bool showSendNotice(dev::eth::TransactionSkeleton const& _t) const; bool showSendNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
bool showUnknownCallNotice(dev::eth::TransactionSkeleton const& _t) const; bool showUnknownCallNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
bool validateTransaction(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
std::queue<std::pair<dev::eth::TransactionSkeleton, bool>> m_queued;
dev::Mutex x_queued;
dev::WebThreeDirect* m_web3; dev::WebThreeDirect* m_web3;
Main* m_main; Main* m_main;

8
alethzero/Transact.cpp

@ -55,7 +55,7 @@ Transact::Transact(Context* _c, QWidget* _parent):
ui->valueUnits->setCurrentIndex(6); ui->valueUnits->setCurrentIndex(6);
ui->gasPriceUnits->setCurrentIndex(4); ui->gasPriceUnits->setCurrentIndex(4);
ui->gasPrice->setValue(10); ui->gasPrice->setValue(10);
on_destination_currentTextChanged(); on_destination_currentTextChanged(QString());
} }
Transact::~Transact() Transact::~Transact()
@ -147,7 +147,7 @@ string Transact::getFunctionHashes(dev::solidity::CompilerStack const& _compiler
return ret; return ret;
} }
void Transact::on_destination_currentTextChanged() void Transact::on_destination_currentTextChanged(QString)
{ {
if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)") if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)")
if (Address a = m_context->fromString(ui->destination->currentText())) if (Address a = m_context->fromString(ui->destination->currentText()))
@ -169,9 +169,7 @@ void Transact::rejigData()
QString lll; QString lll;
QString solidity; QString solidity;
if (src.find_first_not_of("1234567890abcdefABCDEF") == string::npos && src.size() % 2 == 0) if (src.find_first_not_of("1234567890abcdefABCDEF") == string::npos && src.size() % 2 == 0)
{
m_data = fromHex(src); m_data = fromHex(src);
}
else if (sourceIsSolidity(src)) else if (sourceIsSolidity(src))
{ {
dev::solidity::CompilerStack compiler; dev::solidity::CompilerStack compiler;
@ -204,7 +202,7 @@ void Transact::rejigData()
for (auto& i: errors) for (auto& i: errors)
i = "(LLL " + i + ")"; i = "(LLL " + i + ")";
} }
catch (string err) catch (string const& err)
{ {
errors.push_back("Serpent " + err); errors.push_back("Serpent " + err);
} }

12
alethzero/Transact.h

@ -44,12 +44,12 @@ public:
void setEnvironment(QList<dev::KeyPair> _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); void setEnvironment(QList<dev::KeyPair> _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB);
private slots: private slots:
void on_destination_currentTextChanged(); void on_destination_currentTextChanged(QString);
void on_value_valueChanged() { updateFee(); } void on_value_valueChanged(int) { updateFee(); }
void on_gas_valueChanged() { updateFee(); } void on_gas_valueChanged(int) { updateFee(); }
void on_valueUnits_currentIndexChanged() { updateFee(); } void on_valueUnits_currentIndexChanged(int) { updateFee(); }
void on_gasPriceUnits_currentIndexChanged() { updateFee(); } void on_gasPriceUnits_currentIndexChanged(int) { updateFee(); }
void on_gasPrice_valueChanged() { updateFee(); } void on_gasPrice_valueChanged(int) { updateFee(); }
void on_data_textChanged() { rejigData(); } void on_data_textChanged() { rejigData(); }
void on_optimize_clicked() { rejigData(); } void on_optimize_clicked() { rejigData(); }
void on_send_clicked(); void on_send_clicked();

5
cmake/EthDependencies.cmake

@ -128,6 +128,11 @@ if (NOT HEADLESS)
set (MACDEPLOYQT_APP ${Qt5Core_DIR}/../../../bin/macdeployqt) set (MACDEPLOYQT_APP ${Qt5Core_DIR}/../../../bin/macdeployqt)
message(" - macdeployqt path: ${MACDEPLOYQT_APP}") message(" - macdeployqt path: ${MACDEPLOYQT_APP}")
endif() endif()
# we need to find path to windeployqt on windows
if (WIN32)
set (WINDEPLOYQT_APP ${Qt5Core_DIR}/../../../bin/windeployqt)
message(" - windeployqt path: ${WINDEPLOYQT_APP}")
endif()
# TODO check node && npm version # TODO check node && npm version
find_program(ETH_NODE node) find_program(ETH_NODE node)

52
cmake/EthExecutableHelper.cmake

@ -56,14 +56,14 @@ macro(eth_install_executable EXECUTABLE)
set (extra_macro_args ${ARGN}) set (extra_macro_args ${ARGN})
set (options) set (options)
set (one_value_args QMLDIR) set (one_value_args QMLDIR)
set (multi_value_args) set (multi_value_args DLLS)
cmake_parse_arguments (ETH_INSTALL_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}") cmake_parse_arguments (ETH_INSTALL_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}")
if (ETH_INSTALL_EXECUTABLE_QMLDIR) if (ETH_INSTALL_EXECUTABLE_QMLDIR)
if (APPLE) if (APPLE)
set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}") set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}")
elseif (WIN32) elseif (WIN32)
set(eth_qml_dir --qmldir ${ETH_INSTALL_EXECUTABLE_QMLDIR}) set(eth_qml_dir "--qmldir ${ETH_INSTALL_EXECUTABLE_QMLDIR}")
endif() endif()
message(STATUS "${EXECUTABLE} qmldir: ${eth_qml_dir}") message(STATUS "${EXECUTABLE} qmldir: ${eth_qml_dir}")
endif() endif()
@ -88,48 +88,30 @@ macro(eth_install_executable EXECUTABLE)
set(BU_CHMOD_BUNDLE_ITEMS 1) set(BU_CHMOD_BUNDLE_ITEMS 1)
verify_app(\"${APP_BUNDLE_PATH}\") verify_app(\"${APP_BUNDLE_PATH}\")
" COMPONENT RUNTIME ) " COMPONENT RUNTIME )
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# copy all dlls to executable directory elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# TODO improve that by copying only required dlls
file (GLOB DLLS ${ETH_DEPENDENCY_INSTALL_DIR}/bin/*.dll)
foreach(DLL ${DLLS}) get_target_property(TARGET_LIBS ${EXECUTABLE} INTERFACE_LINK_LIBRARIES)
string(REGEX MATCH "Qt5::Core" HAVE_QT ${TARGET_LIBS})
if ("${HAVE_QT}" STREQUAL "Qt5::Core")
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmake -E copy "${DLL}" "$<TARGET_FILE_DIR:${EXECUTABLE}>" COMMAND cmd /C "set PATH=${Qt5Core_DIR}/../../../bin;%PATH% && ${WINDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.exe ${eth_qml_dir}"
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
) )
endforeach() #workaround for https://bugreports.qt.io/browse/QTBUG-42083
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmake -E copy_directory COMMAND cmd /C "(echo [Paths] & echo.Prefix=.)" > "qt.conf"
"${ETH_DEPENDENCY_INSTALL_DIR}/plugins/platforms" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} VERBATIM
$<TARGET_FILE_DIR:${EXECUTABLE}>/platforms
) )
endif()
# ugly way, improve that #copy additional dlls
foreach(dll ${ETH_INSTALL_EXECUTABLE_DLLS})
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmake -E copy_directory COMMAND ${CMAKE_COMMAND}
"${ETH_DEPENDENCY_INSTALL_DIR}/qml" ARGS -E copy ${dll} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}"
$<TARGET_FILE_DIR:${EXECUTABLE}>
)
install( FILES ${DLLS}
DESTINATION bin
COMPONENT ${EXECUTABLE}
)
install( DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/plugins/platforms
DESTINATION bin
COMPONENT ${EXECUTABLE}
)
file (GLOB QMLS ${ETH_DEPENDENCY_INSTALL_DIR}/qml/*)
foreach(QML ${QMLS})
install( DIRECTORY ${QML}
DESTINATION bin
COMPONENT ${EXECUTABLE}
) )
endforeach() endforeach(dll)
install( TARGETS ${EXECUTABLE} RUNTIME install( TARGETS ${EXECUTABLE} RUNTIME
DESTINATION bin DESTINATION bin

10
cmake/FindMHD.cmake

@ -29,13 +29,19 @@ set(MHD_LIBRARIES ${MHD_LIBRARY})
# boost is using the same "hack" as us with "optimized" and "debug" # boost is using the same "hack" as us with "optimized" and "debug"
# official MHD project actually uses _d suffix # official MHD project actually uses _d suffix
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
#TODO: place dlls into CMAKE_CFG_INTDIR subfolders
string(REPLACE ".lib" ".dll" MHD_DLL_RELEASE ${MHD_LIBRARY})
string(REPLACE "/lib/" "/bin/" MHD_DLL_RELEASE ${MHD_DLL_RELEASE})
find_library( find_library(
MHD_LIBRARY_DEBUG MHD_LIBRARY_DEBUG
NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d
DOC "mhd debug library" DOC "mhd debug library"
) )
# always use release for now
set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG}) #string(REPLACE ".lib" ".dll" MHD_DLL_DEBUG ${MHD_LIBRARY_DEBUG})
#set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG})
endif() endif()

4
eth/CMakeLists.txt

@ -28,5 +28,9 @@ endif()
target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} secp256k1)
if (WIN32)
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS -E copy ${MHD_DLL_RELEASE} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
endif()
install( TARGETS ${EXECUTABLE} DESTINATION bin ) install( TARGETS ${EXECUTABLE} DESTINATION bin )

6
eth/main.cpp

@ -749,7 +749,7 @@ int main(int argc, char** argv)
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, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{ {
dev::eth::VM* 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))
@ -898,7 +898,9 @@ int main(int argc, char** argv)
while (!g_exit) while (!g_exit)
this_thread::sleep_for(chrono::milliseconds(1000)); this_thread::sleep_for(chrono::milliseconds(1000));
writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", web3.saveNetwork()); auto netData = web3.saveNetwork();
if (!netData.empty())
writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData);
return 0; return 0;
} }

7
evmjit/CMakeLists.txt

@ -3,13 +3,14 @@ cmake_minimum_required(VERSION 2.8.12)
project(evmjit) project(evmjit)
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_AUTOMOC OFF)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else() else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC") set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-unknown-pragmas")
endif() endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
# Do not allow unresovled symbols in shared library (default on linux) # Do not allow unresovled symbols in shared library (default on linux)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
endif() endif()

21
evmjit/LICENSE.md

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Paweł Bylica <chfast@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

43
evmjit/README.md

@ -0,0 +1,43 @@
# The Ethereum EVM JIT
EVM JIT is a library for just-in-time compilation of Ethereum EVM code.
It can be used to substitute classic interpreter-like EVM Virtual Machine in Ethereum client.
## Build
### Linux / Ubuntu
1. Install llvm-3.5-dev package
1. For Ubuntu 14.04 using LLVM deb packages source: http://llvm.org/apt
2. For Ubuntu 14.10 using Ubuntu packages
2. Build library with cmake
1. `mkdir build && cd $_`
2. `cmake .. && make`
3. Install library
1. `sudo make install`
2. `sudo ldconfig`
### OSX
1. Install llvm35
1. `brew install llvm35 --disable-shared --HEAD`
2. Build library with cmake
1. `mkdir build && cd $_`
2. `cmake -DLLVM_DIR=/usr/local/lib/llvm-3.5/share/llvm/cmake .. && make`
3. Install library
1. `make install` (with admin rights?)
### Windows
Ask me.
## Options
Options to evmjit library can be passed by environmental variables, e.g. `EVMJIT_CACHE=0 testeth --jit`.
Option | Default value | Description
------------- | ------------- | ----------------------------------------------
EVMJIT_CACHE | 1 | Enables on disk cache for compiled EVM objects
EVMJIT_DUMP | 0 | Dumps generated LLVM module to standard output

5
evmjit/libevmjit-cpp/CMakeLists.txt

@ -9,6 +9,11 @@ set(SOURCES
) )
source_group("" FILES ${SOURCES}) source_group("" FILES ${SOURCES})
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive
endif()
add_library(${TARGET_NAME} ${SOURCES}) add_library(${TARGET_NAME} ${SOURCES})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")

21
evmjit/libevmjit-cpp/Env.cpp

@ -1,4 +1,5 @@
#pragma GCC diagnostic ignored "-Wconversion"
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include <libevm/FeeStructure.h> #include <libevm/FeeStructure.h>
#include <libevm/ExtVMFace.h> #include <libevm/ExtVMFace.h>
@ -46,23 +47,22 @@ extern "C"
*o_hash = _env->blockhash(llvm2eth(*_number)); *o_hash = _env->blockhash(llvm2eth(*_number));
} }
EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
{ {
auto endowment = llvm2eth(*_endowment); auto endowment = llvm2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{ {
_env->subBalance(endowment); _env->subBalance(endowment);
auto gas = llvm2eth(*io_gas); u256 gas = *io_gas;
OnOpFunc onOp {}; // TODO: Handle that thing h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight);
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, onOp), h256::AlignRight); *io_gas = static_cast<int64_t>(gas);
*io_gas = eth2llvm(gas);
*o_address = address; *o_address = address;
} }
else else
*o_address = {}; *o_address = {};
} }
EXPORT bool env_call(ExtVMFace* _env, i256* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{ {
auto value = llvm2eth(*_value); auto value = llvm2eth(*_value);
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
@ -70,12 +70,11 @@ extern "C"
_env->subBalance(value); _env->subBalance(value);
auto receiveAddress = right160(*_receiveAddress); auto receiveAddress = right160(*_receiveAddress);
auto inRef = bytesConstRef{_inBeg, _inSize}; auto inRef = bytesConstRef{_inBeg, _inSize};
auto outRef = bytesConstRef{_outBeg, _outSize}; auto outRef = bytesRef{_outBeg, _outSize};
OnOpFunc onOp {}; // TODO: Handle that thing
auto codeAddress = right160(*_codeAddress); auto codeAddress = right160(*_codeAddress);
auto gas = llvm2eth(*io_gas); u256 gas = *io_gas;
auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, onOp, {}, codeAddress); auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, {}, {}, codeAddress);
*io_gas = eth2llvm(gas); *io_gas = static_cast<int64_t>(gas);
return ret; return ret;
} }

34
evmjit/libevmjit-cpp/JitVM.cpp

@ -1,6 +1,9 @@
#pragma GCC diagnostic ignored "-Wconversion"
#include "JitVM.h" #include "JitVM.h"
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libdevcrypto/SHA3.h>
#include <evmjit/libevmjit/ExecutionEngine.h> #include <evmjit/libevmjit/ExecutionEngine.h>
#include "Utils.h" #include "Utils.h"
@ -11,22 +14,26 @@ 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&, uint64_t) bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
{ {
using namespace jit; using namespace jit;
if (m_gas > std::numeric_limits<decltype(m_data.gas)>::max()) auto rejected = false;
BOOST_THROW_EXCEPTION(OutOfGas()); // Do not accept requests with gas > 2^63 (int64 max) // TODO: Return "not accepted" exception to allow interpreted handle that // 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)
if (_ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max()) rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
BOOST_THROW_EXCEPTION(OutOfGas()); rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max();
if (_ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max())
BOOST_THROW_EXCEPTION(OutOfGas());
if (_ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max())
BOOST_THROW_EXCEPTION(OutOfGas());
if (rejected)
{
UNTESTED;
std::cerr << "Rejected\n";
VMFactory::setKind(VMKind::Interpreter);
m_fallbackVM = VMFactory::create(m_gas);
VMFactory::setKind(VMKind::JIT);
return m_fallbackVM->go(_ext, _onOp, _step);
}
m_data.gas = static_cast<decltype(m_data.gas)>(m_gas); m_data.gas = static_cast<decltype(m_data.gas)>(m_gas);
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice); m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
@ -43,9 +50,10 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp); m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data(); m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size(); m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2llvm(sha3(_ext.code));
auto env = reinterpret_cast<Env*>(&_ext); auto env = reinterpret_cast<Env*>(&_ext);
auto exitCode = m_engine.run(_ext.code, &m_data, env); auto exitCode = m_engine.run(&m_data, env);
switch (exitCode) switch (exitCode)
{ {
case ReturnCode::Suicide: case ReturnCode::Suicide:

4
evmjit/libevmjit-cpp/JitVM.h

@ -12,15 +12,13 @@ class JitVM: public VMFace
{ {
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
enum Kind: bool { Interpreter, JIT };
static std::unique_ptr<VMFace> create(Kind, u256 _gas = 0);
private: private:
friend class VMFactory; friend class VMFactory;
explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} explicit JitVM(u256 _gas = 0) : VMFace(_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
}; };

328
evmjit/libevmjit/Arith256.cpp

@ -1,11 +1,14 @@
#include "Arith256.h" #include "Arith256.h"
#include "Runtime.h"
#include "Type.h"
#include "Endianness.h"
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
#include <iostream> #include <iostream>
#include <iomanip>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Endianness.h"
namespace dev namespace dev
{ {
@ -16,29 +19,102 @@ namespace jit
Arith256::Arith256(llvm::IRBuilder<>& _builder) : Arith256::Arith256(llvm::IRBuilder<>& _builder) :
CompilerHelper(_builder) CompilerHelper(_builder)
{ {}
using namespace llvm;
m_result = m_builder.CreateAlloca(Type::Word, nullptr, "arith.result"); void Arith256::debug(llvm::Value* _value, char _c)
m_arg1 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg1"); {
m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg2"); if (!m_debug)
m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg3"); {
llvm::Type* argTypes[] = {Type::Word, m_builder.getInt8Ty()};
m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule());
}
createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)});
}
using Linkage = GlobalValue::LinkageTypes; llvm::Function* Arith256::getMulFunc()
{
auto& func = m_mul;
if (!func)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule());
llvm::Type* arg2Types[] = {Type::WordPtr, Type::WordPtr, Type::WordPtr}; auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
m_mul = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_mul", getModule()); InsertPointGuard guard{m_builder};
auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(bb);
auto i64 = Type::Size;
auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = m_builder.CreateTrunc(x, i64, "x.lo");
auto y_lo = m_builder.CreateTrunc(y, i64, "y.lo");
auto x_mi = m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(64)), i64);
auto y_mi = m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(64)), i64);
auto x_hi = m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128);
auto y_hi = m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128);
auto t1 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_lo, i128));
auto t2 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_mi, i128));
auto t3 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), y_hi);
auto t4 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_lo, i128));
auto t5 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_mi, i128));
auto t6 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), y_hi);
auto t7 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_lo, i128));
auto t8 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_mi, i128));
auto p = m_builder.CreateZExt(t1, i256);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i256), Constant::get(64)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i256), Constant::get(128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i256), Constant::get(64)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t5, i256), Constant::get(128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t6, i256), Constant::get(192)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t7, i256), Constant::get(128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t8, i256), Constant::get(192)));
m_builder.CreateRet(p);
}
return func;
} }
void Arith256::debug(llvm::Value* _value, char _c) llvm::Function* Arith256::getMul512Func()
{ {
if (!m_debug) auto& func = m_mul512;
if (!func)
{ {
llvm::Type* argTypes[] = {Type::Word, m_builder.getInt8Ty()}; auto i512 = m_builder.getIntNTy(512);
m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule()); llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule());
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
InsertPointGuard guard{m_builder};
auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(bb);
auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = m_builder.CreateZExt(m_builder.CreateTrunc(x, i128, "x.lo"), i256);
auto y_lo = m_builder.CreateZExt(m_builder.CreateTrunc(y, i128, "y.lo"), i256);
auto x_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256);
auto y_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256);
auto t1 = createCall(getMulFunc(), {x_lo, y_lo});
auto t2 = createCall(getMulFunc(), {x_lo, y_hi});
auto t3 = createCall(getMulFunc(), {x_hi, y_lo});
auto t4 = createCall(getMulFunc(), {x_hi, y_hi});
auto p = m_builder.CreateZExt(t1, i512);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i512), m_builder.getIntN(512, 128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i512), m_builder.getIntN(512, 128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i512), m_builder.getIntN(512, 256)));
m_builder.CreateRet(p);
} }
createCall(m_debug, {_value, m_builder.getInt8(_c)}); return func;
} }
llvm::Function* Arith256::getDivFunc(llvm::Type* _type) llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
@ -85,6 +161,15 @@ llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0"); auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0");
auto shlBy0 = m_builder.CreateICmpEQ(i0, zero); auto shlBy0 = m_builder.CreateICmpEQ(i0, zero);
auto y0 = m_builder.CreateShl(yArg, i0); auto y0 = m_builder.CreateShl(yArg, i0);
if (_type == m_builder.getIntNTy(512)) // Workaround for shl bug for long shifts
{
const auto treshold = m_builder.getIntN(512, 128);
auto highShift = m_builder.CreateICmpUGT(i0, treshold);
auto s = m_builder.CreateNUWSub(i0, treshold);
auto yhs = m_builder.CreateShl(yArg, treshold);
yhs = m_builder.CreateShl(yhs, s);
y0 = m_builder.CreateSelect(highShift, yhs, y0);
}
y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result
m_builder.CreateBr(loopBB); m_builder.CreateBr(loopBB);
@ -135,7 +220,7 @@ llvm::Function* Arith256::getExpFunc()
if (!m_exp) if (!m_exp)
{ {
llvm::Type* argTypes[] = {Type::Word, Type::Word}; llvm::Type* argTypes[] = {Type::Word, Type::Word};
m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "arith.exp", getModule()); m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "exp", getModule());
auto base = &m_exp->getArgumentList().front(); auto base = &m_exp->getArgumentList().front();
base->setName("base"); base->setName("base");
@ -159,9 +244,6 @@ llvm::Function* Arith256::getExpFunc()
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp); auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp);
m_builder.SetInsertPoint(entryBB); m_builder.SetInsertPoint(entryBB);
auto a1 = m_builder.CreateAlloca(Type::Word, nullptr, "a1");
auto a2 = m_builder.CreateAlloca(Type::Word, nullptr, "a2");
auto a3 = m_builder.CreateAlloca(Type::Word, nullptr, "a3");
m_builder.CreateBr(headerBB); m_builder.CreateBr(headerBB);
m_builder.SetInsertPoint(headerBB); m_builder.SetInsertPoint(headerBB);
@ -176,20 +258,14 @@ llvm::Function* Arith256::getExpFunc()
m_builder.CreateCondBr(eOdd, updateBB, continueBB); m_builder.CreateCondBr(eOdd, updateBB, continueBB);
m_builder.SetInsertPoint(updateBB); m_builder.SetInsertPoint(updateBB);
m_builder.CreateStore(r, a1); auto r0 = createCall(getMulFunc(), {r, b});
m_builder.CreateStore(b, a2);
createCall(m_mul, {a1, a2, a3});
auto r0 = m_builder.CreateLoad(a3, "r0");
m_builder.CreateBr(continueBB); m_builder.CreateBr(continueBB);
m_builder.SetInsertPoint(continueBB); m_builder.SetInsertPoint(continueBB);
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1");
r1->addIncoming(r, bodyBB); r1->addIncoming(r, bodyBB);
r1->addIncoming(r0, updateBB); r1->addIncoming(r0, updateBB);
m_builder.CreateStore(b, a1); auto b1 = createCall(getMulFunc(), {b, b});
m_builder.CreateStore(b, a2);
createCall(m_mul, {a1, a2, a3});
auto b1 = m_builder.CreateLoad(a3, "b1");
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1");
m_builder.CreateBr(headerBB); m_builder.CreateBr(headerBB);
@ -244,9 +320,6 @@ llvm::Function* Arith256::getMulModFunc()
m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule()); m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule());
auto i512Ty = m_builder.getIntNTy(512); auto i512Ty = m_builder.getIntNTy(512);
llvm::Type* mul512ArgTypes[] = {Type::WordPtr, Type::WordPtr, i512Ty->getPointerTo()};
auto mul512 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, mul512ArgTypes, false), llvm::Function::ExternalLinkage, "arith_mul512", getModule());
auto x = &m_mulmod->getArgumentList().front(); auto x = &m_mulmod->getArgumentList().front();
x->setName("x"); x->setName("x");
auto y = x->getNextNode(); auto y = x->getNextNode();
@ -258,32 +331,19 @@ llvm::Function* Arith256::getMulModFunc()
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod); auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod);
m_builder.SetInsertPoint(entryBB); m_builder.SetInsertPoint(entryBB);
auto a1 = m_builder.CreateAlloca(Type::Word); auto p = createCall(getMul512Func(), {x, y});
auto a2 = m_builder.CreateAlloca(Type::Word);
auto a3 = m_builder.CreateAlloca(i512Ty);
m_builder.CreateStore(x, a1);
m_builder.CreateStore(y, a2);
createCall(mul512, {a1, a2, a3});
auto p = m_builder.CreateLoad(a3, "p");
auto m = m_builder.CreateZExt(mod, i512Ty, "m"); auto m = m_builder.CreateZExt(mod, i512Ty, "m");
auto d = createCall(getDivFunc(i512Ty), {p, m}); auto d = createCall(getDivFunc(i512Ty), {p, m});
auto r = m_builder.CreateExtractValue(d, 1, "r"); auto r = m_builder.CreateExtractValue(d, 1, "r");
m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); r = m_builder.CreateTrunc(r, Type::Word);
m_builder.CreateRet(r);
} }
return m_mulmod; return m_mulmod;
} }
llvm::Value* Arith256::binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2)
{
m_builder.CreateStore(_arg1, m_arg1);
m_builder.CreateStore(_arg2, m_arg2);
m_builder.CreateCall3(_op, m_arg1, m_arg2, m_result);
return m_builder.CreateLoad(m_result);
}
llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2) llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2)
{ {
return binaryOp(m_mul, _arg1, _arg2); return createCall(getMulFunc(), {_arg1, _arg2});
} }
std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2)
@ -331,157 +391,6 @@ llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Valu
return createCall(getMulModFunc(), {_arg1, _arg2, _arg3}); return createCall(getMulModFunc(), {_arg1, _arg2, _arg3});
} }
namespace
{
#ifdef __SIZEOF_INT128__
using uint128 = __uint128_t;
#else
struct uint128
{
uint64_t lo = 0;
uint64_t hi = 0;
uint128(uint64_t lo) : lo(lo) {}
uint128 operator+(uint128 a)
{
uint128 r = 0;
bool overflow = lo > std::numeric_limits<uint64_t>::max() - a.lo;
r.lo = lo + a.lo;
r.hi = hi + a.hi + overflow;
return r;
}
uint128 operator>>(int s)
{
assert(s == 64);
return hi;
}
uint128 operator<<(int s)
{
assert(s == 64);
uint128 r = 0;
r.hi = lo;
return r;
}
explicit operator uint64_t() { return lo; }
static uint128 mul(uint64_t a, uint64_t b)
{
auto x_lo = 0xFFFFFFFF & a;
auto y_lo = 0xFFFFFFFF & b;
auto x_hi = a >> 32;
auto y_hi = b >> 32;
auto t1 = x_lo * y_lo;
auto t2 = x_lo * y_hi;
auto t3 = x_hi * y_lo;
auto t4 = x_hi * y_hi;
auto lo = (uint32_t)t1;
auto mid = (uint64_t)(t1 >> 32) + (uint32_t)t2 + (uint32_t)t3;
auto hi = (uint64_t)(t2 >> 32) + (t3 >> 32) + t4 + (mid >> 32);
uint128 r = 0;
r.lo = (uint64_t)lo + (mid << 32);
r.hi = hi;
return r;
}
uint128 operator*(uint128 a)
{
auto t1 = mul(lo, a.lo);
auto t2 = mul(lo, a.hi);
auto t3 = mul(hi, a.lo);
return t1 + (t2 << 64) + (t3 << 64);
}
};
#endif
struct uint256
{
uint64_t lo = 0;
uint64_t mid = 0;
uint128 hi = 0;
uint256(uint64_t lo, uint64_t mid, uint128 hi): lo(lo), mid(mid), hi(hi) {}
uint256(uint128 n)
{
lo = (uint64_t) n;
mid = (uint64_t) (n >> 64);
}
explicit operator uint128()
{
uint128 r = lo;
r = (r + ((uint128) mid)) << 64;
return r;
}
uint256 operator+(uint256 a)
{
auto _lo = (uint128) lo + a.lo;
auto _mid = (uint128) mid + a.mid + (_lo >> 64);
auto _hi = hi + a.hi + (_mid >> 64);
return {(uint64_t)_lo, (uint64_t)_mid, _hi};
}
uint256 lo2hi()
{
hi = (uint128)*this;
lo = 0;
mid = 0;
return *this;
}
};
struct uint512
{
uint128 lo;
uint128 mid;
uint256 hi;
};
uint256 mul(uint256 x, uint256 y)
{
auto t1 = (uint128) x.lo * y.lo;
auto t2 = (uint128) x.lo * y.mid;
auto t3 = (uint128) x.lo * y.hi;
auto t4 = (uint128) x.mid * y.lo;
auto t5 = (uint128) x.mid * y.mid;
auto t6 = (uint128) x.mid * y.hi;
auto t7 = x.hi * y.lo;
auto t8 = x.hi * y.mid;
auto lo = (uint64_t) t1;
auto m1 = (t1 >> 64) + (uint64_t) t2;
auto m2 = (uint64_t) m1;
auto mid = (uint128) m2 + (uint64_t) t4;
auto hi = (t2 >> 64) + t3 + (t4 >> 64) + t5 + (t6 << 64) + t7
+ (t8 << 64) + (m1 >> 64) + (mid >> 64);
return {lo, (uint64_t)mid, hi};
}
uint512 mul512(uint256 x, uint256 y)
{
auto x_lo = (uint128) x;
auto y_lo = (uint128) y;
auto t1 = mul(x_lo, y_lo);
auto t2 = mul(x_lo, y.hi);
auto t3 = mul(x.hi, y_lo);
auto t4 = mul(x.hi, y.hi);
auto lo = (uint128) t1;
auto mid = (uint256) t1.hi + (uint128) t2 + (uint128) t3;
auto hi = (uint256)t2.hi + t3.hi + t4 + mid.hi;
return {lo, (uint128)mid, hi};
}
}
} }
} }
@ -489,20 +398,9 @@ namespace
extern "C" extern "C"
{ {
using namespace dev::eth::jit;
EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z) EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z)
{ {
std::cerr << "DEBUG " << z << ": " << d << c << b << a << std::endl; std::cerr << "DEBUG " << std::dec << z << ": " //<< d << c << b << a
} << " [" << std::hex << std::setfill('0') << std::setw(16) << d << std::setw(16) << c << std::setw(16) << b << std::setw(16) << a << "]\n";
EXPORT void arith_mul(uint256* _arg1, uint256* _arg2, uint256* o_result)
{
*o_result = mul(*_arg1, *_arg2);
}
EXPORT void arith_mul512(uint256* _arg1, uint256* _arg2, uint512* o_result)
{
*o_result = mul512(*_arg1, *_arg2);
} }
} }

13
evmjit/libevmjit/Arith256.h

@ -24,26 +24,21 @@ public:
void debug(llvm::Value* _value, char _c); void debug(llvm::Value* _value, char _c);
private: private:
llvm::Function* getMulFunc();
llvm::Function* getMul512Func();
llvm::Function* getDivFunc(llvm::Type* _type); llvm::Function* getDivFunc(llvm::Type* _type);
llvm::Function* getExpFunc(); llvm::Function* getExpFunc();
llvm::Function* getAddModFunc(); llvm::Function* getAddModFunc();
llvm::Function* getMulModFunc(); llvm::Function* getMulModFunc();
llvm::Value* binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2); llvm::Function* m_mul = nullptr;
llvm::Function* m_mul512 = nullptr;
llvm::Function* m_mul;
llvm::Function* m_div = nullptr; llvm::Function* m_div = nullptr;
llvm::Function* m_div512 = nullptr; llvm::Function* m_div512 = nullptr;
llvm::Function* m_exp = nullptr; llvm::Function* m_exp = nullptr;
llvm::Function* m_addmod = nullptr; llvm::Function* m_addmod = nullptr;
llvm::Function* m_mulmod = nullptr; llvm::Function* m_mulmod = nullptr;
llvm::Function* m_debug = nullptr; llvm::Function* m_debug = nullptr;
llvm::Value* m_arg1;
llvm::Value* m_arg2;
llvm::Value* m_arg3;
llvm::Value* m_result;
}; };

19
evmjit/libevmjit/BasicBlock.cpp

@ -1,13 +1,14 @@
#include "BasicBlock.h" #include "BasicBlock.h"
#include <iostream> #include <iostream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/CFG.h> #include <llvm/IR/CFG.h>
#include <llvm/IR/Function.h> #include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h> #include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h> #include <llvm/IR/IRBuilder.h>
#include <llvm/Support/raw_os_ostream.h> #include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h" #include "Type.h"
@ -18,13 +19,14 @@ namespace eth
namespace jit namespace jit
{ {
const char* BasicBlock::NamePrefix = "Instr."; static const char* jumpDestName = "JmpDst.";
static const char* basicBlockName = "Instr.";
BasicBlock::BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) :
m_firstInstrIdx{_firstInstrIdx},
m_begin(_begin), m_begin(_begin),
m_end(_end), m_end(_end),
// TODO: Add begin index to name m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)),
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), NamePrefix, _mainFunc)),
m_stack(*this), m_stack(*this),
m_builder(_builder), m_builder(_builder),
m_isJumpDest(isJumpDest) m_isJumpDest(isJumpDest)
@ -43,6 +45,7 @@ BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) :
void BasicBlock::LocalStack::push(llvm::Value* _value) void BasicBlock::LocalStack::push(llvm::Value* _value)
{ {
assert(_value->getType() == Type::Word);
m_bblock.m_currentStack.push_back(_value); m_bblock.m_currentStack.push_back(_value);
m_bblock.m_tosOffset += 1; m_bblock.m_tosOffset += 1;
} }
@ -139,7 +142,7 @@ void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
auto endIter = m_currentStack.end(); auto endIter = m_currentStack.end();
// Update (emit set()) changed values // Update (emit set()) changed values
for (int idx = m_currentStack.size() - 1 - m_tosOffset; for (int idx = (int)m_currentStack.size() - 1 - m_tosOffset;
currIter < endIter && idx >= 0; currIter < endIter && idx >= 0;
++currIter, --idx) ++currIter, --idx)
{ {
@ -306,7 +309,7 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
auto& initialStack = bblock.m_initialStack; auto& initialStack = bblock.m_initialStack;
initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems); initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems);
// Initial stack shrinks, so the size difference grows: // Initial stack shrinks, so the size difference grows:
bblock.m_tosOffset += info.inputItems; bblock.m_tosOffset += (int)info.inputItems;
} }
// We must account for the items that were pushed directly to successor // We must account for the items that were pushed directly to successor
@ -319,7 +322,7 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
auto& exitStack = bblock.m_currentStack; auto& exitStack = bblock.m_currentStack;
exitStack.erase(exitStack.end() - info.outputItems, exitStack.end()); exitStack.erase(exitStack.end() - info.outputItems, exitStack.end());
bblock.m_tosOffset -= info.outputItems; bblock.m_tosOffset -= (int)info.outputItems; // FIXME: Fix types
} }
} }

20
evmjit/libevmjit/BasicBlock.h

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <llvm/IR/BasicBlock.h>
#include "Common.h" #include "Common.h"
#include "Stack.h" #include "Stack.h"
@ -11,7 +12,7 @@ namespace eth
namespace jit namespace jit
{ {
using ProgramCounter = uint64_t; // TODO: Rename using instr_idx = uint64_t;
class BasicBlock class BasicBlock
{ {
@ -50,10 +51,7 @@ public:
BasicBlock& m_bblock; BasicBlock& m_bblock;
}; };
/// Basic block name prefix. The rest is instruction index. explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
static const char* NamePrefix;
explicit BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
BasicBlock(const BasicBlock&) = delete; BasicBlock(const BasicBlock&) = delete;
@ -61,8 +59,9 @@ public:
llvm::BasicBlock* llvm() { return m_llvmBB; } llvm::BasicBlock* llvm() { return m_llvmBB; }
bytes::const_iterator begin() { return m_begin; } instr_idx firstInstrIdx() const { return m_firstInstrIdx; }
bytes::const_iterator end() { return m_end; } code_iterator begin() const { return m_begin; }
code_iterator end() const { return m_end; }
bool isJumpDest() const { return m_isJumpDest; } bool isJumpDest() const { return m_isJumpDest; }
@ -84,8 +83,9 @@ public:
void dump(std::ostream& os, bool _dotOutput = false); void dump(std::ostream& os, bool _dotOutput = false);
private: private:
bytes::const_iterator const m_begin; instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block
bytes::const_iterator const m_end; code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block
code_iterator const m_end = {}; ///< Iterator pointing code end of the block
llvm::BasicBlock* const m_llvmBB; llvm::BasicBlock* const m_llvmBB;

10
evmjit/libevmjit/BuildInfo.h.in

@ -0,0 +1,10 @@
#define EVMJIT_VERSION "${EVMJIT_VERSION}"
#define EVMJIT_VERSION_MAJOR ${EVMJIT_VERSION_MAJOR}
#define EVMJIT_VERSION_MINOR ${EVMJIT_VERSION_MINOR}
#define EVMJIT_VERSION_PATCH ${EVMJIT_VERSION_PATCH}
#define EVMJIT_VERSION_PRERELEASE "${EVMJIT_VERSION_PRERELEASE}"
#define EVMJIT_VERSION_FULL "${EVMJIT_VERSION_FULL}"
#define LLVM_VERSION "${LLVM_PACKAGE_VERSION}"
#define LLVM_ASSERTIONS "${LLVM_ENABLE_ASSERTIONS}"

53
evmjit/libevmjit/CMakeLists.txt

@ -6,26 +6,59 @@ set(INTERFACE_HEADERS interface.h)
source_group("" FILES ${HEADERS}) source_group("" FILES ${HEADERS})
source_group("" FILES ${SOURCES}) source_group("" FILES ${SOURCES})
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
# Disable rtti for Cache as LLVM has no rtti else()
set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif() endif()
set(EVMJIT_VERSION "0.0.0")
set(EVMJIT_VERSION_MAJOR 0)
set(EVMJIT_VERSION_MINOR 0)
set(EVMJIT_VERSION_PATCH 0)
set(EVMJIT_VERSION_FULL "v0.0.0-nogit")
find_package(Git) find_package(Git)
if(GIT_FOUND) if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always --match v*
OUTPUT_VARIABLE EVMJIT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_VARIABLE EVMJIT_VERSION_FULL OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
if(${EVMJIT_VERSION_FULL} MATCHES "^v[0-9]+\\.[0-9]+")
string(SUBSTRING ${EVMJIT_VERSION_FULL} 1 -1 EVMJIT_VERSION_FULL) # skip "v"
string(REPLACE "-" ";" VERSION_COMPONENTS ${EVMJIT_VERSION_FULL})
list(LENGTH VERSION_COMPONENTS NUM_VERSION_COMPONENTS)
list(GET VERSION_COMPONENTS 0 EVMJIT_VERSION)
string(REPLACE "." ";" VERSION_NUMBERS ${EVMJIT_VERSION})
list(LENGTH VERSION_NUMBERS NUM_VERSION_NUMBERS)
list(GET VERSION_NUMBERS 0 EVMJIT_VERSION_MAJOR)
list(GET VERSION_NUMBERS 1 EVMJIT_VERSION_MINOR)
if(${NUM_VERSION_NUMBERS} GREATER 2)
list(GET VERSION_NUMBERS 2 EVMJIT_VERSION_PATCH) # patch number is optional
endif() endif()
if(NOT EVMJIT_VERSION) if(${NUM_VERSION_COMPONENTS} GREATER 1)
set(EVMJIT_VERSION "unknown") list(GET VERSION_COMPONENTS 1 VERSION_PRERELEASE_CANDIDATE)
string(REGEX MATCH "^[a-zA-Z]+.*" EVMJIT_VERSION_PRERELEASE ${VERSION_PRERELEASE_CANDIDATE}) # prerelease starts with letter
endif() endif()
endif()
if(${EVMJIT_VERSION_MAJOR} EQUAL 0)
set(EVMJIT_SOVERSION "0.${EVMJIT_VERSION_MINOR}")
else()
set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR})
endif()
configure_file(BuildInfo.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen/BuildInfo.gen.h)
message("EVM JIT version: ${EVMJIT_VERSION}") message(STATUS "EVM JIT version: ${EVMJIT_VERSION_MAJOR}.${EVMJIT_VERSION_MINOR}.${EVMJIT_VERSION_PATCH} ${EVMJIT_VERSION_PRERELEASE} (${EVMJIT_VERSION_FULL})")
add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS}) add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS} gen/BuildInfo.gen.h)
set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${EVMJIT_VERSION} FOLDER "libs") set_target_properties(${TARGET_NAME} PROPERTIES
VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION}
FOLDER "libs")
include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen)
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS})

62
evmjit/libevmjit/Cache.cpp

@ -1,12 +1,19 @@
#include "Cache.h" #include "Cache.h"
#include <unordered_map>
#include <cassert>
#include <iostream> #include <iostream>
#include <cassert>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h> #include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Instructions.h>
#include <llvm/Support/Path.h> #include <llvm/Support/Path.h>
#include <llvm/Support/FileSystem.h> #include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_os_ostream.h> #include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "ExecutionEngine.h"
#include "Utils.h"
namespace dev namespace dev
{ {
@ -15,23 +22,31 @@ namespace eth
namespace jit namespace jit
{ {
//#define LOG(...) std::cerr << "CACHE " //#define CACHE_LOG std::cerr << "CACHE "
#define LOG(...) std::ostream(nullptr) #define CACHE_LOG std::ostream(nullptr)
ObjectCache* Cache::getObjectCache() namespace
{ {
static ObjectCache objectCache; llvm::MemoryBuffer* g_lastObject;
return &objectCache; ExecutionEngineListener* g_listener;
} }
namespace ObjectCache* Cache::getObjectCache(ExecutionEngineListener* _listener)
{ {
llvm::MemoryBuffer* lastObject; static ObjectCache objectCache;
g_listener = _listener;
return &objectCache;
} }
std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id) std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
{ {
assert(!lastObject); if (g_listener)
g_listener->stateChanged(ExecState::CacheLoad);
CACHE_LOG << id << ": search\n";
if (!CHECK(!g_lastObject))
g_lastObject = nullptr;
llvm::SmallString<256> cachePath; llvm::SmallString<256> cachePath;
llvm::sys::path::system_temp_directory(false, cachePath); llvm::sys::path::system_temp_directory(false, cachePath);
llvm::sys::path::append(cachePath, "evm_objs", id); llvm::sys::path::append(cachePath, "evm_objs", id);
@ -51,22 +66,31 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
#endif #endif
if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false))
lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer());
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory))
std::cerr << r.getError().message(); // TODO: Add log std::cerr << r.getError().message(); // TODO: Add log
if (lastObject) // if object found create fake module if (g_lastObject) // if object found create fake module
{ {
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(id, llvm::getGlobalContext())); CACHE_LOG << id << ": found\n";
auto mainFuncType = llvm::FunctionType::get(llvm::IntegerType::get(llvm::getGlobalContext(), 32), {}, false); auto&& context = llvm::getGlobalContext();
llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); auto module = std::unique_ptr<llvm::Module>(new llvm::Module(id, context));
auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(context), {}, false);
auto mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get());
auto bb = llvm::BasicBlock::Create(context, {}, mainFunc);
bb->getInstList().push_back(new llvm::UnreachableInst{context});
return module;
} }
CACHE_LOG << id << ": not found\n";
return nullptr; return nullptr;
} }
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object)
{ {
if (g_listener)
g_listener->stateChanged(ExecState::CacheWrite);
auto&& id = _module->getModuleIdentifier(); auto&& id = _module->getModuleIdentifier();
llvm::SmallString<256> cachePath; llvm::SmallString<256> cachePath;
llvm::sys::path::system_temp_directory(false, cachePath); llvm::sys::path::system_temp_directory(false, cachePath);
@ -77,15 +101,17 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory
llvm::sys::path::append(cachePath, id); llvm::sys::path::append(cachePath, id);
CACHE_LOG << id << ": write\n";
std::string error; std::string error;
llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None); llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None);
cacheFile << _object->getBuffer(); cacheFile << _object->getBuffer();
} }
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const*) llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module)
{ {
auto o = lastObject; CACHE_LOG << _module->getModuleIdentifier() << ": use\n";
lastObject = nullptr; auto o = g_lastObject;
g_lastObject = nullptr;
return o; return o;
} }

9
evmjit/libevmjit/Cache.h

@ -1,9 +1,8 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <unordered_map>
#include <llvm/ExecutionEngine/ObjectCache.h>
#include <llvm/ExecutionEngine/ObjectCache.h>
namespace dev namespace dev
{ {
@ -11,6 +10,7 @@ namespace eth
{ {
namespace jit namespace jit
{ {
class ExecutionEngineListener;
class ObjectCache : public llvm::ObjectCache class ObjectCache : public llvm::ObjectCache
{ {
@ -23,16 +23,13 @@ public:
/// not available. The caller owns both the MemoryBuffer returned by this /// not available. The caller owns both the MemoryBuffer returned by this
/// and the memory it references. /// and the memory it references.
virtual llvm::MemoryBuffer* getObject(llvm::Module const* _module) final override; virtual llvm::MemoryBuffer* getObject(llvm::Module const* _module) final override;
private:
std::unordered_map<std::string, std::unique_ptr<llvm::MemoryBuffer>> m_map;
}; };
class Cache class Cache
{ {
public: public:
static ObjectCache* getObjectCache(); static ObjectCache* getObjectCache(ExecutionEngineListener* _listener);
static std::unique_ptr<llvm::Module> getObject(std::string const& id); static std::unique_ptr<llvm::Module> getObject(std::string const& id);
}; };

17
evmjit/libevmjit/Common.h

@ -20,25 +20,30 @@ namespace jit
using byte = uint8_t; using byte = uint8_t;
using bytes = std::vector<byte>; using bytes = std::vector<byte>;
using bytes_ref = std::tuple<byte const*, size_t>; using bytes_ref = std::tuple<byte const*, size_t>;
using code_iterator = byte const*;
struct NoteChannel {}; // FIXME: Use some log library? struct NoteChannel {}; // FIXME: Use some log library?
enum class ReturnCode enum class ReturnCode
{ {
// Success codes
Stop = 0, Stop = 0,
Return = 1, Return = 1,
Suicide = 2, Suicide = 2,
// Standard error codes
OutOfGas = -1, OutOfGas = -1,
BadJumpDestination = -2, StackTooSmall = -2,
StackTooSmall = -3, BadJumpDestination = -3,
BadInstruction = -4, BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
LLVMConfigError = -5, // Internal error codes
LLVMCompileError = -6, LLVMConfigError = -101,
LLVMLinkError = -7, LLVMCompileError = -102,
LLVMLinkError = -103,
UnexpectedException = -8, UnexpectedException = -111,
LinkerWorkaround = -299, LinkerWorkaround = -299,
}; };

94
evmjit/libevmjit/Compiler.cpp

@ -1,4 +1,3 @@
#include "Compiler.h" #include "Compiler.h"
#include <functional> #include <functional>
@ -6,13 +5,15 @@
#include <chrono> #include <chrono>
#include <sstream> #include <sstream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/ADT/PostOrderIterator.h> #include <llvm/ADT/PostOrderIterator.h>
#include <llvm/IR/CFG.h> #include <llvm/IR/CFG.h>
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include <llvm/IR/MDBuilder.h>
#include <llvm/PassManager.h> #include <llvm/PassManager.h>
#include <llvm/Transforms/Scalar.h> #include <llvm/Transforms/Scalar.h>
#include "preprocessor/llvm_includes_end.h"
#include "Instruction.h" #include "Instruction.h"
#include "Type.h" #include "Type.h"
@ -39,10 +40,10 @@ Compiler::Compiler(Options const& _options):
Type::init(m_builder.getContext()); Type::init(m_builder.getContext());
} }
void Compiler::createBasicBlocks(bytes const& _bytecode) void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd)
{ {
/// Helper function that skips push data and finds next iterator (can be the end) /// Helper function that skips push data and finds next iterator (can be the end)
auto skipPushDataAndGetNext = [](bytes::const_iterator _curr, bytes::const_iterator _end) auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end)
{ {
static const auto push1 = static_cast<size_t>(Instruction::PUSH1); static const auto push1 = static_cast<size_t>(Instruction::PUSH1);
static const auto push32 = static_cast<size_t>(Instruction::PUSH32); static const auto push32 = static_cast<size_t>(Instruction::PUSH32);
@ -52,11 +53,11 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
return _curr + offset; return _curr + offset;
}; };
auto begin = _bytecode.begin(); auto begin = _codeBegin; // begin of current block
bool nextJumpDest = false; bool nextJumpDest = false;
for (auto curr = begin, next = begin; curr != _bytecode.end(); curr = next) for (auto curr = begin, next = begin; curr != _codeEnd; curr = next)
{ {
next = skipPushDataAndGetNext(curr, _bytecode.end()); next = skipPushDataAndGetNext(curr, _codeEnd);
bool isEnd = false; bool isEnd = false;
switch (Instruction(*curr)) switch (Instruction(*curr))
@ -77,22 +78,19 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
break; break;
} }
assert(next <= _bytecode.end()); assert(next <= _codeEnd);
if (next == _bytecode.end() || Instruction(*next) == Instruction::JUMPDEST) if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST)
isEnd = true; isEnd = true;
if (isEnd) if (isEnd)
{ {
auto beginIdx = begin - _bytecode.begin(); auto beginIdx = begin - _codeBegin;
m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx),
std::forward_as_tuple(begin, next, m_mainFunc, m_builder, nextJumpDest)); std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, m_builder, nextJumpDest));
nextJumpDest = false; nextJumpDest = false;
begin = next; begin = next;
} }
} }
// TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
} }
llvm::BasicBlock* Compiler::getJumpTableBlock() llvm::BasicBlock* Compiler::getJumpTableBlock()
@ -125,7 +123,7 @@ llvm::BasicBlock* Compiler::getBadJumpBlock()
return m_badJumpBlock->llvm(); return m_badJumpBlock->llvm();
} }
std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::string const& _id) std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id)
{ {
auto compilationStartTime = std::chrono::high_resolution_clock::now(); auto compilationStartTime = std::chrono::high_resolution_clock::now();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(_id, m_builder.getContext())); auto module = std::unique_ptr<llvm::Module>(new llvm::Module(_id, m_builder.getContext()));
@ -135,21 +133,40 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get()); m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get());
m_mainFunc->getArgumentList().front().setName("rt"); m_mainFunc->getArgumentList().front().setName("rt");
// Create the basic blocks. // Create entry basic block
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc); auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc);
m_builder.SetInsertPoint(entryBlock); m_builder.SetInsertPoint(entryBlock);
createBasicBlocks(_bytecode); auto jmpBufWords = m_builder.CreateAlloca(Type::BytePtr, m_builder.getInt64(3), "jmpBuf.words");
auto frameaddress = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::frameaddress);
auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp");
m_builder.CreateStore(fp, jmpBufWords);
auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave);
auto sp = m_builder.CreateCall(stacksave, "sp");
auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp");
m_builder.CreateStore(sp, jmpBufSp);
auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp);
auto jmpBuf = m_builder.CreateBitCast(jmpBufWords, Type::BytePtr, "jmpBuf");
auto r = m_builder.CreateCall(setjmp, jmpBuf);
auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0));
createBasicBlocks(_begin, _end);
// Init runtime structures. // Init runtime structures.
RuntimeManager runtimeManager(m_builder); RuntimeManager runtimeManager(m_builder, jmpBuf, _begin, _end);
GasMeter gasMeter(m_builder, runtimeManager); GasMeter gasMeter(m_builder, runtimeManager);
Memory memory(runtimeManager, gasMeter); Memory memory(runtimeManager, gasMeter);
Ext ext(runtimeManager, memory); Ext ext(runtimeManager, memory);
Stack stack(m_builder, runtimeManager); Stack stack(m_builder, runtimeManager);
Arith256 arith(m_builder); Arith256 arith(m_builder);
m_builder.CreateBr(m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm()); // TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm();
auto expectTrue = llvm::MDBuilder{m_builder.getContext()}.createBranchWeights(1, 0);
m_builder.CreateCondBr(normalFlow, firstBB, abortBB, expectTrue);
for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt)
{ {
@ -157,7 +174,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
auto iterCopy = basicBlockPairIt; auto iterCopy = basicBlockPairIt;
++iterCopy; ++iterCopy;
auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr;
compileBasicBlock(basicBlock, _bytecode, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock);
} }
// Code for special blocks: // Code for special blocks:
@ -165,6 +182,9 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
m_builder.SetInsertPoint(m_stopBB); m_builder.SetInsertPoint(m_stopBB);
m_builder.CreateRet(Constant::get(ReturnCode::Stop)); m_builder.CreateRet(Constant::get(ReturnCode::Stop));
m_builder.SetInsertPoint(abortBB);
m_builder.CreateRet(Constant::get(ReturnCode::OutOfGas));
removeDeadBlocks(); removeDeadBlocks();
// Link jump table target index // Link jump table target index
@ -224,7 +244,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
} }
void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, RuntimeManager& _runtimeManager, void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager,
Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock) Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock)
{ {
if (!_nextBasicBlock) // this is the last block in the code if (!_nextBasicBlock) // this is the last block in the code
@ -623,7 +643,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::PC: case Instruction::PC:
{ {
auto value = Constant::get(it - _bytecode.begin()); auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx());
stack.push(value); stack.push(value);
break; break;
} }
@ -631,7 +651,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::GAS: case Instruction::GAS:
{ {
_gasMeter.commitCostBlock(); _gasMeter.commitCostBlock();
stack.push(_runtimeManager.getGas()); stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word));
break; break;
} }
@ -741,10 +761,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
_memory.require(initOff, initSize); _memory.require(initOff, initSize);
_gasMeter.commitCostBlock(); _gasMeter.commitCostBlock();
auto address = _ext.create(endowment, initOff, initSize);
auto gas = _runtimeManager.getGas();
auto address = _ext.create(gas, endowment, initOff, initSize);
_runtimeManager.setGas(gas);
stack.push(address); stack.push(address);
break; break;
} }
@ -752,7 +769,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::CALL: case Instruction::CALL:
case Instruction::CALLCODE: case Instruction::CALLCODE:
{ {
auto gas = stack.pop(); auto callGas256 = stack.pop();
auto codeAddress = stack.pop(); auto codeAddress = stack.pop();
auto value = stack.pop(); auto value = stack.pop();
auto inOff = stack.pop(); auto inOff = stack.pop();
@ -770,9 +787,13 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
if (inst == Instruction::CALLCODE) if (inst == Instruction::CALLCODE)
receiveAddress = _runtimeManager.get(RuntimeData::Address); receiveAddress = _runtimeManager.get(RuntimeData::Address);
_gasMeter.count(gas); auto gas = _runtimeManager.getGas();
auto ret = _ext.call(gas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress); _gasMeter.count(callGas256);
_gasMeter.giveBack(gas); auto callGas = m_builder.CreateTrunc(callGas256, Type::Gas);
auto gasLeft = m_builder.CreateNSWSub(gas, callGas);
_runtimeManager.setGas(callGas);
auto ret = _ext.call(receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress);
_gasMeter.giveBack(gasLeft);
stack.push(ret); stack.push(ret);
break; break;
} }
@ -825,12 +846,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
break; break;
} }
default: // Invalid instruction - runtime exception default: // Invalid instruction - abort
{ m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction));
// TODO: Replace with return statement it = _basicBlock.end() - 1; // finish block compilation
_runtimeManager.raiseException(ReturnCode::BadInstruction);
}
} }
} }

9
evmjit/libevmjit/Compiler.h

@ -1,8 +1,5 @@
#pragma once #pragma once
#include <llvm/IR/IRBuilder.h>
#include "Common.h" #include "Common.h"
#include "BasicBlock.h" #include "BasicBlock.h"
@ -33,13 +30,13 @@ public:
Compiler(Options const& _options); Compiler(Options const& _options);
std::unique_ptr<llvm::Module> compile(bytes const& _bytecode, std::string const& _id); std::unique_ptr<llvm::Module> compile(code_iterator _begin, code_iterator _end, std::string const& _id);
private: private:
void createBasicBlocks(bytes const& _bytecode); void createBasicBlocks(code_iterator _begin, code_iterator _end);
void compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock); void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock);
llvm::BasicBlock* getJumpTableBlock(); llvm::BasicBlock* getJumpTableBlock();

4
evmjit/libevmjit/CompilerHelper.cpp

@ -1,8 +1,8 @@
#include "CompilerHelper.h" #include "CompilerHelper.h"
#include <llvm/IR/Function.h> #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"

5
evmjit/libevmjit/CompilerHelper.h

@ -1,7 +1,8 @@
#pragma once #pragma once
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IRBuilder.h> #include <llvm/IR/IRBuilder.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev namespace dev
@ -19,7 +20,7 @@ protected:
CompilerHelper(llvm::IRBuilder<>& _builder); CompilerHelper(llvm::IRBuilder<>& _builder);
CompilerHelper(const CompilerHelper&) = delete; CompilerHelper(const CompilerHelper&) = delete;
void operator=(CompilerHelper) = delete; CompilerHelper& operator=(CompilerHelper) = delete;
/// Reference to the IR module being compiled /// Reference to the IR module being compiled
llvm::Module* getModule(); llvm::Module* getModule();

3
evmjit/libevmjit/Endianness.cpp

@ -1,7 +1,8 @@
#include "Endianness.h" #include "Endianness.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h" #include "Type.h"

3
evmjit/libevmjit/Endianness.h

@ -1,7 +1,8 @@
#pragma once #pragma once
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IRBuilder.h> #include <llvm/IR/IRBuilder.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev namespace dev
{ {

97
evmjit/libevmjit/ExecStats.cpp

@ -0,0 +1,97 @@
#include "ExecStats.h"
#include <iostream>
#include <iomanip>
#include <cassert>
#include "Utils.h"
namespace dev
{
namespace eth
{
namespace jit
{
void ExecStats::stateChanged(ExecState _state)
{
if (!CHECK(m_state != ExecState::Finished))
return;
auto now = clock::now();
if (_state != ExecState::Started)
{
assert(time[(int)m_state] == ExecStats::duration::zero());
time[(int)m_state] = now - m_tp;
}
m_state = _state;
m_tp = now;
}
namespace
{
struct StatsAgg
{
using unit = std::chrono::microseconds;
ExecStats::duration tot = ExecStats::duration::zero();
ExecStats::duration min = ExecStats::duration::max();
ExecStats::duration max = ExecStats::duration::zero();
size_t count = 0;
void update(ExecStats::duration _d)
{
++count;
tot += _d;
min = _d < min ? _d : min;
max = _d > max ? _d : max;
}
void output(char const* _name, std::ostream& _os)
{
auto avg = tot / count;
_os << std::setfill(' ')
<< std::setw(12) << std::left << _name
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(tot).count()
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(avg).count()
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(min).count()
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(max).count()
<< std::endl;
}
};
char const* getExecStateName(ExecState _state)
{
switch (_state)
{
case ExecState::Started: return "Start";
case ExecState::CacheLoad: return "CacheLoad";
case ExecState::CacheWrite: return "CacheWrite";
case ExecState::Compilation: return "Compilation";
case ExecState::CodeGen: return "CodeGen";
case ExecState::Execution: return "Execution";
case ExecState::Return: return "Return";
case ExecState::Finished: return "Finish";
}
return nullptr;
}
}
StatsCollector::~StatsCollector()
{
if (stats.empty())
return;
std::cout << " [us] total avg min max\n";
for (int i = 0; i < (int)ExecState::Finished; ++i)
{
StatsAgg agg;
for (auto&& s : stats)
agg.update(s->time[i]);
agg.output(getExecStateName(ExecState(i)), std::cout);
}
}
}
}
}

44
evmjit/libevmjit/ExecStats.h

@ -0,0 +1,44 @@
#pragma once
#include <string>
#include <chrono>
#include "ExecutionEngine.h"
namespace dev
{
namespace eth
{
namespace jit
{
class ExecStats : public ExecutionEngineListener
{
public:
using clock = std::chrono::high_resolution_clock;
using duration = clock::duration;
using time_point = clock::time_point;
std::string id;
duration time[(int)ExecState::Finished] = {};
void stateChanged(ExecState _state) override;
private:
ExecState m_state = {};
time_point m_tp = {};
};
class StatsCollector
{
public:
std::vector<std::unique_ptr<ExecStats>> stats;
~StatsCollector();
};
}
}
}

131
evmjit/libevmjit/ExecutionEngine.cpp

@ -1,24 +1,25 @@
#include "ExecutionEngine.h" #include "ExecutionEngine.h"
#include <chrono> #include <array>
#include <cstdlib> // env options #include <cstdlib> // env options
#include <iostream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h> #include <llvm/ADT/Triple.h>
#pragma warning(push)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <llvm/ExecutionEngine/ExecutionEngine.h> #include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h> #include <llvm/ExecutionEngine/SectionMemoryManager.h>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include <llvm/ExecutionEngine/MCJIT.h> #include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/Support/TargetSelect.h> #include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Host.h> #include <llvm/Support/Host.h>
#include "preprocessor/llvm_includes_end.h"
#include "Runtime.h" #include "Runtime.h"
#include "Compiler.h" #include "Compiler.h"
#include "Cache.h" #include "Cache.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev namespace dev
{ {
@ -31,34 +32,20 @@ namespace
{ {
using EntryFuncPtr = ReturnCode(*)(Runtime*); using EntryFuncPtr = ReturnCode(*)(Runtime*);
ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime) std::string codeHash(i256 const& _hash)
{ {
// That function uses long jumps to handle "execeptions". static const auto size = sizeof(_hash);
// Do not create any non-POD objects here static const auto hexChars = "0123456789abcdef";
std::string str;
ReturnCode returnCode{}; str.resize(size * 2);
auto sj = setjmp(_runtime->getJmpBuf()); auto outIt = str.rbegin(); // reverse for BE
if (sj == 0) auto& arr = *(std::array<byte, size>*)&_hash;
returnCode = _mainFunc(_runtime); for (auto b : arr)
else
returnCode = static_cast<ReturnCode>(sj);
return returnCode;
}
std::string codeHash(bytes const& _code)
{ {
uint32_t hash = 0; *(outIt++) = hexChars[b & 0xf];
for (auto b : _code) *(outIt++) = hexChars[b >> 4];
{
hash += b;
hash += (hash << 10);
hash ^= (hash >> 6);
} }
hash += (hash << 3); return str;
hash ^= (hash >> 11);
hash += (hash << 15);
return std::to_string(hash);
} }
bool getEnvOption(char const* _name, bool _default) bool getEnvOption(char const* _name, bool _default)
@ -69,41 +56,41 @@ bool getEnvOption(char const* _name, bool _default)
return std::strtol(var, nullptr, 10) != 0; return std::strtol(var, nullptr, 10) != 0;
} }
bool showInfo()
{
auto show = getEnvOption("EVMJIT_INFO", false);
if (show)
{
std::cout << "The Ethereum EVM JIT " EVMJIT_VERSION_FULL " LLVM " LLVM_VERSION << std::endl;
}
return show;
}
} }
ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env) ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
{ {
static std::unique_ptr<llvm::ExecutionEngine> ee; // TODO: Use Managed Objects from LLVM?
static auto debugDumpModule = getEnvOption("EVMJIT_DUMP", false); static auto debugDumpModule = getEnvOption("EVMJIT_DUMP", false);
static auto objectCacheEnabled = getEnvOption("EVMJIT_CACHE", true); static auto objectCacheEnabled = getEnvOption("EVMJIT_CACHE", true);
static auto statsCollectingEnabled = getEnvOption("EVMJIT_STATS", false);
static auto infoShown = showInfo();
(void) infoShown;
auto mainFuncName = codeHash(_code); std::unique_ptr<ExecStats> listener{new ExecStats};
EntryFuncPtr entryFuncPtr{}; listener->stateChanged(ExecState::Started);
Runtime runtime(_data, _env); // TODO: I don't know why but it must be created before getFunctionAddress() calls
if (ee && (entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName))) auto objectCache = objectCacheEnabled ? Cache::getObjectCache(listener.get()) : nullptr;
{
} static std::unique_ptr<llvm::ExecutionEngine> ee;
else
{
auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr;
std::unique_ptr<llvm::Module> module;
if (objectCache)
module = Cache::getObject(mainFuncName);
if (!module)
module = Compiler({}).compile(_code, mainFuncName);
if (debugDumpModule)
module->dump();
if (!ee) if (!ee)
{ {
llvm::InitializeNativeTarget(); llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter(); llvm::InitializeNativeTargetAsmPrinter();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext()));
llvm::EngineBuilder builder(module.get()); llvm::EngineBuilder builder(module.get());
builder.setEngineKind(llvm::EngineKind::JIT); builder.setEngineKind(llvm::EngineKind::JIT);
builder.setUseMCJIT(true); builder.setUseMCJIT(true);
std::unique_ptr<llvm::SectionMemoryManager> memoryManager(new llvm::SectionMemoryManager);
builder.setMCJITMemoryManager(memoryManager.get());
builder.setOptLevel(llvm::CodeGenOpt::None); builder.setOptLevel(llvm::CodeGenOpt::None);
auto triple = llvm::Triple(llvm::sys::getProcessTriple()); auto triple = llvm::Triple(llvm::sys::getProcessTriple());
@ -112,39 +99,51 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en
module->setTargetTriple(triple.str()); module->setTargetTriple(triple.str());
ee.reset(builder.create()); ee.reset(builder.create());
if (!ee) if (!CHECK(ee))
return ReturnCode::LLVMConfigError; return ReturnCode::LLVMConfigError;
module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module
memoryManager.release(); // and memory manager
if (objectCache)
ee->setObjectCache(objectCache); ee->setObjectCache(objectCache);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
} }
else
{ static StatsCollector statsCollector;
auto mainFuncName = codeHash(_data->codeHash);
Runtime runtime(_data, _env); // TODO: I don't know why but it must be created before getFunctionAddress() calls
auto entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
if (!entryFuncPtr) if (!entryFuncPtr)
{ {
auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr;
if (!module)
{
listener->stateChanged(ExecState::Compilation);
assert(_data->code || !_data->codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler({}).compile(_data->code, _data->code + _data->codeSize, mainFuncName);
}
if (debugDumpModule)
module->dump();
ee->addModule(module.get()); ee->addModule(module.get());
module.release(); module.release();
listener->stateChanged(ExecState::CodeGen);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
} }
} if (!CHECK(entryFuncPtr))
} return ReturnCode::LLVMLinkError;
assert(entryFuncPtr);
auto executionStartTime = std::chrono::high_resolution_clock::now(); listener->stateChanged(ExecState::Execution);
auto returnCode = entryFuncPtr(&runtime);
listener->stateChanged(ExecState::Return);
auto returnCode = runEntryFunc(entryFuncPtr, &runtime);
if (returnCode == ReturnCode::Return) if (returnCode == ReturnCode::Return)
{ {
returnData = runtime.getReturnData(); // Save reference to return data returnData = runtime.getReturnData(); // Save reference to return data
std::swap(m_memory, runtime.getMemory()); // Take ownership of memory std::swap(m_memory, runtime.getMemory()); // Take ownership of memory
} }
listener->stateChanged(ExecState::Finished);
auto executionEndTime = std::chrono::high_resolution_clock::now(); if (statsCollectingEnabled)
clog(JIT) << " + " << std::chrono::duration_cast<std::chrono::milliseconds>(executionEndTime - executionStartTime).count() << " ms\n"; statsCollector.stats.push_back(std::move(listener));
return returnCode; return returnCode;
} }

32
evmjit/libevmjit/ExecutionEngine.h

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

31
evmjit/libevmjit/Ext.cpp

@ -1,9 +1,8 @@
#include "Ext.h" #include "Ext.h"
#include <llvm/IR/Function.h> #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/TypeBuilder.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
#include "Memory.h" #include "Memory.h"
@ -41,8 +40,8 @@ std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})}, FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})}, FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
@ -60,14 +59,16 @@ llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module)
llvm::Value* Ext::getArgAlloca() llvm::Value* Ext::getArgAlloca()
{ {
auto& a = m_argAllocas[m_argCounter++]; auto& a = m_argAllocas[m_argCounter];
if (!a) if (!a)
{ {
// FIXME: Improve order and names
InsertPointGuard g{getBuilder()}; InsertPointGuard g{getBuilder()};
getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI()); auto allocaIt = getMainFunction()->front().begin();
a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg"); std::advance(allocaIt, m_argCounter); // Skip already created allocas
getBuilder().SetInsertPoint(allocaIt);
a = getBuilder().CreateAlloca(Type::Word, nullptr, {"a.", std::to_string(m_argCounter)});
} }
++m_argCounter;
return a; return a;
} }
@ -124,30 +125,26 @@ llvm::Value* Ext::blockhash(llvm::Value* _number)
return Endianness::toNative(getBuilder(), hash); return Endianness::toNative(getBuilder(), hash);
} }
llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize) llvm::Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize)
{ {
auto gas = byPtr(_gas);
auto ret = getArgAlloca(); auto ret = getArgAlloca();
auto begin = m_memoryMan.getBytePtr(_initOff); auto begin = m_memoryMan.getBytePtr(_initOff);
auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size"); auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size");
createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret}); createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(_endowment), begin, size, ret});
_gas = m_builder.CreateLoad(gas); // Return gas
llvm::Value* address = m_builder.CreateLoad(ret); llvm::Value* address = m_builder.CreateLoad(ret);
address = Endianness::toNative(m_builder, address); address = Endianness::toNative(m_builder, address);
return address; return address;
} }
llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress) llvm::Value* Ext::call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress)
{ {
auto gas = byPtr(_gas);
auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress); auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress);
auto inBeg = m_memoryMan.getBytePtr(_inOff); auto inBeg = m_memoryMan.getBytePtr(_inOff);
auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size"); auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size");
auto outBeg = m_memoryMan.getBytePtr(_outOff); auto outBeg = m_memoryMan.getBytePtr(_outOff);
auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size"); auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size");
auto codeAddress = Endianness::toBE(m_builder, _codeAddress); auto codeAddress = Endianness::toBE(m_builder, _codeAddress);
auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), gas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)}); auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)});
_gas = m_builder.CreateLoad(gas); // Return gas
return m_builder.CreateZExt(ret, Type::Word, "ret"); return m_builder.CreateZExt(ret, Type::Word, "ret");
} }

6
evmjit/libevmjit/Ext.h

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include "CompilerHelper.h" #include "CompilerHelper.h"
namespace dev namespace dev
@ -50,8 +50,8 @@ public:
llvm::Value* balance(llvm::Value* _address); llvm::Value* balance(llvm::Value* _address);
llvm::Value* calldataload(llvm::Value* _index); llvm::Value* calldataload(llvm::Value* _index);
llvm::Value* create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize);
llvm::Value* call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress); llvm::Value* call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress);
llvm::Value* blockhash(llvm::Value* _number); llvm::Value* blockhash(llvm::Value* _number);
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize);

107
evmjit/libevmjit/GasMeter.cpp

@ -1,11 +1,9 @@
#include "GasMeter.h" #include "GasMeter.h"
#include <llvm/IR/GlobalVariable.h> #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Ext.h" #include "Ext.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
@ -19,29 +17,29 @@ namespace jit
namespace // Helper functions namespace // Helper functions
{ {
uint64_t const c_stepGas = 1; int64_t const c_stepGas = 1;
uint64_t const c_balanceGas = 20; int64_t const c_balanceGas = 20;
uint64_t const c_sha3Gas = 10; int64_t const c_sha3Gas = 10;
uint64_t const c_sha3WordGas = 10; int64_t const c_sha3WordGas = 10;
uint64_t const c_sloadGas = 20; int64_t const c_sloadGas = 20;
uint64_t const c_sstoreSetGas = 300; int64_t const c_sstoreSetGas = 300;
uint64_t const c_sstoreResetGas = 100; int64_t const c_sstoreResetGas = 100;
uint64_t const c_sstoreRefundGas = 100; int64_t const c_sstoreRefundGas = 100;
uint64_t const c_createGas = 100; int64_t const c_createGas = 100;
uint64_t const c_createDataGas = 5; int64_t const c_createDataGas = 5;
uint64_t const c_callGas = 20; int64_t const c_callGas = 20;
uint64_t const c_expGas = 1; int64_t const c_expGas = 1;
uint64_t const c_expByteGas = 1; int64_t const c_expByteGas = 1;
uint64_t const c_memoryGas = 1; int64_t const c_memoryGas = 1;
uint64_t const c_txDataZeroGas = 1; int64_t const c_txDataZeroGas = 1;
uint64_t const c_txDataNonZeroGas = 5; int64_t const c_txDataNonZeroGas = 5;
uint64_t const c_txGas = 500; int64_t const c_txGas = 500;
uint64_t const c_logGas = 32; int64_t const c_logGas = 32;
uint64_t const c_logDataGas = 1; int64_t const c_logDataGas = 1;
uint64_t const c_logTopicGas = 32; int64_t const c_logTopicGas = 32;
uint64_t const c_copyGas = 1; int64_t const c_copyGas = 1;
uint64_t getStepCost(Instruction inst) int64_t getStepCost(Instruction inst)
{ {
switch (inst) switch (inst)
{ {
@ -72,7 +70,7 @@ uint64_t getStepCost(Instruction inst)
case Instruction::LOG3: case Instruction::LOG3:
case Instruction::LOG4: case Instruction::LOG4:
{ {
auto numTopics = static_cast<uint64_t>(inst) - static_cast<uint64_t>(Instruction::LOG0); auto numTopics = static_cast<int64_t>(inst) - static_cast<int64_t>(Instruction::LOG0);
return c_logGas + numTopics * c_logTopicGas; return c_logGas + numTopics * c_logTopicGas;
} }
} }
@ -86,7 +84,7 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
{ {
auto module = getModule(); auto module = getModule();
llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Word}; llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Gas};
m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", module); m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", module);
InsertPointGuard guard(m_builder); InsertPointGuard guard(m_builder);
@ -94,22 +92,22 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc); auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc);
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc); auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc);
auto rt = &m_gasCheckFunc->getArgumentList().front();
rt->setName("rt");
auto cost = rt->getNextNode();
cost->setName("cost");
m_builder.SetInsertPoint(checkBB); m_builder.SetInsertPoint(checkBB);
auto arg = m_gasCheckFunc->arg_begin();
arg->setName("rt");
++arg;
arg->setName("cost");
auto cost = arg;
auto gas = m_runtimeManager.getGas(); auto gas = m_runtimeManager.getGas();
auto isOutOfGas = m_builder.CreateICmpUGT(cost, gas, "isOutOfGas"); gas = m_builder.CreateNSWSub(gas, cost, "gasUpdated");
auto isOutOfGas = m_builder.CreateICmpSLT(gas, m_builder.getInt64(0), "isOutOfGas"); // gas < 0, with gas == 0 we can still do 0 cost instructions
m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB); m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB);
m_builder.SetInsertPoint(outOfGasBB); m_builder.SetInsertPoint(outOfGasBB);
m_runtimeManager.raiseException(ReturnCode::OutOfGas); m_runtimeManager.abort();
m_builder.CreateUnreachable(); m_builder.CreateUnreachable();
m_builder.SetInsertPoint(updateBB); m_builder.SetInsertPoint(updateBB);
gas = m_builder.CreateSub(gas, cost);
m_runtimeManager.setGas(gas); m_runtimeManager.setGas(gas);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
} }
@ -119,7 +117,7 @@ void GasMeter::count(Instruction _inst)
if (!m_checkCall) if (!m_checkCall)
{ {
// Create gas check call with mocked block cost at begining of current cost-block // Create gas check call with mocked block cost at begining of current cost-block
m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word)}); m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Gas)});
} }
m_blockCost += getStepCost(_inst); m_blockCost += getStepCost(_inst);
@ -127,6 +125,15 @@ void GasMeter::count(Instruction _inst)
void GasMeter::count(llvm::Value* _cost) void GasMeter::count(llvm::Value* _cost)
{ {
if (_cost->getType() == Type::Word)
{
auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word);
auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh");
auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas);
_cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost");
}
assert(_cost->getType() == Type::Gas);
createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost}); createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost});
} }
@ -136,12 +143,13 @@ void GasMeter::countExp(llvm::Value* _exponent)
// lz - leading zeros // lz - leading zeros
// cost = ((256 - lz) + 7) / 8 // cost = ((256 - lz) + 7) / 8
// OPT: All calculations can be done on 32/64 bits // OPT: Can gas update be done in exp algorithm?
auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
auto lz = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false)); auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false));
auto sigBits = m_builder.CreateSub(Constant::get(256), lz); auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, Constant::get(7)), Constant::get(8)); auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits");
auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8));
count(sigBytes); count(sigBytes);
} }
@ -154,8 +162,8 @@ void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValu
auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero");
auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert"); auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert");
auto isDelete = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDelete"); auto isDelete = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDelete");
auto cost = m_builder.CreateSelect(isInsert, Constant::get(c_sstoreSetGas), Constant::get(c_sstoreResetGas), "cost"); auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(c_sstoreSetGas), m_builder.getInt64(c_sstoreResetGas), "cost");
cost = m_builder.CreateSelect(isDelete, Constant::get(0), cost, "cost"); cost = m_builder.CreateSelect(isDelete, m_builder.getInt64(0), cost, "cost");
count(cost); count(cost);
} }
@ -173,17 +181,16 @@ void GasMeter::countSha3Data(llvm::Value* _dataLength)
assert(m_blockCost > 0); // SHA3 instruction is already counted assert(m_blockCost > 0); // SHA3 instruction is already counted
// TODO: This round ups to 32 happens in many places // TODO: This round ups to 32 happens in many places
// FIXME: 64-bit arith used, but not verified
static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter"); static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter");
auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::lowPrecision); auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::Gas);
auto words64 = m_builder.CreateUDiv(m_builder.CreateAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32)); auto words64 = m_builder.CreateUDiv(m_builder.CreateNUWAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32));
auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64); auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64);
auto cost = getBuilder().CreateZExt(cost64, Type::Word); count(cost64);
count(cost);
} }
void GasMeter::giveBack(llvm::Value* _gas) void GasMeter::giveBack(llvm::Value* _gas)
{ {
assert(_gas->getType() == Type::Gas);
m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas)); m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas));
} }
@ -199,7 +206,7 @@ void GasMeter::commitCostBlock()
return; return;
} }
m_checkCall->setArgOperand(1, Constant::get(m_blockCost)); // Update block cost in gas check call m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call
m_checkCall = nullptr; // End cost-block m_checkCall = nullptr; // End cost-block
m_blockCost = 0; m_blockCost = 0;
} }

3
evmjit/libevmjit/GasMeter.h

@ -1,4 +1,3 @@
#pragma once #pragma once
#include "CompilerHelper.h" #include "CompilerHelper.h"
@ -50,7 +49,7 @@ public:
private: private:
/// Cumulative gas cost of a block of instructions /// Cumulative gas cost of a block of instructions
/// @TODO Handle overflow /// @TODO Handle overflow
uint64_t m_blockCost = 0; int64_t m_blockCost = 0;
llvm::CallInst* m_checkCall = nullptr; llvm::CallInst* m_checkCall = nullptr;
llvm::Function* m_gasCheckFunc = nullptr; llvm::Function* m_gasCheckFunc = nullptr;

8
evmjit/libevmjit/Instruction.cpp

@ -1,6 +1,8 @@
#include "Instruction.h" #include "Instruction.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/ADT/APInt.h> #include <llvm/ADT/APInt.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev namespace dev
{ {
@ -9,7 +11,7 @@ namespace eth
namespace jit namespace jit
{ {
llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) llvm::APInt readPushData(code_iterator& _curr, code_iterator _end)
{ {
auto pushInst = *_curr; auto pushInst = *_curr;
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32);
@ -26,7 +28,7 @@ llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _en
return value; return value;
} }
void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) void skipPushData(code_iterator& _curr, code_iterator _end)
{ {
auto pushInst = *_curr; auto pushInst = *_curr;
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32);

4
evmjit/libevmjit/Instruction.h

@ -161,11 +161,11 @@ enum class Instruction: uint8_t
/// Reads PUSH data from pointed fragment of bytecode and constructs number out of it /// Reads PUSH data from pointed fragment of bytecode and constructs number out of it
/// Reading out of bytecode means reading 0 /// Reading out of bytecode means reading 0
/// @param _curr is updated and points the last real byte read /// @param _curr is updated and points the last real byte read
llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end); llvm::APInt readPushData(code_iterator& _curr, code_iterator _end);
/// Skips PUSH data in pointed fragment of bytecode. /// Skips PUSH data in pointed fragment of bytecode.
/// @param _curr is updated and points the last real byte skipped /// @param _curr is updated and points the last real byte skipped
void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end); void skipPushData(code_iterator& _curr, code_iterator _end);
#define ANY_PUSH PUSH1: \ #define ANY_PUSH PUSH1: \
case Instruction::PUSH2: \ case Instruction::PUSH2: \

93
evmjit/libevmjit/Memory.cpp

@ -1,14 +1,8 @@
#include "Memory.h" #include "Memory.h"
#include <vector> #include "preprocessor/llvm_includes_start.h"
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <cassert>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h" #include "Type.h"
#include "Runtime.h" #include "Runtime.h"
@ -26,23 +20,15 @@ namespace jit
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
m_gasMeter(_gasMeter) m_gasMeter(_gasMeter)
{ {}
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule());
llvm::AttrBuilder attrBuilder;
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly);
m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder));
m_require = createRequireFunc(_gasMeter); llvm::Function* Memory::getRequireFunc()
m_loadWord = createFunc(false, Type::Word, _gasMeter); {
m_storeWord = createFunc(true, Type::Word, _gasMeter); auto& func = m_require;
m_storeByte = createFunc(true, Type::Byte, _gasMeter); if (!func)
}
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
{ {
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule());
auto rt = func->arg_begin(); auto rt = func->arg_begin();
rt->setName("rt"); rt->setName("rt");
auto offset = rt->getNextNode(); auto offset = rt->getNextNode();
@ -50,6 +36,12 @@ llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
auto size = offset->getNextNode(); auto size = offset->getNextNode();
size->setName("size"); size->setName("size");
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
auto resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule());
llvm::AttrBuilder attrBuilder;
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly);
resize->setAttributes(llvm::AttributeSet::get(resize->getContext(), 1, attrBuilder));
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func);
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
@ -87,10 +79,10 @@ llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq");
auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k
auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
_gasMeter.countMemory(newWords); m_gasMeter.countMemory(newWords);
// Resize // Resize
m_builder.CreateStore(sizeRequired, sizePtr); m_builder.CreateStore(sizeRequired, sizePtr);
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); auto newData = m_builder.CreateCall2(resize, rt, sizePtr, "newData");
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
m_builder.CreateStore(newData, dataPtr); m_builder.CreateStore(newData, dataPtr);
m_builder.CreateBr(returnBB); m_builder.CreateBr(returnBB);
@ -98,6 +90,7 @@ llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
// BB "Return" // BB "Return"
m_builder.SetInsertPoint(returnBB); m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
}
return func; return func;
} }
@ -143,21 +136,45 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMet
return func; return func;
} }
llvm::Function* Memory::getLoadWordFunc()
{
auto& func = m_loadWord;
if (!func)
func = createFunc(false, Type::Word, m_gasMeter);
return func;
}
llvm::Function* Memory::getStoreWordFunc()
{
auto& func = m_storeWord;
if (!func)
func = createFunc(true, Type::Word, m_gasMeter);
return func;
}
llvm::Function* Memory::getStoreByteFunc()
{
auto& func = m_storeByte;
if (!func)
func = createFunc(true, Type::Byte, m_gasMeter);
return func;
}
llvm::Value* Memory::loadWord(llvm::Value* _addr) llvm::Value* Memory::loadWord(llvm::Value* _addr)
{ {
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); return createCall(getLoadWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr});
} }
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
{ {
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); createCall(getStoreWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr, _word});
} }
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
{ {
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); createCall(getStoreByteFunc(), {getRuntimeManager().getRuntimePtr(), _addr, byte});
} }
llvm::Value* Memory::getData() llvm::Value* Memory::getData()
@ -182,7 +199,12 @@ llvm::Value* Memory::getBytePtr(llvm::Value* _index)
void Memory::require(llvm::Value* _offset, llvm::Value* _size) void Memory::require(llvm::Value* _offset, llvm::Value* _size)
{ {
createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size}); if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_size))
{
if (!constant->getValue())
return;
}
createCall(getRequireFunc(), {getRuntimeManager().getRuntimePtr(), _offset, _size});
} }
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
@ -192,7 +214,8 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value*
// Additional copy cost // Additional copy cost
// TODO: This round ups to 32 happens in many places // TODO: This round ups to 32 happens in many places
auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Gas);
auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32));
m_gasMeter.countCopy(copyWords); m_gasMeter.countCopy(copyWords);
// Algorithm: // Algorithm:
@ -205,14 +228,12 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value*
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0) // bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize);
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision); auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size);
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size);
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize);
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize); auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes);
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner);
auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner);
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); auto src = m_builder.CreateGEP(_srcPtr, idx64, "src");
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64 auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64

15
evmjit/libevmjit/Memory.h

@ -31,13 +31,16 @@ private:
GasMeter& m_gasMeter; GasMeter& m_gasMeter;
llvm::Function* createFunc(bool _isStore, llvm::Type* _type, GasMeter& _gasMeter); llvm::Function* createFunc(bool _isStore, llvm::Type* _type, GasMeter& _gasMeter);
llvm::Function* createRequireFunc(GasMeter& _gasMeter);
llvm::Function* m_resize; llvm::Function* getRequireFunc();
llvm::Function* m_require; llvm::Function* getLoadWordFunc();
llvm::Function* m_loadWord; llvm::Function* getStoreWordFunc();
llvm::Function* m_storeWord; llvm::Function* getStoreByteFunc();
llvm::Function* m_storeByte;
llvm::Function* m_require = nullptr;
llvm::Function* m_loadWord = nullptr;
llvm::Function* m_storeWord = nullptr;
llvm::Function* m_storeByte = nullptr;
}; };
} }

8
evmjit/libevmjit/Runtime.cpp

@ -1,9 +1,6 @@
#include "Runtime.h" #include "Runtime.h"
#include <llvm/IR/GlobalVariable.h> #include <cassert>
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
namespace dev namespace dev
{ {
@ -14,8 +11,7 @@ namespace jit
Runtime::Runtime(RuntimeData* _data, Env* _env) : Runtime::Runtime(RuntimeData* _data, Env* _env) :
m_data(*_data), m_data(*_data),
m_env(*_env), m_env(*_env)
m_currJmpBuf(m_jmpBuf)
{} {}
bytes_ref Runtime::getReturnData() const bytes_ref Runtime::getReturnData() const

8
evmjit/libevmjit/Runtime.h

@ -1,7 +1,5 @@
#pragma once #pragma once
#include <csetjmp>
#include "RuntimeData.h" #include "RuntimeData.h"
namespace dev namespace dev
@ -13,7 +11,6 @@ namespace jit
using StackImpl = std::vector<i256>; using StackImpl = std::vector<i256>;
using MemoryImpl = bytes; using MemoryImpl = bytes;
using jmp_buf_ref = decltype(&std::jmp_buf{}[0]);
class Runtime class Runtime
{ {
@ -25,18 +22,15 @@ public:
StackImpl& getStack() { return m_stack; } StackImpl& getStack() { return m_stack; }
MemoryImpl& getMemory() { return m_memory; } MemoryImpl& getMemory() { return m_memory; }
Env* getEnvPtr() { return &m_env; }
bytes_ref getReturnData() const; bytes_ref getReturnData() const;
jmp_buf_ref getJmpBuf() { return m_jmpBuf; }
private: private:
RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract. RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract. Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract.
jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract. void* m_currJmpBuf = nullptr; ///< Pointer to jump buffer. Expected by compiled contract.
byte* m_memoryData = nullptr; byte* m_memoryData = nullptr;
i256 m_memorySize; i256 m_memorySize;
std::jmp_buf m_jmpBuf;
StackImpl m_stack; StackImpl m_stack;
MemoryImpl m_memory; MemoryImpl m_memory;
}; };

4
evmjit/libevmjit/RuntimeData.h

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "Utils.h" #include "Common.h"
namespace dev namespace dev
{ {
@ -50,6 +49,7 @@ struct RuntimeData
int64_t timestamp = 0; int64_t timestamp = 0;
byte const* code = nullptr; byte const* code = nullptr;
uint64_t codeSize = 0; uint64_t codeSize = 0;
i256 codeHash;
}; };
/// VM Environment (ExtVM) opaque type /// VM Environment (ExtVM) opaque type

54
evmjit/libevmjit/RuntimeManager.cpp

@ -1,12 +1,8 @@
#include "RuntimeManager.h" #include "RuntimeManager.h"
#include <llvm/IR/GlobalVariable.h> #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeData.h"
#include "Instruction.h"
namespace dev namespace dev
{ {
@ -87,9 +83,17 @@ llvm::Twine getName(RuntimeData::Index _index)
} }
} }
RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder): CompilerHelper(_builder) RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd):
CompilerHelper(_builder),
m_jmpBuf(_jmpBuf),
m_codeBegin(_codeBegin),
m_codeEnd(_codeEnd)
{ {
m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::longjmp); m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
// save jmpBuf to be used in helper functions
auto ptr = m_builder.CreateStructGEP(getRuntimePtr(), 2);
m_builder.CreateStore(m_jmpBuf, ptr, "jmpBufExt");
// Unpack data // Unpack data
auto rtPtr = getRuntimePtr(); auto rtPtr = getRuntimePtr();
@ -161,9 +165,10 @@ void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress)
set(RuntimeData::SuicideDestAddress, _balanceAddress); set(RuntimeData::SuicideDestAddress, _balanceAddress);
} }
void RuntimeManager::raiseException(ReturnCode _returnCode) void RuntimeManager::abort(llvm::Value* _jmpBuf)
{ {
m_builder.CreateCall2(m_longjmp, getJmpBuf(), Constant::get(_returnCode)); auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
createCall(longjmp, {_jmpBuf});
} }
llvm::Value* RuntimeManager::get(Instruction _inst) llvm::Value* RuntimeManager::get(Instruction _inst)
@ -191,14 +196,14 @@ llvm::Value* RuntimeManager::getCallData()
llvm::Value* RuntimeManager::getCode() llvm::Value* RuntimeManager::getCode()
{ {
return get(RuntimeData::Code); // OPT Check what is faster
//return get(RuntimeData::Code);
return m_builder.CreateGlobalStringPtr({reinterpret_cast<char const*>(m_codeBegin), static_cast<size_t>(m_codeEnd - m_codeBegin)}, "code");
} }
llvm::Value* RuntimeManager::getCodeSize() llvm::Value* RuntimeManager::getCodeSize()
{ {
auto value = get(RuntimeData::CodeSize); return Constant::get(m_codeEnd - m_codeBegin);
assert(value->getType() == Type::Size);
return getBuilder().CreateZExt(value, Type::Word);
} }
llvm::Value* RuntimeManager::getCallDataSize() llvm::Value* RuntimeManager::getCallDataSize()
@ -208,23 +213,28 @@ llvm::Value* RuntimeManager::getCallDataSize()
return getBuilder().CreateZExt(value, Type::Word); return getBuilder().CreateZExt(value, Type::Word);
} }
llvm::Value* RuntimeManager::getJmpBuf() llvm::Value* RuntimeManager::getJmpBufExt()
{ {
auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2, "jmpbufPtr"); auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2);
return getBuilder().CreateLoad(ptr, "jmpbuf"); return getBuilder().CreateLoad(ptr, "jmpBufExt");
} }
llvm::Value* RuntimeManager::getGas() llvm::Value* RuntimeManager::getGas()
{ {
auto value = get(RuntimeData::Gas); auto gas = get(RuntimeData::Gas);
assert(value->getType() == Type::Size); assert(gas->getType() == Type::Gas);
return getBuilder().CreateZExt(value, Type::Word); return gas;
}
llvm::Value* RuntimeManager::getGasPtr()
{
return getPtr(RuntimeData::Gas);
} }
void RuntimeManager::setGas(llvm::Value* _gas) void RuntimeManager::setGas(llvm::Value* _gas)
{ {
auto newGas = getBuilder().CreateTrunc(_gas, Type::Size); assert(_gas->getType() == Type::Gas);
set(RuntimeData::Gas, newGas); set(RuntimeData::Gas, _gas);
} }
} }

17
evmjit/libevmjit/RuntimeManager.h

@ -15,25 +15,28 @@ namespace jit
class RuntimeManager: public CompilerHelper class RuntimeManager: public CompilerHelper
{ {
public: public:
RuntimeManager(llvm::IRBuilder<>& _builder); RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd);
llvm::Value* getRuntimePtr(); llvm::Value* getRuntimePtr();
llvm::Value* getDataPtr(); llvm::Value* getDataPtr();
llvm::Value* getEnvPtr(); // TODO: Can we make it const? llvm::Value* getEnvPtr();
llvm::Value* get(RuntimeData::Index _index); llvm::Value* get(RuntimeData::Index _index);
llvm::Value* get(Instruction _inst); llvm::Value* get(Instruction _inst);
llvm::Value* getGas(); // TODO: Remove llvm::Value* getGas();
llvm::Value* getGasPtr();
llvm::Value* getCallData(); llvm::Value* getCallData();
llvm::Value* getCode(); llvm::Value* getCode();
llvm::Value* getCodeSize(); llvm::Value* getCodeSize();
llvm::Value* getCallDataSize(); llvm::Value* getCallDataSize();
llvm::Value* getJmpBuf() { return m_jmpBuf; }
void setGas(llvm::Value* _gas); void setGas(llvm::Value* _gas);
void registerReturnData(llvm::Value* _index, llvm::Value* _size); void registerReturnData(llvm::Value* _index, llvm::Value* _size);
void registerSuicide(llvm::Value* _balanceAddress); void registerSuicide(llvm::Value* _balanceAddress);
void raiseException(ReturnCode _returnCode); void abort(llvm::Value* _jmpBuf);
void abort() { abort(getJmpBufExt()); }
static llvm::StructType* getRuntimeType(); static llvm::StructType* getRuntimeType();
static llvm::StructType* getRuntimeDataType(); static llvm::StructType* getRuntimeDataType();
@ -41,11 +44,15 @@ public:
private: private:
llvm::Value* getPtr(RuntimeData::Index _index); llvm::Value* getPtr(RuntimeData::Index _index);
void set(RuntimeData::Index _index, llvm::Value* _value); void set(RuntimeData::Index _index, llvm::Value* _value);
llvm::Value* getJmpBuf(); llvm::Value* getJmpBufExt();
llvm::Function* m_longjmp = nullptr; llvm::Function* m_longjmp = nullptr;
llvm::Value* const m_jmpBuf;
llvm::Value* m_dataPtr = nullptr; llvm::Value* m_dataPtr = nullptr;
llvm::Value* m_envPtr = nullptr; llvm::Value* m_envPtr = nullptr;
code_iterator m_codeBegin = {};
code_iterator m_codeEnd = {};
}; };
} }

111
evmjit/libevmjit/Stack.cpp

@ -1,10 +1,11 @@
#include "Stack.h" #include "Stack.h"
#include "RuntimeManager.h"
#include "Runtime.h"
#include "Type.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Function.h> #include <llvm/IR/Function.h>
#include <llvm/IR/TypeBuilder.h> #include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Runtime.h"
namespace dev namespace dev
{ {
@ -27,18 +28,87 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
llvm::Type* pushArgTypes[] = {Type::RuntimePtr, Type::WordPtr}; llvm::Type* pushArgTypes[] = {Type::RuntimePtr, Type::WordPtr};
m_push = Function::Create(FunctionType::get(Type::Void, pushArgTypes, false), Linkage::ExternalLinkage, "stack_push", module); m_push = Function::Create(FunctionType::get(Type::Void, pushArgTypes, false), Linkage::ExternalLinkage, "stack_push", module);
llvm::Type* popArgTypes[] = {Type::RuntimePtr, Type::Size};
m_pop = Function::Create(FunctionType::get(Type::Void, popArgTypes, false), Linkage::ExternalLinkage, "stack_pop", module);
llvm::Type* getSetArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr}; llvm::Type* getSetArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr};
m_get = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_get", module);
m_set = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_set", module); m_set = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_set", module);
} }
llvm::Function* Stack::getPopFunc()
{
auto& func = m_pop;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.pop", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size};
auto extPopFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_pop", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto jmpBuf = index->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
m_builder.SetInsertPoint(entryBB);
auto ok = createCall(extPopFunc, {rt, index});
m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight
m_builder.SetInsertPoint(underflowBB);
m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getGetFunc()
{
auto& func = m_get;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr};
func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::ExternalLinkage, "stack.get", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size};
auto extGetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_get", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto jmpBuf = index->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
m_builder.SetInsertPoint(entryBB);
auto valuePtr = createCall(extGetFunc, {rt, index});
auto ok = m_builder.CreateICmpNE(valuePtr, llvm::ConstantPointerNull::get(Type::WordPtr));
m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight
m_builder.SetInsertPoint(underflowBB);
m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRet(valuePtr);
}
return func;
}
llvm::Value* Stack::get(size_t _index) llvm::Value* Stack::get(size_t _index)
{ {
m_builder.CreateCall3(m_get, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _index, false), m_arg); auto valuePtr = createCall(getGetFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_index), m_runtimeManager.getJmpBuf()});
return m_builder.CreateLoad(m_arg); return m_builder.CreateLoad(valuePtr);
} }
void Stack::set(size_t _index, llvm::Value* _value) void Stack::set(size_t _index, llvm::Value* _value)
@ -49,7 +119,7 @@ void Stack::set(size_t _index, llvm::Value* _value)
void Stack::pop(size_t _count) void Stack::pop(size_t _count)
{ {
m_builder.CreateCall2(m_pop, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _count, false)); createCall(getPopFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_count), m_runtimeManager.getJmpBuf()});
} }
void Stack::push(llvm::Value* _value) void Stack::push(llvm::Value* _value)
@ -69,13 +139,14 @@ extern "C"
{ {
using namespace dev::eth::jit; using namespace dev::eth::jit;
EXPORT void stack_pop(Runtime* _rt, uint64_t _count) EXPORT bool stack_pop(Runtime* _rt, uint64_t _count)
{ {
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
if (stack.size() < _count) if (stack.size() < _count)
longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall)); return false;
stack.erase(stack.end() - _count, stack.end()); stack.erase(stack.end() - _count, stack.end());
return true;
} }
EXPORT void stack_push(Runtime* _rt, i256 const* _word) EXPORT void stack_push(Runtime* _rt, i256 const* _word)
@ -87,22 +158,18 @@ extern "C"
Stack::maxStackSize = stack.size(); Stack::maxStackSize = stack.size();
} }
EXPORT void stack_get(Runtime* _rt, uint64_t _index, i256* o_ret) EXPORT i256* stack_get(Runtime* _rt, uint64_t _index)
{ {
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code return _index < stack.size() ? &*(stack.rbegin() + _index) : nullptr;
if (stack.size() <= _index)
longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall));
*o_ret = *(stack.rbegin() + _index);
} }
EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word) EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word)
{ {
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code assert(_index < stack.size());
if (stack.size() <= _index) if (_index >= stack.size())
longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall)); return;
*(stack.rbegin() + _index) = *_word; *(stack.rbegin() + _index) = *_word;
} }

9
evmjit/libevmjit/Stack.h

@ -2,8 +2,6 @@
#include "CompilerHelper.h" #include "CompilerHelper.h"
#include <llvm/IR/Module.h>
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -25,11 +23,14 @@ public:
static size_t maxStackSize; static size_t maxStackSize;
private: private:
llvm::Function* getPopFunc();
llvm::Function* getGetFunc();
RuntimeManager& m_runtimeManager; RuntimeManager& m_runtimeManager;
llvm::Function* m_pop = nullptr;
llvm::Function* m_push; llvm::Function* m_push;
llvm::Function* m_pop; llvm::Function* m_get = nullptr;
llvm::Function* m_get;
llvm::Function* m_set; llvm::Function* m_set;
llvm::Value* m_arg; llvm::Value* m_arg;

11
evmjit/libevmjit/Type.cpp

@ -1,8 +1,4 @@
#include "Type.h" #include "Type.h"
#include <llvm/IR/DerivedTypes.h>
#include "RuntimeManager.h" #include "RuntimeManager.h"
namespace dev namespace dev
@ -17,6 +13,8 @@ llvm::PointerType* Type::WordPtr;
llvm::IntegerType* Type::lowPrecision; llvm::IntegerType* Type::lowPrecision;
llvm::IntegerType* Type::Bool; llvm::IntegerType* Type::Bool;
llvm::IntegerType* Type::Size; llvm::IntegerType* Type::Size;
llvm::IntegerType* Type::Gas;
llvm::PointerType* Type::GasPtr;
llvm::IntegerType* Type::Byte; llvm::IntegerType* Type::Byte;
llvm::PointerType* Type::BytePtr; llvm::PointerType* Type::BytePtr;
llvm::Type* Type::Void; llvm::Type* Type::Void;
@ -24,6 +22,7 @@ llvm::IntegerType* Type::MainReturn;
llvm::PointerType* Type::EnvPtr; llvm::PointerType* Type::EnvPtr;
llvm::PointerType* Type::RuntimeDataPtr; llvm::PointerType* Type::RuntimeDataPtr;
llvm::PointerType* Type::RuntimePtr; llvm::PointerType* Type::RuntimePtr;
llvm::ConstantInt* Constant::gasMax;
void Type::init(llvm::LLVMContext& _context) void Type::init(llvm::LLVMContext& _context)
{ {
@ -35,6 +34,8 @@ void Type::init(llvm::LLVMContext& _context)
// TODO: Size should be architecture-dependent // TODO: Size should be architecture-dependent
Bool = llvm::Type::getInt1Ty(_context); Bool = llvm::Type::getInt1Ty(_context);
Size = llvm::Type::getInt64Ty(_context); Size = llvm::Type::getInt64Ty(_context);
Gas = Size;
GasPtr = Gas->getPointerTo();
Byte = llvm::Type::getInt8Ty(_context); Byte = llvm::Type::getInt8Ty(_context);
BytePtr = Byte->getPointerTo(); BytePtr = Byte->getPointerTo();
Void = llvm::Type::getVoidTy(_context); Void = llvm::Type::getVoidTy(_context);
@ -43,6 +44,8 @@ void Type::init(llvm::LLVMContext& _context)
EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo(); EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo();
RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo(); RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo();
RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo();
Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits<int64_t>::max());
} }
} }

8
evmjit/libevmjit/Type.h

@ -1,8 +1,10 @@
#pragma once #pragma once
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Type.h> #include <llvm/IR/Type.h>
#include <llvm/IR/Constants.h> #include <llvm/IR/Constants.h>
#include "preprocessor/llvm_includes_end.h"
#include "Common.h" #include "Common.h"
namespace dev namespace dev
@ -23,6 +25,8 @@ struct Type
static llvm::IntegerType* Bool; static llvm::IntegerType* Bool;
static llvm::IntegerType* Size; static llvm::IntegerType* Size;
static llvm::IntegerType* Gas;
static llvm::PointerType* GasPtr;
static llvm::IntegerType* Byte; static llvm::IntegerType* Byte;
static llvm::PointerType* BytePtr; static llvm::PointerType* BytePtr;
@ -41,6 +45,8 @@ struct Type
struct Constant struct Constant
{ {
static llvm::ConstantInt* gasMax;
/// Returns word-size constant /// Returns word-size constant
static llvm::ConstantInt* get(int64_t _n); static llvm::ConstantInt* get(int64_t _n);
static llvm::ConstantInt* get(llvm::APInt const& _n); static llvm::ConstantInt* get(llvm::APInt const& _n);

1
evmjit/libevmjit/Utils.cpp

@ -1,4 +1,3 @@
#include "Utils.h" #include "Utils.h"
namespace dev namespace dev

3
evmjit/libevmjit/Utils.h

@ -16,6 +16,9 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } };
//#define clog(CHANNEL) std::cerr //#define clog(CHANNEL) std::cerr
#define clog(CHANNEL) std::ostream(nullptr) #define clog(CHANNEL) std::ostream(nullptr)
// The same as assert, but expression is always evaluated and result returned
#define CHECK(expr) (assert(expr), expr)
} }
} }
} }

11
evmjit/libevmjit/interface.cpp

@ -12,6 +12,7 @@ using namespace dev::eth::jit;
EXPORT void* evmjit_create() noexcept EXPORT void* evmjit_create() noexcept
{ {
// TODO: Make sure ExecutionEngine constructor does not throw
return new(std::nothrow) ExecutionEngine; return new(std::nothrow) ExecutionEngine;
} }
@ -22,14 +23,12 @@ EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept
EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept
{ {
if (!_engine || !_data)
return static_cast<int>(ReturnCode::UnexpectedException);
try try
{ {
auto codePtr = _data->code; auto returnCode = _engine->run(_data, _env);
auto codeSize = _data->codeSize;
bytes bytecode;
bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize);
auto returnCode = _engine->run(bytecode, _data, _env);
return static_cast<int>(returnCode); return static_cast<int>(returnCode);
} }
catch(...) catch(...)

7
evmjit/libevmjit/preprocessor/llvm_includes_end.h

@ -0,0 +1,7 @@
#if defined(_MSC_VER)
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#else
#pragma GCC diagnostic pop
#endif

12
evmjit/libevmjit/preprocessor/llvm_includes_start.h

@ -0,0 +1,12 @@
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4267 4244 4800)
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wconversion"
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wconversion"
#endif

6
exp/main.cpp

@ -26,6 +26,7 @@
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libdevcrypto/TrieDB.h>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libp2p/All.h> #include <libp2p/All.h>
#include <libethereum/DownloadMan.h> #include <libethereum/DownloadMan.h>
@ -33,11 +34,13 @@
#include <liblll/All.h> #include <liblll/All.h>
#include <libwhisper/WhisperPeer.h> #include <libwhisper/WhisperPeer.h>
#include <libwhisper/WhisperHost.h> #include <libwhisper/WhisperHost.h>
#include <test/JsonSpiritHeaders.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::p2p; using namespace dev::p2p;
using namespace dev::shh; using namespace dev::shh;
namespace js = json_spirit;
#if 0 #if 0
int main() int main()
@ -98,8 +101,7 @@ int main()
#else #else
int main() int main()
{ {
cnote << KeyPair(Secret("0000000000000000000000000000000000000000000000000000000000000000")).address(); return 0;
cnote << KeyPair(Secret("1111111111111111111111111111111111111111111111111111111111111111")).address();
} }
#endif #endif

2
extdep/CMakeLists.txt

@ -7,7 +7,7 @@ include(eth_download.cmake)
# all dependencies will be installed into this directory, separated by platform # all dependencies will be installed into this directory, separated by platform
string(TOLOWER ${CMAKE_SYSTEM_NAME} _system_name) string(TOLOWER ${CMAKE_SYSTEM_NAME} _system_name)
set(ETH_DEPENDENCY_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/install/${_system_name}") set(ETH_DEPENDENCY_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/install/${_system_name}")
set(ETH_DEPENDENCY_SERVER "http://build.ethdev.com/builds/${_system_name}-precompiled") set(ETH_DEPENDENCY_SERVER "https://build.ethdev.com/builds/${_system_name}-precompiled")
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/lib) file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/lib)
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/include) file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/include)
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/bin) file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/bin)

2
libdevcore/Common.cpp

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

2
libdevcore/FixedHash.h

@ -80,7 +80,7 @@ public:
operator Arith() const { return fromBigEndian<Arith>(m_data); } operator Arith() const { return fromBigEndian<Arith>(m_data); }
/// @returns true iff this is the empty hash. /// @returns true iff this is the empty hash.
operator bool() const { return ((Arith)*this) != 0; } explicit operator bool() const { return ((Arith)*this) != 0; }
// The obvious comparison operators. // The obvious comparison operators.
bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; }

4
libdevcore/RLP.h

@ -242,7 +242,9 @@ public:
AllowNonCanon = 1, AllowNonCanon = 1,
ThrowOnFail = 4, ThrowOnFail = 4,
FailIfTooBig = 8, FailIfTooBig = 8,
FailIfTooSmall = 16,
Strict = ThrowOnFail | FailIfTooBig, Strict = ThrowOnFail | FailIfTooBig,
VeryStrict = ThrowOnFail | FailIfTooBig | FailIfTooSmall,
LaisezFaire = AllowNonCanon LaisezFaire = AllowNonCanon
}; };
@ -269,7 +271,7 @@ public:
template <class _N> _N toHash(int _flags = Strict) const template <class _N> _N toHash(int _flags = Strict) const
{ {
if (!isData() || (length() > _N::size && (_flags & FailIfTooBig))) if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)) || (length() < _N::size && (_flags & FailIfTooSmall)))
if (_flags & ThrowOnFail) if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast()); BOOST_THROW_EXCEPTION(BadCast());
else else

2
libdevcore/Worker.h

@ -64,7 +64,7 @@ protected:
private: private:
std::string m_name; std::string m_name;
unsigned m_idleWaitMs; unsigned m_idleWaitMs = 0;
mutable Mutex x_work; ///< Lock for the network existance. mutable Mutex x_work; ///< Lock for the network existance.
std::unique_ptr<std::thread> m_work; ///< The network thread. std::unique_ptr<std::thread> m_work; ///< The network thread.

7
libdevcore/vector_ref.h

@ -19,7 +19,7 @@ public:
vector_ref(): m_data(nullptr), m_count(0) {} vector_ref(): m_data(nullptr), m_count(0) {}
vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {}
vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const*, std::string*>::type _data): m_data((_T*)_data->data()), m_count(_data->size() / sizeof(_T)) {} vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const*, std::string*>::type _data): m_data(reinterpret_cast<_T*>(_data->data())), m_count(_data->size() / sizeof(_T)) {}
vector_ref(typename std::conditional<std::is_const<_T>::value, std::vector<typename std::remove_const<_T>::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {} vector_ref(typename std::conditional<std::is_const<_T>::value, std::vector<typename std::remove_const<_T>::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {}
vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const&, std::string&>::type _data): m_data((_T*)_data.data()), m_count(_data.size() / sizeof(_T)) {} vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const&, std::string&>::type _data): m_data((_T*)_data.data()), m_count(_data.size() / sizeof(_T)) {}
#ifdef STORAGE_LEVELDB_INCLUDE_DB_H_ #ifdef STORAGE_LEVELDB_INCLUDE_DB_H_
@ -29,9 +29,10 @@ public:
bool contentsEqual(std::vector<mutable_value_type> const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); } bool contentsEqual(std::vector<mutable_value_type> const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); }
std::vector<mutable_value_type> toVector() const { return std::vector<mutable_value_type>(m_data, m_data + m_count); } std::vector<mutable_value_type> toVector() const { return std::vector<mutable_value_type>(m_data, m_data + m_count); }
std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>((unsigned char const*)m_data, (unsigned char const*)m_data + m_count * sizeof(_T)); } std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>(reinterpret_cast<unsigned char const*>(m_data), reinterpret_cast<unsigned char const*>(m_data) + m_count * sizeof(_T)); }
std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); } std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); }
template <class _T2> operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>((_T2*)m_data, m_count * sizeof(_T) / sizeof(_T2)); } template <class _T2> explicit operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>(reinterpret_cast<_T2*>(m_data), m_count * sizeof(_T) / sizeof(_T2)); }
operator vector_ref<_T const>() const { return vector_ref<_T const>(m_data, m_count); }
_T* data() const { return m_data; } _T* data() const { return m_data; }
size_t count() const { return m_count; } size_t count() const { return m_count; }

2
libdevcrypto/Common.cpp

@ -44,6 +44,8 @@ bool dev::SignatureStruct::isValid() const
return true; return true;
} }
Address dev::ZeroAddress = Address();
Public dev::toPublic(Secret const& _secret) Public dev::toPublic(Secret const& _secret)
{ {
Public p; Public p;

3
libdevcrypto/Common.h

@ -62,6 +62,9 @@ struct SignatureStruct
/// @NOTE This is not endian-specific; it's just a bunch of bytes. /// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Address = h160; using Address = h160;
/// The zero address.
extern Address ZeroAddress;
/// A vector of Ethereum addresses. /// A vector of Ethereum addresses.
using Addresses = h160s; using Addresses = h160s;

2
libdevcrypto/CryptoPP.cpp

@ -134,7 +134,7 @@ bool Secp256k1::verify(Signature const& _signature, bytesConstRef _message)
bool Secp256k1::verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed) bool Secp256k1::verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed)
{ {
// todo: verify w/o recovery (if faster) // todo: verify w/o recovery (if faster)
return _p == _hashed ? recover(_sig, _message) : recover(_sig, sha3(_message).ref()); return (bool)_p == _hashed ? (bool)recover(_sig, _message) : (bool)recover(_sig, sha3(_message).ref());
} }
Public Secp256k1::recover(Signature _signature, bytesConstRef _message) Public Secp256k1::recover(Signature _signature, bytesConstRef _message)

6
libdevcrypto/TrieDB.h

@ -161,7 +161,7 @@ public:
std::string at(bytesConstRef _key) const; std::string at(bytesConstRef _key) const;
void insert(bytesConstRef _key, bytesConstRef _value); void insert(bytesConstRef _key, bytesConstRef _value);
void remove(bytesConstRef _key); void remove(bytesConstRef _key);
void contains(bytesConstRef _key) { return !at(_key).empty(); } bool contains(bytesConstRef _key) { return !at(_key).empty(); }
class iterator class iterator
{ {
@ -809,7 +809,10 @@ template <class DB> bytes GenericTrieDB<DB>::deleteAt(RLP const& _orig, NibbleSl
// exactly our node - return null. // exactly our node - return null.
if (k == _k && isLeaf(_orig)) if (k == _k && isLeaf(_orig))
{
killNode(_orig);
return RLPNull; return RLPNull;
}
// partial key is our key - move down. // partial key is our key - move down.
if (_k.contains(k)) if (_k.contains(k))
@ -917,7 +920,6 @@ template <class DB> bytes GenericTrieDB<DB>::place(RLP const& _orig, NibbleSlice
tdebug << "place " << _orig << _k; tdebug << "place " << _orig << _k;
#endif #endif
killNode(_orig); killNode(_orig);
if (_orig.isEmpty()) if (_orig.isEmpty())
return (RLPStream(2) << hexPrefixEncode(_k, true) << _s).out(); return (RLPStream(2) << hexPrefixEncode(_k, true) << _s).out();

2
libethcore/CommonEth.cpp

@ -32,7 +32,7 @@ namespace dev
namespace eth namespace eth
{ {
const unsigned c_protocolVersion = 53; const unsigned c_protocolVersion = 54;
const unsigned c_databaseVersion = 5; const unsigned c_databaseVersion = 5;
vector<pair<u256, string>> const& units() vector<pair<u256, string>> const& units()

2
libethcore/ProofOfWork.h

@ -82,7 +82,7 @@ template <class Evaluator>
std::pair<MineInfo, h256> ProofOfWorkEngine<Evaluator>::mine(h256 const& _root, u256 const& _difficulty, unsigned _msTimeout, bool _continue, bool _turbo) std::pair<MineInfo, h256> ProofOfWorkEngine<Evaluator>::mine(h256 const& _root, u256 const& _difficulty, unsigned _msTimeout, bool _continue, bool _turbo)
{ {
std::pair<MineInfo, h256> ret; std::pair<MineInfo, h256> ret;
static std::mt19937_64 s_eng((time(0) + (unsigned)m_last)); static std::mt19937_64 s_eng((time(0) + *reinterpret_cast<unsigned*>(m_last.data())));
u256 s = (m_last = h256::random(s_eng)); u256 s = (m_last = h256::random(s_eng));
bigint d = (bigint(1) << 256) / _difficulty; bigint d = (bigint(1) << 256) / _difficulty;

3
libethereum/BlockChain.cpp

@ -22,6 +22,7 @@
#include "BlockChain.h" #include "BlockChain.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <test/JsonSpiritHeaders.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
@ -29,11 +30,13 @@
#include <libethcore/ProofOfWork.h> #include <libethcore/ProofOfWork.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include "GenesisInfo.h"
#include "State.h" #include "State.h"
#include "Defaults.h" #include "Defaults.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
namespace js = json_spirit;
#define ETH_CATCH 1 #define ETH_CATCH 1

3
libethereum/BlockQueue.cpp

@ -59,7 +59,6 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
catch (Exception const& _e) catch (Exception const& _e)
{ {
cwarn << "Ignoring malformed block: " << diagnostic_information(_e); cwarn << "Ignoring malformed block: " << diagnostic_information(_e);
return false;
return ImportResult::Malformed; return ImportResult::Malformed;
} }
#endif #endif
@ -128,7 +127,7 @@ void BlockQueue::drain(std::vector<bytes>& o_out)
void BlockQueue::noteReadyWithoutWriteGuard(h256 _good) void BlockQueue::noteReadyWithoutWriteGuard(h256 _good)
{ {
list<h256> goodQueue(1, _good); list<h256> goodQueue(1, _good);
while (goodQueue.size()) while (!goodQueue.empty())
{ {
auto r = m_unknown.equal_range(goodQueue.front()); auto r = m_unknown.equal_range(goodQueue.front());
goodQueue.pop_front(); goodQueue.pop_front();

32
libethereum/CanonBlockChain.cpp

@ -21,6 +21,7 @@
#include "CanonBlockChain.h" #include "CanonBlockChain.h"
#include <test/JsonSpiritHeaders.h>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
@ -29,11 +30,13 @@
#include <libethcore/ProofOfWork.h> #include <libethcore/ProofOfWork.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include "GenesisInfo.h"
#include "State.h" #include "State.h"
#include "Defaults.h" #include "Defaults.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
namespace js = json_spirit;
#define ETH_CATCH 1 #define ETH_CATCH 1
@ -42,18 +45,23 @@ std::map<Address, Account> const& dev::eth::genesisState()
static std::map<Address, Account> s_ret; static std::map<Address, Account> s_ret;
if (s_ret.empty()) if (s_ret.empty())
{ {
// Initialise. js::mValue val;
for (auto i: vector<string>({ json_spirit::read_string(c_genesisInfo, val);
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6", for (auto account: val.get_obj())
"e6716f9544a56c530d868e4bfbacb172315bdead", {
"b9c015918bdaba24b4ff057a92a3873d6eb201be", u256 balance;
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", if (account.second.get_obj().count("wei"))
"2ef47100e0787b915105fd5e3f4ff6752079d5cb", balance = u256(account.second.get_obj()["wei"].get_str());
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826", else
"6c386a4b26f73c802f34673f7248bb118f97424a", balance = u256(account.second.get_obj()["finney"].get_str()) * finney;
"e4157b34ea9615cfbde6b4fda419828124b70c78" if (account.second.get_obj().count("code"))
})) {
s_ret[Address(fromHex(i))] = Account(u256(1) << 200, Account::NormalCreation); s_ret[Address(fromHex(account.first))] = Account(balance, Account::ContractConception);
s_ret[Address(fromHex(account.first))].setCode(fromHex(account.second.get_obj()["code"].get_str()));
}
else
s_ret[Address(fromHex(account.first))] = Account(balance, Account::NormalCreation);
}
} }
return s_ret; return s_ret;
} }

2
libethereum/Client.h

@ -127,7 +127,7 @@ template <class T> struct ABIDeserialiser {};
template <unsigned N> struct ABIDeserialiser<FixedHash<N>> { static FixedHash<N> deserialise(bytesConstRef& io_t) { static_assert(N <= 32, "Parameter sizes must be at most 32 bytes."); FixedHash<N> ret; io_t.cropped(32 - N, N).populate(ret.ref()); io_t = io_t.cropped(32); return ret; } }; template <unsigned N> struct ABIDeserialiser<FixedHash<N>> { static FixedHash<N> deserialise(bytesConstRef& io_t) { static_assert(N <= 32, "Parameter sizes must be at most 32 bytes."); FixedHash<N> ret; io_t.cropped(32 - N, N).populate(ret.ref()); io_t = io_t.cropped(32); return ret; } };
template <> struct ABIDeserialiser<u256> { static u256 deserialise(bytesConstRef& io_t) { u256 ret = fromBigEndian<u256>(io_t.cropped(0, 32)); io_t = io_t.cropped(32); return ret; } }; template <> struct ABIDeserialiser<u256> { static u256 deserialise(bytesConstRef& io_t) { u256 ret = fromBigEndian<u256>(io_t.cropped(0, 32)); io_t = io_t.cropped(32); return ret; } };
template <> struct ABIDeserialiser<u160> { static u160 deserialise(bytesConstRef& io_t) { u160 ret = fromBigEndian<u160>(io_t.cropped(12, 20)); io_t = io_t.cropped(32); return ret; } }; template <> struct ABIDeserialiser<u160> { static u160 deserialise(bytesConstRef& io_t) { u160 ret = fromBigEndian<u160>(io_t.cropped(12, 20)); io_t = io_t.cropped(32); return ret; } };
template <> struct ABIDeserialiser<string32> { static string32 deserialise(bytesConstRef& io_t) { string32 ret; io_t.cropped(0, 32).populate(vector_ref<char>(ret.data(), 32)); io_t = io_t.cropped(32); return ret; } }; template <> struct ABIDeserialiser<string32> { static string32 deserialise(bytesConstRef& io_t) { string32 ret; io_t.cropped(0, 32).populate(bytesRef((byte*)ret.data(), 32)); io_t = io_t.cropped(32); return ret; } };
template <class T> T abiOut(bytes const& _data) template <class T> T abiOut(bytes const& _data)
{ {

2
libethereum/EthereumHost.h

@ -94,7 +94,7 @@ private:
h256Set neededBlocks(h256Set const& _exclude); h256Set neededBlocks(h256Set const& _exclude);
/// Check to see if the network peer-state initialisation has happened. /// Check to see if the network peer-state initialisation has happened.
bool isInitialised() const { return m_latestBlockSent; } bool isInitialised() const { return (bool)m_latestBlockSent; }
/// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first. /// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first.
bool ensureInitialised(); bool ensureInitialised();

40
libethereum/GenesisInfo.cpp

@ -0,0 +1,40 @@
/*
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 GenesisInfo.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "GenesisInfo.h"
std::string const dev::eth::c_genesisInfo =
R"ETHEREUM(
{
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": { "wei": "1606938044258990275541962092341162602522202993782792835301376" },
"e6716f9544a56c530d868e4bfbacb172315bdead": { "wei": "1606938044258990275541962092341162602522202993782792835301376" },
"b9c015918bdaba24b4ff057a92a3873d6eb201be": { "wei": "1606938044258990275541962092341162602522202993782792835301376" },
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4": { "wei": "1606938044258990275541962092341162602522202993782792835301376" },
"2ef47100e0787b915105fd5e3f4ff6752079d5cb": { "wei": "1606938044258990275541962092341162602522202993782792835301376" },
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826": { "wei": "1606938044258990275541962092341162602522202993782792835301376" },
"6c386a4b26f73c802f34673f7248bb118f97424a": { "wei": "1606938044258990275541962092341162602522202993782792835301376" },
"e4157b34ea9615cfbde6b4fda419828124b70c78": { "wei": "1606938044258990275541962092341162602522202993782792835301376" },
"b0afc46d9ce366d06ab4952ca27db1d9557ae9fd": { "finney": "154162184" },
"f6b1e9dc460d4d62cc22ec5f987d726929c0f9f0": { "finney": "102774789" },
"cc45122d8b7fa0b1eaa6b29e0fb561422a9239d0": { "finney": "51387394" },
"b7576e9d314df41ec5506494293afb1bd5d3f65d": { "finney": "69423399" },
}
)ETHEREUM";

32
libethereum/GenesisInfo.h

@ -0,0 +1,32 @@
/*
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 GenesisInfo.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <string>
namespace dev {
namespace eth {
extern std::string const c_genesisInfo;
}
}

4
libethereum/State.cpp

@ -1199,11 +1199,11 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s)
else if (j.second) else if (j.second)
cached.insert(j.first); cached.insert(j.first);
} }
if (delta.size()) if (!delta.empty())
lead = (lead == " . ") ? "*.* " : "*** "; lead = (lead == " . ") ? "*.* " : "*** ";
contout << " @:"; contout << " @:";
if (delta.size()) if (!delta.empty())
contout << "???"; contout << "???";
else else
contout << r[2].toHash<h256>(); contout << r[2].toHash<h256>();

2
libethereum/Transaction.cpp

@ -40,7 +40,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
m_gasPrice = rlp[field = 1].toInt<u256>(); m_gasPrice = rlp[field = 1].toInt<u256>();
m_gas = rlp[field = 2].toInt<u256>(); m_gas = rlp[field = 2].toInt<u256>();
m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall; m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
m_receiveAddress = rlp[field = 3].toHash<Address>(); m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash<Address>(RLP::VeryStrict);
m_value = rlp[field = 4].toInt<u256>(); m_value = rlp[field = 4].toInt<u256>();
m_data = rlp[field = 5].toBytes(); m_data = rlp[field = 5].toBytes();
byte v = rlp[field = 6].toInt<byte>() - 27; byte v = rlp[field = 6].toInt<byte>() - 27;

2
libethereum/Transaction.h

@ -81,7 +81,7 @@ public:
Address safeSender() const noexcept; Address safeSender() const noexcept;
/// @returns true if transaction is non-null. /// @returns true if transaction is non-null.
operator bool() const { return m_type != NullTransaction; } explicit operator bool() const { return m_type != NullTransaction; }
/// @returns true if transaction is contract-creation. /// @returns true if transaction is contract-creation.
bool isCreation() const { return m_type == ContractCreation; } bool isCreation() const { return m_type == ContractCreation; }

12
libevm/VM.cpp

@ -34,7 +34,13 @@ void VM::reset(u256 _gas) noexcept
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{ {
auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; };
auto gasForMem = [](bigint _size) -> bigint
{
bigint s = _size / 32;
// return (bigint)c_memoryGas * (s + s * s / 1024);
return (bigint)c_memoryGas * s;
};
if (m_jumpDests.empty()) if (m_jumpDests.empty())
for (unsigned i = 0; i < _ext.code.size(); ++i) for (unsigned i = 0; i < _ext.code.size(); ++i)
@ -297,7 +303,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
newTempSize = (newTempSize + 31) / 32 * 32; newTempSize = (newTempSize + 31) / 32 * 32;
if (newTempSize > m_temp.size()) if (newTempSize > m_temp.size())
runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32; runGas += gasForMem(newTempSize) - gasForMem(m_temp.size());
runGas += c_copyGas * (copySize + 31) / 32; runGas += c_copyGas * (copySize + 31) / 32;
onOperation(); onOperation();
@ -529,7 +535,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.push_back(_ext.currentBlock.difficulty); m_stack.push_back(_ext.currentBlock.difficulty);
break; break;
case Instruction::GASLIMIT: case Instruction::GASLIMIT:
m_stack.push_back(1000000); m_stack.push_back(_ext.currentBlock.gasLimit);
break; break;
case Instruction::PUSH1: case Instruction::PUSH1:
case Instruction::PUSH2: case Instruction::PUSH2:

2
libevm/VMFace.h

@ -40,7 +40,7 @@ public:
explicit VMFace(u256 _gas): m_gas(_gas) {} explicit VMFace(u256 _gas): m_gas(_gas) {}
virtual ~VMFace() = default; virtual ~VMFace() = default;
VMFace(VMFace const&) = delete; VMFace(VMFace const&) = delete;
void operator=(VMFace const&) = delete; VMFace& operator=(VMFace const&) = delete;
virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; } virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; }
u256 gas() const noexcept { return m_gas; } u256 gas() const noexcept { return m_gas; }

2
libevm/VMFactory.cpp

@ -39,7 +39,7 @@ void VMFactory::setKind(VMKind _kind)
std::unique_ptr<VMFace> VMFactory::create(u256 _gas) std::unique_ptr<VMFace> VMFactory::create(u256 _gas)
{ {
#if ETH_EVMJIT #if ETH_EVMJIT
return std::unique_ptr<VMFace>(g_kind == VMKind::JIT ? (VMFace*)new JitVM(_gas) : new VM(_gas)); return std::unique_ptr<VMFace>(g_kind == VMKind::JIT ? static_cast<VMFace*>(new JitVM(_gas)) : static_cast<VMFace*>(new VM(_gas)));
#else #else
asserts(g_kind == VMKind::Interpreter && "JIT disabled in build configuration"); asserts(g_kind == VMKind::Interpreter && "JIT disabled in build configuration");
return std::unique_ptr<VMFace>(new VM(_gas)); return std::unique_ptr<VMFace>(new VM(_gas));

6
libevmcore/Assembly.cpp

@ -214,7 +214,7 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix) const
BOOST_THROW_EXCEPTION(InvalidOpcode()); BOOST_THROW_EXCEPTION(InvalidOpcode());
} }
if (m_data.size() || m_subs.size()) if (!m_data.empty() || !m_subs.empty())
{ {
_out << _prefix << ".data:" << endl; _out << _prefix << ".data:" << endl;
for (auto const& i: m_data) for (auto const& i: m_data)
@ -441,7 +441,7 @@ Assembly& Assembly::optimise(bool _enable)
if (i.type() == PushTag) if (i.type() == PushTag)
tags.erase(i.data()); tags.erase(i.data());
if (tags.size()) if (!tags.empty())
{ {
auto t = *tags.begin(); auto t = *tags.begin();
unsigned i = t.second; unsigned i = t.second;
@ -567,7 +567,7 @@ bytes Assembly::assemble() const
toBigEndian(tagPos[i.second], r); toBigEndian(tagPos[i.second], r);
} }
if (m_data.size()) if (!m_data.empty())
{ {
ret.push_back(0); ret.push_back(0);
for (auto const& i: m_data) for (auto const& i: m_data)

2
libevmcore/Assembly.h

@ -72,6 +72,8 @@ inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _i) { r
class Assembly class Assembly
{ {
public: public:
Assembly() {}
AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); }
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); }

1
libjsqrc/ethereumjs/.gitignore

@ -5,6 +5,7 @@
# git config --global core.excludesfile ~/.gitignore_global # git config --global core.excludesfile ~/.gitignore_global
*.swp *.swp
/coverage
/tmp /tmp
*/**/*un~ */**/*un~
*un~ *un~

2
libjsqrc/ethereumjs/.travis.yml

@ -9,5 +9,5 @@ script:
- "jshint *.js lib" - "jshint *.js lib"
after_script: after_script:
- npm run-script build - npm run-script build
- npm test - npm run-script test-coveralls

8
libjsqrc/ethereumjs/README.md

@ -3,7 +3,7 @@
This is the Ethereum compatible [JavaScript API](https://github.com/ethereum/wiki/wiki/JavaScript-API) This is the Ethereum compatible [JavaScript API](https://github.com/ethereum/wiki/wiki/JavaScript-API)
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. It's available on npm as a node module and also for bower and component as an embeddable js which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. It's available on npm as a node module and also for bower and component as an embeddable js
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url] [![dev dependency status][dep-dev-image]][dep-dev-url] [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url] [![dev dependency status][dep-dev-image]][dep-dev-url][![Coverage Status][coveralls-image]][coveralls-url]
<!-- [![browser support](https://ci.testling.com/ethereum/ethereum.js.png)](https://ci.testling.com/ethereum/ethereum.js) --> <!-- [![browser support](https://ci.testling.com/ethereum/ethereum.js.png)](https://ci.testling.com/ethereum/ethereum.js) -->
@ -30,9 +30,9 @@ Require the library:
var web3 = require('web3'); var web3 = require('web3');
Set a provider (QtProvider, WebSocketProvider, HttpRpcProvider) Set a provider (QtSyncProvider, HttpSyncProvider)
var web3.setProvider(new web3.providers.WebSocketProvider('ws://localhost:40404/eth')); web3.setProvider(new web3.providers.HttpSyncProvider());
There you go, now you can use it: There you go, now you can use it:
@ -93,4 +93,6 @@ ethereum -ws -loglevel=4
[dep-url]: https://david-dm.org/ethereum/ethereum.js [dep-url]: https://david-dm.org/ethereum/ethereum.js
[dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg [dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg
[dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies [dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies
[coveralls-image]: https://coveralls.io/repos/ethereum/ethereum.js/badge.svg?branch=master
[coveralls-url]: https://coveralls.io/r/ethereum/ethereum.js?branch=master

2
libjsqrc/ethereumjs/bower.json

@ -1,7 +1,7 @@
{ {
"name": "ethereum.js", "name": "ethereum.js",
"namespace": "ethereum", "namespace": "ethereum",
"version": "0.0.13", "version": "0.0.15",
"description": "Ethereum Compatible JavaScript API", "description": "Ethereum Compatible JavaScript API",
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"], "main": ["./dist/ethereum.js", "./dist/ethereum.min.js"],
"dependencies": { "dependencies": {

649
libjsqrc/ethereumjs/dist/ethereum.js

@ -210,7 +210,7 @@ module.exports = {
}; };
},{"./const":2,"./formatters":6,"./types":11,"./utils":12,"./web3":13}],2:[function(require,module,exports){ },{"./const":2,"./formatters":8,"./types":14,"./utils":15,"./web3":17}],2:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -264,7 +264,8 @@ module.exports = {
ETH_PADDING: 32, ETH_PADDING: 32,
ETH_SIGNATURE_LENGTH: 4, ETH_SIGNATURE_LENGTH: 4,
ETH_UNITS: ETH_UNITS, ETH_UNITS: ETH_UNITS,
ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN } ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN },
ETH_POLLING_TIMEOUT: 1000
}; };
@ -295,6 +296,7 @@ var web3 = require('./web3');
var abi = require('./abi'); var abi = require('./abi');
var utils = require('./utils'); var utils = require('./utils');
var eventImpl = require('./event'); var eventImpl = require('./event');
var filter = require('./filter');
var exportNatspecGlobals = function (vars) { var exportNatspecGlobals = function (vars) {
// it's used byt natspec.js // it's used byt natspec.js
@ -416,11 +418,11 @@ var addEventsToContract = function (contract, desc, address) {
var signature = abi.eventSignatureFromAscii(e.name); var signature = abi.eventSignatureFromAscii(e.name);
var event = eventImpl.inputParser(address, signature, e); var event = eventImpl.inputParser(address, signature, e);
var o = event.apply(null, params); var o = event.apply(null, params);
o._onWatchEventResult = function (data) { var outputFormatter = function (data) {
var parser = eventImpl.outputParser(e); var parser = eventImpl.outputParser(e);
return parser(data); return parser(data);
}; };
return web3.eth.watch(o); return web3.eth.watch(o, undefined, undefined, outputFormatter);
}; };
// this property should be used by eth.filter to check if object is an event // this property should be used by eth.filter to check if object is an event
@ -487,7 +489,131 @@ var contract = function (address, desc) {
module.exports = contract; module.exports = contract;
},{"./abi":1,"./event":4,"./utils":12,"./web3":13}],4:[function(require,module,exports){ },{"./abi":1,"./event":6,"./filter":7,"./utils":15,"./web3":17}],4:[function(require,module,exports){
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file db.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
/// @returns an array of objects describing web3.db api methods
var methods = function () {
return [
{ name: 'put', call: 'db_put' },
{ name: 'get', call: 'db_get' },
{ name: 'putString', call: 'db_putString' },
{ name: 'getString', call: 'db_getString' }
];
};
module.exports = {
methods: methods
};
},{}],5:[function(require,module,exports){
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file eth.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
/// @returns an array of objects describing web3.eth api methods
var methods = function () {
var blockCall = function (args) {
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
};
var transactionCall = function (args) {
return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber';
};
var uncleCall = function (args) {
return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber';
};
var transactionCountCall = function (args) {
return typeof args[0] === "string" ? 'eth_transactionCountByHash' : 'eth_transactionCountByNumber';
};
var uncleCountCall = function (args) {
return typeof args[0] === "string" ? 'eth_uncleCountByHash' : 'eth_uncleCountByNumber';
};
return [
{ name: 'balanceAt', call: 'eth_balanceAt' },
{ name: 'stateAt', call: 'eth_stateAt' },
{ name: 'storageAt', call: 'eth_storageAt' },
{ name: 'countAt', call: 'eth_countAt'},
{ name: 'codeAt', call: 'eth_codeAt' },
{ name: 'transact', call: 'eth_transact' },
{ name: 'call', call: 'eth_call' },
{ name: 'block', call: blockCall },
{ name: 'transaction', call: transactionCall },
{ name: 'uncle', call: uncleCall },
{ name: 'compilers', call: 'eth_compilers' },
{ name: 'flush', call: 'eth_flush' },
{ name: 'lll', call: 'eth_lll' },
{ name: 'solidity', call: 'eth_solidity' },
{ name: 'serpent', call: 'eth_serpent' },
{ name: 'logs', call: 'eth_logs' },
{ name: 'transactionCount', call: transactionCountCall },
{ name: 'uncleCount', call: uncleCountCall }
];
};
/// @returns an array of objects describing web3.eth api properties
var properties = function () {
return [
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
{ name: 'gasPrice', getter: 'eth_gasPrice' },
{ name: 'accounts', getter: 'eth_accounts' },
{ name: 'peerCount', getter: 'eth_peerCount' },
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
{ name: 'number', getter: 'eth_number'}
];
};
module.exports = {
methods: methods,
properties: properties
};
},{}],6:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -589,6 +715,7 @@ var outputParser = function (event) {
args: {} args: {}
}; };
output.topics = output.topic; // fallback for go-ethereum
if (!output.topic) { if (!output.topic) {
return result; return result;
} }
@ -624,7 +751,7 @@ module.exports = {
}; };
},{"./abi":1,"./utils":12}],5:[function(require,module,exports){ },{"./abi":1,"./utils":15}],7:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -650,24 +777,33 @@ module.exports = {
* @date 2014 * @date 2014
*/ */
var web3 = require('./web3'); // jshint ignore:line /// Should be called to check if filter implementation is valid
/// @returns true if it is, otherwise false
/// should be used when we want to watch something var implementationIsValid = function (i) {
/// it's using inner polling mechanism and is notified about changes return !!i &&
/// TODO: change 'options' name cause it may be not the best matching one, since we have events typeof i.newFilter === 'function' &&
var Filter = function(options, impl) { typeof i.getMessages === 'function' &&
typeof i.uninstallFilter === 'function' &&
typeof i.startPolling === 'function' &&
typeof i.stopPolling === 'function';
};
/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones
/// @param should be string or object
/// @returns options string or object
var getOptions = function (options) {
if (typeof options === 'string') {
return options;
}
if (typeof options !== "string") { options = options || {};
// topics property is deprecated, warn about it!
if (options.topics) { if (options.topics) {
console.warn('"topics" is deprecated, use "topic" instead'); console.warn('"topics" is deprecated, is "topic" instead');
} }
this._onWatchResult = options._onWatchEventResult;
// evaluate lazy properties // evaluate lazy properties
options = { return {
to: options.to, to: options.to,
topic: options.topic, topic: options.topic,
earliest: options.earliest, earliest: options.earliest,
@ -676,58 +812,61 @@ var Filter = function(options, impl) {
skip: options.skip, skip: options.skip,
address: options.address address: options.address
}; };
};
/// Should be used when we want to watch something
/// it's using inner polling mechanism and is notified about changes
/// @param options are filter options
/// @param implementation, an abstract polling implementation
/// @param formatter (optional), callback function which formats output before 'real' callback
var filter = function(options, implementation, formatter) {
if (!implementationIsValid(implementation)) {
console.error('filter implemenation is invalid');
return;
} }
this.impl = impl; options = getOptions(options);
this.callbacks = []; var callbacks = [];
var filterId = implementation.newFilter(options);
this.id = impl.newFilter(options); var onMessages = function (messages) {
web3.provider.startPolling({method: impl.changed, params: [this.id]}, this.id, this.trigger.bind(this)); messages.forEach(function (message) {
message = formatter ? formatter(message) : message;
callbacks.forEach(function (callback) {
callback(message);
});
});
}; };
/// alias for changed* implementation.startPolling(filterId, onMessages, implementation.uninstallFilter);
Filter.prototype.arrived = function(callback) {
this.changed(callback);
};
Filter.prototype.happened = function(callback) {
this.changed(callback);
};
/// gets called when there is new eth/shh message var changed = function (callback) {
Filter.prototype.changed = function(callback) { callbacks.push(callback);
this.callbacks.push(callback);
}; };
/// trigger calling new message from people var messages = function () {
Filter.prototype.trigger = function(messages) { return implementation.getMessages(filterId);
for (var i = 0; i < this.callbacks.length; i++) {
for (var j = 0; j < messages.length; j++) {
var message = this._onWatchResult ? this._onWatchResult(messages[j]) : messages[j];
this.callbacks[i].call(this, message);
}
}
}; };
/// should be called to uninstall current filter var uninstall = function (callback) {
Filter.prototype.uninstall = function() { implementation.stopPolling(filterId);
this.impl.uninstallFilter(this.id); implementation.uninstallFilter(filterId);
web3.provider.stopPolling(this.id); callbacks = [];
}; };
/// should be called to manually trigger getting latest messages from the client return {
Filter.prototype.messages = function() { changed: changed,
return this.impl.getMessages(this.id); arrived: changed,
happened: changed,
messages: messages,
logs: messages,
uninstall: uninstall
}; };
/// alias for messages
Filter.prototype.logs = function () {
return this.messages();
}; };
module.exports = Filter; module.exports = filter;
},{"./web3":13}],6:[function(require,module,exports){
},{}],8:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -883,7 +1022,7 @@ module.exports = {
}; };
},{"./const":2,"./utils":12}],7:[function(require,module,exports){ },{"./const":2,"./utils":15}],9:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -913,7 +1052,7 @@ if ("build" !== 'build') {/*
var HttpSyncProvider = function (host) { var HttpSyncProvider = function (host) {
this.handlers = []; this.handlers = [];
this.host = host || 'http://localhost:8080'; this.host = host || 'http://127.0.0.1:8080';
}; };
HttpSyncProvider.prototype.send = function (payload) { HttpSyncProvider.prototype.send = function (payload) {
@ -923,15 +1062,17 @@ HttpSyncProvider.prototype.send = function (payload) {
request.open('POST', this.host, false); request.open('POST', this.host, false);
request.send(JSON.stringify(payload)); request.send(JSON.stringify(payload));
// check request.status
var result = request.responseText; var result = request.responseText;
// check request.status
if(request.status !== 200)
return;
return JSON.parse(result); return JSON.parse(result);
}; };
module.exports = HttpSyncProvider; module.exports = HttpSyncProvider;
},{}],8:[function(require,module,exports){ },{}],10:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -998,7 +1139,42 @@ module.exports = {
},{}],9:[function(require,module,exports){ },{}],11:[function(require,module,exports){
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file qtsync.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* @date 2014
*/
var QtSyncProvider = function () {
};
QtSyncProvider.prototype.send = function (payload) {
var result = navigator.qt.callMethod(JSON.stringify(payload));
return JSON.parse(result);
};
module.exports = QtSyncProvider;
},{}],12:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1015,7 +1191,7 @@ module.exports = {
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file providermanager.js /** @file requestmanager.js
* @authors: * @authors:
* Jeffrey Wilcke <jeff@ethdev.com> * Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com> * Marek Kotewicz <marek@ethdev.com>
@ -1024,51 +1200,27 @@ module.exports = {
* @date 2014 * @date 2014
*/ */
var web3 = require('./web3');
var jsonrpc = require('./jsonrpc'); var jsonrpc = require('./jsonrpc');
var c = require('./const');
/** /**
* Provider manager object prototype
* It's responsible for passing messages to providers * It's responsible for passing messages to providers
* If no provider is set it's responsible for queuing requests
* It's also responsible for polling the ethereum node for incoming messages * It's also responsible for polling the ethereum node for incoming messages
* Default poll timeout is 12 seconds * Default poll timeout is 1 second
* If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,
* and provider manager polling mechanism is not used
*/ */
var ProviderManager = function() { var requestManager = function() {
this.polls = []; var polls = [];
this.provider = undefined; var provider;
var self = this;
var poll = function () {
self.polls.forEach(function (data) {
var result = self.send(data.data);
if (!(result instanceof Array) || result.length === 0) { var send = function (data) {
return;
}
data.callback(result);
});
setTimeout(poll, 1000);
};
poll();
};
/// sends outgoing requests
/// @params data - an object with at least 'method' property
ProviderManager.prototype.send = function(data) {
var payload = jsonrpc.toPayload(data.method, data.params); var payload = jsonrpc.toPayload(data.method, data.params);
if (this.provider === undefined) { if (!provider) {
console.error('provider is not set'); console.error('provider is not set');
return null; return null;
} }
var result = this.provider.send(payload); var result = provider.send(payload);
if (!jsonrpc.isValidResponse(result)) { if (!jsonrpc.isValidResponse(result)) {
console.log(result); console.log(result);
@ -1078,31 +1230,56 @@ ProviderManager.prototype.send = function(data) {
return result.result; return result.result;
}; };
/// setups provider, which will be used for sending messages var setProvider = function (p) {
ProviderManager.prototype.set = function(provider) { provider = p;
this.provider = provider;
}; };
/// this method is only used, when we do not have native qt bindings and have to do polling on our own var startPolling = function (data, pollId, callback, uninstall) {
/// should be callled, on start watching for eth/shh changes polls.push({data: data, id: pollId, callback: callback, uninstall: uninstall});
ProviderManager.prototype.startPolling = function (data, pollId, callback) {
this.polls.push({data: data, id: pollId, callback: callback});
}; };
/// should be called to stop polling for certain watch changes var stopPolling = function (pollId) {
ProviderManager.prototype.stopPolling = function (pollId) { for (var i = polls.length; i--;) {
for (var i = this.polls.length; i--;) { var poll = polls[i];
var poll = this.polls[i];
if (poll.id === pollId) { if (poll.id === pollId) {
this.polls.splice(i, 1); polls.splice(i, 1);
} }
} }
}; };
module.exports = ProviderManager; var reset = function () {
polls.forEach(function (poll) {
poll.uninstall(poll.id);
});
polls = [];
};
var poll = function () {
polls.forEach(function (data) {
var result = send(data.data);
if (!(result instanceof Array) || result.length === 0) {
return;
}
data.callback(result);
});
setTimeout(poll, c.ETH_POLLING_TIMEOUT);
};
poll();
return {
send: send,
setProvider: setProvider,
startPolling: startPolling,
stopPolling: stopPolling,
reset: reset
};
};
},{"./jsonrpc":8,"./web3":13}],10:[function(require,module,exports){ module.exports = requestManager;
},{"./const":2,"./jsonrpc":10}],13:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1119,25 +1296,29 @@ module.exports = ProviderManager;
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file qtsync.js /** @file shh.js
* @authors: * @authors:
* Marek Kotewicz <marek@ethdev.com> * Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com> * @date 2015
* @date 2014
*/ */
var QtSyncProvider = function () { /// @returns an array of objects describing web3.shh api methods
var methods = function () {
return [
{ name: 'post', call: 'shh_post' },
{ name: 'newIdentity', call: 'shh_newIdentity' },
{ name: 'haveIdentity', call: 'shh_haveIdentity' },
{ name: 'newGroup', call: 'shh_newGroup' },
{ name: 'addToGroup', call: 'shh_addToGroup' }
];
}; };
QtSyncProvider.prototype.send = function (payload) { module.exports = {
var result = navigator.qt.callMethod(JSON.stringify(payload)); methods: methods
return JSON.parse(result);
}; };
module.exports = QtSyncProvider;
},{}],11:[function(require,module,exports){ },{}],14:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1218,7 +1399,7 @@ module.exports = {
}; };
},{"./formatters":6}],12:[function(require,module,exports){ },{"./formatters":8}],15:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1362,7 +1543,7 @@ module.exports = {
}; };
},{"./const":2}],13:[function(require,module,exports){ },{"./const":2}],16:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1379,100 +1560,14 @@ module.exports = {
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file web3.js /** @file watches.js
* @authors: * @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com> * Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com> * @date 2015
* Gav Wood <g@ethdev.com>
* @date 2014
*/ */
if ("build" !== 'build') {/*
var BigNumber = require('bignumber.js');
*/}
var utils = require('./utils');
/// @returns an array of objects describing web3 api methods
var web3Methods = function () {
return [
{ name: 'sha3', call: 'web3_sha3' }
];
};
/// @returns an array of objects describing web3.eth api methods
var ethMethods = function () {
var blockCall = function (args) {
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
};
var transactionCall = function (args) {
return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber';
};
var uncleCall = function (args) {
return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber';
};
var methods = [
{ name: 'balanceAt', call: 'eth_balanceAt' },
{ name: 'stateAt', call: 'eth_stateAt' },
{ name: 'storageAt', call: 'eth_storageAt' },
{ name: 'countAt', call: 'eth_countAt'},
{ name: 'codeAt', call: 'eth_codeAt' },
{ name: 'transact', call: 'eth_transact' },
{ name: 'call', call: 'eth_call' },
{ name: 'block', call: blockCall },
{ name: 'transaction', call: transactionCall },
{ name: 'uncle', call: uncleCall },
{ name: 'compilers', call: 'eth_compilers' },
{ name: 'flush', call: 'eth_flush' },
{ name: 'lll', call: 'eth_lll' },
{ name: 'solidity', call: 'eth_solidity' },
{ name: 'serpent', call: 'eth_serpent' },
{ name: 'logs', call: 'eth_logs' }
];
return methods;
};
/// @returns an array of objects describing web3.eth api properties
var ethProperties = function () {
return [
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
{ name: 'gasPrice', getter: 'eth_gasPrice' },
{ name: 'accounts', getter: 'eth_accounts' },
{ name: 'peerCount', getter: 'eth_peerCount' },
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
{ name: 'number', getter: 'eth_number'}
];
};
/// @returns an array of objects describing web3.db api methods
var dbMethods = function () {
return [
{ name: 'put', call: 'db_put' },
{ name: 'get', call: 'db_get' },
{ name: 'putString', call: 'db_putString' },
{ name: 'getString', call: 'db_getString' }
];
};
/// @returns an array of objects describing web3.shh api methods
var shhMethods = function () {
return [
{ name: 'post', call: 'shh_post' },
{ name: 'newIdentity', call: 'shh_newIdentity' },
{ name: 'haveIdentity', call: 'shh_haveIdentity' },
{ name: 'newGroup', call: 'shh_newGroup' },
{ name: 'addToGroup', call: 'shh_addToGroup' }
];
};
/// @returns an array of objects describing web3.eth.watch api methods /// @returns an array of objects describing web3.eth.watch api methods
var ethWatchMethods = function () { var eth = function () {
var newFilter = function (args) { var newFilter = function (args) {
return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';
}; };
@ -1485,7 +1580,7 @@ var ethWatchMethods = function () {
}; };
/// @returns an array of objects describing web3.shh.watch api methods /// @returns an array of objects describing web3.shh.watch api methods
var shhWatchMethods = function () { var shh = function () {
return [ return [
{ name: 'newFilter', call: 'shh_newFilter' }, { name: 'newFilter', call: 'shh_newFilter' },
{ name: 'uninstallFilter', call: 'shh_uninstallFilter' }, { name: 'uninstallFilter', call: 'shh_uninstallFilter' },
@ -1493,6 +1588,57 @@ var shhWatchMethods = function () {
]; ];
}; };
module.exports = {
eth: eth,
shh: shh
};
},{}],17:[function(require,module,exports){
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file web3.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* Gav Wood <g@ethdev.com>
* @date 2014
*/
if ("build" !== 'build') {/*
var BigNumber = require('bignumber.js');
*/}
var eth = require('./eth');
var db = require('./db');
var shh = require('./shh');
var watches = require('./watches');
var filter = require('./filter');
var utils = require('./utils');
var requestManager = require('./requestmanager');
/// @returns an array of objects describing web3 api methods
var web3Methods = function () {
return [
{ name: 'sha3', call: 'web3_sha3' }
];
};
/// creates methods in a given object based on method description on input /// creates methods in a given object based on method description on input
/// setups api calls for these methods /// setups api calls for these methods
var setupMethods = function (obj, methods) { var setupMethods = function (obj, methods) {
@ -1500,7 +1646,7 @@ var setupMethods = function (obj, methods) {
obj[method.name] = function () { obj[method.name] = function () {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
var call = typeof method.call === 'function' ? method.call(args) : method.call; var call = typeof method.call === 'function' ? method.call(args) : method.call;
return web3.provider.send({ return web3.manager.send({
method: call, method: call,
params: args params: args
}); });
@ -1514,14 +1660,14 @@ var setupProperties = function (obj, properties) {
properties.forEach(function (property) { properties.forEach(function (property) {
var proto = {}; var proto = {};
proto.get = function () { proto.get = function () {
return web3.provider.send({ return web3.manager.send({
method: property.getter method: property.getter
}); });
}; };
if (property.setter) { if (property.setter) {
proto.set = function (val) { proto.set = function (val) {
return web3.provider.send({ return web3.manager.send({
method: property.setter, method: property.setter,
params: [val] params: [val]
}); });
@ -1531,10 +1677,30 @@ var setupProperties = function (obj, properties) {
}); });
}; };
var startPolling = function (method, id, callback, uninstall) {
web3.manager.startPolling({
method: method,
params: [id]
}, id, callback, uninstall);
};
var stopPolling = function (id) {
web3.manager.stopPolling(id);
};
var ethWatch = {
startPolling: startPolling.bind(null, 'eth_changed'),
stopPolling: stopPolling
};
var shhWatch = {
startPolling: startPolling.bind(null, 'shh_changed'),
stopPolling: stopPolling
};
/// setups web3 object, and it's in-browser executed methods /// setups web3 object, and it's in-browser executed methods
var web3 = { var web3 = {
_callbacks: {}, manager: requestManager(),
_events: {},
providers: {}, providers: {},
/// @returns ascii string representation of hex value prefixed with 0x /// @returns ascii string representation of hex value prefixed with 0x
@ -1573,11 +1739,12 @@ var web3 = {
/// @param filter may be a string, object or event /// @param filter may be a string, object or event
/// @param indexed is optional, this is an object with optional event indexed params /// @param indexed is optional, this is an object with optional event indexed params
/// @param options is optional, this is an object with optional event options ('max'...) /// @param options is optional, this is an object with optional event options ('max'...)
watch: function (filter, indexed, options) { /// TODO: fix it, 4 params? no way
if (filter._isEvent) { watch: function (fil, indexed, options, formatter) {
return filter(indexed, options); if (fil._isEvent) {
return fil(indexed, options);
} }
return new web3.filter(filter, ethWatch); return filter(fil, ethWatch, formatter);
} }
}, },
@ -1586,54 +1753,44 @@ var web3 = {
/// shh object prototype /// shh object prototype
shh: { shh: {
/// @param filter may be a string, object or event /// @param filter may be a string, object or event
watch: function (filter, indexed) { watch: function (fil) {
return new web3.filter(filter, shhWatch); return filter(fil, shhWatch);
} }
}, },
setProvider: function (provider) {
web3.manager.setProvider(provider);
},
/// Should be called to reset state of web3 object
/// Resets everything except manager
reset: function () {
web3.manager.reset();
}
}; };
/// setups all api methods /// setups all api methods
setupMethods(web3, web3Methods()); setupMethods(web3, web3Methods());
setupMethods(web3.eth, ethMethods()); setupMethods(web3.eth, eth.methods());
setupProperties(web3.eth, ethProperties()); setupProperties(web3.eth, eth.properties());
setupMethods(web3.db, dbMethods()); setupMethods(web3.db, db.methods());
setupMethods(web3.shh, shhMethods()); setupMethods(web3.shh, shh.methods());
setupMethods(ethWatch, watches.eth());
var ethWatch = { setupMethods(shhWatch, watches.shh());
changed: 'eth_changed'
};
setupMethods(ethWatch, ethWatchMethods());
var shhWatch = {
changed: 'shh_changed'
};
setupMethods(shhWatch, shhWatchMethods());
web3.setProvider = function(provider) {
web3.provider.set(provider);
};
module.exports = web3; module.exports = web3;
},{"./utils":12}],"web3":[function(require,module,exports){ },{"./db":4,"./eth":5,"./filter":7,"./requestmanager":12,"./shh":13,"./utils":15,"./watches":16}],"web3":[function(require,module,exports){
var web3 = require('./lib/web3'); var web3 = require('./lib/web3');
var ProviderManager = require('./lib/providermanager');
web3.provider = new ProviderManager();
web3.filter = require('./lib/filter');
web3.providers.HttpSyncProvider = require('./lib/httpsync'); web3.providers.HttpSyncProvider = require('./lib/httpsync');
web3.providers.QtSyncProvider = require('./lib/qtsync'); web3.providers.QtSyncProvider = require('./lib/qtsync');
web3.eth.contract = require('./lib/contract'); web3.eth.contract = require('./lib/contract');
web3.abi = require('./lib/abi'); web3.abi = require('./lib/abi');
module.exports = web3; module.exports = web3;
},{"./lib/abi":1,"./lib/contract":3,"./lib/filter":5,"./lib/httpsync":7,"./lib/providermanager":9,"./lib/qtsync":10,"./lib/web3":13}]},{},["web3"]) },{"./lib/abi":1,"./lib/contract":3,"./lib/httpsync":9,"./lib/qtsync":11,"./lib/web3":17}]},{},["web3"])
//# sourceMappingURL=ethereum.js.map //# sourceMappingURL=ethereum.js.map

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

Loading…
Cancel
Save