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. 7
      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. 58
      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. 25
      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. 169
      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. 201
      evmjit/libevmjit/Memory.cpp
  53. 15
      evmjit/libevmjit/Memory.h
  54. 8
      evmjit/libevmjit/Runtime.cpp
  55. 12
      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. 6
      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. 701
      libjsqrc/ethereumjs/dist/ethereum.js

9
README.md

@ -1,12 +1,12 @@
## Ethereum C++ Client.
By Gav Wood, 2014.
By Gav Wood et al, 2013, 2014, 2015.
| 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)
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)
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
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](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](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)
@ -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)
### License
See [LICENSE](LICENSE)

2
alethzero/CMakeLists.txt

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

7
alethzero/GraphParameters.h

@ -37,8 +37,11 @@ static T graphParameters(T _min, T _max, unsigned _divisions, T* o_from = 0, T*
T uMax = _max / _divisor;
if (uMax == uMin || !_divisions)
{
*o_from = 0;
*o_delta = 1;
if (o_delta && o_from)
{
*o_from = 0;
*o_delta = 1;
}
return 1;
}
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;
protected:
QPainter* p;
QPainter* p = nullptr;
QRect active;
std::pair<float, float> xRange;
std::pair<float, float> yRange;
float xM;
float xC;
float yM;
float yC;
float xM = 0;
float xC = 0;
float yM = 0;
float yC = 0;
float dx;
float dy;
float dx = 0;
float dy = 0;
std::function<std::string(float _f)> xLabel;
std::function<std::string(float _f)> yLabel;
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.
float xT(float _dataIndex) const { return _dataIndex * xM + xC; }

12
alethzero/Main.ui

@ -163,6 +163,7 @@
<property name="title">
<string>&amp;Special</string>
</property>
<addaction name="natSpec"/>
<addaction name="paranoia"/>
<addaction name="clearPending"/>
<addaction name="killBlockchain"/>
@ -1652,6 +1653,17 @@ font-size: 14pt</string>
<string>New &amp;Transaction...</string>
</property>
</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>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

25
alethzero/MainWin.cpp

@ -201,6 +201,11 @@ Main::~Main()
g_logPost = simpleDebugOut;
}
bool Main::confirm() const
{
return ui->natSpec->isChecked();
}
void Main::on_newIdentity_triggered()
{
KeyPair kp = KeyPair::create();
@ -651,6 +656,7 @@ void Main::writeSettings()
s.setValue("localNetworking", ui->localNetworking->isChecked());
s.setValue("forceMining", ui->forceMining->isChecked());
s.setValue("paranoia", ui->paranoia->isChecked());
s.setValue("natSpec", ui->natSpec->isChecked());
s.setValue("showAll", ui->showAll->isChecked());
s.setValue("showAllAccounts", ui->showAllAccounts->isChecked());
s.setValue("clientName", ui->clientName->text());
@ -662,7 +668,7 @@ void Main::writeSettings()
s.setValue("jitvm", ui->jitvm->isChecked());
bytes d = m_webThree->saveNetwork();
if (d.size())
if (!d.empty())
m_networkConfig = QByteArray((char*)d.data(), (int)d.size());
s.setValue("peers", m_networkConfig);
s.setValue("nameReg", ui->nameReg->text());
@ -720,6 +726,7 @@ void Main::readSettings(bool _skipGeometry)
ui->forceMining->setChecked(s.value("forceMining", false).toBool());
on_forceMining_triggered();
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->showAllAccounts->setChecked(s.value("showAllAccounts", false).toBool());
ui->clientName->setText(s.value("clientName", "").toString());
@ -1160,6 +1167,7 @@ void Main::timerEvent(QTimerEvent*)
interval = 0;
refreshNetwork();
refreshWhispers();
poll();
}
else
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())
{
@ -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()
{
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());
}
@ -1515,11 +1518,11 @@ void Main::on_ourAccounts_doubleClicked()
qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray())));
}
void Main::on_log_doubleClicked()
/*void Main::on_log_doubleClicked()
{
ui->log->setPlainText("");
m_logHistory.clear();
}
}*/
void Main::on_accounts_doubleClicked()
{
@ -1626,7 +1629,7 @@ void Main::on_net_triggered()
{
web3()->setIdealPeerCount(ui->idealPeers->value());
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
// if (m_networkConfig.size()/* && ui->usePast->isChecked()*/)
// 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(); }
std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); }
bool confirm() const;
NatSpecFace* natSpec() { return &m_natSpecDB; }
QVariant evalRaw(QString const& _js);
@ -108,7 +109,7 @@ private slots:
void on_go_triggered();
void on_net_triggered();
void on_connect_triggered();
void on_idealPeers_valueChanged();
void on_idealPeers_valueChanged(int);
// Mining
void on_mine_triggered();
@ -141,7 +142,7 @@ private slots:
void on_blocks_currentItemChanged();
// Logging
void on_log_doubleClicked();
// void on_log_doubleClicked();
void on_verbosity_valueChanged();
// Misc
@ -161,8 +162,8 @@ private slots:
// Debugger
void on_debugCurrent_triggered();
void on_debugDumpState_triggered(int _add = 1);
void on_debugDumpStatePre_triggered();
void on_debugDumpState_triggered() { debugDumpState(1); }
void on_debugDumpStatePre_triggered() { debugDumpState(0); }
// Whisper
void on_newIdentity_triggered();
@ -176,6 +177,8 @@ signals:
void poll();
private:
void debugDumpState(int _add);
dev::p2p::NetworkPreferences netPrefs() 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)
{
}
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)
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)
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;
update();

1
alethzero/NatspecHandler.cpp

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

2
alethzero/NatspecHandler.h

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

78
alethzero/OurWebThreeStubServer.cpp

@ -33,9 +33,11 @@ using namespace dev;
using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3,
vector<KeyPair> const& _accounts, Main* main):
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(main)
{}
vector<KeyPair> const& _accounts, Main* _main):
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(_main)
{
connect(_main, SIGNAL(poll()), this, SLOT(doValidations()));
}
string OurWebThreeStubServer::shh_newIdentity()
{
@ -44,29 +46,44 @@ string OurWebThreeStubServer::shh_newIdentity()
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;
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;
if (!m_main->confirm())
{
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!",
"ÐApp is attempting to call into an unknown contract at address " +
m_main->pretty(_t.to).toStdString() +
".\n\nCall involves sending " +
m_main->pretty(_t.to).toStdString() + ".\n\n" +
(_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.gas * _t.gasPrice) +
"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!");
}
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)
{
// recipient has no code - nothing special about this transaction, show basic value transfer info
return showCreationNotice(_t);
// show notice concerning the creation code. TODO: this needs entering into natspec.
return showCreationNotice(_t, _toProxy);
}
h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to);
if (contractCodeHash == EmptySHA3)
{
// 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);
if (userNotice.empty())
return showUnknownCallNotice(_t);
return showUnknownCallNotice(_t, _toProxy);
NatspecExpressionEvaluator evaluator;
userNotice = evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString();
@ -104,11 +139,12 @@ bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t)
"ÐApp attempting to conduct contract interaction with " +
m_main->pretty(_t.to).toStdString() +
": <b>" + userNotice + "</b>.\n\n" +
(_toProxy ? "This transaction is not executed directly, but forwarded to another ÐApp.\n\n" : "") +
(_t.value > 0 ?
"In addition, ÐApp is attempting to send " +
formatBalance(_t.value) + " to said recipient, with additional network fees of up to " +
formatBalance(_t.gas * _t.gasPrice) + " = <b>" +
formatBalance(_t.value + _t.gas * _t.gasPrice) + "</b>."
formatBalance(_t.gas * _t.gasPrice) + " = " +
formatBalance(_t.value + _t.gas * _t.gasPrice) + "."
:
"Additional network fees are at most" +
formatBalance(_t.gas * _t.gasPrice) + ".")

20
alethzero/OurWebThreeStubServer.h

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

8
alethzero/Transact.cpp

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

5
cmake/EthDependencies.cmake

@ -128,6 +128,11 @@ if (NOT HEADLESS)
set (MACDEPLOYQT_APP ${Qt5Core_DIR}/../../../bin/macdeployqt)
message(" - macdeployqt path: ${MACDEPLOYQT_APP}")
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
find_program(ETH_NODE node)

58
cmake/EthExecutableHelper.cmake

@ -56,14 +56,14 @@ macro(eth_install_executable EXECUTABLE)
set (extra_macro_args ${ARGN})
set (options)
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}")
if (ETH_INSTALL_EXECUTABLE_QMLDIR)
if (APPLE)
set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}")
elseif (WIN32)
set(eth_qml_dir --qmldir ${ETH_INSTALL_EXECUTABLE_QMLDIR})
set(eth_qml_dir "--qmldir ${ETH_INSTALL_EXECUTABLE_QMLDIR}")
endif()
message(STATUS "${EXECUTABLE} qmldir: ${eth_qml_dir}")
endif()
@ -88,48 +88,30 @@ macro(eth_install_executable EXECUTABLE)
set(BU_CHMOD_BUNDLE_ITEMS 1)
verify_app(\"${APP_BUNDLE_PATH}\")
" COMPONENT RUNTIME )
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# copy all dlls to executable directory
# TODO improve that by copying only required dlls
file (GLOB DLLS ${ETH_DEPENDENCY_INSTALL_DIR}/bin/*.dll)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
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
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()
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmake -E copy_directory
"${ETH_DEPENDENCY_INSTALL_DIR}/plugins/platforms"
$<TARGET_FILE_DIR:${EXECUTABLE}>/platforms
)
# ugly way, improve that
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmake -E copy_directory
"${ETH_DEPENDENCY_INSTALL_DIR}/qml"
$<TARGET_FILE_DIR:${EXECUTABLE}>
)
install( FILES ${DLLS}
DESTINATION bin
COMPONENT ${EXECUTABLE}
)
#workaround for https://bugreports.qt.io/browse/QTBUG-42083
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmd /C "(echo [Paths] & echo.Prefix=.)" > "qt.conf"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} VERBATIM
)
endif()
install( DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/plugins/platforms
DESTINATION bin
COMPONENT ${EXECUTABLE}
#copy additional dlls
foreach(dll ${ETH_INSTALL_EXECUTABLE_DLLS})
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E copy ${dll} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}"
)
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
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"
# official MHD project actually uses _d suffix
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(
MHD_LIBRARY_DEBUG
NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d
DOC "mhd debug library"
)
set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG})
# always use release for now
#string(REPLACE ".lib" ".dll" MHD_DLL_DEBUG ${MHD_LIBRARY_DEBUG})
#set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG})
endif()

4
eth/CMakeLists.txt

@ -28,5 +28,9 @@ endif()
target_link_libraries(${EXECUTABLE} webthree)
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 )

6
eth/main.cpp

@ -749,7 +749,7 @@ int main(int argc, char** argv)
else if (format == "standard+")
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);
if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE)
for (auto const& i: ext->state().storage(ext->myAddress))
@ -898,7 +898,9 @@ int main(int argc, char** argv)
while (!g_exit)
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;
}

7
evmjit/CMakeLists.txt

@ -3,13 +3,14 @@ cmake_minimum_required(VERSION 2.8.12)
project(evmjit)
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()
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()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
# Do not allow unresovled symbols in shared library (default on linux)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
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})
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})
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 <libevm/FeeStructure.h>
#include <libevm/ExtVMFace.h>
@ -46,23 +47,22 @@ extern "C"
*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);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
_env->subBalance(endowment);
auto gas = llvm2eth(*io_gas);
OnOpFunc onOp {}; // TODO: Handle that thing
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, onOp), h256::AlignRight);
*io_gas = eth2llvm(gas);
u256 gas = *io_gas;
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight);
*io_gas = static_cast<int64_t>(gas);
*o_address = address;
}
else
*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);
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
@ -70,12 +70,11 @@ extern "C"
_env->subBalance(value);
auto receiveAddress = right160(*_receiveAddress);
auto inRef = bytesConstRef{_inBeg, _inSize};
auto outRef = bytesConstRef{_outBeg, _outSize};
OnOpFunc onOp {}; // TODO: Handle that thing
auto outRef = bytesRef{_outBeg, _outSize};
auto codeAddress = right160(*_codeAddress);
auto gas = llvm2eth(*io_gas);
auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, onOp, {}, codeAddress);
*io_gas = eth2llvm(gas);
u256 gas = *io_gas;
auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, {}, {}, codeAddress);
*io_gas = static_cast<int64_t>(gas);
return ret;
}

34
evmjit/libevmjit-cpp/JitVM.cpp

@ -1,6 +1,9 @@
#pragma GCC diagnostic ignored "-Wconversion"
#include "JitVM.h"
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libdevcrypto/SHA3.h>
#include <evmjit/libevmjit/ExecutionEngine.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
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
{
using namespace jit;
if (m_gas > std::numeric_limits<decltype(m_data.gas)>::max())
BOOST_THROW_EXCEPTION(OutOfGas()); // Do not accept requests with gas > 2^63 (int64 max) // TODO: Return "not accepted" exception to allow interpreted handle that
if (_ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max())
BOOST_THROW_EXCEPTION(OutOfGas());
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());
auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= m_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max();
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.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.code = _ext.code.data();
m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2llvm(sha3(_ext.code));
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)
{
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;
enum Kind: bool { Interpreter, JIT };
static std::unique_ptr<VMFace> create(Kind, u256 _gas = 0);
private:
friend class VMFactory;
explicit JitVM(u256 _gas = 0) : VMFace(_gas) {}
jit::RuntimeData m_data;
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 "Runtime.h"
#include "Type.h"
#include "Endianness.h"
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
#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
{
@ -16,29 +19,102 @@ namespace jit
Arith256::Arith256(llvm::IRBuilder<>& _builder) :
CompilerHelper(_builder)
{
using namespace llvm;
{}
m_result = m_builder.CreateAlloca(Type::Word, nullptr, "arith.result");
m_arg1 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg1");
m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg2");
m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg3");
void Arith256::debug(llvm::Value* _value, char _c)
{
if (!m_debug)
{
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()};
m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule());
auto i512 = m_builder.getIntNTy(512);
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule());
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)
@ -85,6 +161,15 @@ llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0");
auto shlBy0 = m_builder.CreateICmpEQ(i0, zero);
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
m_builder.CreateBr(loopBB);
@ -135,7 +220,7 @@ llvm::Function* Arith256::getExpFunc()
if (!m_exp)
{
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();
base->setName("base");
@ -159,9 +244,6 @@ llvm::Function* Arith256::getExpFunc()
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp);
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.SetInsertPoint(headerBB);
@ -176,20 +258,14 @@ llvm::Function* Arith256::getExpFunc()
m_builder.CreateCondBr(eOdd, updateBB, continueBB);
m_builder.SetInsertPoint(updateBB);
m_builder.CreateStore(r, a1);
m_builder.CreateStore(b, a2);
createCall(m_mul, {a1, a2, a3});
auto r0 = m_builder.CreateLoad(a3, "r0");
auto r0 = createCall(getMulFunc(), {r, b});
m_builder.CreateBr(continueBB);
m_builder.SetInsertPoint(continueBB);
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1");
r1->addIncoming(r, bodyBB);
r1->addIncoming(r0, updateBB);
m_builder.CreateStore(b, a1);
m_builder.CreateStore(b, a2);
createCall(m_mul, {a1, a2, a3});
auto b1 = m_builder.CreateLoad(a3, "b1");
auto b1 = createCall(getMulFunc(), {b, b});
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1");
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());
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();
x->setName("x");
auto y = x->getNextNode();
@ -258,32 +331,19 @@ llvm::Function* Arith256::getMulModFunc()
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod);
m_builder.SetInsertPoint(entryBB);
auto a1 = m_builder.CreateAlloca(Type::Word);
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 p = createCall(getMul512Func(), {x, y});
auto m = m_builder.CreateZExt(mod, i512Ty, "m");
auto d = createCall(getDivFunc(i512Ty), {p, m});
auto r = m_builder.CreateExtractValue(d, 1, "r");
m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word));
r = m_builder.CreateTrunc(r, Type::Word);
m_builder.CreateRet(r);
}
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)
{
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)
@ -331,157 +391,6 @@ llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Valu
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"
{
using namespace dev::eth::jit;
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;
}
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);
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";
}
}

13
evmjit/libevmjit/Arith256.h

@ -24,26 +24,21 @@ public:
void debug(llvm::Value* _value, char _c);
private:
llvm::Function* getMulFunc();
llvm::Function* getMul512Func();
llvm::Function* getDivFunc(llvm::Type* _type);
llvm::Function* getExpFunc();
llvm::Function* getAddModFunc();
llvm::Function* getMulModFunc();
llvm::Value* binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Function* m_mul;
llvm::Function* m_mul = nullptr;
llvm::Function* m_mul512 = nullptr;
llvm::Function* m_div = nullptr;
llvm::Function* m_div512 = nullptr;
llvm::Function* m_exp = nullptr;
llvm::Function* m_addmod = nullptr;
llvm::Function* m_mulmod = nullptr;
llvm::Function* m_debug = nullptr;
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 <iostream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/CFG.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
@ -18,13 +19,14 @@ namespace eth
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_end(_end),
// TODO: Add begin index to name
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), NamePrefix, _mainFunc)),
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)),
m_stack(*this),
m_builder(_builder),
m_isJumpDest(isJumpDest)
@ -43,6 +45,7 @@ BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) :
void BasicBlock::LocalStack::push(llvm::Value* _value)
{
assert(_value->getType() == Type::Word);
m_bblock.m_currentStack.push_back(_value);
m_bblock.m_tosOffset += 1;
}
@ -139,7 +142,7 @@ void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
auto endIter = m_currentStack.end();
// 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, --idx)
{
@ -306,7 +309,7 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
auto& initialStack = bblock.m_initialStack;
initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems);
// 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
@ -319,7 +322,7 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
auto& exitStack = bblock.m_currentStack;
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
#include <vector>
#include <llvm/IR/BasicBlock.h>
#include "Common.h"
#include "Stack.h"
@ -11,7 +12,7 @@ namespace eth
namespace jit
{
using ProgramCounter = uint64_t; // TODO: Rename
using instr_idx = uint64_t;
class BasicBlock
{
@ -50,10 +51,7 @@ public:
BasicBlock& m_bblock;
};
/// Basic block name prefix. The rest is instruction index.
static const char* NamePrefix;
explicit BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, 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;
@ -61,8 +59,9 @@ public:
llvm::BasicBlock* llvm() { return m_llvmBB; }
bytes::const_iterator begin() { return m_begin; }
bytes::const_iterator end() { return m_end; }
instr_idx firstInstrIdx() const { return m_firstInstrIdx; }
code_iterator begin() const { return m_begin; }
code_iterator end() const { return m_end; }
bool isJumpDest() const { return m_isJumpDest; }
@ -84,8 +83,9 @@ public:
void dump(std::ostream& os, bool _dotOutput = false);
private:
bytes::const_iterator const m_begin;
bytes::const_iterator const m_end;
instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block
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;

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 ${SOURCES})
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Disable rtti for Cache as LLVM has no rtti
set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
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)
if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always
OUTPUT_VARIABLE EVMJIT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always --match v*
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()
if(${NUM_VERSION_COMPONENTS} GREATER 1)
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()
if(NOT EVMJIT_VERSION)
set(EVMJIT_VERSION "unknown")
if(${EVMJIT_VERSION_MAJOR} EQUAL 0)
set(EVMJIT_SOVERSION "0.${EVMJIT_VERSION_MINOR}")
else()
set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR})
endif()
message("EVM JIT version: ${EVMJIT_VERSION}")
configure_file(BuildInfo.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen/BuildInfo.gen.h)
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})
set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${EVMJIT_VERSION} FOLDER "libs")
add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS} gen/BuildInfo.gen.h)
set_target_properties(${TARGET_NAME} PROPERTIES
VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION}
FOLDER "libs")
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen)
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS})

62
evmjit/libevmjit/Cache.cpp

@ -1,12 +1,19 @@
#include "Cache.h"
#include <unordered_map>
#include <cassert>
#include <iostream>
#include <cassert>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Instructions.h>
#include <llvm/Support/Path.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "ExecutionEngine.h"
#include "Utils.h"
namespace dev
{
@ -15,23 +22,31 @@ namespace eth
namespace jit
{
//#define LOG(...) std::cerr << "CACHE "
#define LOG(...) std::ostream(nullptr)
//#define CACHE_LOG std::cerr << "CACHE "
#define CACHE_LOG std::ostream(nullptr)
ObjectCache* Cache::getObjectCache()
namespace
{
static ObjectCache objectCache;
return &objectCache;
llvm::MemoryBuffer* g_lastObject;
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)
{
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::sys::path::system_temp_directory(false, cachePath);
llvm::sys::path::append(cachePath, "evm_objs", id);
@ -51,22 +66,31 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
#endif
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))
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()));
auto mainFuncType = llvm::FunctionType::get(llvm::IntegerType::get(llvm::getGlobalContext(), 32), {}, false);
llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get());
CACHE_LOG << id << ": found\n";
auto&& context = llvm::getGlobalContext();
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;
}
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object)
{
if (g_listener)
g_listener->stateChanged(ExecState::CacheWrite);
auto&& id = _module->getModuleIdentifier();
llvm::SmallString<256> 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);
CACHE_LOG << id << ": write\n";
std::string error;
llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None);
cacheFile << _object->getBuffer();
}
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const*)
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module)
{
auto o = lastObject;
lastObject = nullptr;
CACHE_LOG << _module->getModuleIdentifier() << ": use\n";
auto o = g_lastObject;
g_lastObject = nullptr;
return o;
}

9
evmjit/libevmjit/Cache.h

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

25
evmjit/libevmjit/Common.h

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

94
evmjit/libevmjit/Compiler.cpp

@ -1,4 +1,3 @@
#include "Compiler.h"
#include <functional>
@ -6,13 +5,15 @@
#include <chrono>
#include <sstream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/ADT/PostOrderIterator.h>
#include <llvm/IR/CFG.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h>
#include <llvm/IR/MDBuilder.h>
#include <llvm/PassManager.h>
#include <llvm/Transforms/Scalar.h>
#include "preprocessor/llvm_includes_end.h"
#include "Instruction.h"
#include "Type.h"
@ -39,10 +40,10 @@ Compiler::Compiler(Options const& _options):
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)
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 push32 = static_cast<size_t>(Instruction::PUSH32);
@ -52,11 +53,11 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
return _curr + offset;
};
auto begin = _bytecode.begin();
auto begin = _codeBegin; // begin of current block
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;
switch (Instruction(*curr))
@ -77,22 +78,19 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
break;
}
assert(next <= _bytecode.end());
if (next == _bytecode.end() || Instruction(*next) == Instruction::JUMPDEST)
assert(next <= _codeEnd);
if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST)
isEnd = true;
if (isEnd)
{
auto beginIdx = begin - _bytecode.begin();
auto beginIdx = begin - _codeBegin;
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;
begin = next;
}
}
// TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
}
llvm::BasicBlock* Compiler::getJumpTableBlock()
@ -125,7 +123,7 @@ llvm::BasicBlock* Compiler::getBadJumpBlock()
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 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->getArgumentList().front().setName("rt");
// Create the basic blocks.
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc);
// Create entry basic block
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc);
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.
RuntimeManager runtimeManager(m_builder);
RuntimeManager runtimeManager(m_builder, jmpBuf, _begin, _end);
GasMeter gasMeter(m_builder, runtimeManager);
Memory memory(runtimeManager, gasMeter);
Ext ext(runtimeManager, memory);
Stack stack(m_builder, runtimeManager);
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)
{
@ -157,7 +174,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
auto iterCopy = basicBlockPairIt;
++iterCopy;
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:
@ -165,6 +182,9 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
m_builder.SetInsertPoint(m_stopBB);
m_builder.CreateRet(Constant::get(ReturnCode::Stop));
m_builder.SetInsertPoint(abortBB);
m_builder.CreateRet(Constant::get(ReturnCode::OutOfGas));
removeDeadBlocks();
// 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)
{
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:
{
auto value = Constant::get(it - _bytecode.begin());
auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx());
stack.push(value);
break;
}
@ -631,7 +651,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::GAS:
{
_gasMeter.commitCostBlock();
stack.push(_runtimeManager.getGas());
stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word));
break;
}
@ -741,10 +761,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
_memory.require(initOff, initSize);
_gasMeter.commitCostBlock();
auto gas = _runtimeManager.getGas();
auto address = _ext.create(gas, endowment, initOff, initSize);
_runtimeManager.setGas(gas);
auto address = _ext.create(endowment, initOff, initSize);
stack.push(address);
break;
}
@ -752,7 +769,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::CALL:
case Instruction::CALLCODE:
{
auto gas = stack.pop();
auto callGas256 = stack.pop();
auto codeAddress = stack.pop();
auto value = stack.pop();
auto inOff = stack.pop();
@ -770,9 +787,13 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
if (inst == Instruction::CALLCODE)
receiveAddress = _runtimeManager.get(RuntimeData::Address);
_gasMeter.count(gas);
auto ret = _ext.call(gas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress);
_gasMeter.giveBack(gas);
auto gas = _runtimeManager.getGas();
_gasMeter.count(callGas256);
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);
break;
}
@ -825,12 +846,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
break;
}
default: // Invalid instruction - runtime exception
{
// TODO: Replace with return statement
_runtimeManager.raiseException(ReturnCode::BadInstruction);
}
default: // Invalid instruction - abort
m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction));
it = _basicBlock.end() - 1; // finish block compilation
}
}

9
evmjit/libevmjit/Compiler.h

@ -1,8 +1,5 @@
#pragma once
#include <llvm/IR/IRBuilder.h>
#include "Common.h"
#include "BasicBlock.h"
@ -33,13 +30,13 @@ public:
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:
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();

4
evmjit/libevmjit/CompilerHelper.cpp

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

5
evmjit/libevmjit/CompilerHelper.h

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

3
evmjit/libevmjit/Endianness.cpp

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

3
evmjit/libevmjit/Endianness.h

@ -1,7 +1,8 @@
#pragma once
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IRBuilder.h>
#include "preprocessor/llvm_includes_end.h"
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();
};
}
}
}

169
evmjit/libevmjit/ExecutionEngine.cpp

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

32
evmjit/libevmjit/ExecutionEngine.h

@ -1,5 +1,7 @@
#pragma once
#include <memory>
#include "RuntimeData.h"
namespace dev
@ -9,14 +11,40 @@ namespace eth
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
{
public:
ExecutionEngine() = default;
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)
bytes_ref returnData;

31
evmjit/libevmjit/Ext.cpp

@ -1,9 +1,8 @@
#include "Ext.h"
#include <llvm/IR/Function.h>
#include <llvm/IR/TypeBuilder.h>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.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_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, 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_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, 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::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_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
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()
{
auto& a = m_argAllocas[m_argCounter++];
auto& a = m_argAllocas[m_argCounter];
if (!a)
{
// FIXME: Improve order and names
InsertPointGuard g{getBuilder()};
getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI());
a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg");
auto allocaIt = getMainFunction()->front().begin();
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;
}
@ -124,30 +125,26 @@ llvm::Value* Ext::blockhash(llvm::Value* _number)
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 begin = m_memoryMan.getBytePtr(_initOff);
auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size");
createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret});
_gas = m_builder.CreateLoad(gas); // Return gas
createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(_endowment), begin, size, ret});
llvm::Value* address = m_builder.CreateLoad(ret);
address = Endianness::toNative(m_builder, 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 inBeg = m_memoryMan.getBytePtr(_inOff);
auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size");
auto outBeg = m_memoryMan.getBytePtr(_outOff);
auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size");
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)});
_gas = m_builder.CreateLoad(gas); // Return gas
auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)});
return m_builder.CreateZExt(ret, Type::Word, "ret");
}

6
evmjit/libevmjit/Ext.h

@ -1,7 +1,7 @@
#pragma once
#include <array>
#include "CompilerHelper.h"
namespace dev
@ -50,8 +50,8 @@ public:
llvm::Value* balance(llvm::Value* _address);
llvm::Value* calldataload(llvm::Value* _index);
llvm::Value* create(llvm::Value*& _gas, 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* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize);
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* sha3(llvm::Value* _inOff, llvm::Value* _inSize);

107
evmjit/libevmjit/GasMeter.cpp

@ -1,11 +1,9 @@
#include "GasMeter.h"
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Ext.h"
#include "RuntimeManager.h"
@ -19,29 +17,29 @@ namespace jit
namespace // Helper functions
{
uint64_t const c_stepGas = 1;
uint64_t const c_balanceGas = 20;
uint64_t const c_sha3Gas = 10;
uint64_t const c_sha3WordGas = 10;
uint64_t const c_sloadGas = 20;
uint64_t const c_sstoreSetGas = 300;
uint64_t const c_sstoreResetGas = 100;
uint64_t const c_sstoreRefundGas = 100;
uint64_t const c_createGas = 100;
uint64_t const c_createDataGas = 5;
uint64_t const c_callGas = 20;
uint64_t const c_expGas = 1;
uint64_t const c_expByteGas = 1;
uint64_t const c_memoryGas = 1;
uint64_t const c_txDataZeroGas = 1;
uint64_t const c_txDataNonZeroGas = 5;
uint64_t const c_txGas = 500;
uint64_t const c_logGas = 32;
uint64_t const c_logDataGas = 1;
uint64_t const c_logTopicGas = 32;
uint64_t const c_copyGas = 1;
uint64_t getStepCost(Instruction inst)
int64_t const c_stepGas = 1;
int64_t const c_balanceGas = 20;
int64_t const c_sha3Gas = 10;
int64_t const c_sha3WordGas = 10;
int64_t const c_sloadGas = 20;
int64_t const c_sstoreSetGas = 300;
int64_t const c_sstoreResetGas = 100;
int64_t const c_sstoreRefundGas = 100;
int64_t const c_createGas = 100;
int64_t const c_createDataGas = 5;
int64_t const c_callGas = 20;
int64_t const c_expGas = 1;
int64_t const c_expByteGas = 1;
int64_t const c_memoryGas = 1;
int64_t const c_txDataZeroGas = 1;
int64_t const c_txDataNonZeroGas = 5;
int64_t const c_txGas = 500;
int64_t const c_logGas = 32;
int64_t const c_logDataGas = 1;
int64_t const c_logTopicGas = 32;
int64_t const c_copyGas = 1;
int64_t getStepCost(Instruction inst)
{
switch (inst)
{
@ -72,7 +70,7 @@ uint64_t getStepCost(Instruction inst)
case Instruction::LOG3:
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;
}
}
@ -86,7 +84,7 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
{
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);
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 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);
auto arg = m_gasCheckFunc->arg_begin();
arg->setName("rt");
++arg;
arg->setName("cost");
auto cost = arg;
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.SetInsertPoint(outOfGasBB);
m_runtimeManager.raiseException(ReturnCode::OutOfGas);
m_runtimeManager.abort();
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(updateBB);
gas = m_builder.CreateSub(gas, cost);
m_runtimeManager.setGas(gas);
m_builder.CreateRetVoid();
}
@ -119,7 +117,7 @@ void GasMeter::count(Instruction _inst)
if (!m_checkCall)
{
// 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);
@ -127,6 +125,15 @@ void GasMeter::count(Instruction _inst)
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});
}
@ -136,12 +143,13 @@ void GasMeter::countExp(llvm::Value* _exponent)
// lz - leading zeros
// 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 lz = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false));
auto sigBits = m_builder.CreateSub(Constant::get(256), lz);
auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, Constant::get(7)), Constant::get(8));
auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false));
auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
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);
}
@ -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 isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert");
auto isDelete = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDelete");
auto cost = m_builder.CreateSelect(isInsert, Constant::get(c_sstoreSetGas), Constant::get(c_sstoreResetGas), "cost");
cost = m_builder.CreateSelect(isDelete, Constant::get(0), cost, "cost");
auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(c_sstoreSetGas), m_builder.getInt64(c_sstoreResetGas), "cost");
cost = m_builder.CreateSelect(isDelete, m_builder.getInt64(0), cost, "cost");
count(cost);
}
@ -173,17 +181,16 @@ void GasMeter::countSha3Data(llvm::Value* _dataLength)
assert(m_blockCost > 0); // SHA3 instruction is already counted
// 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");
auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::lowPrecision);
auto words64 = m_builder.CreateUDiv(m_builder.CreateAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32));
auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::Gas);
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 cost = getBuilder().CreateZExt(cost64, Type::Word);
count(cost);
count(cost64);
}
void GasMeter::giveBack(llvm::Value* _gas)
{
assert(_gas->getType() == Type::Gas);
m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas));
}
@ -199,7 +206,7 @@ void GasMeter::commitCostBlock()
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_blockCost = 0;
}

3
evmjit/libevmjit/GasMeter.h

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

8
evmjit/libevmjit/Instruction.cpp

@ -1,6 +1,8 @@
#include "Instruction.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/ADT/APInt.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev
{
@ -9,7 +11,7 @@ namespace eth
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;
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;
}
void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end)
void skipPushData(code_iterator& _curr, code_iterator _end)
{
auto pushInst = *_curr;
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
/// Reading out of bytecode means reading 0
/// @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.
/// @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: \
case Instruction::PUSH2: \

201
evmjit/libevmjit/Memory.cpp

@ -1,14 +1,8 @@
#include "Memory.h"
#include <vector>
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <cassert>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Runtime.h"
@ -26,78 +20,77 @@ namespace jit
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
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);
m_loadWord = createFunc(false, Type::Word, _gasMeter);
m_storeWord = createFunc(true, Type::Word, _gasMeter);
m_storeByte = createFunc(true, Type::Byte, _gasMeter);
}
{}
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
llvm::Function* Memory::getRequireFunc()
{
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());
auto rt = func->arg_begin();
rt->setName("rt");
auto offset = rt->getNextNode();
offset->setName("offset");
auto size = offset->getNextNode();
size->setName("size");
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func);
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func);
InsertPointGuard guard(m_builder); // Restores insert point at function exit
// BB "Pre": Ignore checks with size 0
m_builder.SetInsertPoint(preBB);
auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0));
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB);
// BB "Check"
m_builder.SetInsertPoint(checkBB);
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word);
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res");
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq");
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1");
auto rtPtr = getRuntimeManager().getRuntimePtr();
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
auto currSize = m_builder.CreateLoad(sizePtr, "currSize");
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall");
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded");
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
// BB "Resize"
m_builder.SetInsertPoint(resizeBB);
// Check gas first
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res");
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0);
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2");
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow");
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired);
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq");
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 newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
_gasMeter.countMemory(newWords);
// Resize
m_builder.CreateStore(sizeRequired, sizePtr);
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData");
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
m_builder.CreateStore(newData, dataPtr);
m_builder.CreateBr(returnBB);
// BB "Return"
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
auto& func = m_require;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule());
auto rt = func->arg_begin();
rt->setName("rt");
auto offset = rt->getNextNode();
offset->setName("offset");
auto size = offset->getNextNode();
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 checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func);
InsertPointGuard guard(m_builder); // Restores insert point at function exit
// BB "Pre": Ignore checks with size 0
m_builder.SetInsertPoint(preBB);
auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0));
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB);
// BB "Check"
m_builder.SetInsertPoint(checkBB);
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word);
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res");
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq");
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1");
auto rtPtr = getRuntimeManager().getRuntimePtr();
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
auto currSize = m_builder.CreateLoad(sizePtr, "currSize");
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall");
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded");
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
// BB "Resize"
m_builder.SetInsertPoint(resizeBB);
// Check gas first
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res");
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0);
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2");
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow");
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired);
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq");
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 newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
m_gasMeter.countMemory(newWords);
// Resize
m_builder.CreateStore(sizeRequired, sizePtr);
auto newData = m_builder.CreateCall2(resize, rt, sizePtr, "newData");
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
m_builder.CreateStore(newData, dataPtr);
m_builder.CreateBr(returnBB);
// BB "Return"
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
}
return func;
}
@ -143,21 +136,45 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMet
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)
{
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr});
return createCall(getLoadWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr});
}
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)
{
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()
@ -182,7 +199,12 @@ llvm::Value* Memory::getBytePtr(llvm::Value* _index)
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,
@ -192,7 +214,8 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value*
// Additional copy cost
// 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);
// Algorithm:
@ -205,14 +228,12 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value*
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize);
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision);
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision);
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size);
auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size);
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision);
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize);
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64);
auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner);
auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize);
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes);
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner);
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

15
evmjit/libevmjit/Memory.h

@ -31,13 +31,16 @@ private:
GasMeter& m_gasMeter;
llvm::Function* createFunc(bool _isStore, llvm::Type* _type, GasMeter& _gasMeter);
llvm::Function* createRequireFunc(GasMeter& _gasMeter);
llvm::Function* m_resize;
llvm::Function* m_require;
llvm::Function* m_loadWord;
llvm::Function* m_storeWord;
llvm::Function* m_storeByte;
llvm::Function* getRequireFunc();
llvm::Function* getLoadWordFunc();
llvm::Function* getStoreWordFunc();
llvm::Function* getStoreByteFunc();
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 <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
#include <cassert>
namespace dev
{
@ -14,8 +11,7 @@ namespace jit
Runtime::Runtime(RuntimeData* _data, Env* _env) :
m_data(*_data),
m_env(*_env),
m_currJmpBuf(m_jmpBuf)
m_env(*_env)
{}
bytes_ref Runtime::getReturnData() const

12
evmjit/libevmjit/Runtime.h

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

4
evmjit/libevmjit/RuntimeData.h

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

54
evmjit/libevmjit/RuntimeManager.cpp

@ -1,12 +1,8 @@
#include "RuntimeManager.h"
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "RuntimeData.h"
#include "Instruction.h"
#include "preprocessor/llvm_includes_end.h"
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
auto rtPtr = getRuntimePtr();
@ -161,9 +165,10 @@ void RuntimeManager::registerSuicide(llvm::Value* _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)
@ -191,14 +196,14 @@ llvm::Value* RuntimeManager::getCallData()
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()
{
auto value = get(RuntimeData::CodeSize);
assert(value->getType() == Type::Size);
return getBuilder().CreateZExt(value, Type::Word);
return Constant::get(m_codeEnd - m_codeBegin);
}
llvm::Value* RuntimeManager::getCallDataSize()
@ -208,23 +213,28 @@ llvm::Value* RuntimeManager::getCallDataSize()
return getBuilder().CreateZExt(value, Type::Word);
}
llvm::Value* RuntimeManager::getJmpBuf()
llvm::Value* RuntimeManager::getJmpBufExt()
{
auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2, "jmpbufPtr");
return getBuilder().CreateLoad(ptr, "jmpbuf");
auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2);
return getBuilder().CreateLoad(ptr, "jmpBufExt");
}
llvm::Value* RuntimeManager::getGas()
{
auto value = get(RuntimeData::Gas);
assert(value->getType() == Type::Size);
return getBuilder().CreateZExt(value, Type::Word);
auto gas = get(RuntimeData::Gas);
assert(gas->getType() == Type::Gas);
return gas;
}
llvm::Value* RuntimeManager::getGasPtr()
{
return getPtr(RuntimeData::Gas);
}
void RuntimeManager::setGas(llvm::Value* _gas)
{
auto newGas = getBuilder().CreateTrunc(_gas, Type::Size);
set(RuntimeData::Gas, newGas);
assert(_gas->getType() == Type::Gas);
set(RuntimeData::Gas, _gas);
}
}

17
evmjit/libevmjit/RuntimeManager.h

@ -15,25 +15,28 @@ namespace jit
class RuntimeManager: public CompilerHelper
{
public:
RuntimeManager(llvm::IRBuilder<>& _builder);
RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd);
llvm::Value* getRuntimePtr();
llvm::Value* getDataPtr();
llvm::Value* getEnvPtr(); // TODO: Can we make it const?
llvm::Value* getEnvPtr();
llvm::Value* get(RuntimeData::Index _index);
llvm::Value* get(Instruction _inst);
llvm::Value* getGas(); // TODO: Remove
llvm::Value* getGas();
llvm::Value* getGasPtr();
llvm::Value* getCallData();
llvm::Value* getCode();
llvm::Value* getCodeSize();
llvm::Value* getCallDataSize();
llvm::Value* getJmpBuf() { return m_jmpBuf; }
void setGas(llvm::Value* _gas);
void registerReturnData(llvm::Value* _index, llvm::Value* _size);
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* getRuntimeDataType();
@ -41,11 +44,15 @@ public:
private:
llvm::Value* getPtr(RuntimeData::Index _index);
void set(RuntimeData::Index _index, llvm::Value* _value);
llvm::Value* getJmpBuf();
llvm::Value* getJmpBufExt();
llvm::Function* m_longjmp = nullptr;
llvm::Value* const m_jmpBuf;
llvm::Value* m_dataPtr = 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 "RuntimeManager.h"
#include "Runtime.h"
#include "Type.h"
#include "preprocessor/llvm_includes_start.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
{
@ -27,18 +28,87 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
llvm::Type* pushArgTypes[] = {Type::RuntimePtr, Type::WordPtr};
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};
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);
}
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)
{
m_builder.CreateCall3(m_get, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _index, false), m_arg);
return m_builder.CreateLoad(m_arg);
auto valuePtr = createCall(getGetFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_index), m_runtimeManager.getJmpBuf()});
return m_builder.CreateLoad(valuePtr);
}
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)
{
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)
@ -69,13 +139,14 @@ extern "C"
{
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();
if (stack.size() < _count)
longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall));
return false;
stack.erase(stack.end() - _count, stack.end());
return true;
}
EXPORT void stack_push(Runtime* _rt, i256 const* _word)
@ -87,22 +158,18 @@ extern "C"
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();
// TODO: encode _index and stack size in the return code
if (stack.size() <= _index)
longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall));
*o_ret = *(stack.rbegin() + _index);
return _index < stack.size() ? &*(stack.rbegin() + _index) : nullptr;
}
EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word)
{
auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code
if (stack.size() <= _index)
longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall));
assert(_index < stack.size());
if (_index >= stack.size())
return;
*(stack.rbegin() + _index) = *_word;
}

9
evmjit/libevmjit/Stack.h

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

11
evmjit/libevmjit/Type.cpp

@ -1,8 +1,4 @@
#include "Type.h"
#include <llvm/IR/DerivedTypes.h>
#include "RuntimeManager.h"
namespace dev
@ -17,6 +13,8 @@ llvm::PointerType* Type::WordPtr;
llvm::IntegerType* Type::lowPrecision;
llvm::IntegerType* Type::Bool;
llvm::IntegerType* Type::Size;
llvm::IntegerType* Type::Gas;
llvm::PointerType* Type::GasPtr;
llvm::IntegerType* Type::Byte;
llvm::PointerType* Type::BytePtr;
llvm::Type* Type::Void;
@ -24,6 +22,7 @@ llvm::IntegerType* Type::MainReturn;
llvm::PointerType* Type::EnvPtr;
llvm::PointerType* Type::RuntimeDataPtr;
llvm::PointerType* Type::RuntimePtr;
llvm::ConstantInt* Constant::gasMax;
void Type::init(llvm::LLVMContext& _context)
{
@ -35,6 +34,8 @@ void Type::init(llvm::LLVMContext& _context)
// TODO: Size should be architecture-dependent
Bool = llvm::Type::getInt1Ty(_context);
Size = llvm::Type::getInt64Ty(_context);
Gas = Size;
GasPtr = Gas->getPointerTo();
Byte = llvm::Type::getInt8Ty(_context);
BytePtr = Byte->getPointerTo();
Void = llvm::Type::getVoidTy(_context);
@ -43,6 +44,8 @@ void Type::init(llvm::LLVMContext& _context)
EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo();
RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->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
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Type.h>
#include <llvm/IR/Constants.h>
#include "preprocessor/llvm_includes_end.h"
#include "Common.h"
namespace dev
@ -23,6 +25,8 @@ struct Type
static llvm::IntegerType* Bool;
static llvm::IntegerType* Size;
static llvm::IntegerType* Gas;
static llvm::PointerType* GasPtr;
static llvm::IntegerType* Byte;
static llvm::PointerType* BytePtr;
@ -41,6 +45,8 @@ struct Type
struct Constant
{
static llvm::ConstantInt* gasMax;
/// Returns word-size constant
static llvm::ConstantInt* get(int64_t _n);
static llvm::ConstantInt* get(llvm::APInt const& _n);

1
evmjit/libevmjit/Utils.cpp

@ -1,4 +1,3 @@
#include "Utils.h"
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::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
{
// TODO: Make sure ExecutionEngine constructor does not throw
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
{
if (!_engine || !_data)
return static_cast<int>(ReturnCode::UnexpectedException);
try
{
auto codePtr = _data->code;
auto codeSize = _data->codeSize;
bytes bytecode;
bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize);
auto returnCode = _engine->run(bytecode, _data, _env);
auto returnCode = _engine->run(_data, _env);
return static_cast<int>(returnCode);
}
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/CommonData.h>
#include <libdevcore/RLP.h>
#include <libdevcrypto/TrieDB.h>
#include <libdevcore/CommonIO.h>
#include <libp2p/All.h>
#include <libethereum/DownloadMan.h>
@ -33,11 +34,13 @@
#include <liblll/All.h>
#include <libwhisper/WhisperPeer.h>
#include <libwhisper/WhisperHost.h>
#include <test/JsonSpiritHeaders.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::p2p;
using namespace dev::shh;
namespace js = json_spirit;
#if 0
int main()
@ -98,8 +101,7 @@ int main()
#else
int main()
{
cnote << KeyPair(Secret("0000000000000000000000000000000000000000000000000000000000000000")).address();
cnote << KeyPair(Secret("1111111111111111111111111111111111111111111111111111111111111111")).address();
return 0;
}
#endif

2
extdep/CMakeLists.txt

@ -7,7 +7,7 @@ include(eth_download.cmake)
# all dependencies will be installed into this directory, separated by platform
string(TOLOWER ${CMAKE_SYSTEM_NAME} _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}/include)
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/bin)

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using 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); }
/// @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.
bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; }

4
libdevcore/RLP.h

@ -242,7 +242,9 @@ public:
AllowNonCanon = 1,
ThrowOnFail = 4,
FailIfTooBig = 8,
FailIfTooSmall = 16,
Strict = ThrowOnFail | FailIfTooBig,
VeryStrict = ThrowOnFail | FailIfTooBig | FailIfTooSmall,
LaisezFaire = AllowNonCanon
};
@ -269,7 +271,7 @@ public:
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)
BOOST_THROW_EXCEPTION(BadCast());
else

2
libdevcore/Worker.h

@ -64,7 +64,7 @@ protected:
private:
std::string m_name;
unsigned m_idleWaitMs;
unsigned m_idleWaitMs = 0;
mutable Mutex x_work; ///< Lock for the network existance.
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(_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::string const&, std::string&>::type _data): m_data((_T*)_data.data()), m_count(_data.size() / sizeof(_T)) {}
#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); }
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)); }
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; }
size_t count() const { return m_count; }

2
libdevcrypto/Common.cpp

@ -44,6 +44,8 @@ bool dev::SignatureStruct::isValid() const
return true;
}
Address dev::ZeroAddress = Address();
Public dev::toPublic(Secret const& _secret)
{
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.
using Address = h160;
/// The zero address.
extern Address ZeroAddress;
/// A vector of Ethereum addresses.
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)
{
// 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)

6
libdevcrypto/TrieDB.h

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

2
libethcore/CommonEth.cpp

@ -32,7 +32,7 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 53;
const unsigned c_protocolVersion = 54;
const unsigned c_databaseVersion = 5;
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> 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));
bigint d = (bigint(1) << 256) / _difficulty;

3
libethereum/BlockChain.cpp

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

3
libethereum/BlockQueue.cpp

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

32
libethereum/CanonBlockChain.cpp

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

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 <> 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<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)
{

2
libethereum/EthereumHost.h

@ -94,7 +94,7 @@ private:
h256Set neededBlocks(h256Set const& _exclude);
/// 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.
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;
}
}

6
libethereum/State.cpp

@ -537,7 +537,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
cwarn << "Hex: " << toHex(b);
cwarn << TransactionReceipt(&b);
}
cwarn << "Recorded: " << m_currentBlock.receiptsRoot;
cwarn << "Recorded: " << m_currentBlock.receiptsRoot;
auto rs = _bc.receipts(m_currentBlock.hash);
for (unsigned j = 0; j < rs.receipts.size(); ++j)
{
@ -1199,11 +1199,11 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s)
else if (j.second)
cached.insert(j.first);
}
if (delta.size())
if (!delta.empty())
lead = (lead == " . ") ? "*.* " : "*** ";
contout << " @:";
if (delta.size())
if (!delta.empty())
contout << "???";
else
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_gas = rlp[field = 2].toInt<u256>();
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_data = rlp[field = 5].toBytes();
byte v = rlp[field = 6].toInt<byte>() - 27;

2
libethereum/Transaction.h

@ -81,7 +81,7 @@ public:
Address safeSender() const noexcept;
/// @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.
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)
{
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())
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;
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;
onOperation();
@ -529,7 +535,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.push_back(_ext.currentBlock.difficulty);
break;
case Instruction::GASLIMIT:
m_stack.push_back(1000000);
m_stack.push_back(_ext.currentBlock.gasLimit);
break;
case Instruction::PUSH1:
case Instruction::PUSH2:

2
libevm/VMFace.h

@ -40,7 +40,7 @@ public:
explicit VMFace(u256 _gas): m_gas(_gas) {}
virtual ~VMFace() = default;
VMFace(VMFace const&) = delete;
void operator=(VMFace const&) = delete;
VMFace& operator=(VMFace const&) = delete;
virtual void reset(u256 _gas = 0) noexcept { m_gas = _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)
{
#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
asserts(g_kind == VMKind::Interpreter && "JIT disabled in build configuration");
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());
}
if (m_data.size() || m_subs.size())
if (!m_data.empty() || !m_subs.empty())
{
_out << _prefix << ".data:" << endl;
for (auto const& i: m_data)
@ -441,7 +441,7 @@ Assembly& Assembly::optimise(bool _enable)
if (i.type() == PushTag)
tags.erase(i.data());
if (tags.size())
if (!tags.empty())
{
auto t = *tags.begin();
unsigned i = t.second;
@ -567,7 +567,7 @@ bytes Assembly::assemble() const
toBigEndian(tagPos[i.second], r);
}
if (m_data.size())
if (!m_data.empty())
{
ret.push_back(0);
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
{
public:
Assembly() {}
AssemblyItem newTag() { return AssemblyItem(Tag, 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); }

1
libjsqrc/ethereumjs/.gitignore

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

2
libjsqrc/ethereumjs/.travis.yml

@ -9,5 +9,5 @@ script:
- "jshint *.js lib"
after_script:
- 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)
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) -->
@ -30,9 +30,9 @@ Require the library:
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:
@ -93,4 +93,6 @@ ethereum -ws -loglevel=4
[dep-url]: https://david-dm.org/ethereum/ethereum.js
[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
[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",
"namespace": "ethereum",
"version": "0.0.13",
"version": "0.0.15",
"description": "Ethereum Compatible JavaScript API",
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"],
"dependencies": {

701
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.
@ -264,7 +264,8 @@ module.exports = {
ETH_PADDING: 32,
ETH_SIGNATURE_LENGTH: 4,
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 utils = require('./utils');
var eventImpl = require('./event');
var filter = require('./filter');
var exportNatspecGlobals = function (vars) {
// it's used byt natspec.js
@ -416,11 +418,11 @@ var addEventsToContract = function (contract, desc, address) {
var signature = abi.eventSignatureFromAscii(e.name);
var event = eventImpl.inputParser(address, signature, e);
var o = event.apply(null, params);
o._onWatchEventResult = function (data) {
var outputFormatter = function (data) {
var parser = eventImpl.outputParser(e);
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
@ -487,7 +489,131 @@ var contract = function (address, desc) {
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.
@ -589,6 +715,7 @@ var outputParser = function (event) {
args: {}
};
output.topics = output.topic; // fallback for go-ethereum
if (!output.topic) {
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.
@ -650,84 +777,96 @@ module.exports = {
* @date 2014
*/
var web3 = require('./web3'); // jshint ignore:line
/// should be used when we want to watch something
/// it's using inner polling mechanism and is notified about changes
/// TODO: change 'options' name cause it may be not the best matching one, since we have events
var Filter = function(options, impl) {
/// Should be called to check if filter implementation is valid
/// @returns true if it is, otherwise false
var implementationIsValid = function (i) {
return !!i &&
typeof i.newFilter === 'function' &&
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) {
console.warn('"topics" is deprecated, use "topic" instead');
}
if (options.topics) {
console.warn('"topics" is deprecated, is "topic" instead');
}
this._onWatchResult = options._onWatchEventResult;
// evaluate lazy properties
options = {
to: options.to,
topic: options.topic,
earliest: options.earliest,
latest: options.latest,
max: options.max,
skip: options.skip,
address: options.address
};
// evaluate lazy properties
return {
to: options.to,
topic: options.topic,
earliest: options.earliest,
latest: options.latest,
max: options.max,
skip: options.skip,
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;
this.callbacks = [];
this.id = impl.newFilter(options);
web3.provider.startPolling({method: impl.changed, params: [this.id]}, this.id, this.trigger.bind(this));
};
options = getOptions(options);
var callbacks = [];
var filterId = implementation.newFilter(options);
var onMessages = function (messages) {
messages.forEach(function (message) {
message = formatter ? formatter(message) : message;
callbacks.forEach(function (callback) {
callback(message);
});
});
};
/// alias for changed*
Filter.prototype.arrived = function(callback) {
this.changed(callback);
};
Filter.prototype.happened = function(callback) {
this.changed(callback);
};
implementation.startPolling(filterId, onMessages, implementation.uninstallFilter);
/// gets called when there is new eth/shh message
Filter.prototype.changed = function(callback) {
this.callbacks.push(callback);
};
var changed = function (callback) {
callbacks.push(callback);
};
/// trigger calling new message from people
Filter.prototype.trigger = function(messages) {
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);
}
}
};
var messages = function () {
return implementation.getMessages(filterId);
};
/// should be called to uninstall current filter
Filter.prototype.uninstall = function() {
this.impl.uninstallFilter(this.id);
web3.provider.stopPolling(this.id);
};
var uninstall = function (callback) {
implementation.stopPolling(filterId);
implementation.uninstallFilter(filterId);
callbacks = [];
};
/// should be called to manually trigger getting latest messages from the client
Filter.prototype.messages = function() {
return this.impl.getMessages(this.id);
return {
changed: changed,
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.
@ -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.
@ -913,7 +1052,7 @@ if ("build" !== 'build') {/*
var HttpSyncProvider = function (host) {
this.handlers = [];
this.host = host || 'http://localhost:8080';
this.host = host || 'http://127.0.0.1:8080';
};
HttpSyncProvider.prototype.send = function (payload) {
@ -923,15 +1062,17 @@ HttpSyncProvider.prototype.send = function (payload) {
request.open('POST', this.host, false);
request.send(JSON.stringify(payload));
// check request.status
var result = request.responseText;
// check request.status
if(request.status !== 200)
return;
return JSON.parse(result);
};
module.exports = HttpSyncProvider;
},{}],8:[function(require,module,exports){
},{}],10:[function(require,module,exports){
/*
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.
@ -1015,7 +1191,7 @@ module.exports = {
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 providermanager.js
/** @file requestmanager.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
@ -1024,85 +1200,86 @@ module.exports = {
* @date 2014
*/
var web3 = require('./web3');
var jsonrpc = require('./jsonrpc');
var c = require('./const');
/**
* Provider manager object prototype
* 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
* Default poll timeout is 12 seconds
* 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
* Default poll timeout is 1 second
*/
var ProviderManager = function() {
this.polls = [];
this.provider = undefined;
var requestManager = function() {
var polls = [];
var provider;
var self = this;
var poll = function () {
self.polls.forEach(function (data) {
var result = self.send(data.data);
var send = function (data) {
var payload = jsonrpc.toPayload(data.method, data.params);
if (!(result instanceof Array) || result.length === 0) {
return;
}
if (!provider) {
console.error('provider is not set');
return null;
}
data.callback(result);
});
var result = provider.send(payload);
setTimeout(poll, 1000);
};
poll();
};
if (!jsonrpc.isValidResponse(result)) {
console.log(result);
return null;
}
/// 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);
return result.result;
};
if (this.provider === undefined) {
console.error('provider is not set');
return null;
}
var setProvider = function (p) {
provider = p;
};
var result = this.provider.send(payload);
var startPolling = function (data, pollId, callback, uninstall) {
polls.push({data: data, id: pollId, callback: callback, uninstall: uninstall});
};
if (!jsonrpc.isValidResponse(result)) {
console.log(result);
return null;
}
var stopPolling = function (pollId) {
for (var i = polls.length; i--;) {
var poll = polls[i];
if (poll.id === pollId) {
polls.splice(i, 1);
}
}
};
return result.result;
};
var reset = function () {
polls.forEach(function (poll) {
poll.uninstall(poll.id);
});
polls = [];
};
/// setups provider, which will be used for sending messages
ProviderManager.prototype.set = function(provider) {
this.provider = provider;
};
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);
};
/// this method is only used, when we do not have native qt bindings and have to do polling on our own
/// should be callled, on start watching for eth/shh changes
ProviderManager.prototype.startPolling = function (data, pollId, callback) {
this.polls.push({data: data, id: pollId, callback: callback});
};
poll();
/// should be called to stop polling for certain watch changes
ProviderManager.prototype.stopPolling = function (pollId) {
for (var i = this.polls.length; i--;) {
var poll = this.polls[i];
if (poll.id === pollId) {
this.polls.splice(i, 1);
}
}
return {
send: send,
setProvider: setProvider,
startPolling: startPolling,
stopPolling: stopPolling,
reset: reset
};
};
module.exports = ProviderManager;
module.exports = requestManager;
},{"./jsonrpc":8,"./web3":13}],10:[function(require,module,exports){
},{"./const":2,"./jsonrpc":10}],13:[function(require,module,exports){
/*
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
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file qtsync.js
/** @file shh.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* @date 2014
* @date 2015
*/
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) {
var result = navigator.qt.callMethod(JSON.stringify(payload));
return JSON.parse(result);
module.exports = {
methods: methods
};
module.exports = QtSyncProvider;
},{}],11:[function(require,module,exports){
},{}],14:[function(require,module,exports){
/*
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.
@ -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.
@ -1379,100 +1560,14 @@ module.exports = {
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
/** @file watches.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* Gav Wood <g@ethdev.com>
* @date 2014
* @date 2015
*/
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
var ethWatchMethods = function () {
var eth = function () {
var newFilter = function (args) {
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
var shhWatchMethods = function () {
var shh = function () {
return [
{ name: 'newFilter', call: 'shh_newFilter' },
{ 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
/// setups api calls for these methods
var setupMethods = function (obj, methods) {
@ -1500,7 +1646,7 @@ var setupMethods = function (obj, methods) {
obj[method.name] = function () {
var args = Array.prototype.slice.call(arguments);
var call = typeof method.call === 'function' ? method.call(args) : method.call;
return web3.provider.send({
return web3.manager.send({
method: call,
params: args
});
@ -1514,14 +1660,14 @@ var setupProperties = function (obj, properties) {
properties.forEach(function (property) {
var proto = {};
proto.get = function () {
return web3.provider.send({
return web3.manager.send({
method: property.getter
});
};
if (property.setter) {
proto.set = function (val) {
return web3.provider.send({
return web3.manager.send({
method: property.setter,
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
var web3 = {
_callbacks: {},
_events: {},
manager: requestManager(),
providers: {},
/// @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 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'...)
watch: function (filter, indexed, options) {
if (filter._isEvent) {
return filter(indexed, options);
/// TODO: fix it, 4 params? no way
watch: function (fil, indexed, options, formatter) {
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: {
/// @param filter may be a string, object or event
watch: function (filter, indexed) {
return new web3.filter(filter, shhWatch);
watch: function (fil) {
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
setupMethods(web3, web3Methods());
setupMethods(web3.eth, ethMethods());
setupProperties(web3.eth, ethProperties());
setupMethods(web3.db, dbMethods());
setupMethods(web3.shh, shhMethods());
var ethWatch = {
changed: 'eth_changed'
};
setupMethods(ethWatch, ethWatchMethods());
var shhWatch = {
changed: 'shh_changed'
};
setupMethods(shhWatch, shhWatchMethods());
web3.setProvider = function(provider) {
web3.provider.set(provider);
};
setupMethods(web3.eth, eth.methods());
setupProperties(web3.eth, eth.properties());
setupMethods(web3.db, db.methods());
setupMethods(web3.shh, shh.methods());
setupMethods(ethWatch, watches.eth());
setupMethods(shhWatch, watches.shh());
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 ProviderManager = require('./lib/providermanager');
web3.provider = new ProviderManager();
web3.filter = require('./lib/filter');
web3.providers.HttpSyncProvider = require('./lib/httpsync');
web3.providers.QtSyncProvider = require('./lib/qtsync');
web3.eth.contract = require('./lib/contract');
web3.abi = require('./lib/abi');
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

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

Loading…
Cancel
Save