Browse Source

Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc

Conflicts:
	test/vm.cpp
cl-refactor
artur-zawlocki 10 years ago
parent
commit
0e77fc9d70
  1. 2
      CMakeLists.txt
  2. 4
      CodingStandards.txt
  3. 257
      alethzero/Main.ui
  4. 321
      alethzero/MainWin.cpp
  5. 15
      alethzero/MainWin.h
  6. 1
      evmcc/test/except/badinst1.evm
  7. 16
      exp/main.cpp
  8. 6
      libdevcore/CMakeLists.txt
  9. 2
      libdevcore/Common.cpp
  10. 30
      libdevcore/CommonData.h
  11. 37
      libdevcore/FixedHash.h
  12. 3
      libdevcrypto/All.h
  13. 68
      libdevcrypto/Common.cpp
  14. 27
      libdevcrypto/Common.h
  15. 43
      libdevcrypto/CryptoHeaders.h
  16. 74
      libdevcrypto/CryptoPP.cpp
  17. 85
      libdevcrypto/CryptoPP.h
  18. 77
      libdevcrypto/EC.cpp
  19. 41
      libdevcrypto/EC.h
  20. 3
      libdevcrypto/SHA3.cpp
  21. 39
      libdevcrypto/SHA3MAC.cpp
  22. 38
      libdevcrypto/SHA3MAC.h
  23. 3
      libdevcrypto/TrieDB.cpp
  24. 7
      libdevcrypto/TrieDB.h
  25. 1
      libethcore/All.h
  26. 41
      libethcore/BlockInfo.cpp
  27. 13
      libethcore/BlockInfo.h
  28. 2
      libethcore/CommonEth.cpp
  29. 3
      libethcore/Exceptions.h
  30. 2
      libethcore/ProofOfWork.cpp
  31. 8
      libethereum/BlockChain.cpp
  32. 2
      libethereum/BlockDetails.h
  33. 5
      libethereum/Client.cpp
  34. 2
      libethereum/Client.h
  35. 15
      libethereum/Executive.cpp
  36. 3
      libethereum/Executive.h
  37. 24
      libethereum/ExtVM.h
  38. 4
      libethereum/Manifest.cpp
  39. 2
      libethereum/Manifest.h
  40. 4
      libethereum/MessageFilter.cpp
  41. 2
      libethereum/MessageFilter.h
  42. 150
      libethereum/State.cpp
  43. 75
      libethereum/State.h
  44. 77
      libethereum/Transaction.cpp
  45. 26
      libethereum/Transaction.h
  46. 73
      libevm/ExtVMFace.h
  47. 4
      libevm/FeeStructure.cpp
  48. 4
      libevm/FeeStructure.h
  49. 1
      libevm/VM.cpp
  50. 199
      libevm/VM.h
  51. 7
      libevm/VMFace.cpp
  52. 2
      libevm/VMFace.h
  53. 19
      libevmface/CMakeLists.txt
  54. 21
      libevmface/Instruction.cpp
  55. 12
      libevmface/Instruction.h
  56. 15
      libevmjit/Compiler.cpp
  57. 6
      libevmjit/CompilerHelper.cpp
  58. 7
      libevmjit/CompilerHelper.h
  59. 8
      libevmjit/ExecutionEngine.cpp
  60. 30
      libevmjit/Ext.cpp
  61. 13
      libevmjit/GasMeter.cpp
  62. 2
      libevmjit/GasMeter.h
  63. 18
      libevmjit/Memory.cpp
  64. 12
      libevmjit/Memory.h
  65. 66
      libevmjit/Runtime.cpp
  66. 22
      libevmjit/Runtime.h
  67. 8
      libevmjit/Stack.cpp
  68. 3
      libevmjit/Type.h
  69. 16
      libevmjit/VM.cpp
  70. 4
      liblll/Assembly.cpp
  71. 6
      liblll/Assembly.h
  72. 50
      libp2p/Host.cpp
  73. 2
      libp2p/Host.h
  74. 1
      libp2p/Session.cpp
  75. 12
      libpyserpent/pyserpent.cpp
  76. 275
      libqethereum/QEthereum.cpp
  77. 117
      libqethereum/QEthereum.h
  78. 32
      libserpent/compiler.cpp
  79. 12
      libserpent/funcs.cpp
  80. 6
      libserpent/funcs.h
  81. 4
      libserpent/opcodes.h
  82. 24
      libserpent/parser.cpp
  83. 60
      libserpent/rewriter.cpp
  84. 3
      libserpent/rewriter.h
  85. 6
      libserpent/util.cpp
  86. 481
      libsolidity/AST.cpp
  87. 537
      libsolidity/AST.h
  88. 78
      libsolidity/ASTForward.h
  89. 443
      libsolidity/ASTPrinter.cpp
  90. 117
      libsolidity/ASTPrinter.h
  91. 104
      libsolidity/ASTVisitor.h
  92. 52
      libsolidity/BaseTypes.h
  93. 24
      libsolidity/CMakeLists.txt
  94. 42
      libsolidity/Exceptions.h
  95. 202
      libsolidity/NameAndTypeResolver.cpp
  96. 100
      libsolidity/NameAndTypeResolver.h
  97. 544
      libsolidity/Parser.cpp
  98. 86
      libsolidity/Parser.h
  99. 702
      libsolidity/Scanner.cpp
  100. 204
      libsolidity/Scanner.h

2
CMakeLists.txt

@ -109,6 +109,7 @@ add_subdirectory(libdevcore)
add_subdirectory(libevmface)
add_subdirectory(liblll)
add_subdirectory(libserpent)
add_subdirectory(libsolidity)
if(NOT APPLE)
if(PYTHON_LS)
add_subdirectory(libpyserpent)
@ -116,6 +117,7 @@ if(NOT APPLE)
endif()
add_subdirectory(lllc)
add_subdirectory(solc)
add_subdirectory(sc)
if (NOT LANGUAGES)
add_subdirectory(secp256k1)

4
CodingStandards.txt

@ -72,8 +72,8 @@ All other entities' first alpha is lower case.
4. Variable prefixes:
a. Leading underscore "_" to parameter names (both normal and template).
- Exception: "o_parameterName" when it is used exclusively for output. See 7(f).
- Exception: "io_parameterName" when it is used for both input and output. See 7(f).
- Exception: "o_parameterName" when it is used exclusively for output. See 6(f).
- Exception: "io_parameterName" when it is used for both input and output. See 6(f).
b. Leading "c_" to const variables (unless part of an external API).
c. Leading "g_" to global (non-const) variables.
d. Leading "s_" to static (non-const, non-global) variables.

257
alethzero/Main.ui

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1711</width>
<height>1138</height>
<width>1617</width>
<height>1371</height>
</rect>
</property>
<property name="windowTitle">
@ -116,7 +116,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1711</width>
<width>1617</width>
<height>25</height>
</rect>
</property>
@ -204,11 +204,18 @@
<addaction name="debugStepBackInto"/>
<addaction name="debugStepBackOut"/>
</widget>
<widget class="QMenu" name="menuWhispe">
<property name="title">
<string>&amp;Whisper</string>
</property>
<addaction name="newIdentity"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_View"/>
<addaction name="menu_Network"/>
<addaction name="menu_Tools"/>
<addaction name="menuDebugger"/>
<addaction name="menuWhispe"/>
<addaction name="menu_Debug"/>
<addaction name="menu_Help"/>
</widget>
@ -1514,6 +1521,242 @@ font-size: 14pt</string>
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_14">
<property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set>
</property>
<property name="windowTitle">
<string>Whisper</string>
</property>
<attribute name="dockWidgetArea">
<number>1</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_14">
<layout class="QGridLayout" name="gridLayout_4">
<item row="2" column="4">
<widget class="QSpinBox" name="shhWork">
<property name="suffix">
<string> ms</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>50</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Topic</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>data</cstring>
</property>
</widget>
</item>
<item row="3" column="1" colspan="4">
<widget class="QPlainTextEdit" name="shhTopic">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
</item>
<item row="5" column="1" colspan="4">
<widget class="QPlainTextEdit" name="shhData">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
</item>
<item row="6" column="4">
<widget class="QPushButton" name="post">
<property name="text">
<string>Post</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label5_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TTL</string>
</property>
<property name="buddy">
<cstring>destination</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label5_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>From</string>
</property>
<property name="buddy">
<cstring>destination</cstring>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label5_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>To</string>
</property>
<property name="buddy">
<cstring>destination</cstring>
</property>
</widget>
</item>
<item row="1" column="1" colspan="4">
<widget class="QComboBox" name="shhFrom">
<property name="editable">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1" colspan="4">
<widget class="QComboBox" name="shhTo">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0" rowspan="2">
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Data</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>data</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="shhTtl">
<property name="suffix">
<string> seconds</string>
</property>
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>259200</number>
</property>
</widget>
</item>
<item row="2" column="2" colspan="2">
<widget class="QLabel" name="label5_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Work to Prove</string>
</property>
<property name="buddy">
<cstring>destination</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_15">
<property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set>
</property>
<property name="windowTitle">
<string>Active Whispers</string>
</property>
<attribute name="dockWidgetArea">
<number>2</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_15">
<layout class="QHBoxLayout" name="horizontalLayout_11">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="whispers">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<action name="quit">
<property name="text">
<string>&amp;Quit</string>
@ -1789,6 +2032,9 @@ font-size: 14pt</string>
</property>
</action>
<action name="importKeyFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Claim Ether Presale &amp;Wallet...</string>
</property>
@ -1798,6 +2044,11 @@ font-size: 14pt</string>
<string>Go!</string>
</property>
</action>
<action name="newIdentity">
<property name="text">
<string>New Identity</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

321
alethzero/MainWin.cpp

@ -107,10 +107,10 @@ Main::Main(QWidget *parent) :
m_servers.append(QString::fromStdString(Host::pocHost() + ":30303"));
cerr << "State root: " << BlockChain::genesis().stateRoot << endl;
cerr << "Block Hash: " << sha3(BlockChain::createGenesisBlock()) << endl;
auto block = BlockChain::createGenesisBlock();
cerr << "Block Hash: " << BlockChain::genesis().hash << endl;
cerr << "Block RLP: " << RLP(block) << endl;
cerr << "Block Hex: " << toHex(BlockChain::createGenesisBlock()) << endl;
cerr << "Block Hex: " << toHex(block) << endl;
cerr << "Network protocol version: " << dev::eth::c_protocolVersion << endl;
cerr << "Client database version: " << dev::eth::c_databaseVersion << endl;
@ -131,13 +131,16 @@ Main::Main(QWidget *parent) :
connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved()));
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/AlethZero", false, {"eth", "shh"}));
m_ldb = new QLDB(this);
connect(ui->webView, &QWebView::loadStarted, [this]()
{
m_ethereum = nullptr;
m_whisper = nullptr;
// NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it.
m_dev = new QDev(this);
m_ethereum = new QEthereum(this, ethereum(), owned());
m_whisper = new QWhisper(this, whisper());
m_ethereum = new QEthereum(this, ethereum(), m_myKeys);
m_whisper = new QWhisper(this, whisper(), owned());
QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
QWebFrame* f = ui->webView->page()->mainFrame();
@ -145,12 +148,15 @@ Main::Main(QWidget *parent) :
auto qdev = m_dev;
auto qeth = m_ethereum;
auto qshh = m_whisper;
connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qdev, qeth, qshh));
auto qldb = m_ldb;
connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qdev, qeth, qshh, qldb));
connect(m_whisper, SIGNAL(newIdToAdd(QString)), this, SLOT(addNewId(QString)));
});
connect(ui->webView, &QWebView::loadFinished, [=]()
{
m_ethereum->poll();
m_whisper->poll();
});
connect(ui->webView, &QWebView::titleChanged, [=]()
@ -179,11 +185,20 @@ Main::~Main()
// Must do this here since otherwise m_ethereum'll be deleted (and therefore clearWatches() called by the destructor)
// *after* the client is dead.
m_ethereum->clientDieing();
m_whisper->faceDieing();
g_logPost = simpleDebugOut;
writeSettings();
}
void Main::addNewId(QString _ids)
{
Secret _id = toSecret(_ids);
KeyPair kp(_id);
m_myIdentities.push_back(kp);
m_whisper->setIdentities(owned());
}
dev::p2p::NetworkPreferences Main::netPrefs() const
{
return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked());
@ -331,8 +346,6 @@ void Main::load(QString _s)
}
}
// env.load("/home/gav/eth/init.eth")
void Main::on_loadJS_triggered()
{
QString f = QFileDialog::getOpenFileName(this, "Load Javascript", QString(), "Javascript (*.js);;All files (*)");
@ -387,6 +400,17 @@ void Main::eval(QString const& _js)
ui->jsConsole->setHtml(s);
}
static Public stringToPublic(QString const& _a)
{
string sn = _a.toStdString();
if (_a.size() == sizeof(Public) * 2)
return Public(fromHex(_a.toStdString()));
else if (_a.size() == sizeof(Public) * 2 + 2 && _a.startsWith("0x"))
return Public(fromHex(_a.mid(2).toStdString()));
else
return Public();
}
QString Main::pretty(dev::Address _a) const
{
h256 n;
@ -481,15 +505,28 @@ void Main::on_paranoia_triggered()
void Main::writeSettings()
{
QSettings s("ethereum", "alethzero");
QByteArray b;
b.resize(sizeof(Secret) * m_myKeys.size());
auto p = b.data();
for (auto i: m_myKeys)
{
memcpy(p, &(i.secret()), sizeof(Secret));
p += sizeof(Secret);
QByteArray b;
b.resize(sizeof(Secret) * m_myKeys.size());
auto p = b.data();
for (auto i: m_myKeys)
{
memcpy(p, &(i.secret()), sizeof(Secret));
p += sizeof(Secret);
}
s.setValue("address", b);
}
{
QByteArray b;
b.resize(sizeof(Secret) * m_myIdentities.size());
auto p = b.data();
for (auto i: m_myIdentities)
{
memcpy(p, &(i.secret()), sizeof(Secret));
p += sizeof(Secret);
}
s.setValue("identities", b);
}
s.setValue("address", b);
s.setValue("upnp", ui->upnp->isChecked());
s.setValue("forceAddress", ui->forceAddress->text());
@ -525,21 +562,39 @@ void Main::readSettings(bool _skipGeometry)
restoreGeometry(s.value("geometry").toByteArray());
restoreState(s.value("windowState").toByteArray());
m_myKeys.clear();
QByteArray b = s.value("address").toByteArray();
if (b.isEmpty())
m_myKeys.append(KeyPair::create());
else
{
h256 k;
for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i)
m_myKeys.clear();
QByteArray b = s.value("address").toByteArray();
if (b.isEmpty())
m_myKeys.append(KeyPair::create());
else
{
memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret));
if (!count(m_myKeys.begin(), m_myKeys.end(), KeyPair(k)))
m_myKeys.append(KeyPair(k));
h256 k;
for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i)
{
memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret));
if (!count(m_myKeys.begin(), m_myKeys.end(), KeyPair(k)))
m_myKeys.append(KeyPair(k));
}
}
ethereum()->setAddress(m_myKeys.back().address());
}
ethereum()->setAddress(m_myKeys.back().address());
{
m_myIdentities.clear();
QByteArray b = s.value("identities").toByteArray();
if (!b.isEmpty())
{
h256 k;
for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i)
{
memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret));
if (!count(m_myIdentities.begin(), m_myIdentities.end(), KeyPair(k)))
m_myIdentities.append(KeyPair(k));
}
}
}
m_peers = s.value("peers").toByteArray();
ui->upnp->setChecked(s.value("upnp", true).toBool());
ui->forceAddress->setText(s.value("forceAddress", "").toString());
@ -553,6 +608,8 @@ void Main::readSettings(bool _skipGeometry)
m_enableOptimizer = s.value("enableOptimizer", true).toBool();
ui->enableOptimizer->setChecked(m_enableOptimizer);
ui->clientName->setText(s.value("clientName", "").toString());
if (ui->clientName->text().isEmpty())
ui->clientName->setText(QInputDialog::getText(this, "Enter identity", "Enter a name that will identify you on the peer network"));
ui->idealPeers->setValue(s.value("idealPeers", ui->idealPeers->value()).toInt());
ui->port->setValue(s.value("port", ui->port->value()).toInt());
ui->nameReg->setText(s.value("nameReg", "").toString());
@ -752,7 +809,6 @@ void Main::refreshNetwork()
{
auto ps = web3()->peers();
ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)");
ui->peers->clear();
ui->nodes->clear();
@ -995,12 +1051,15 @@ void Main::timerEvent(QTimerEvent*)
{
interval = 0;
refreshNetwork();
refreshWhispers();
}
else
interval += 100;
if (m_ethereum)
m_ethereum->poll();
if (m_whisper)
m_whisper->poll();
for (auto const& i: m_handlers)
if (ethereum()->checkWatch(i.first))
@ -1168,6 +1227,7 @@ void Main::on_blocks_currentItemChanged()
s << "<br/>Nonce: <b>" << info.nonce << "</b>";
s << "<br/>Parent: <b>" << info.parentHash << "</b>";
s << "<br/>Bloom: <b>" << details.bloom << "</b>";
s << "<br/>Log Bloom: <b>" << info.logBloom << "</b>";
s << "<br/>Transactions: <b>" << block[1].itemCount() << "</b> @<b>" << info.transactionsRoot << "</b>";
s << "<br/>Uncles: <b>" << block[2].itemCount() << "</b> @<b>" << info.sha3Uncles << "</b>";
if (info.parentHash)
@ -1382,6 +1442,112 @@ void Main::on_destination_currentTextChanged()
// updateFee();
}
static bytes dataFromText(QString _s)
{
bytes ret;
while (_s.size())
{
QRegExp r("(@|\\$)?\"([^\"]*)\"(\\s.*)?");
QRegExp d("(@|\\$)?([0-9]+)(\\s*(ether)|(finney)|(szabo))?(\\s.*)?");
QRegExp h("(@|\\$)?(0x)?(([a-fA-F0-9])+)(\\s.*)?");
if (r.exactMatch(_s))
{
for (auto i: r.cap(2))
ret.push_back((byte)i.toLatin1());
if (r.cap(1) != "$")
for (int i = r.cap(2).size(); i < 32; ++i)
ret.push_back(0);
else
ret.push_back(0);
_s = r.cap(3);
}
else if (d.exactMatch(_s))
{
u256 v(d.cap(2).toStdString());
if (d.cap(6) == "szabo")
v *= dev::eth::szabo;
else if (d.cap(5) == "finney")
v *= dev::eth::finney;
else if (d.cap(4) == "ether")
v *= dev::eth::ether;
bytes bs = dev::toCompactBigEndian(v);
if (d.cap(1) != "$")
for (auto i = bs.size(); i < 32; ++i)
ret.push_back(0);
for (auto b: bs)
ret.push_back(b);
_s = d.cap(7);
}
else if (h.exactMatch(_s))
{
bytes bs = fromHex((((h.cap(3).size() & 1) ? "0" : "") + h.cap(3)).toStdString());
if (h.cap(1) != "$")
for (auto i = bs.size(); i < 32; ++i)
ret.push_back(0);
for (auto b: bs)
ret.push_back(b);
_s = h.cap(5);
}
else
_s = _s.mid(1);
}
return ret;
}
static shh::Topic topicFromText(QString _s)
{
shh::BuildTopic ret;
while (_s.size())
{
QRegExp r("(@|\\$)?\"([^\"]*)\"(\\s.*)?");
QRegExp d("(@|\\$)?([0-9]+)(\\s*(ether)|(finney)|(szabo))?(\\s.*)?");
QRegExp h("(@|\\$)?(0x)?(([a-fA-F0-9])+)(\\s.*)?");
bytes part;
if (r.exactMatch(_s))
{
for (auto i: r.cap(2))
part.push_back((byte)i.toLatin1());
if (r.cap(1) != "$")
for (int i = r.cap(2).size(); i < 32; ++i)
part.push_back(0);
else
part.push_back(0);
_s = r.cap(3);
}
else if (d.exactMatch(_s))
{
u256 v(d.cap(2).toStdString());
if (d.cap(6) == "szabo")
v *= dev::eth::szabo;
else if (d.cap(5) == "finney")
v *= dev::eth::finney;
else if (d.cap(4) == "ether")
v *= dev::eth::ether;
bytes bs = dev::toCompactBigEndian(v);
if (d.cap(1) != "$")
for (auto i = bs.size(); i < 32; ++i)
part.push_back(0);
for (auto b: bs)
part.push_back(b);
_s = d.cap(7);
}
else if (h.exactMatch(_s))
{
bytes bs = fromHex((((h.cap(3).size() & 1) ? "0" : "") + h.cap(3)).toStdString());
if (h.cap(1) != "$")
for (auto i = bs.size(); i < 32; ++i)
part.push_back(0);
for (auto b: bs)
part.push_back(b);
_s = h.cap(5);
}
else
_s = _s.mid(1);
ret.shift(part);
}
return ret;
}
void Main::on_data_textChanged()
{
m_pcWarp.clear();
@ -1436,54 +1602,7 @@ void Main::on_data_textChanged()
}
else
{
m_data.clear();
QString s = ui->data->toPlainText();
while (s.size())
{
QRegExp r("(@|\\$)?\"([^\"]*)\"(\\s.*)?");
QRegExp d("(@|\\$)?([0-9]+)(\\s*(ether)|(finney)|(szabo))?(\\s.*)?");
QRegExp h("(@|\\$)?(0x)?(([a-fA-F0-9])+)(\\s.*)?");
if (r.exactMatch(s))
{
for (auto i: r.cap(2))
m_data.push_back((byte)i.toLatin1());
if (r.cap(1) != "$")
for (int i = r.cap(2).size(); i < 32; ++i)
m_data.push_back(0);
else
m_data.push_back(0);
s = r.cap(3);
}
else if (d.exactMatch(s))
{
u256 v(d.cap(2).toStdString());
if (d.cap(6) == "szabo")
v *= dev::eth::szabo;
else if (d.cap(5) == "finney")
v *= dev::eth::finney;
else if (d.cap(4) == "ether")
v *= dev::eth::ether;
bytes bs = dev::toCompactBigEndian(v);
if (d.cap(1) != "$")
for (auto i = bs.size(); i < 32; ++i)
m_data.push_back(0);
for (auto b: bs)
m_data.push_back(b);
s = d.cap(7);
}
else if (h.exactMatch(s))
{
bytes bs = fromHex((((h.cap(3).size() & 1) ? "0" : "") + h.cap(3)).toStdString());
if (h.cap(1) != "$")
for (auto i = bs.size(); i < 32; ++i)
m_data.push_back(0);
for (auto b: bs)
m_data.push_back(b);
s = h.cap(5);
}
else
s = s.mid(1);
}
m_data = dataFromText(ui->data->toPlainText());
ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true)));
if (ethereum()->codeAt(fromString(ui->destination->currentText()), 0).size())
{
@ -1666,6 +1785,7 @@ void Main::on_debug_clicked()
t.gasPrice = gasPrice();
t.gas = ui->gas->value();
t.data = m_data;
t.type = isCreation() ? Transaction::ContractCreation : Transaction::MessageCall;
t.receiveAddress = isCreation() ? Address() : fromString(ui->destination->currentText());
t.sign(s);
auto r = t.rlp();
@ -2012,6 +2132,59 @@ void Main::updateDebugger()
}
}
void Main::on_post_clicked()
{
shh::Message m;
m.setTo(stringToPublic(ui->shhTo->currentText()));
m.setPayload(dataFromText(ui->shhData->toPlainText()));
Public f = stringToPublic(ui->shhFrom->currentText());
Secret from;
if (m_whisper->ids().count(f))
from = m_whisper->ids().at(f);
whisper()->inject(m.seal(from, topicFromText(ui->shhTopic->toPlainText()), ui->shhTtl->value(), ui->shhWork->value()));
}
void Main::on_newIdentity_triggered()
{
m_whisper->makeIdentity();
}
void Main::refreshWhisper()
{
ui->shhFrom->clear();
for (auto i: m_whisper->ids())
ui->shhFrom->addItem(QString::fromStdString(toHex(i.first.ref())));
}
void Main::refreshWhispers()
{
ui->whispers->clear();
for (auto const& w: whisper()->all())
{
shh::Envelope const& e = w.second;
shh::Message m;
for (pair<Public, Secret> const& i: m_whisper->ids())
if (!!(m = e.open(i.second)))
break;
if (!m)
m = e.open();
QString msg;
if (m.from())
// Good message.
msg = QString("{%1 -> %2} %3").arg(m.from() ? m.from().abridged().c_str() : "???").arg(m.to() ? m.to().abridged().c_str() : "*").arg(toHex(m.payload()).c_str());
else if (m)
// Maybe message.
msg = QString("{%1 -> %2} %3 (?)").arg(m.from() ? m.from().abridged().c_str() : "???").arg(m.to() ? m.to().abridged().c_str() : "*").arg(toHex(m.payload()).c_str());
time_t ex = e.expiry();
QString t(ctime(&ex));
t.chop(1);
QString item = QString("[%1 - %2s] *%3 %5 %4").arg(t).arg(e.ttl()).arg(e.workProved()).arg(toString(e.topic()).c_str()).arg(msg);
ui->whispers->addItem(item);
}
}
// extra bits needed to link on VS
#ifdef _MSC_VER

15
alethzero/MainWin.h

@ -77,7 +77,7 @@ public:
dev::eth::Client* ethereum() const { return m_webThree->ethereum(); }
std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); }
QList<dev::KeyPair> const& owned() const { return m_myKeys; }
QList<dev::KeyPair> owned() const { return m_myIdentities + m_myKeys; }
public slots:
void load(QString _file);
@ -149,9 +149,15 @@ private slots:
void on_turboMining_triggered();
void on_go_triggered();
void on_importKeyFile_triggered();
void on_post_clicked();
void on_newIdentity_triggered();
void refreshWhisper();
void addNewId(QString);
signals:
void poll();
void idsChanged();
private:
dev::p2p::NetworkPreferences netPrefs() const;
@ -175,6 +181,8 @@ private:
void readSettings(bool _skipGeometry = false);
void writeSettings();
void keysChanged();
bool isCreation() const;
dev::u256 fee() const;
dev::u256 total() const;
@ -185,8 +193,6 @@ private:
unsigned installWatch(dev::h256 _tf, std::function<void()> const& _f);
void uninstallWatch(unsigned _w);
void keysChanged();
void onNewPending();
void onNewBlock();
void onNameRegChange();
@ -202,6 +208,7 @@ private:
void refreshNetwork();
void refreshMining();
void refreshWhispers();
void refreshAll();
void refreshPending();
@ -223,6 +230,7 @@ private:
QByteArray m_peers;
QStringList m_servers;
QList<dev::KeyPair> m_myKeys;
QList<dev::KeyPair> m_myIdentities;
QString m_privateChain;
dev::bytes m_data;
dev::Address m_nameReg;
@ -250,4 +258,5 @@ private:
QDev* m_dev = nullptr;
QEthereum* m_ethereum = nullptr;
QWhisper* m_whisper = nullptr;
QLDB* m_ldb = nullptr;
};

1
evmcc/test/except/badinst1.evm

@ -0,0 +1 @@
4a

16
exp/main.cpp

@ -95,25 +95,27 @@ int main(int argc, char** argv)
}
Host ph("Test", NetworkPreferences(listenPort, "", false, true));
ph.registerCapability(new WhisperHost());
auto wh = ph.cap<WhisperHost>();
auto wh = ph.registerCapability(new WhisperHost());
ph.start();
if (!remoteHost.empty())
ph.connect(remoteHost, remotePort);
/// Only interested in the packet if the lowest bit is 1
auto w = wh->installWatch(MessageFilter(TopicMasks({{Topic("0000000000000000000000000000000000000000000000000000000000000001"), Topic("0000000000000000000000000000000000000000000000000000000000000001")}})));
/// Only interested in odd packets
auto w = wh->installWatch(BuildTopicMask()("odd"));
KeyPair us = KeyPair::create();
for (int i = 0; ; ++i)
{
wh->sendRaw(RLPStream().append(i * i).out(), Topic(u256(i)), 1000);
wh->post(us.sec(), RLPStream().append(i * i).out(), BuildTopic(i)(i % 2 ? "odd" : "even"));
for (auto i: wh->checkWatch(w))
{
auto p = wh->message(i).payload;
cnote << "New message:" << RLP(p).toInt<unsigned>();
Message msg = wh->envelope(i).open();
cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>();
}
this_thread::sleep_for(chrono::seconds(1));
}
return 0;
}

6
libdevcore/CMakeLists.txt

@ -19,8 +19,6 @@ endif()
include_directories(..)
target_link_libraries(${EXECUTABLE} devcore)
if("${TARGET_PLATFORM}" STREQUAL "w64")
include_directories(/usr/x86_64-w64-mingw32/include/cryptopp)
target_link_libraries(${EXECUTABLE} boost_system-mt-s)
@ -37,7 +35,9 @@ elseif (APPLE)
find_package(Threads REQUIRED)
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
elseif (UNIX)
target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY})
find_package(Boost 1.53 REQUIRED COMPONENTS thread system)
target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY})
find_package(Threads REQUIRED)
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
else ()
target_link_libraries(${EXECUTABLE} boost_thread)

2
libdevcore/Common.cpp

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

30
libdevcore/CommonData.h

@ -190,7 +190,7 @@ void pushFront(_T& _t, _U _e)
_t[0] = _e;
}
/// Concatenate two vectors of elements. _T must be POD.
/// Concatenate two vectors of elements of POD types.
template <class _T>
inline std::vector<_T>& operator+=(std::vector<typename std::enable_if<std::is_pod<_T>::value, _T>::type>& _a, std::vector<_T> const& _b)
{
@ -201,30 +201,38 @@ inline std::vector<_T>& operator+=(std::vector<typename std::enable_if<std::is_p
}
/// Concatenate two vectors of elements. _T must be POD.
/// Concatenate two vectors of elements.
template <class _T>
inline std::vector<_T> operator+(std::vector<typename std::enable_if<std::is_pod<_T>::value, _T>::type> const& _a, std::vector<_T> const& _b)
inline std::vector<_T>& operator+=(std::vector<typename std::enable_if<!std::is_pod<_T>::value, _T>::type>& _a, std::vector<_T> const& _b)
{
_a.reserve(_a.size() + _b.size());
for (auto& i: _b)
_a.push_back(i);
return _a;
}
/// Concatenate two vectors of elements.
template <class _T>
inline std::vector<_T> operator+(std::vector<_T> const& _a, std::vector<_T> const& _b)
{
std::vector<_T> ret(_a);
return ret += _b;
}
/// Concatenate two vectors of elements. _T must be POD.
/// Merge two sets of elements.
template <class _T>
inline std::vector<_T>& operator+=(std::vector<typename std::enable_if<!std::is_pod<_T>::value, _T>::type>& _a, std::vector<_T> const& _b)
inline std::set<_T>& operator+=(std::set<_T>& _a, std::set<_T> const& _b)
{
_a.reserve(_a.size() + _b.size());
for (auto& i: _b)
_a.push_back(i);
_a.insert(i);
return _a;
}
/// Concatenate two vectors of elements. _T must be POD.
/// Merge two sets of elements.
template <class _T>
inline std::vector<_T> operator+(std::vector<typename std::enable_if<!std::is_pod<_T>::value, _T>::type> const& _a, std::vector<_T> const& _b)
inline std::set<_T> operator+(std::set<_T> const& _a, std::set<_T> const& _b)
{
std::vector<_T> ret(_a);
std::set<_T> ret(_a);
return ret += _b;
}

37
libdevcore/FixedHash.h

@ -59,7 +59,7 @@ public:
FixedHash() { m_data.fill(0); }
/// Construct from another hash, filling with zeroes or cropping as necessary.
template <unsigned M> FixedHash(FixedHash<M> const& _h, ConstructFromHashType _t = AlignLeft) { m_data.fill(0); unsigned c = std::min(M, N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i]; }
template <unsigned M> explicit FixedHash(FixedHash<M> const& _h, ConstructFromHashType _t = AlignLeft) { m_data.fill(0); unsigned c = std::min(M, N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i]; }
/// Convert from the corresponding arithmetic type.
FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); }
@ -158,6 +158,40 @@ public:
return ret;
}
template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> const& _h) { return (*this |= _h.template nbloom<P, N>()); }
template <unsigned P, unsigned M> inline FixedHash<M> nbloom() const
{
static const unsigned c_bloomBytes = (M + 7) / 8;
unsigned mask = (1 << c_bloomBytes) - 1;
FixedHash<M> ret;
byte const* p = data();
for (unsigned i = 0; i < P; ++i)
{
unsigned index = 0;
for (unsigned j = 0; j < c_bloomBytes; ++j, ++p)
index = (index << 8) | *p;
index &= mask;
ret[N - 1 - index / 8] |= (1 << (index % 8));
}
return ret;
}
/// Returns the index of the first bit set to one, or size() * 8 if no bits are set.
inline unsigned firstBitSet() const
{
unsigned ret = 0;
for (auto d: m_data)
if (d)
for (;; ++ret, d <<= 1)
if (d & 0x80)
return ret;
else {}
else
ret += 8;
return ret;
}
private:
std::array<byte, N> m_data; ///< The binary data.
};
@ -193,6 +227,7 @@ inline std::ostream& operator<<(std::ostream& _out, FixedHash<N> const& _h)
}
// Common types of FixedHash.
using h520 = FixedHash<65>;
using h512 = FixedHash<64>;
using h256 = FixedHash<32>;
using h160 = FixedHash<20>;

3
libdevcrypto/All.h

@ -1,9 +1,12 @@
#pragma once
#include "Common.h"
#include "CryptoPP.h"
#include "EC.h"
#include "FileSystem.h"
#include "MemoryDB.h"
#include "OverlayDB.h"
#include "SHA3.h"
#include "SHA3MAC.h"
#include "TrieCommon.h"
#include "TrieDB.h"

68
libdevcrypto/Common.cpp

@ -22,6 +22,7 @@
#include "Common.h"
#include <random>
#include <secp256k1/secp256k1.h>
#include "EC.h"
#include "SHA3.h"
using namespace std;
using namespace dev;
@ -108,3 +109,70 @@ KeyPair KeyPair::fromEncryptedSeed(bytesConstRef _seed, std::string const& _pass
return KeyPair(sha3(aesDecrypt(_seed, _password)));
}
void dev::encrypt(Public _k, bytesConstRef _plain, bytes& o_cipher)
{
bytes io = _plain.toBytes();
crypto::encrypt(_k, io);
o_cipher = std::move(io);
}
bool dev::decrypt(Secret _k, bytesConstRef _cipher, bytes& o_plaintext)
{
bytes io = _cipher.toBytes();
crypto::decrypt(_k, io);
if (io.empty())
return false;
o_plaintext = std::move(io);
return true;
}
Public dev::recover(Signature _sig, h256 _message)
{
secp256k1_start();
byte pubkey[65];
int pubkeylen = 65;
if (!secp256k1_ecdsa_recover_compact(_message.data(), 32, _sig.data(), pubkey, &pubkeylen, 0, (int)_sig[64]))
return Public();
// right160(dev::sha3(bytesConstRef(&(pubkey[1]), 64)));
#if ETH_CRYPTO_TRACE
h256* sig = (h256 const*)_sig.data();
cout << "---- RECOVER -------------------------------" << endl;
cout << "MSG: " << _message << endl;
cout << "R S V: " << sig[0] << " " << sig[1] << " " << (int)(_sig[64] - 27) << "+27" << endl;
cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl;
#endif
Public ret;
memcpy(&ret, &(pubkey[1]), sizeof(Public));
return ret;
}
inline h256 kFromMessage(h256 _msg, h256 _priv)
{
return _msg ^ _priv;
}
Signature dev::sign(Secret _k, h256 _message)
{
int v = 0;
secp256k1_start();
SignatureStruct ret;
h256 nonce = kFromMessage(_message, _k);
if (!secp256k1_ecdsa_sign_compact(_message.data(), 32, ret.r.data(), _k.data(), nonce.data(), &v))
return Signature();
#if ETH_ADDRESS_DEBUG
cout << "---- SIGN -------------------------------" << endl;
cout << "MSG: " << _message << endl;
cout << "SEC: " << _k << endl;
cout << "NON: " << nonce << endl;
cout << "R S V: " << ret.r << " " << ret.s << " " << v << "+27" << endl;
#endif
ret.v = v;
return *(Signature const*)&ret;
}

27
libdevcrypto/Common.h

@ -37,6 +37,12 @@ using Secret = h256;
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Public = h512;
/// A signature: 65 bytes: r: [0, 32), s: [32, 64), v: 64.
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Signature = h520;
struct SignatureStruct { h256 r; h256 s; byte v; };
/// An Ethereum address: 20 bytes.
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Address = h160;
@ -44,9 +50,24 @@ using Address = h160;
/// A vector of Ethereum addresses.
using Addresses = h160s;
/// Convert a private key into the public key equivalent.
/// @returns 0 if it's not a valid private key.
Address toAddress(h256 _private);
/// A vector of secrets.
using Secrets = h256s;
/// Convert a secret key into the public key equivalent.
/// @returns 0 if it's not a valid secret key.
Address toAddress(Secret _secret);
/// Encrypts plain text using Public key.
void encrypt(Public _k, bytesConstRef _plain, bytes& o_cipher);
/// Decrypts cipher using Secret key.
bool decrypt(Secret _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Recovers Public key from signed message.
Public recover(Signature _sig, h256 _message);
/// Returns siganture of message hash.
Signature sign(Secret _k, h256 _message);
/// Simple class that represents a "key pair".
/// All of the data of the class can be regenerated from the secret key (m_secret) alone.

43
libdevcrypto/CryptoHeaders.h

@ -1,43 +0,0 @@
/*
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 CryptoHeaders.h
* @author Tim Hughes <tim@twistedfury.com>
* @date 2014
*/
#pragma once
// need to leave this one disabled
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma warning(push)
#pragma warning(disable:4100 4244)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
#pragma GCC diagnostic ignored "-Wextra"
#include <sha.h>
#include <sha3.h>
#include <ripemd.h>
#include <aes.h>
#include <pwdbased.h>
#include <modes.h>
#include <filters.h>
#include <secp256k1/secp256k1.h>
#pragma warning(pop)
#pragma GCC diagnostic pop

74
libdevcrypto/CryptoPP.cpp

@ -0,0 +1,74 @@
/*
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 CryptoPP.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
#include "CryptoPP.h"
using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
ECP::Point pp::PointFromPublic(Public const& _p)
{
ECP::Point p;
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> pub;
pub.AccessGroupParameters().Initialize(pp::secp256k1());
bytes prefixedKey(pub.GetGroupParameters().GetEncodedElementSize(true));
prefixedKey[0] = 0x04;
assert(Public::size == prefixedKey.size() - 1);
memcpy(&prefixedKey[1], _p.data(), prefixedKey.size() - 1);
pub.GetGroupParameters().GetCurve().DecodePoint(p, prefixedKey.data(), prefixedKey.size());
return std::move(p);
}
Integer pp::ExponentFromSecret(Secret const& _s)
{
static_assert(Secret::size == 32, "Secret key must be 32 bytes.");
return std::move(Integer(_s.data(), Secret::size));
}
void pp::PublicFromExponent(Integer const& _e, Public& _p)
{
CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> k;
k.AccessGroupParameters().Initialize(secp256k1());
k.SetPrivateExponent(_e);
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> p;
p.AccessGroupParameters().Initialize(secp256k1());
k.MakePublicKey(p);
pp::PublicFromDL_PublicKey_EC(p, _p);
}
void pp::PublicFromDL_PublicKey_EC(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p)
{
bytes prefixedKey(_k.GetGroupParameters().GetEncodedElementSize(true));
_k.GetGroupParameters().GetCurve().EncodePoint(prefixedKey.data(), _k.GetPublicElement(), false);
static_assert(Public::size == 64, "Public key must be 64 bytes.");
assert(Public::size + 1 == _k.GetGroupParameters().GetEncodedElementSize(true));
memcpy(_p.data(), &prefixedKey[1], Public::size);
}
void pp::SecretFromDL_PrivateKey_EC(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s)
{
_k.GetPrivateExponent().Encode(_s.data(), Secret::size);
}

85
libdevcrypto/CryptoPP.h

@ -0,0 +1,85 @@
/*
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 CryptoPP.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* CryptoPP headers and helper methods
*/
#pragma once
// need to leave this one disabled for link-time. blame cryptopp.
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma warning(push)
#pragma warning(disable:4100 4244)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
#pragma GCC diagnostic ignored "-Wextra"
#include <sha.h>
#include <sha3.h>
#include <ripemd.h>
#include <aes.h>
#include <pwdbased.h>
#include <modes.h>
#include <filters.h>
#include <eccrypto.h>
#include <ecp.h>
#include <files.h>
#include <osrng.h>
#include <oids.h>
#include <secp256k1/secp256k1.h>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include "Common.h"
namespace dev
{
namespace crypto
{
namespace pp
{
/// RNG used by CryptoPP
inline CryptoPP::AutoSeededRandomPool& PRNG() { static CryptoPP::AutoSeededRandomPool prng; return prng; }
/// EC curve used by CryptoPP
inline CryptoPP::OID const& secp256k1() { static CryptoPP::OID curve = CryptoPP::ASN1::secp256k1(); return curve; }
/// Conversion from bytes to cryptopp point
CryptoPP::ECP::Point PointFromPublic(Public const& _p);
/// Conversion from bytes to cryptopp exponent
CryptoPP::Integer ExponentFromSecret(Secret const& _s);
/// Conversion from cryptopp exponent Integer to bytes
void PublicFromExponent(CryptoPP::Integer const& _k, Public& _s);
/// Conversion from cryptopp public key to bytes
void PublicFromDL_PublicKey_EC(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p);
/// Conversion from cryptopp private key to bytes
void SecretFromDL_PrivateKey_EC(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s);
}
}
}

77
libdevcrypto/EC.cpp

@ -0,0 +1,77 @@
/*
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 EC.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* Shared EC classes and functions.
*/
#pragma warning(push)
#pragma warning(disable:4100 4244)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
#pragma GCC diagnostic ignored "-Wextra"
#include <files.h>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include "CryptoPP.h"
#include "SHA3.h"
#include "EC.h"
// CryptoPP and dev conflict so dev and pp namespace are used explicitly
using namespace std;
using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
void dev::crypto::encrypt(Public const& _key, bytes& io_cipher)
{
ECIES<ECP>::Encryptor e;
e.AccessKey().AccessGroupParameters().Initialize(pp::secp256k1());
e.AccessKey().SetPublicElement(pp::PointFromPublic(_key));
size_t plen = io_cipher.size();
bytes c;
c.resize(e.CiphertextLength(plen));
// todo: use StringSource with _plain as input and output.
e.Encrypt(pp::PRNG(), io_cipher.data(), plen, c.data());
memset(io_cipher.data(), 0, io_cipher.size());
io_cipher = std::move(c);
}
void dev::crypto::decrypt(Secret const& _k, bytes& io_text)
{
CryptoPP::ECIES<CryptoPP::ECP>::Decryptor d;
d.AccessKey().AccessGroupParameters().Initialize(pp::secp256k1());
d.AccessKey().SetPrivateExponent(pp::ExponentFromSecret(_k));
size_t clen = io_text.size();
bytes p;
p.resize(d.MaxPlaintextLength(io_text.size()));
// todo: use StringSource with _c as input and output.
DecodingResult r = d.Decrypt(pp::PRNG(), io_text.data(), clen, p.data());
if (!r.isValidCoding)
{
io_text.clear();
return;
}
io_text.resize(r.messageLength);
io_text = std::move(p);
}

41
libdevcrypto/EC.h

@ -0,0 +1,41 @@
/*
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 EC.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* Shared EC classes and functions.
*/
#pragma once
#include "Common.h"
namespace dev
{
namespace crypto
{
/// Encrypts text (in place).
void encrypt(Public const& _k, bytes& io_cipher);
/// Decrypts text (in place).
void decrypt(Secret const& _k, bytes& io_text);
}
}

3
libdevcrypto/SHA3.cpp

@ -20,8 +20,9 @@
*/
#include "SHA3.h"
#include "CryptoHeaders.h"
#include <libdevcore/RLP.h>
#include "CryptoPP.h"
using namespace std;
using namespace dev;

39
libdevcrypto/SHA3MAC.cpp

@ -0,0 +1,39 @@
/*
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 SHA3MAC.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* SHA3 MAC
*/
#include "CryptoPP.h"
#include "SHA3MAC.h"
using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output)
{
CryptoPP::SHA3_256 ctx;
ctx.Update((byte*)_secret.data(), _secret.size());
ctx.Update((byte*)_plain.data(), _plain.size());
assert(_output.size() >= 32);
ctx.Final(_output.data());
}

38
libdevcrypto/SHA3MAC.h

@ -0,0 +1,38 @@
/*
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 SHA3MAC.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* SHA3 MAC
*/
#pragma once
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
namespace dev
{
namespace crypto
{
void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output);
}
}

3
libdevcrypto/TrieDB.cpp

@ -26,6 +26,7 @@ using namespace dev;
#if !ETH_LANGUAGES
const h256 dev::c_shaNull = sha3(rlp(""));
h256 const dev::c_shaNull = sha3(rlp(""));
h256 const dev::EmptyTrie = c_shaNull;
#endif

7
libdevcrypto/TrieDB.h

@ -45,6 +45,7 @@ struct TrieDBChannel: public LogChannel { static const char* name() { return "-
struct InvalidTrie: virtual dev::Exception {};
extern const h256 c_shaNull;
extern const h256 EmptyTrie;
/**
* @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree.
@ -74,7 +75,7 @@ public:
void init();
void setRoot(h256 _root)
{
m_root = _root == h256() ? c_shaNull : _root;
m_root = _root;
if (m_root == c_shaNull && !m_db->exists(m_root))
init();
@ -82,14 +83,14 @@ public:
if (!node(m_root).size())
BOOST_THROW_EXCEPTION(RootNotFound());
}
bool haveRoot(h256 _root, bool _enforceRefs = true) { return _root == h256() ? true : m_db->lookup(_root, _enforceRefs).size(); }
bool haveRoot(h256 _root, bool _enforceRefs = true) { return _root == c_shaNull ? true : m_db->lookup(_root, _enforceRefs).size(); }
/// True if the trie is uninitialised (i.e. that the DB doesn't contain the root node).
bool isNull() const { return !node(m_root).size(); }
/// True if the trie is initialised but empty (i.e. that the DB contains the root node which is empty).
bool isEmpty() const { return m_root == c_shaNull && node(m_root).size(); }
h256 root() const { assert(node(m_root).size()); h256 ret = (m_root == c_shaNull ? h256() : m_root); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return ret; } // patch the root in the case of the empty trie. TODO: handle this properly.
h256 root() const { assert(node(m_root).size()); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return m_root; } // patch the root in the case of the empty trie. TODO: handle this properly.
void debugPrint() {}

1
libethcore/All.h

@ -3,6 +3,5 @@
#include "BlockInfo.h"
#include "CommonEth.h"
#include "ProofOfWork.h"
#include "CryptoHeaders.h"
#include "Exceptions.h"

41
libethcore/BlockInfo.cpp

@ -37,9 +37,9 @@ BlockInfo::BlockInfo(): timestamp(Invalid256)
{
}
BlockInfo::BlockInfo(bytesConstRef _block)
BlockInfo::BlockInfo(bytesConstRef _block, bool _checkNonce)
{
populate(_block);
populate(_block, _checkNonce);
}
BlockInfo BlockInfo::fromHeader(bytesConstRef _block)
@ -52,15 +52,17 @@ BlockInfo BlockInfo::fromHeader(bytesConstRef _block)
h256 BlockInfo::headerHashWithoutNonce() const
{
RLPStream s;
fillStream(s, false);
streamRLP(s, false);
return sha3(s.out());
}
void BlockInfo::fillStream(RLPStream& _s, bool _nonce) const
auto static const c_sha3EmptyList = sha3(RLPEmptyList);
void BlockInfo::streamRLP(RLPStream& _s, bool _nonce) const
{
_s.appendList(_nonce ? 13 : 12) << parentHash << sha3Uncles << coinbaseAddress;
_s.append(stateRoot, false, true).append(transactionsRoot, false, true);
_s << difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData;
_s.appendList(_nonce ? 15 : 14)
<< parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot << receiptsRoot << logBloom
<< difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData;
if (_nonce)
_s << nonce;
}
@ -82,17 +84,19 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce)
coinbaseAddress = _header[field = 2].toHash<Address>();
stateRoot = _header[field = 3].toHash<h256>();
transactionsRoot = _header[field = 4].toHash<h256>();
difficulty = _header[field = 5].toInt<u256>();
number = _header[field = 6].toInt<u256>();
minGasPrice = _header[field = 7].toInt<u256>();
gasLimit = _header[field = 8].toInt<u256>();
gasUsed = _header[field = 9].toInt<u256>();
timestamp = _header[field = 10].toInt<u256>();
extraData = _header[field = 11].toBytes();
nonce = _header[field = 12].toHash<h256>();
receiptsRoot = _header[field = 5].toHash<h256>();
logBloom = _header[field = 6].toHash<h512>();
difficulty = _header[field = 7].toInt<u256>();
number = _header[field = 8].toInt<u256>();
minGasPrice = _header[field = 9].toInt<u256>();
gasLimit = _header[field = 10].toInt<u256>();
gasUsed = _header[field = 11].toInt<u256>();
timestamp = _header[field = 12].toInt<u256>();
extraData = _header[field = 13].toBytes();
nonce = _header[field = 14].toHash<h256>();
}
catch (Exception & _e)
catch (Exception const& _e)
{
_e << errinfo_name("invalid block header format") << BadFieldError(field, toHex(_header[field].data().toBytes()));
throw;
@ -138,7 +142,7 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
{
bytes k = rlp(i);
t.insert(&k, tr.data());
u256 gp = tr[0][1].toInt<u256>();
u256 gp = tr[1].toInt<u256>();
mgp = min(mgp, gp);
++i;
}
@ -179,8 +183,7 @@ u256 BlockInfo::calculateDifficulty(BlockInfo const& _parent) const
}
void BlockInfo::verifyParent(BlockInfo const& _parent) const
{
// Check difficulty is correct given the two timestamps.
{ // Check difficulty is correct given the two timestamps.
if (difficulty != calculateDifficulty(_parent))
BOOST_THROW_EXCEPTION(InvalidDifficulty());

13
libethcore/BlockInfo.h

@ -47,7 +47,7 @@ extern u256 c_genesisDifficulty;
* corresponding RLP block created with createGenesisBlock().
*
* The difficulty and gas-limit derivations may be calculated with the calculateDifficulty()
* and calculateGasLimit() and the object serialised to RLP with fillStream. To determine the
* and calculateGasLimit() and the object serialised to RLP with streamRLP. To determine the
* header hash without the nonce (for mining), the method headerHashWithoutNonce() is provided.
*
* The default constructor creates an empty object, which can be tested against with the boolean
@ -62,6 +62,8 @@ public:
Address coinbaseAddress;
h256 stateRoot;
h256 transactionsRoot;
h256 receiptsRoot;
h512 logBloom; // TODO LogBloom - get include
u256 difficulty;
u256 number;
u256 minGasPrice;
@ -73,7 +75,7 @@ public:
BlockInfo();
explicit BlockInfo(bytes const& _block): BlockInfo(&_block) {}
explicit BlockInfo(bytesConstRef _block);
explicit BlockInfo(bytesConstRef _block, bool _checkNonce = true);
static h256 headerHash(bytes const& _block) { return headerHash(&_block); }
static h256 headerHash(bytesConstRef _block);
@ -89,6 +91,8 @@ public:
coinbaseAddress == _cmp.coinbaseAddress &&
stateRoot == _cmp.stateRoot &&
transactionsRoot == _cmp.transactionsRoot &&
receiptsRoot == _cmp.receiptsRoot &&
logBloom == _cmp.logBloom &&
difficulty == _cmp.difficulty &&
number == _cmp.number &&
minGasPrice == _cmp.minGasPrice &&
@ -112,13 +116,14 @@ public:
/// No-nonce sha3 of the header only.
h256 headerHashWithoutNonce() const;
void fillStream(RLPStream& _s, bool _nonce) const;
void streamRLP(RLPStream& _s, bool _nonce) const;
};
inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi)
{
_out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " <<
_bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " << _bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce;
_bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " <<
_bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce;
return _out;
}

2
libethcore/CommonEth.cpp

@ -34,7 +34,7 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 35;
const unsigned c_protocolVersion = 37;
const unsigned c_databaseVersion = 3;
static const vector<pair<u256, string>> g_units =

3
libethcore/Exceptions.h

@ -57,7 +57,8 @@ struct InvalidDifficulty: virtual dev::Exception {};
class InvalidGasLimit: virtual public dev::Exception { public: InvalidGasLimit(u256 _provided = 0, u256 _valid = 0): provided(_provided), valid(_valid) {} u256 provided; u256 valid; virtual const char* what() const noexcept; };
class InvalidMinGasPrice: virtual public dev::Exception { public: InvalidMinGasPrice(u256 _provided = 0, u256 _limit = 0): provided(_provided), limit(_limit) {} u256 provided; u256 limit; virtual const char* what() const noexcept; };
struct InvalidTransactionGasUsed: virtual dev::Exception {};
struct InvalidTransactionStateRoot: virtual dev::Exception {};
struct InvalidTransactionsStateRoot: virtual dev::Exception {};
struct InvalidReceiptsStateRoot: virtual dev::Exception {};
struct InvalidTimestamp: virtual dev::Exception {};
class InvalidNonce: virtual public dev::Exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; virtual const char* what() const noexcept; };
class InvalidBlockNonce: virtual public dev::Exception { public: InvalidBlockNonce(h256 _h = h256(), h256 _n = h256(), u256 _d = 0): h(_h), n(_n), d(_d) {} h256 h; h256 n; u256 d; virtual const char* what() const noexcept; };

2
libethcore/ProofOfWork.cpp

@ -26,7 +26,7 @@
#include <array>
#include <random>
#include <thread>
#include <libethcore/CryptoHeaders.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcore/Common.h>
#include "ProofOfWork.h"
using namespace std;

8
libethereum/BlockChain.cpp

@ -91,7 +91,6 @@ ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub)
bytes BlockChain::createGenesisBlock()
{
RLPStream block(3);
auto sha3EmptyList = sha3(RLPEmptyList);
h256 stateRoot;
{
@ -102,8 +101,9 @@ bytes BlockChain::createGenesisBlock()
stateRoot = state.root();
}
block.appendList(13) << h256() << sha3EmptyList << h160();
block.append(stateRoot, false, true) << bytes() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
block.appendList(15)
// TODO: maybe make logbloom correct?
<< h256() << EmptySHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
@ -306,7 +306,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
// Get total difficulty increase and update state, checking it.
State s(bi.coinbaseAddress, _db);
auto tdIncrease = s.enactOn(&_block, bi, *this);
auto b = s.bloom();
auto b = s.oldBloom();
BlockBlooms bb;
BlockTraces bt;
for (unsigned i = 0; i < s.pending().size(); ++i)

2
libethereum/BlockDetails.h

@ -66,7 +66,7 @@ struct BlockTraces
{
BlockTraces() {}
BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); }
bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamOut(s); return s.out(); }
bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamRLP(s); return s.out(); }
Manifests traces;
};

5
libethereum/Client.cpp

@ -159,7 +159,7 @@ void Client::clearPending()
if (!m_postMine.pending().size())
return;
for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
appendFromNewPending(m_postMine.bloom(i), changeds);
appendFromNewPending(m_postMine.oldBloom(i), changeds);
changeds.insert(PendingChangedFilter);
m_postMine = m_preMine;
}
@ -325,6 +325,7 @@ void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _
t.value = _value;
t.gasPrice = _gasPrice;
t.gas = _gas;
t.type = Transaction::MessageCall;
t.receiveAddress = _dest;
t.data = _data;
t.sign(_secret);
@ -348,6 +349,7 @@ bytes Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _dat
t.value = _value;
t.gasPrice = _gasPrice;
t.gas = _gas;
t.type = Transaction::ContractCreation;
t.receiveAddress = _dest;
t.data = _data;
t.sign(_secret);
@ -373,6 +375,7 @@ Address Client::transact(Secret _secret, u256 _endowment, bytes const& _init, u2
t.value = _endowment;
t.gasPrice = _gasPrice;
t.gas = _gas;
t.type = Transaction::ContractCreation;
t.receiveAddress = Address();
t.data = _init;
t.sign(_secret);

2
libethereum/Client.h

@ -285,7 +285,7 @@ private:
State m_preMine; ///< The present state of the client.
State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
std::weak_ptr<EthereumHost> m_host; ///< Our Ethereum Host. Don't do anything if we can't lock.
std::weak_ptr<EthereumHost> m_host; ///< Our Ethereum Host. Don't do anything if we can't lock.
std::vector<Miner> m_miners;
mutable boost::shared_mutex x_miners;

15
libethereum/Executive.cpp

@ -123,7 +123,11 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu
m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms);
}
else
{
m_endGas = _gas;
if (m_ext)
m_ext->sub.logs.push_back(LogEntry(_receiveAddress, {u256((u160)_senderAddress) + 1}, bytes()));
}
return !m_ext;
}
@ -172,6 +176,8 @@ bool Executive::go(OnOpFunc const& _onOp)
try
{
m_out = m_vm->go(*m_ext, _onOp);
if (m_ext)
m_endGas += min((m_t.gas - m_endGas) / 2, m_ext->sub.refunds);
m_endGas = m_vm->gas();
}
catch (StepsDone const&)
@ -187,14 +193,17 @@ bool Executive::go(OnOpFunc const& _onOp)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
m_endGas = m_vm->gas();
revert = true;
}
catch (Exception const& _e)
{
clog(StateChat) << "Exception in VM: " << diagnostic_information(_e);
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e);
}
catch (std::exception const& _e)
{
clog(StateChat) << "std::exception in VM: " << _e.what();
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
}
cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
@ -236,6 +245,6 @@ void Executive::finalize(OnOpFunc const&)
// Suicides...
if (m_ext)
for (auto a: m_ext->suicides)
for (auto a: m_ext->sub.suicides)
m_s.m_cache[a].kill();
}

3
libethereum/Executive.h

@ -60,6 +60,7 @@ public:
bytesConstRef out() const { return m_out; }
h160 newAddress() const { return m_newAddress; }
LogEntries const& logs() const { return m_logs; }
VMFace const& vm() const { return *m_vm; }
State const& state() const { return m_s; }
@ -76,6 +77,8 @@ private:
Transaction m_t;
Address m_sender;
u256 m_endGas;
LogEntries m_logs;
};
}

24
libethereum/ExtVM.h

@ -46,49 +46,49 @@ public:
}
/// Read storage location.
u256 store(u256 _n) { return m_s.storage(myAddress, _n); }
virtual u256 store(u256 _n) override final { return m_s.storage(myAddress, _n); }
/// Write a value in storage.
void setStore(u256 _n, u256 _v) { m_s.setStorage(myAddress, _n, _v); if (m_ms) m_ms->altered.push_back(_n); }
virtual void setStore(u256 _n, u256 _v) override final { m_s.setStorage(myAddress, _n, _v); if (m_ms) m_ms->altered.push_back(_n); }
/// Read address's code.
bytes const& codeAt(Address _a) { return m_s.code(_a); }
virtual bytes const& codeAt(Address _a) override final { return m_s.code(_a); }
/// Create a new contract.
h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, OnOpFunc const& _onOp = OnOpFunc())
virtual h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, OnOpFunc const& _onOp = OnOpFunc()) override final
{
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1);
auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back();
return ret;
}
/// Create a new message call. Leave _myAddressOverride as the default to use the present address as caller.
bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out, OnOpFunc const& _onOp = OnOpFunc(), Address _myAddressOverride = Address(), Address _codeAddressOverride = Address())
virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final
{
if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1);
auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back();
return ret;
}
/// Read address's balance.
u256 balance(Address _a) { return m_s.balance(_a); }
virtual u256 balance(Address _a) override final { return m_s.balance(_a); }
/// Subtract amount from account's balance.
void subBalance(u256 _a) { m_s.subBalance(myAddress, _a); }
virtual void subBalance(u256 _a) override final { m_s.subBalance(myAddress, _a); }
/// Determine account's TX count.
u256 txCount(Address _a) { return m_s.transactionsFrom(_a); }
virtual u256 txCount(Address _a) override final { return m_s.transactionsFrom(_a); }
/// Suicide the associated contract to the given address.
void suicide(Address _a)
virtual void suicide(Address _a) override final
{
m_s.addBalance(_a, m_s.balance(myAddress));
ExtVMFace::suicide(_a);
@ -96,7 +96,7 @@ public:
/// Revert any changes made (by any of the other calls).
/// @TODO check call site for the parent manifest being discarded.
void revert() { if (m_ms) *m_ms = Manifest(); m_s.m_cache = m_origCache; }
virtual void revert() override final { if (m_ms) *m_ms = Manifest(); m_s.m_cache = m_origCache; }
State& state() const { return m_s; }

4
libethereum/Manifest.cpp

@ -37,10 +37,10 @@ Manifest::Manifest(bytesConstRef _r)
internal.emplace_back(i.data());
}
void Manifest::streamOut(RLPStream& _s) const
void Manifest::streamRLP(RLPStream& _s) const
{
_s.appendList(7) << from << to << value << altered << input << output;
_s.appendList(internal.size());
for (auto const& i: internal)
i.streamOut(_s);
i.streamRLP(_s);
}

2
libethereum/Manifest.h

@ -41,7 +41,7 @@ struct Manifest
{
Manifest() {}
Manifest(bytesConstRef _r);
void streamOut(RLPStream& _s) const;
void streamRLP(RLPStream& _s) const;
h256 bloom() const { h256 ret = from.bloom() | to.bloom(); for (auto const& i: internal) ret |= i.bloom(); for (auto const& i: altered) ret |= h256(i).bloom(); return ret; }

4
libethereum/MessageFilter.cpp

@ -27,7 +27,7 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
void MessageFilter::fillStream(RLPStream& _s) const
void MessageFilter::streamRLP(RLPStream& _s) const
{
_s.appendList(8) << m_from << m_to << m_stateAltered << m_altered << m_earliest << m_latest << m_max << m_skip;
}
@ -35,7 +35,7 @@ void MessageFilter::fillStream(RLPStream& _s) const
h256 MessageFilter::sha3() const
{
RLPStream s;
fillStream(s);
streamRLP(s);
return dev::sha3(s.out());
}

2
libethereum/MessageFilter.h

@ -39,7 +39,7 @@ class MessageFilter
public:
MessageFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {}
void fillStream(RLPStream& _s) const;
void streamRLP(RLPStream& _s) const;
h256 sha3() const;
int earliest() const { return m_earliest; }

150
libethereum/State.cpp

@ -161,6 +161,7 @@ State::State(State const& _s):
m_db(_s.m_db),
m_state(&m_db, _s.m_state.root()),
m_transactions(_s.m_transactions),
m_receipts(_s.m_receipts),
m_transactionSet(_s.m_transactionSet),
m_cache(_s.m_cache),
m_previousBlock(_s.m_previousBlock),
@ -193,6 +194,7 @@ State& State::operator=(State const& _s)
m_db = _s.m_db;
m_state.open(&m_db, _s.m_state.root());
m_transactions = _s.m_transactions;
m_receipts = _s.m_receipts;
m_transactionSet = _s.m_transactionSet;
m_cache = _s.m_cache;
m_previousBlock = _s.m_previousBlock;
@ -355,9 +357,9 @@ void State::ensureCached(std::map<Address, AddressState>& _cache, Address _a, bo
RLP state(stateBack);
AddressState s;
if (state.isNull())
s = AddressState(0, 0, h256(), EmptySHA3);
s = AddressState(0, 0, EmptyTrie, EmptySHA3);
else
s = AddressState(state[0].toInt<u256>(), state[1].toInt<u256>(), state[2].toHash<h256>(), state[3].isEmpty() ? EmptySHA3 : state[3].toHash<h256>());
s = AddressState(state[0].toInt<u256>(), state[1].toInt<u256>(), state[2].toHash<h256>(), state[3].toHash<h256>());
bool ok;
tie(it, ok) = _cache.insert(make_pair(_a, s));
}
@ -486,6 +488,7 @@ map<Address, u256> State::addresses() const
void State::resetCurrent()
{
m_transactions.clear();
m_receipts.clear();
m_transactionSet.clear();
m_cache.clear();
m_currentBlock = BlockInfo();
@ -549,7 +552,7 @@ h256s State::sync(TransactionQueue& _tq, bool* o_transactionQueueChanged)
{
uncommitToMine();
execute(i.second);
ret.push_back(m_transactions.back().changes.bloom());
ret.push_back(m_receipts.back().changes().bloom());
_tq.noteGood(i);
++goodTxs;
}
@ -590,7 +593,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const* _bc, bool _checkNonce)
// m_currentBlock is assumed to be prepopulated and reset.
#if !ETH_RELEASE
BlockInfo bi(_block);
BlockInfo bi(_block, _checkNonce);
assert(m_previousBlock.hash == bi.parentHash);
assert(m_currentBlock.parentHash == bi.parentHash);
assert(rootHash() == m_previousBlock.stateRoot);
@ -607,16 +610,32 @@ u256 State::enact(bytesConstRef _block, BlockChain const* _bc, bool _checkNonce)
// cnote << m_state;
MemoryDB tm;
GenericTrieDB<MemoryDB> transactionManifest(&tm);
transactionManifest.init();
GenericTrieDB<MemoryDB> transactionsTrie(&tm);
transactionsTrie.init();
MemoryDB rm;
GenericTrieDB<MemoryDB> receiptsTrie(&rm);
receiptsTrie.init();
// All ok with the block generally. Play back the transactions now...
unsigned i = 0;
for (auto const& tr: RLP(_block)[1])
{
RLPStream k;
k << i;
RLPStream txrlp;
m_transactions[i].streamRLP(txrlp);
transactionsTrie.insert(&k.out(), tr.data());
// cnote << m_state.root() << m_state;
// cnote << *this;
execute(tr[0].data());
execute(tr.data());
RLPStream receiptrlp;
m_receipts.back().streamRLP(receiptrlp);
receiptsTrie.insert(&k.out(), &receiptrlp.out());
/*
if (tr[1].toHash<h256>() != m_state.root())
{
// Invalid state root
@ -627,15 +646,20 @@ u256 State::enact(bytesConstRef _block, BlockChain const* _bc, bool _checkNonce)
}
if (tr[2].toInt<u256>() != gasUsed())
BOOST_THROW_EXCEPTION(InvalidTransactionGasUsed());
bytes k = rlp(i);
transactionManifest.insert(&k, tr.data());
*/
++i;
}
if (m_currentBlock.transactionsRoot && transactionManifest.root() != m_currentBlock.transactionsRoot)
if (transactionsTrie.root() != m_currentBlock.transactionsRoot)
{
cwarn << "Bad transactions state root!";
BOOST_THROW_EXCEPTION(InvalidTransactionStateRoot());
BOOST_THROW_EXCEPTION(InvalidTransactionsStateRoot());
}
if (receiptsTrie.root() != m_currentBlock.receiptsRoot)
{
cwarn << "Bad receipts state root!";
BOOST_THROW_EXCEPTION(InvalidReceiptsStateRoot());
}
// Initialise total difficulty calculation.
@ -715,7 +739,7 @@ void State::uncommitToMine()
if (!m_transactions.size())
m_state.setRoot(m_previousBlock.stateRoot);
else
m_state.setRoot(m_transactions[m_transactions.size() - 1].stateRoot);
m_state.setRoot(m_receipts[m_receipts.size() - 1].stateRoot());
m_db = m_lastTx;
paranoia("Uncommited to mine", true);
m_currentBlock.sha3Uncles = h256();
@ -732,7 +756,7 @@ bool State::amIJustParanoid(BlockChain const& _bc)
// Compile block:
RLPStream block;
block.appendList(3);
m_currentBlock.fillStream(block, true);
m_currentBlock.streamRLP(block, true);
block.appendRaw(m_currentTxs);
block.appendRaw(m_currentUncles);
@ -759,11 +783,20 @@ bool State::amIJustParanoid(BlockChain const& _bc)
return false;
}
h256 State::bloom() const
h256 State::oldBloom() const
{
h256 ret = m_currentBlock.coinbaseAddress.bloom();
for (auto const& i: m_transactions)
ret |= i.changes.bloom();
for (auto const& i: m_receipts)
ret |= i.changes().bloom();
return ret;
}
LogBloom State::logBloom() const
{
LogBloom ret;
ret.shiftBloom<3>(sha3(m_currentBlock.coinbaseAddress.ref()));
for (TransactionReceipt const& i: m_receipts)
ret |= i.bloom();
return ret;
}
@ -799,7 +832,7 @@ void State::commitToMine(BlockChain const& _bc)
if (!knownUncles.count(u)) // ignore any uncles/mainline blocks that we know about.
{
BlockInfo ubi(_bc.block(u));
ubi.fillStream(unclesData, true);
ubi.streamRLP(unclesData, true);
++unclesCount;
uncleAddresses.push_back(ubi.coinbaseAddress);
}
@ -807,8 +840,12 @@ void State::commitToMine(BlockChain const& _bc)
}
MemoryDB tm;
GenericTrieDB<MemoryDB> transactionReceipts(&tm);
transactionReceipts.init();
GenericTrieDB<MemoryDB> transactionsTrie(&tm);
transactionsTrie.init();
MemoryDB rm;
GenericTrieDB<MemoryDB> receiptsTrie(&rm);
receiptsTrie.init();
RLPStream txs;
txs.appendList(m_transactions.size());
@ -817,17 +854,25 @@ void State::commitToMine(BlockChain const& _bc)
{
RLPStream k;
k << i;
RLPStream v;
m_transactions[i].fillStream(v);
transactionReceipts.insert(&k.out(), &v.out());
txs.appendRaw(v.out());
RLPStream receiptrlp;
m_receipts[i].streamRLP(receiptrlp);
receiptsTrie.insert(&k.out(), &receiptrlp.out());
RLPStream txrlp;
m_transactions[i].streamRLP(txrlp);
transactionsTrie.insert(&k.out(), &txrlp.out());
txs.appendRaw(txrlp.out());
}
txs.swapOut(m_currentTxs);
RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles);
m_currentBlock.transactionsRoot = transactionReceipts.root();
m_currentBlock.transactionsRoot = transactionsTrie.root();
m_currentBlock.receiptsRoot = receiptsTrie.root();
m_currentBlock.logBloom = logBloom();
m_currentBlock.sha3Uncles = sha3(m_currentUncles);
// Apply rewards last of all.
@ -855,6 +900,10 @@ MineInfo State::mine(unsigned _msTimeout, bool _turbo)
if (!ret.completed)
m_currentBytes.clear();
else
{
cnote << "Completed" << m_currentBlock.headerHashWithoutNonce().abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHashWithoutNonce(), m_currentBlock.nonce, m_currentBlock.difficulty);
}
return ret;
}
@ -867,7 +916,7 @@ void State::completeMine()
// Compile block:
RLPStream ret;
ret.appendList(3);
m_currentBlock.fillStream(ret, true);
m_currentBlock.streamRLP(ret, true);
ret.appendRaw(m_currentTxs);
ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes);
@ -877,6 +926,7 @@ void State::completeMine()
// Quickly reset the transactions.
// TODO: Leave this in a better state than this limbo, or at least record that it's in limbo.
m_transactions.clear();
m_receipts.clear();
m_transactionSet.clear();
m_lastTx = m_db;
}
@ -1116,12 +1166,13 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
// TODO: CHECK TRIE after level DB flush to make sure exactly the same.
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(TransactionReceipt(e.t(), rootHash(), startGasUsed + e.gasUsed(), ms));
m_transactions.push_back(e.t());
m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs(), ms));
m_transactionSet.insert(e.t().sha3());
return e.gasUsed();
}
bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set<Address>* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{
if (!_originAddress)
_originAddress = _senderAddress;
@ -1157,9 +1208,8 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
{
auto out = vm.go(evm, _onOp);
memcpy(_out.data(), out.data(), std::min(out.size(), _out.size()));
if (o_suicides)
for (auto i: evm.suicides)
o_suicides->insert(i);
if (o_sub)
*o_sub += evm.sub;
if (o_ms)
o_ms->output = out.toBytes();
}
@ -1171,6 +1221,7 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
revert = true;
}
catch (Exception const& _e)
{
@ -1189,10 +1240,16 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
return !revert;
}
else
{
// non-contract call
if (o_sub)
o_sub->logs.push_back(LogEntry(_receiveAddress, {u256((u160)_senderAddress) + 1}, bytes()));
}
return true;
}
h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set<Address>* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{
if (!_origin)
_origin = _sender;
@ -1222,9 +1279,8 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
out = vm.go(evm, _onOp);
if (o_ms)
o_ms->output = out.toBytes();
if (o_suicides)
for (auto i: evm.suicides)
o_suicides->insert(i);
if (o_sub)
*o_sub += evm.sub;
}
catch (OutOfGas const& /*_e*/)
{
@ -1234,25 +1290,32 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
revert = true;
}
catch (Exception const& _e)
{
clog(StateChat) << "Exception in VM: " << diagnostic_information(_e);
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e);
}
catch (std::exception const& _e)
{
clog(StateChat) << "std::exception in VM: " << _e.what();
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
}
// TODO: CHECK: IS THIS CORRECT?! (esp. given account created prior to revertion init.)
// TODO: CHECK: AUDIT: IS THIS CORRECT?! (esp. given account created prior to revertion init.)
// Write state out only in the case of a non-out-of-gas transaction.
if (revert)
{
evm.revert();
// Set code.
if (addressInUse(newAddress))
m_cache[newAddress].setCode(out);
m_cache.erase(newAddress);
newAddress = Address();
}
else
// Set code.
if (addressInUse(newAddress))
m_cache[newAddress].setCode(out);
*_gas = vm.gas();
@ -1267,11 +1330,12 @@ State State::fromPending(unsigned _i) const
if (!_i)
ret.m_state.setRoot(m_previousBlock.stateRoot);
else
ret.m_state.setRoot(m_transactions[_i - 1].stateRoot);
ret.m_state.setRoot(m_receipts[_i - 1].stateRoot());
while (ret.m_transactions.size() > _i)
{
ret.m_transactionSet.erase(ret.m_transactions.back().transaction.sha3());
ret.m_transactionSet.erase(ret.m_transactions.back().sha3());
ret.m_transactions.pop_back();
ret.m_receipts.pop_back();
}
return ret;
}

75
libethereum/State.h

@ -53,25 +53,37 @@ struct StateChat: public LogChannel { static const char* name() { return "-S-";
struct StateTrace: public LogChannel { static const char* name() { return "=S="; } static const int verbosity = 7; };
struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; };
struct TransactionReceipt
class TransactionReceipt
{
TransactionReceipt(Transaction const& _t, h256 _root, u256 _gasUsed, Manifest const& _ms): transaction(_t), stateRoot(_root), gasUsed(_gasUsed), changes(_ms) {}
public:
TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log, Manifest const& _ms): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log), m_changes(_ms) {}
Manifest const& changes() const { return m_changes; }
// Manifest const& changes() const { return changes; }
h256 const& stateRoot() const { return m_stateRoot; }
u256 const& gasUsed() const { return m_gasUsed; }
LogBloom const& bloom() const { return m_bloom; }
LogEntries const& log() const { return m_log; }
void fillStream(RLPStream& _s) const
void streamRLP(RLPStream& _s) const
{
_s.appendList(3);
transaction.fillStream(_s);
_s.append(stateRoot, false, true) << gasUsed;
_s.appendList(4) << m_stateRoot << m_gasUsed << m_bloom;
_s.appendList(m_log.size());
for (LogEntry const& l: m_log)
l.streamRLP(_s);
}
Transaction transaction;
h256 stateRoot;
u256 gasUsed;
Manifest changes;
private:
h256 m_stateRoot;
u256 m_gasUsed;
LogBloom m_bloom;
LogEntries m_log;
Manifest m_changes; ///< TODO: PoC-7: KILL
};
using TransactionReceipts = std::vector<TransactionReceipt>;
struct PrecompiledAddress
{
unsigned gas;
@ -227,16 +239,28 @@ public:
h256 rootHash() const { return m_state.root(); }
/// Get the list of pending transactions.
Transactions pending() const { Transactions ret; for (auto const& t: m_transactions) ret.push_back(t.transaction); return ret; }
Transactions const& pending() const { return m_transactions; }
/// Get the list of pending transactions. TODO: PoC-7: KILL
Manifest changesFromPending(unsigned _i) const { return m_receipts[_i].changes(); }
/// Get the bloom filter of all changes happened in the block. TODO: PoC-7: KILL
h256 oldBloom() const;
/// Get the bloom filter of a particular transaction that happened in the block. TODO: PoC-7: KILL
h256 oldBloom(unsigned _i) const { return m_receipts[_i].changes().bloom(); }
/// Get the transaction receipt for the transaction of the given index.
TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; }
/// Get the list of pending transactions.
Manifest changesFromPending(unsigned _i) const { return m_transactions[_i].changes; }
LogEntries const& log(unsigned _i) const { return m_receipts[_i].log(); }
/// Get the bloom filter of all changes happened in the block.
h256 bloom() const;
/// Get the bloom filter of all logs that happened in the block.
LogBloom logBloom() const;
/// Get the bloom filter of a particular transaction that happened in the block.
h256 bloom(unsigned _i) const { return m_transactions[_i].changes.bloom(); }
LogBloom const& logBloom(unsigned _i) const { return m_receipts[_i].bloom(); }
/// Get the State immediately after the given number of pending transactions have been applied.
/// If (_i == 0) returns the initial state of the block.
@ -295,12 +319,12 @@ private:
// We assume all instrinsic fees are paid up before this point.
/// Execute a contract-creation transaction.
h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set<Address>* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Execute a call.
/// @a _gas points to the amount of gas to use for the call, and will lower it accordingly.
/// @returns false if the call ran out of gas before completion. true otherwise.
bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set<Address>* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();
@ -311,14 +335,15 @@ private:
void refreshManifest(RLPStream* _txs = nullptr);
/// @returns gas used by transactions thus far executed.
u256 gasUsed() const { return m_transactions.size() ? m_transactions.back().gasUsed : 0; }
u256 gasUsed() const { return m_receipts.size() ? m_receipts.back().gasUsed() : 0; }
bool isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const;
void paranoia(std::string const& _when, bool _enforceRefs = false) const;
OverlayDB m_db; ///< Our overlay for the state tree.
TrieDB<Address, OverlayDB> m_state; ///< Our state tree, as an OverlayDB DB.
std::vector<TransactionReceipt> m_transactions; ///< The current list of transactions that we've included in the state.
Transactions m_transactions; ///< The current list of transactions that we've included in the state.
TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts.
std::set<h256> m_transactionSet; ///< The set of transaction hashes that we've included in the state.
OverlayDB m_lastTx;
@ -376,16 +401,10 @@ void commit(std::map<Address, AddressState> const& _cache, DB& _db, TrieDB<Addre
{
h256 ch = sha3(i.second.code());
_db.insert(ch, &i.second.code());
if (i.second.code().size())
s << ch;
else
s << "";
s << ch;
}
else
if (i.second.codeHash() == EmptySHA3)
s << "";
else
s << i.second.codeHash();
s << i.second.codeHash();
_state.insert(i.first, &s.out());
}

77
libethereum/Transaction.cpp

@ -19,9 +19,9 @@
* @date 2014
*/
#include <secp256k1/secp256k1.h>
#include <libdevcore/vector_ref.h>
#include <libdevcore/Log.h>
#include <libdevcrypto/Common.h>
#include <libethcore/Exceptions.h>
#include "Transaction.h"
using namespace std;
@ -39,10 +39,11 @@ Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender)
nonce = rlp[field = 0].toInt<u256>();
gasPrice = rlp[field = 1].toInt<u256>();
gas = rlp[field = 2].toInt<u256>();
type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
receiveAddress = rlp[field = 3].toHash<Address>();
value = rlp[field = 4].toInt<u256>();
data = rlp[field = 5].toBytes();
vrs = Signature{ rlp[field = 6].toInt<byte>(), rlp[field = 7].toInt<u256>(), rlp[field = 8].toInt<u256>() };
vrs = SignatureStruct{ rlp[field = 7].toInt<u256>(), rlp[field = 8].toInt<u256>(), byte(rlp[field = 6].toInt<byte>() - 27) };
if (_checkSender)
m_sender = sender();
}
@ -70,85 +71,29 @@ Address Transaction::sender() const
{
if (!m_sender)
{
secp256k1_start();
h256 sig[2] = { vrs.r, vrs.s };
h256 msg = sha3(false);
byte pubkey[65];
int pubkeylen = 65;
if (!secp256k1_ecdsa_recover_compact(msg.data(), 32, sig[0].data(), pubkey, &pubkeylen, 0, (int)vrs.v - 27))
auto p = recover(*(Signature const*)&vrs, sha3(false));
if (!p)
BOOST_THROW_EXCEPTION(InvalidSignature());
// TODO: check right160 is correct and shouldn't be left160.
m_sender = right160(dev::sha3(bytesConstRef(&(pubkey[1]), 64)));
#if ETH_ADDRESS_DEBUG
cout << "---- RECOVER -------------------------------" << endl;
cout << "MSG: " << msg << endl;
cout << "R S V: " << sig[0] << " " << sig[1] << " " << (int)(vrs.v - 27) << "+27" << endl;
cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl;
cout << "ADR: " << m_sender << endl;
#endif
m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));
}
return m_sender;
}
void Transaction::sign(Secret _priv)
{
int v = 0;
secp256k1_start();
h256 msg = sha3(false);
h256 sig[2];
h256 nonce = kFromMessage(msg, _priv);
if (!secp256k1_ecdsa_sign_compact(msg.data(), 32, sig[0].data(), _priv.data(), nonce.data(), &v))
BOOST_THROW_EXCEPTION(InvalidSignature());
#if ETH_ADDRESS_DEBUG
cout << "---- SIGN -------------------------------" << endl;
cout << "MSG: " << msg << endl;
cout << "SEC: " << _priv << endl;
cout << "NON: " << nonce << endl;
cout << "R S V: " << sig[0] << " " << sig[1] << " " << v << "+27" << endl;
#endif
vrs.v = (byte)(v + 27);
vrs.r = (u256)sig[0];
vrs.s = (u256)sig[1];
auto sig = dev::sign(_priv, sha3(false));
vrs = *(SignatureStruct const*)&sig;
}
void Transaction::fillStream(RLPStream& _s, bool _sig) const
void Transaction::streamRLP(RLPStream& _s, bool _sig) const
{
_s.appendList((_sig ? 3 : 0) + 6);
_s << nonce << gasPrice << gas;
if (receiveAddress)
if (type == MessageCall)
_s << receiveAddress;
else
_s << "";
_s << value << data;
if (_sig)
_s << vrs.v << vrs.r << vrs.s;
}
// If the h256 return is an integer, store it in bigendian (i.e. u256 ret; ... return (h256)ret; )
h256 Transaction::kFromMessage(h256 _msg, h256 _priv)
{
// TODO!
// bytes v(32, 1);
// bytes k(32, 0);
/*
v = '\x01' * 32
k = '\x00' * 32
priv = encode_privkey(priv,'bin')
msghash = encode(hash_to_int(msghash),256,32)
k = hmac.new(k, v+'\x00'+priv+msghash, hashlib.sha256).digest()
v = hmac.new(k, v, hashlib.sha256).digest()
k = hmac.new(k, v+'\x01'+priv+msghash, hashlib.sha256).digest()
v = hmac.new(k, v, hashlib.sha256).digest()
return decode(hmac.new(k, v, hashlib.sha256).digest(),256)
*/
return _msg ^ _priv;
_s << (vrs.v + 27) << (u256)vrs.r << (u256)vrs.s;
}

26
libethereum/Transaction.h

@ -30,22 +30,22 @@ namespace dev
namespace eth
{
struct Signature
{
byte v;
u256 r;
u256 s;
};
struct Transaction
{
enum Type
{
ContractCreation,
MessageCall
};
Transaction() {}
Transaction(bytesConstRef _rlp, bool _checkSender = false);
Transaction(bytes const& _rlp, bool _checkSender = false): Transaction(&_rlp, _checkSender) {}
bool operator==(Transaction const& _c) const { return receiveAddress == _c.receiveAddress && value == _c.value && data == _c.data; }
bool operator==(Transaction const& _c) const { return type == _c.type && (type == ContractCreation || receiveAddress == _c.receiveAddress) && value == _c.value && data == _c.data; }
bool operator!=(Transaction const& _c) const { return !operator==(_c); }
Type type; ///< True if this is a contract-creation transaction. F
u256 nonce; ///< The transaction-count of the sender.
u256 value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
Address receiveAddress; ///< The receiving address of the transaction.
@ -54,7 +54,7 @@ struct Transaction
bytes data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction.
Signature vrs; ///< The signature of the transaction. Encodes the sender.
SignatureStruct vrs; ///< The signature of the transaction. Encodes the sender.
Address safeSender() const noexcept; ///< Like sender() but will never throw.
Address sender() const; ///< Determine the sender of the transaction from the signature (and hash).
@ -64,11 +64,11 @@ struct Transaction
static h256 kFromMessage(h256 _msg, h256 _priv);
void fillStream(RLPStream& _s, bool _sig = true) const;
bytes rlp(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return s.out(); }
void streamRLP(RLPStream& _s, bool _sig = true) const;
bytes rlp(bool _sig = true) const { RLPStream s; streamRLP(s, _sig); return s.out(); }
std::string rlpString(bool _sig = true) const { return asString(rlp(_sig)); }
h256 sha3(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return dev::sha3(s.out()); }
bytes sha3Bytes(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return dev::sha3Bytes(s.out()); }
h256 sha3(bool _sig = true) const { RLPStream s; streamRLP(s, _sig); return dev::sha3(s.out()); }
bytes sha3Bytes(bool _sig = true) const { RLPStream s; streamRLP(s, _sig); return dev::sha3Bytes(s.out()); }
private:
mutable Address m_sender;

73
libevm/ExtVMFace.h

@ -21,9 +21,12 @@
#pragma once
#include <list>
#include <set>
#include <functional>
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/RLP.h>
#include <libdevcrypto/SHA3.h>
#include <libevmface/Instruction.h>
#include <libethcore/CommonEth.h>
#include <libethcore/BlockInfo.h>
@ -33,31 +36,73 @@ namespace dev
namespace eth
{
struct Post
using LogBloom = h512;
struct LogEntry
{
LogEntry() {}
LogEntry(RLP const& _r) { from = (Address)_r[0]; topics = (h256s)_r[1]; data = (bytes)_r[2]; }
LogEntry(Address const& _f, h256s&& _ts, bytes&& _d): from(_f), topics(std::move(_ts)), data(std::move(_d)) {}
void streamRLP(RLPStream& _s) const { _s.appendList(3) << from << topics << data; }
LogBloom bloom() const
{
LogBloom ret;
ret.shiftBloom<3, 32>(sha3(from.ref()));
for (auto t: topics)
ret.shiftBloom<3, 32>(sha3(t.ref()));
return ret;
}
Address from;
Address to;
u256 value;
h256s topics;
bytes data;
u256 gas;
};
using LogEntries = std::vector<LogEntry>;
inline LogBloom bloom(LogEntries const& _logs)
{
LogBloom ret;
for (auto const& l: _logs)
ret |= l.bloom();
return ret;
}
struct SubState
{
std::set<Address> suicides; ///< Any accounts that have suicided.
LogEntries logs; ///< Any logs.
u256 refunds; ///< Refund counter of SSTORE nonzero->zero.
SubState& operator+=(SubState const& _s)
{
suicides += _s.suicides;
refunds += _s.refunds;
suicides += _s.suicides;
return *this;
}
};
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, void/*VM*/*, void/*ExtVM*/ const*)>;
/**
* @brief A null implementation of the class for specifying VM externalities.
* @brief Interface and null implementation of the class for specifying VM externalities.
*/
class ExtVMFace
{
public:
/// Null constructor.
ExtVMFace() {}
ExtVMFace() = default;
/// Full constructor.
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, unsigned _depth);
/// Get the code at the given location in code ROM.
virtual byte getCode(u256 _n) const { return _n < code.size() ? code[(unsigned)_n] : 0; }
virtual ~ExtVMFace() = default;
ExtVMFace(ExtVMFace const&) = delete;
void operator=(ExtVMFace) = delete;
/// Read storage location.
virtual u256 store(u256) { return 0; }
@ -78,7 +123,7 @@ public:
virtual u256 txCount(Address) { return 0; }
/// Suicide the associated contract and give proceeds to the given address.
virtual void suicide(Address) { suicides.insert(myAddress); }
virtual void suicide(Address) { sub.suicides.insert(myAddress); }
/// Create a new (contract) account.
virtual h160 create(u256, u256*, bytesConstRef, OnOpFunc const&) { return h160(); }
@ -86,9 +131,15 @@ public:
/// Make a new message call.
virtual bool call(Address, u256, bytesConstRef, u256*, bytesRef, OnOpFunc const&, Address, Address) { return false; }
/// Revert any changes made (by any of the other calls).
virtual void log(h256s&& _topics, bytesConstRef _data) { sub.logs.push_back(LogEntry(myAddress, std::move(_topics), _data.toBytes())); }
/// Revert any changes made (by any of the other calls).
virtual void revert() {}
/// Get the code at the given location in code ROM.
byte getCode(u256 _n) const { return _n < code.size() ? code[(size_t)_n] : 0; }
Address myAddress; ///< Address associated with executing code (a contract, or contract-to-be).
Address caller; ///< Address which sent the message (either equal to origin or a contract).
Address origin; ///< Original transactor.
@ -98,7 +149,7 @@ public:
bytesConstRef code; ///< Current code that is executing.
BlockInfo previousBlock; ///< The previous block's information.
BlockInfo currentBlock; ///< The current block's information.
std::set<Address> suicides; ///< Any accounts that have suicided.
SubState sub; ///< Sub-band VM state (suicides, refund counter, logs).
unsigned depth; ///< Depth of the present call.
};

4
libevm/FeeStructure.cpp

@ -29,7 +29,9 @@ u256 const dev::eth::c_stepGas = 1;
u256 const dev::eth::c_balanceGas = 20;
u256 const dev::eth::c_sha3Gas = 20;
u256 const dev::eth::c_sloadGas = 20;
u256 const dev::eth::c_sstoreGas = 100;
u256 const dev::eth::c_sstoreSetGas = 300;
u256 const dev::eth::c_sstoreResetGas = 100;
u256 const dev::eth::c_sstoreRefundGas = 100;
u256 const dev::eth::c_createGas = 100;
u256 const dev::eth::c_callGas = 20;
u256 const dev::eth::c_memoryGas = 1;

4
libevm/FeeStructure.h

@ -32,7 +32,9 @@ extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD
extern u256 const c_balanceGas; ///< Once per BALANCE operation.
extern u256 const c_sha3Gas; ///< Once per SHA3 operation.
extern u256 const c_sloadGas; ///< Once per SLOAD operation.
extern u256 const c_sstoreGas; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once).
extern u256 const c_sstoreSetGas; ///< Once per SSTORE operation if the zeroness changes from zero.
extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness doesn't change.
extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero.
extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction.
extern u256 const c_callGas; ///< Once per CALL operation & message call transaction.
extern u256 const c_memoryGas; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.

1
libevm/VM.cpp

@ -29,6 +29,7 @@ void VM::reset(u256 _gas) noexcept
{
VMFace::reset(_gas);
m_curPC = 0;
m_jumpDests.clear();
}
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)

199
libevm/VM.h

@ -62,6 +62,7 @@ private:
u256 m_curPC = 0;
bytes m_temp;
u256s m_stack;
std::set<unsigned> m_jumpDests;
};
}
@ -71,6 +72,16 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
{
auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? _offset + _size : 0; };
if (m_jumpDests.empty())
{
m_jumpDests.insert(0);
for (unsigned i = 1; i < _ext.code.size(); ++i)
if (_ext.code[i] == (byte)Instruction::JUMPDEST)
m_jumpDests.insert(i + 1);
else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32)
i += _ext.code[i] - (int)Instruction::PUSH1 + 1;
}
u256 nextPC = m_curPC + 1;
auto osteps = _steps;
for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1)
@ -88,20 +99,25 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::SUICIDE:
require(1);
runGas = 0;
break;
case Instruction::SSTORE:
require(2);
if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2])
runGas = c_sstoreGas * 2;
runGas = c_sstoreSetGas;
else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
{
runGas = 0;
_ext.sub.refunds += c_sstoreRefundGas;
}
else
runGas = c_sstoreGas;
runGas = c_sstoreResetGas;
break;
case Instruction::SLOAD:
require(1);
runGas = c_sloadGas;
break;
@ -141,15 +157,23 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::BALANCE:
require(1);
runGas = c_balanceGas;
break;
case Instruction::CALL:
require(7);
runGas = c_callGas + m_stack[m_stack.size() - 1];
newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]));
case Instruction::LOG0:
case Instruction::LOG1:
case Instruction::LOG2:
case Instruction::LOG3:
case Instruction::LOG4:
{
unsigned n = (unsigned)inst - (unsigned)Instruction::LOG0;
require(n + 2);
newTempSize = memNeed(m_stack[m_stack.size() - 1 - n], m_stack[m_stack.size() - 2 - n]);
break;
}
case Instruction::CALL:
case Instruction::CALLCODE:
require(7);
runGas = c_callGas + m_stack[m_stack.size() - 1];
@ -166,35 +190,16 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
}
case Instruction::ADD:
case Instruction::MUL:
case Instruction::SUB:
case Instruction::DIV:
case Instruction::SDIV:
case Instruction::MOD:
case Instruction::SMOD:
case Instruction::EXP:
case Instruction::NEG:
case Instruction::LT:
case Instruction::GT:
case Instruction::SLT:
case Instruction::SGT:
case Instruction::EQ:
case Instruction::NOT:
case Instruction::AND:
case Instruction::OR:
case Instruction::XOR:
case Instruction::BYTE:
case Instruction::ADDMOD:
case Instruction::MULMOD:
case Instruction::PC:
case Instruction::MSIZE:
case Instruction::GAS:
case Instruction::JUMPDEST:
case Instruction::ADDRESS:
case Instruction::ORIGIN:
case Instruction::CALLER:
case Instruction::CALLVALUE:
case Instruction::CALLDATALOAD:
case Instruction::CALLDATASIZE:
case Instruction::CODESIZE:
case Instruction::EXTCODESIZE:
case Instruction::GASPRICE:
case Instruction::PREVHASH:
case Instruction::COINBASE:
@ -234,7 +239,40 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::PUSH30:
case Instruction::PUSH31:
case Instruction::PUSH32:
break;
case Instruction::BNOT:
case Instruction::NOT:
case Instruction::CALLDATALOAD:
case Instruction::EXTCODESIZE:
case Instruction::POP:
case Instruction::JUMP:
require(1);
break;
case Instruction::ADD:
case Instruction::MUL:
case Instruction::SUB:
case Instruction::DIV:
case Instruction::SDIV:
case Instruction::MOD:
case Instruction::SMOD:
case Instruction::EXP:
case Instruction::LT:
case Instruction::GT:
case Instruction::SLT:
case Instruction::SGT:
case Instruction::EQ:
case Instruction::AND:
case Instruction::OR:
case Instruction::XOR:
case Instruction::BYTE:
case Instruction::JUMPI:
case Instruction::SIGNEXTEND:
require(2);
break;
case Instruction::ADDMOD:
case Instruction::MULMOD:
require(3);
break;
case Instruction::DUP1:
case Instruction::DUP2:
case Instruction::DUP3:
@ -251,6 +289,8 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::DUP14:
case Instruction::DUP15:
case Instruction::DUP16:
require(1 + (int)inst - (int)Instruction::DUP1);
break;
case Instruction::SWAP1:
case Instruction::SWAP2:
case Instruction::SWAP3:
@ -267,12 +307,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::SWAP14:
case Instruction::SWAP15:
case Instruction::SWAP16:
case Instruction::JUMP:
case Instruction::JUMPI:
case Instruction::PC:
case Instruction::MSIZE:
case Instruction::GAS:
case Instruction::JUMPDEST:
require((int)inst - (int)Instruction::SWAP1 + 2);
break;
default:
BOOST_THROW_EXCEPTION(BadInstruction());
@ -302,118 +337,107 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
{
case Instruction::ADD:
//pops two items and pushes S[-1] + S[-2] mod 2^256.
require(2);
m_stack[m_stack.size() - 2] += m_stack.back();
m_stack.pop_back();
break;
case Instruction::MUL:
//pops two items and pushes S[-1] * S[-2] mod 2^256.
require(2);
m_stack[m_stack.size() - 2] *= m_stack.back();
m_stack.pop_back();
break;
case Instruction::SUB:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() - m_stack[m_stack.size() - 2];
m_stack.pop_back();
break;
case Instruction::DIV:
require(2);
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? m_stack.back() / m_stack[m_stack.size() - 2] : 0;
m_stack.pop_back();
break;
case Instruction::SDIV:
require(2);
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0;
m_stack.pop_back();
break;
case Instruction::MOD:
require(2);
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? m_stack.back() % m_stack[m_stack.size() - 2] : 0;
m_stack.pop_back();
break;
case Instruction::SMOD:
require(2);
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0;
m_stack.pop_back();
break;
case Instruction::EXP:
{
require(2);
auto base = m_stack.back();
auto expon = m_stack[m_stack.size() - 2];
m_stack.pop_back();
m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256);
break;
}
case Instruction::NEG:
require(1);
m_stack.back() = ~(m_stack.back() - 1);
case Instruction::BNOT:
m_stack.back() = ~m_stack.back();
break;
case Instruction::LT:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() < m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::GT:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() > m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::SLT:
require(2);
m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::SGT:
require(2);
m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::EQ:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() == m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::NOT:
require(1);
m_stack.back() = m_stack.back() ? 0 : 1;
break;
case Instruction::AND:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() & m_stack[m_stack.size() - 2];
m_stack.pop_back();
break;
case Instruction::OR:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() | m_stack[m_stack.size() - 2];
m_stack.pop_back();
break;
case Instruction::XOR:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() ^ m_stack[m_stack.size() - 2];
m_stack.pop_back();
break;
case Instruction::BYTE:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() < 32 ? (m_stack[m_stack.size() - 2] >> (unsigned)(8 * (31 - m_stack.back()))) & 0xff : 0;
m_stack.pop_back();
break;
case Instruction::ADDMOD:
require(3);
m_stack[m_stack.size() - 3] = u256((bigint(m_stack.back()) + bigint(m_stack[m_stack.size() - 2])) % m_stack[m_stack.size() - 3]);
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::MULMOD:
require(3);
m_stack[m_stack.size() - 3] = u256((bigint(m_stack.back()) * bigint(m_stack[m_stack.size() - 2])) % m_stack[m_stack.size() - 3]);
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::SIGNEXTEND:
{
unsigned k = m_stack.back().convert_to<unsigned>();
m_stack.pop_back();
auto& b = m_stack.back();
if (k <= 31)
if ((b >> (k * 8)) & 0x80)
for (unsigned i = 31; i > k; --i)
b |= (u256(0xff) << i);
break;
}
case Instruction::SHA3:
{
require(2);
unsigned inOff = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned inSize = (unsigned)m_stack.back();
@ -429,7 +453,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::BALANCE:
{
require(1);
m_stack.back() = _ext.balance(asAddress(m_stack.back()));
break;
}
@ -441,7 +464,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::CALLDATALOAD:
{
require(1);
if ((unsigned)m_stack.back() + 31 < _ext.data.size())
m_stack.back() = (u256)*(h256 const*)(_ext.data.data() + (unsigned)m_stack.back());
else
@ -458,7 +480,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::CALLDATACOPY:
{
require(3);
unsigned mf = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned cf = (unsigned)m_stack.back();
@ -475,7 +496,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::CODECOPY:
{
require(3);
unsigned mf = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned cf = (unsigned)m_stack.back();
@ -488,12 +508,10 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
}
case Instruction::EXTCODESIZE:
require(1);
m_stack.back() = _ext.codeAt(asAddress(m_stack.back())).size();
break;
case Instruction::EXTCODECOPY:
{
require(4);
Address a = asAddress(m_stack.back());
m_stack.pop_back();
unsigned mf = (unsigned)m_stack.back();
@ -569,7 +587,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
}
case Instruction::POP:
require(1);
m_stack.pop_back();
break;
case Instruction::DUP1:
@ -590,7 +607,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::DUP16:
{
auto n = 1 + (int)inst - (int)Instruction::DUP1;
require(n);
m_stack.push_back(m_stack[m_stack.size() - n]);
break;
}
@ -612,7 +628,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::SWAP16:
{
unsigned n = (int)inst - (int)Instruction::SWAP1 + 2;
require(n);
auto d = m_stack.back();
m_stack.back() = m_stack[m_stack.size() - n];
m_stack[m_stack.size() - n] = d;
@ -620,13 +635,11 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
}
case Instruction::MLOAD:
{
require(1);
m_stack.back() = (u256)*(h256 const*)(m_temp.data() + (unsigned)m_stack.back());
break;
}
case Instruction::MSTORE:
{
require(2);
*(h256*)&m_temp[(unsigned)m_stack.back()] = (h256)m_stack[m_stack.size() - 2];
m_stack.pop_back();
m_stack.pop_back();
@ -634,35 +647,30 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
}
case Instruction::MSTORE8:
{
require(2);
m_temp[(unsigned)m_stack.back()] = (byte)(m_stack[m_stack.size() - 2] & 0xff);
m_stack.pop_back();
m_stack.pop_back();
break;
}
case Instruction::SLOAD:
require(1);
m_stack.back() = _ext.store(m_stack.back());
break;
case Instruction::SSTORE:
require(2);
_ext.setStore(m_stack.back(), m_stack[m_stack.size() - 2]);
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::JUMP:
require(1);
nextPC = m_stack.back();
if (nextPC && (Instruction)_ext.getCode(nextPC - 1) != Instruction::JUMPDEST)
if (!m_jumpDests.count((unsigned)nextPC))
BOOST_THROW_EXCEPTION(BadJumpDestination());
m_stack.pop_back();
break;
case Instruction::JUMPI:
require(2);
if (m_stack[m_stack.size() - 2])
{
nextPC = m_stack.back();
if (nextPC && (Instruction)_ext.getCode(nextPC - 1) != Instruction::JUMPDEST)
if (!m_jumpDests.count((unsigned)nextPC))
BOOST_THROW_EXCEPTION(BadJumpDestination());
}
m_stack.pop_back();
@ -679,10 +687,38 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::JUMPDEST:
break;
/* case Instruction::LOG0:
_ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
break;
case Instruction::LOG1:
_ext.log({m_stack[m_stack.size() - 1]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 2], (unsigned)m_stack[m_stack.size() - 3]));
break;
case Instruction::LOG2:
_ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 3], (unsigned)m_stack[m_stack.size() - 4]));
break;
case Instruction::LOG3:
_ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 4], (unsigned)m_stack[m_stack.size() - 5]));
break;
case Instruction::LOG4:
_ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 5], (unsigned)m_stack[m_stack.size() - 6]));
break;*/
case Instruction::LOG0:
_ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
break;
case Instruction::LOG1:
_ext.log({m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
break;
case Instruction::LOG2:
_ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
break;
case Instruction::LOG3:
_ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
break;
case Instruction::LOG4:
_ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5], m_stack[m_stack.size() - 6]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
break;
case Instruction::CREATE:
{
require(3);
u256 endowment = m_stack.back();
m_stack.pop_back();
unsigned initOff = (unsigned)m_stack.back();
@ -704,8 +740,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::CALL:
case Instruction::CALLCODE:
{
require(7);
u256 gas = m_stack.back();
m_stack.pop_back();
Address receiveAddress = asAddress(m_stack.back());
@ -737,8 +771,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
}
case Instruction::RETURN:
{
require(2);
unsigned b = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned s = (unsigned)m_stack.back();
@ -748,7 +780,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
}
case Instruction::SUICIDE:
{
require(1);
Address dest = asAddress(m_stack.back());
_ext.suicide(dest);
// ...follow through to...

7
libevm/VMFace.cpp

@ -24,7 +24,12 @@ using namespace dev::eth;
std::unique_ptr<VMFace> VMFace::create(VMFace::Kind _kind, u256 _gas)
{
std::unique_ptr<VMFace> vm(_kind == Kind::JIT ? static_cast<VMFace*>(new jit::VM) : new VM);
std::unique_ptr<VMFace> vm;
#if ETH_JIT
vm.reset(_kind == Kind::JIT ? static_cast<VMFace*>(new jit::VM) : new VM);
#else
vm.reset(new VM);
#endif
vm->reset(_gas);
return vm;
}

2
libevm/VMFace.h

@ -32,7 +32,7 @@ struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {};
class StackTooSmall: virtual public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
struct StackTooSmall: virtual public VMException { StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash.
// Currently we just pull out the right (low-order in BE) 160-bits.

19
libevmface/CMakeLists.txt

@ -17,25 +17,6 @@ include_directories(..)
target_link_libraries(${EXECUTABLE} devcore)
if("${TARGET_PLATFORM}" STREQUAL "w64")
target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s)
target_link_libraries(${EXECUTABLE} iphlpapi)
target_link_libraries(${EXECUTABLE} ws2_32)
target_link_libraries(${EXECUTABLE} mswsock)
target_link_libraries(${EXECUTABLE} shlwapi)
elseif (APPLE)
# Latest mavericks boost libraries only come with -mt
find_package(Threads REQUIRED)
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
elseif (UNIX)
target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY})
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
else ()
target_link_libraries(${EXECUTABLE} boost_thread)
find_package(Threads REQUIRED)
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
endif ()
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

21
libevmface/Instruction.cpp

@ -39,7 +39,7 @@ const std::map<std::string, Instruction> dev::eth::c_instructions =
{ "MOD", Instruction::MOD },
{ "SMOD", Instruction::SMOD },
{ "EXP", Instruction::EXP },
{ "NEG", Instruction::NEG },
{ "BNOT", Instruction::BNOT },
{ "LT", Instruction::LT },
{ "GT", Instruction::GT },
{ "SLT", Instruction::SLT },
@ -52,6 +52,7 @@ const std::map<std::string, Instruction> dev::eth::c_instructions =
{ "BYTE", Instruction::BYTE },
{ "ADDMOD", Instruction::ADDMOD },
{ "MULMOD", Instruction::MULMOD },
{ "SIGNEXTEND", Instruction::SIGNEXTEND },
{ "SHA3", Instruction::SHA3 },
{ "ADDRESS", Instruction::ADDRESS },
{ "BALANCE", Instruction::BALANCE },
@ -148,6 +149,11 @@ const std::map<std::string, Instruction> dev::eth::c_instructions =
{ "SWAP14", Instruction::SWAP14 },
{ "SWAP15", Instruction::SWAP15 },
{ "SWAP16", Instruction::SWAP16 },
{ "LOG0", Instruction::LOG0 },
{ "LOG1", Instruction::LOG1 },
{ "LOG2", Instruction::LOG2 },
{ "LOG3", Instruction::LOG3 },
{ "LOG4", Instruction::LOG4 },
{ "CREATE", Instruction::CREATE },
{ "CALL", Instruction::CALL },
{ "CALLCODE", Instruction::CALLCODE },
@ -166,7 +172,7 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::MOD, { "MOD", 0, 2, 1 } },
{ Instruction::SMOD, { "SMOD", 0, 2, 1 } },
{ Instruction::EXP, { "EXP", 0, 2, 1 } },
{ Instruction::NEG, { "NEG", 0, 1, 1 } },
{ Instruction::BNOT, { "BNOT", 0, 1, 1 } },
{ Instruction::LT, { "LT", 0, 2, 1 } },
{ Instruction::GT, { "GT", 0, 2, 1 } },
{ Instruction::SLT, { "SLT", 0, 2, 1 } },
@ -179,6 +185,7 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::BYTE, { "BYTE", 0, 2, 1 } },
{ Instruction::ADDMOD, { "ADDMOD", 0, 3, 1 } },
{ Instruction::MULMOD, { "MULMOD", 0, 3, 1 } },
{ Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1 } },
{ Instruction::SHA3, { "SHA3", 0, 2, 1 } },
{ Instruction::ADDRESS, { "ADDRESS", 0, 0, 1 } },
{ Instruction::BALANCE, { "BALANCE", 0, 1, 1 } },
@ -275,6 +282,11 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::SWAP14, { "SWAP14", 0, 15, 15 } },
{ Instruction::SWAP15, { "SWAP15", 0, 16, 16 } },
{ Instruction::SWAP16, { "SWAP16", 0, 17, 17 } },
{ Instruction::LOG0, { "LOG0", 0, 1, 0 } },
{ Instruction::LOG1, { "LOG1", 0, 2, 0 } },
{ Instruction::LOG2, { "LOG2", 0, 3, 0 } },
{ Instruction::LOG3, { "LOG3", 0, 4, 0 } },
{ Instruction::LOG4, { "LOG4", 0, 5, 0 } },
{ Instruction::CREATE, { "CREATE", 0, 3, 1 } },
{ Instruction::CALL, { "CALL", 0, 7, 1 } },
{ Instruction::CALLCODE, { "CALLCODE", 0, 7, 1 } },
@ -318,3 +330,8 @@ InstructionInfo dev::eth::instructionInfo(Instruction _inst)
return InstructionInfo({"<INVALID_INSTRUCTION: " + toString((unsigned)_inst) + ">", 0, 0, 0});
}
}
bool dev::eth::isValidInstruction(Instruction _inst)
{
return !!c_instructionInfo.count(_inst);
}

12
libevmface/Instruction.h

@ -44,7 +44,7 @@ enum class Instruction: uint8_t
MOD, ///< modulo remainder operation
SMOD, ///< signed modulo remainder operation
EXP, ///< exponential operation
NEG, ///< negation operation
BNOT, ///< bitwise not
LT, ///< less-than comparision
GT, ///< greater-than comparision
SLT, ///< signed less-than comparision
@ -58,6 +58,7 @@ enum class Instruction: uint8_t
BYTE, ///< retrieve single byte from word
ADDMOD, ///< unsigned modular addition
MULMOD, ///< unsigned modular multiplication
SIGNEXTEND, ///< extend length of signed integer
SHA3 = 0x20, ///< compute SHA3-256 hash
ADDRESS = 0x30, ///< get address of currently executing account
@ -161,6 +162,12 @@ enum class Instruction: uint8_t
SWAP15, ///< swaps the highest and 16th highest value on the stack
SWAP16, ///< swaps the highest and 17th highest value on the stack
LOG0 = 0xa0, ///< Makes a log entry; no topics.
LOG1, ///< Makes a log entry; 1 topic.
LOG2, ///< Makes a log entry; 2 topics.
LOG3, ///< Makes a log entry; 3 topics.
LOG4, ///< Makes a log entry; 4 topics.
CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account
RETURN, ///< halt execution returning output data
@ -180,6 +187,9 @@ struct InstructionInfo
/// Information on all the instructions.
InstructionInfo instructionInfo(Instruction _inst);
/// check whether instructions exists
bool isValidInstruction(Instruction _inst);
/// Convert from string mnemonic to Instruction type.
extern const std::map<std::string, Instruction> c_instructions;

15
libevmjit/Compiler.cpp

@ -176,6 +176,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
llvm::Type* mainFuncArgTypes[] = {m_builder.getInt32Ty(), Type::RuntimePtr}; // There must be int in first place because LLVM does not support other signatures
auto mainFuncType = llvm::FunctionType::get(Type::MainReturn, mainFuncArgTypes, false);
m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, "main", module.get());
m_mainFunc->arg_begin()->getNextNode()->setName("rt");
// Create the basic blocks.
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc);
@ -186,7 +187,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
// Init runtime structures.
RuntimeManager runtimeManager(m_builder);
GasMeter gasMeter(m_builder, runtimeManager);
Memory memory(m_builder, gasMeter, runtimeManager);
Memory memory(runtimeManager, gasMeter);
Ext ext(runtimeManager);
Stack stack(m_builder, runtimeManager);
Arith256 arith(m_builder);
@ -371,14 +372,14 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
break;
}
case Instruction::NEG:
/*case Instruction::NEG:
{
auto top = stack.pop();
auto zero = Constant::get(0);
auto res = m_builder.CreateSub(zero, top);
stack.push(res);
break;
}
}*/
case Instruction::LT:
{
@ -796,7 +797,8 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto index = stack.pop();
auto size = stack.pop();
memory.registerReturnData(index, size);
memory.require(index, size);
_runtimeManager.registerReturnData(index, size);
m_builder.CreateRet(Constant::get(ReturnCode::Return));
break;
@ -814,6 +816,11 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
break;
}
default: // Invalid instruction - runtime exception
{
_runtimeManager.raiseException(ReturnCode::BadInstruction);
}
}
}

6
libevmjit/CompilerHelper.cpp

@ -27,8 +27,10 @@ llvm::Function* CompilerHelper::getMainFunction()
{
assert(m_builder.GetInsertBlock());
auto mainFunc = m_builder.GetInsertBlock()->getParent();
assert(mainFunc && mainFunc->getName() == "main");
return mainFunc;
assert(mainFunc);
if (mainFunc->getName() == "main")
return mainFunc;
return nullptr;
}

7
libevmjit/CompilerHelper.h

@ -31,6 +31,13 @@ protected:
llvm::IRBuilder<>& m_builder;
llvm::IRBuilder<>& getBuilder() { return m_builder; }
template<typename ..._Args>
llvm::CallInst* createCall(llvm::Function* _func, _Args*... _args)
{
llvm::Value* args[] = {_args...};
return getBuilder().CreateCall(_func, args);
}
friend class RuntimeHelper;
};

8
libevmjit/ExecutionEngine.cpp

@ -35,8 +35,6 @@ ExecutionEngine::ExecutionEngine()
}
extern "C" { EXPORT std::jmp_buf* rt_jmpBuf; }
int ExecutionEngine::run(std::unique_ptr<llvm::Module> _module, u256& _gas, ExtVMFace* _ext)
{
auto module = _module.get(); // Keep ownership of the module in _module
@ -102,21 +100,19 @@ int ExecutionEngine::run(std::unique_ptr<llvm::Module> _module, u256& _gas, ExtV
_ext->code = decltype(_ext->code)(fakecode, 8);
}
// Init runtime
Runtime runtime(_gas, *_ext);
auto entryFunc = module->getFunction("main");
if (!entryFunc)
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("main function not found"));
ReturnCode returnCode;
std::jmp_buf buf;
Runtime runtime(_gas, *_ext, buf);
auto r = setjmp(buf);
if (r == 0)
{
auto executionStartTime = std::chrono::high_resolution_clock::now();
rt_jmpBuf = &buf;
auto result = exec->runFunction(entryFunc, {{}, llvm::GenericValue(runtime.getDataPtr())});
returnCode = static_cast<ReturnCode>(result.IntVal.getZExtValue());

30
libevmjit/Ext.cpp

@ -63,7 +63,7 @@ Ext::Ext(RuntimeManager& _runtimeManager):
m_call = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argsTypes, false), Linkage::ExternalLinkage, "ext_call", module);
m_sha3 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 4}, false), Linkage::ExternalLinkage, "ext_sha3", module);
m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 4}, false), Linkage::ExternalLinkage, "ext_exp", module);
m_codeAt = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, Type::WordPtr, false), Linkage::ExternalLinkage, "ext_codeAt", module);
m_codeAt = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, {argsTypes, 2}, false), Linkage::ExternalLinkage, "ext_codeAt", module);
m_codesizeAt = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "ext_codesizeAt", module);
}
@ -109,8 +109,7 @@ llvm::Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::V
m_builder.CreateStore(_endowment, m_args[0]);
m_builder.CreateStore(_initOff, m_arg2);
m_builder.CreateStore(_initSize, m_arg3);
llvm::Value* args[] = {getRuntimeManager().getRuntimePtr(), m_args[0], m_arg2, m_arg3, m_args[1]};
m_builder.CreateCall(m_create, args);
createCall(m_create, getRuntimeManager().getRuntimePtr(), m_args[0], m_arg2, m_arg3, m_args[1]);
llvm::Value* address = m_builder.CreateLoad(m_args[1]);
address = Endianness::toNative(m_builder, address);
return address;
@ -128,9 +127,7 @@ llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::V
m_builder.CreateStore(_outSize, m_arg7);
auto codeAddress = Endianness::toBE(m_builder, _codeAddress);
m_builder.CreateStore(codeAddress, m_arg8);
llvm::Value* args[] = {getRuntimeManager().getRuntimePtr(), m_args[0], m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_arg8, m_args[1]};
m_builder.CreateCall(m_call, args);
createCall(m_call, getRuntimeManager().getRuntimePtr(), m_args[0], m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_arg8, m_args[1]);
_gas = m_builder.CreateLoad(m_args[0]); // Return gas
return m_builder.CreateLoad(m_args[1]);
}
@ -139,8 +136,7 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
{
m_builder.CreateStore(_inOff, m_args[0]);
m_builder.CreateStore(_inSize, m_arg2);
llvm::Value* args[] = {getRuntimeManager().getRuntimePtr(), m_args[0], m_arg2, m_args[1]};
m_builder.CreateCall(m_sha3, args);
createCall(m_sha3, getRuntimeManager().getRuntimePtr(), m_args[0], m_arg2, m_args[1]);
llvm::Value* hash = m_builder.CreateLoad(m_args[1]);
hash = Endianness::toNative(m_builder, hash);
return hash;
@ -148,10 +144,10 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
llvm::Value* Ext::exp(llvm::Value* _left, llvm::Value* _right)
{
// TODO: Move ext to Arith256
m_builder.CreateStore(_left, m_args[0]);
m_builder.CreateStore(_right, m_arg2);
llvm::Value* args[] = {getRuntimeManager().getRuntimePtr(), m_args[0], m_arg2, m_args[1]};
m_builder.CreateCall(m_exp, args);
createCall(m_exp, getRuntimeManager().getRuntimePtr(), m_args[0], m_arg2, m_args[1]);
return m_builder.CreateLoad(m_args[1]);
}
@ -166,8 +162,7 @@ llvm::Value* Ext::codesizeAt(llvm::Value* _addr)
{
auto addr = Endianness::toBE(m_builder, _addr);
m_builder.CreateStore(addr, m_args[0]);
llvm::Value* args[] = {getRuntimeManager().getRuntimePtr(), m_args[0], m_args[1]};
m_builder.CreateCall(m_codesizeAt, args);
createCall(m_codesizeAt, getRuntimeManager().getRuntimePtr(), m_args[0], m_args[1]);
return m_builder.CreateLoad(m_args[1]);
}
@ -225,10 +220,9 @@ EXPORT void ext_create(Runtime* _rt, i256* _endowment, i256* _initOff, i256* _in
u256 gas; // TODO: Handle gas
auto initOff = static_cast<size_t>(llvm2eth(*_initOff));
auto initSize = static_cast<size_t>(llvm2eth(*_initSize));
//auto&& initRef = bytesConstRef(Runtime::getMemory().data() + initOff, initSize);
bytesConstRef initRef;
auto&& initRef = bytesConstRef(_rt->getMemory().data() + initOff, initSize);
OnOpFunc onOp{}; // TODO: Handle that thing
h256 address = ext.create(endowment, &gas, initRef, onOp);
h256 address(ext.create(endowment, &gas, initRef, onOp), h256::AlignRight);
*_address = address;
}
else
@ -252,8 +246,8 @@ EXPORT void ext_call(Runtime* _rt, i256* _gas, h256* _receiveAddress, i256* _val
auto inSize = static_cast<size_t>(llvm2eth(*_inSize));
auto outOff = static_cast<size_t>(llvm2eth(*_outOff));
auto outSize = static_cast<size_t>(llvm2eth(*_outSize));
auto&& inRef = bytesConstRef(); //Runtime::getMemory().data() + inOff, inSize);
auto&& outRef = bytesConstRef(); // Runtime::getMemory().data() + outOff, outSize);
auto&& inRef = bytesConstRef(_rt->getMemory().data() + inOff, inSize);
auto&& outRef = bytesConstRef(_rt->getMemory().data() + outOff, outSize);
OnOpFunc onOp{}; // TODO: Handle that thing
auto codeAddress = right160(*_codeAddress);
ret = ext.call(receiveAddress, value, inRef, &gas, outRef, onOp, {}, codeAddress);
@ -267,7 +261,7 @@ EXPORT void ext_sha3(Runtime* _rt, i256* _inOff, i256* _inSize, i256* _ret)
{
auto inOff = static_cast<size_t>(llvm2eth(*_inOff));
auto inSize = static_cast<size_t>(llvm2eth(*_inSize));
auto dataRef = bytesConstRef(); // Runtime::getMemory().data() + inOff, inSize);
auto dataRef = bytesConstRef(_rt->getMemory().data() + inOff, inSize);
auto hash = sha3(dataRef);
*_ret = *reinterpret_cast<i256*>(&hash);
}

13
libevmjit/GasMeter.cpp

@ -31,7 +31,7 @@ uint64_t getStepCost(Instruction inst) // TODO: Add this function to FeeSructure
return 0;
case Instruction::SSTORE:
return static_cast<uint64_t>(c_sstoreGas);
return static_cast<uint64_t>(c_sstoreResetGas); // FIXME: Check store gas
case Instruction::SLOAD:
return static_cast<uint64_t>(c_sloadGas);
@ -101,12 +101,7 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB);
m_builder.SetInsertPoint(outOfGasBB);
//auto longjmpFunc = llvm::Intrinsic::getDeclaration(_module, llvm::Intrinsic::eh_sjlj_longjmp);
auto extJmpBuf = new llvm::GlobalVariable(*module, Type::BytePtr, false, llvm::GlobalVariable::ExternalLinkage, nullptr, "rt_jmpBuf");
llvm::Type* args[] = {Type::BytePtr, m_builder.getInt32Ty()};
auto longjmpNative = llvm::Function::Create(llvm::FunctionType::get(Type::Void, args, false), llvm::Function::ExternalLinkage, "longjmp", module);
m_builder.CreateCall2(longjmpNative, m_builder.CreateLoad(extJmpBuf), Constant::get(ReturnCode::OutOfGas));
_runtimeManager.raiseException(ReturnCode::OutOfGas);
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(updateBB);
@ -134,7 +129,7 @@ void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValu
{
assert(!m_checkCall); // Everything should've been commited before
static const auto sstoreCost = static_cast<uint64_t>(c_sstoreGas);
static const auto sstoreCost = static_cast<uint64_t>(c_sstoreResetGas); // FIXME: Check store gas
// [ADD] if oldValue == 0 and newValue != 0 => 2*cost
// [DEL] if oldValue != 0 and newValue == 0 => 0
@ -148,7 +143,7 @@ void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValu
auto isDel = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDel");
auto cost = m_builder.CreateSelect(isAdd, Constant::get(2 * sstoreCost), Constant::get(sstoreCost), "cost");
cost = m_builder.CreateSelect(isDel, Constant::get(0), cost, "cost");
m_builder.CreateCall(m_gasCheckFunc, cost);
createCall(m_gasCheckFunc, cost);
}
void GasMeter::giveBack(llvm::Value* _gas)

2
libevmjit/GasMeter.h

@ -13,7 +13,7 @@ namespace jit
{
class RuntimeManager;
class GasMeter : public CompilerHelper
class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper
{
public:
GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager);

18
libevmjit/Memory.cpp

@ -24,8 +24,8 @@ namespace eth
namespace jit
{
Memory::Memory(llvm::IRBuilder<>& _builder, GasMeter& _gasMeter, RuntimeManager& _runtimeManager):
CompilerHelper(_builder)
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
RuntimeHelper(_runtimeManager)
{
auto module = getModule();
auto i64Ty = m_builder.getInt64Ty();
@ -40,12 +40,6 @@ Memory::Memory(llvm::IRBuilder<>& _builder, GasMeter& _gasMeter, RuntimeManager&
m_size = new llvm::GlobalVariable(*module, Type::i256, false, llvm::GlobalVariable::PrivateLinkage, Constant::get(0), "mem.size");
m_size->setUnnamedAddr(true); // Address is not important
m_returnDataOffset = new llvm::GlobalVariable(*module, Type::i256, false, llvm::GlobalVariable::ExternalLinkage, nullptr, "mem_returnDataOffset");
m_returnDataOffset->setUnnamedAddr(true); // Address is not important
m_returnDataSize = new llvm::GlobalVariable(*module, Type::i256, false, llvm::GlobalVariable::ExternalLinkage, nullptr, "mem_returnDataSize");
m_returnDataSize->setUnnamedAddr(true); // Address is not important
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", module);
llvm::AttrBuilder attrBuilder;
@ -180,14 +174,6 @@ void Memory::require(llvm::Value* _offset, llvm::Value* _size)
require(sizeRequired);
}
void Memory::registerReturnData(llvm::Value* _index, llvm::Value* _size)
{
require(_index, _size); // Make sure that memory is allocated and count gas
m_builder.CreateStore(_index, m_returnDataOffset);
m_builder.CreateStore(_size, m_returnDataSize);
}
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
llvm::Value* _destMemIdx, llvm::Value* _reqBytes)
{

12
libevmjit/Memory.h

@ -10,12 +10,11 @@ namespace eth
{
namespace jit
{
class RuntimeManager;
class Memory : public CompilerHelper
class Memory : public RuntimeHelper
{
public:
Memory(llvm::IRBuilder<>& _builder, class GasMeter& _gasMeter, RuntimeManager& _runtimeManager);
Memory(RuntimeManager& _runtimeManager, class GasMeter& _gasMeter);
llvm::Value* loadWord(llvm::Value* _addr);
void storeWord(llvm::Value* _addr, llvm::Value* _word);
@ -31,9 +30,6 @@ public:
/// Requires the amount of memory to for data defined by offset and size. And counts gas fee for that memory.
void require(llvm::Value* _offset, llvm::Value* _size);
void registerReturnData(llvm::Value* _index, llvm::Value* _size);
bytesConstRef getReturnData();
void dump(uint64_t _begin, uint64_t _end = 0);
private:
@ -44,10 +40,6 @@ private:
llvm::GlobalVariable* m_data;
llvm::GlobalVariable* m_size;
/// @TODO: m_data and m_size could be used
llvm::GlobalVariable* m_returnDataOffset;
llvm::GlobalVariable* m_returnDataSize;
llvm::Function* m_resize;
llvm::Function* m_require;
llvm::Function* m_loadWord;

66
libevmjit/Runtime.cpp

@ -24,9 +24,10 @@ llvm::StructType* RuntimeData::getType()
{
llvm::ArrayType::get(Type::i256, _size),
Type::BytePtr,
Type::BytePtr,
Type::BytePtr
};
type = llvm::StructType::create(elems, "RuntimeData");
type = llvm::StructType::create(elems, "Runtime");
}
return type;
}
@ -56,13 +57,9 @@ llvm::Twine getName(RuntimeData::Index _index)
}
}
static Runtime* g_runtime; // FIXME: Remove
Runtime::Runtime(u256 _gas, ExtVMFace& _ext):
Runtime::Runtime(u256 _gas, ExtVMFace& _ext, jmp_buf _jmpBuf):
m_ext(_ext)
{
assert(!g_runtime);
g_runtime = this;
set(RuntimeData::Gas, _gas);
set(RuntimeData::Address, fromAddress(_ext.myAddress));
set(RuntimeData::Caller, fromAddress(_ext.caller));
@ -79,11 +76,7 @@ Runtime::Runtime(u256 _gas, ExtVMFace& _ext):
set(RuntimeData::CodeSize, _ext.code.size()); // TODO: Use constant
m_data.callData = _ext.data.data();
m_data.code = _ext.code.data();
}
Runtime::~Runtime()
{
g_runtime = nullptr;
m_data.jmpBuf = _jmpBuf;
}
void Runtime::set(RuntimeData::Index _index, u256 _value)
@ -96,16 +89,14 @@ u256 Runtime::getGas() const
return llvm2eth(m_data.elems[RuntimeData::Gas]);
}
extern "C" {
EXPORT i256 mem_returnDataOffset; // FIXME: Dis-globalize
EXPORT i256 mem_returnDataSize;
}
bytesConstRef Runtime::getReturnData() const
{
// TODO: Handle large indexes
auto offset = static_cast<size_t>(llvm2eth(mem_returnDataOffset));
auto size = static_cast<size_t>(llvm2eth(mem_returnDataSize));
auto offset = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataOffset]));
auto size = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataSize]));
assert(offset + size <= m_memory.size());
// TODO: Handle invalid data access by returning empty ref
return {m_memory.data() + offset, size};
}
@ -113,6 +104,8 @@ bytesConstRef Runtime::getReturnData() const
RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder): CompilerHelper(_builder)
{
m_dataPtr = new llvm::GlobalVariable(*getModule(), Type::RuntimePtr, false, llvm::GlobalVariable::PrivateLinkage, llvm::UndefValue::get(Type::RuntimePtr), "rt");
llvm::Type* args[] = {Type::BytePtr, m_builder.getInt32Ty()};
m_longjmp = llvm::Function::Create(llvm::FunctionType::get(Type::Void, args, false), llvm::Function::ExternalLinkage, "longjmp", getModule());
// Export data
auto mainFunc = getMainFunction();
@ -122,15 +115,36 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder): CompilerHelper(_bui
llvm::Value* RuntimeManager::getRuntimePtr()
{
// TODO: If in main function - get it from param
return m_builder.CreateLoad(m_dataPtr);
if (auto mainFunc = getMainFunction())
return mainFunc->arg_begin()->getNextNode(); // Runtime is the second parameter of main function
return m_builder.CreateLoad(m_dataPtr, "rt");
}
llvm::Value* RuntimeManager::get(RuntimeData::Index _index)
llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
{
llvm::Value* idxList[] = {m_builder.getInt32(0), m_builder.getInt32(0), m_builder.getInt32(_index)};
auto ptr = m_builder.CreateInBoundsGEP(getRuntimePtr(), idxList, getName(_index) + "Ptr");
return m_builder.CreateLoad(ptr, getName(_index));
return m_builder.CreateInBoundsGEP(getRuntimePtr(), idxList, getName(_index) + "Ptr");
}
llvm::Value* RuntimeManager::get(RuntimeData::Index _index)
{
return m_builder.CreateLoad(getPtr(_index), getName(_index));
}
void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value)
{
m_builder.CreateStore(_value, getPtr(_index));
}
void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size)
{
set(RuntimeData::ReturnDataOffset, _offset);
set(RuntimeData::ReturnDataSize, _size);
}
void RuntimeManager::raiseException(ReturnCode _returnCode)
{
m_builder.CreateCall2(m_longjmp, getJmpBuf(), Constant::get(_returnCode));
}
llvm::Value* RuntimeManager::get(Instruction _inst)
@ -167,6 +181,12 @@ llvm::Value* RuntimeManager::getCode()
return getBuilder().CreateLoad(ptr, "code");
}
llvm::Value* RuntimeManager::getJmpBuf()
{
auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 3, "jmpbufPtr");
return getBuilder().CreateLoad(ptr, "jmpbuf");
}
llvm::Value* RuntimeManager::getGas()
{
return get(RuntimeData::Gas);

22
libevmjit/Runtime.h

@ -7,6 +7,7 @@
#include "CompilerHelper.h"
#include "Utils.h"
#include "Type.h"
#ifdef _MSC_VER
@ -41,12 +42,16 @@ struct RuntimeData
GasLimit,
CodeSize,
_size
_size,
ReturnDataOffset = CallValue, // Reuse 2 fields for return data reference
ReturnDataSize = CallDataSize
};
i256 elems[_size];
byte const* callData;
byte const* code;
decltype(&jmp_buf{}[0]) jmpBuf;
static llvm::StructType* getType();
};
@ -57,8 +62,7 @@ using MemoryImpl = bytes;
class Runtime
{
public:
Runtime(u256 _gas, ExtVMFace& _ext);
~Runtime();
Runtime(u256 _gas, ExtVMFace& _ext, jmp_buf _jmpBuf);
Runtime(const Runtime&) = delete;
void operator=(const Runtime&) = delete;
@ -71,6 +75,7 @@ public:
u256 getGas() const;
bytesConstRef getReturnData() const;
decltype(&jmp_buf{}[0]) getJmpBuf() { return m_data.jmpBuf; }
private:
void set(RuntimeData::Index _index, u256 _value);
@ -96,8 +101,17 @@ public:
llvm::Value* getCode();
void setGas(llvm::Value* _gas);
void registerReturnData(llvm::Value* _index, llvm::Value* _size);
void raiseException(ReturnCode _returnCode);
private:
llvm::GlobalVariable* m_dataPtr;
llvm::Value* getPtr(RuntimeData::Index _index);
void set(RuntimeData::Index _index, llvm::Value* _value);
llvm::Value* getJmpBuf();
llvm::GlobalVariable* m_dataPtr = nullptr;
llvm::Function* m_longjmp = nullptr;
};
}

8
libevmjit/Stack.cpp

@ -74,13 +74,11 @@ extern "C"
using namespace dev::eth::jit;
extern std::jmp_buf* rt_jmpBuf;
EXPORT void stack_pop(Runtime* _rt, uint64_t _count)
{
auto& stack = _rt->getStack();
if (stack.size() < _count)
longjmp(*rt_jmpBuf, static_cast<uint64_t>(ReturnCode::StackTooSmall));
longjmp(_rt->getJmpBuf(), static_cast<uint64_t>(ReturnCode::StackTooSmall));
stack.erase(stack.end() - _count, stack.end());
}
@ -99,7 +97,7 @@ EXPORT void stack_get(Runtime* _rt, uint64_t _index, i256* _ret)
auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code
if (stack.size() <= _index)
longjmp(*rt_jmpBuf, static_cast<uint64_t>(ReturnCode::StackTooSmall));
longjmp(_rt->getJmpBuf(), static_cast<uint64_t>(ReturnCode::StackTooSmall));
*_ret = *(stack.rbegin() + _index);
}
@ -109,7 +107,7 @@ EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256* _word)
auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code
if (stack.size() <= _index)
longjmp(*rt_jmpBuf, static_cast<uint64_t>(ReturnCode::StackTooSmall));
longjmp(_rt->getJmpBuf(), static_cast<uint64_t>(ReturnCode::StackTooSmall));
*(stack.rbegin() + _index) = *_word;
}

3
libevmjit/Type.h

@ -43,7 +43,8 @@ enum class ReturnCode
BadJumpDestination = 101,
OutOfGas = 102,
StackTooSmall = 103
StackTooSmall = 103,
BadInstruction = 104,
};
struct Constant

16
libevmjit/VM.cpp

@ -5,6 +5,7 @@
#include "ExecutionEngine.h"
#include "Compiler.h"
#include "Type.h"
namespace dev
{
@ -13,24 +14,23 @@ namespace eth
namespace jit
{
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
{
assert(!_onOp); // Parameter ignored
assert(_steps == (uint64_t)-1); // Parameter ignored
auto module = Compiler().compile(_ext.code);
ExecutionEngine engine;
auto exitCode = engine.run(std::move(module), m_gas, &_ext);
switch (exitCode)
switch (static_cast<ReturnCode>(exitCode))
{
case 101:
case ReturnCode::BadJumpDestination:
BOOST_THROW_EXCEPTION(BadJumpDestination());
case 102:
case ReturnCode::OutOfGas:
BOOST_THROW_EXCEPTION(OutOfGas());
case 103:
case ReturnCode::StackTooSmall:
BOOST_THROW_EXCEPTION(StackTooSmall(1,0));
case ReturnCode::BadInstruction:
BOOST_THROW_EXCEPTION(BadInstruction());
}
m_output = std::move(engine.returnData);

4
liblll/Assembly.cpp

@ -147,7 +147,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i)
return _out;
}
ostream& Assembly::streamOut(ostream& _out, string const& _prefix) const
ostream& Assembly::streamRLP(ostream& _out, string const& _prefix) const
{
_out << _prefix << ".code:" << endl;
for (AssemblyItem const& i: m_items)
@ -189,7 +189,7 @@ ostream& Assembly::streamOut(ostream& _out, string const& _prefix) const
for (auto const& i: m_subs)
{
_out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << endl;
i.second.streamOut(_out, _prefix + " ");
i.second.streamRLP(_out, _prefix + " ");
}
}
return _out;

6
liblll/Assembly.h

@ -104,11 +104,11 @@ public:
void injectStart(AssemblyItem const& _i);
std::string out() const { std::stringstream ret; streamOut(ret); return ret.str(); }
std::string out() const { std::stringstream ret; streamRLP(ret); return ret.str(); }
int deposit() const { return m_deposit; }
bytes assemble() const;
Assembly& optimise(bool _enable);
std::ostream& streamOut(std::ostream& _out, std::string const& _prefix = "") const;
std::ostream& streamRLP(std::ostream& _out, std::string const& _prefix = "") const;
private:
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
@ -127,7 +127,7 @@ private:
inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a)
{
_a.streamOut(_out);
_a.streamRLP(_out);
return _out;
}

50
libp2p/Host.cpp

@ -143,6 +143,9 @@ void Host::stop()
void Host::quit()
{
// called to force io_service to kill any remaining tasks it might have -
// such tasks may involve socket reads from Capabilities that maintain references
// to resources we're about to free.
stop();
m_ioService.reset();
// m_acceptor & m_socket are DANGEROUS now.
@ -249,17 +252,27 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp)
m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p);
else
{
m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), (unsigned short)p);
bi::address adr = adr = bi::address::from_string(eip);
try
{
adr = bi::address::from_string(_publicAddress);
}
catch (...) {}
m_public = bi::tcp::endpoint(adr, (unsigned short)p);
m_addresses.push_back(m_public.address());
}
}
else
{
// No UPnP - fallback on given public address or, if empty, the assumed peer address.
m_public = bi::tcp::endpoint(_publicAddress.size() ? bi::address::from_string(_publicAddress)
: m_peerAddresses.size() ? m_peerAddresses[0]
: bi::address(), m_listenPort);
m_addresses.push_back(m_public.address());
bi::address adr = m_peerAddresses.size() ? m_peerAddresses[0] : bi::address();
try
{
adr = bi::address::from_string(_publicAddress);
}
catch (...) {}
m_public = bi::tcp::endpoint(adr, m_listenPort);
m_addresses.push_back(adr);
}
}
@ -366,7 +379,7 @@ void Host::populateAddresses()
shared_ptr<Node> Host::noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId)
{
RecursiveGuard l(x_peers);
if (_a.port() < 30300 && _a.port() > 30303)
if (_a.port() < 30300 || _a.port() > 30303)
cwarn << "Wierd port being recorded!";
if (_a.port() >= /*49152*/32768)
@ -375,7 +388,7 @@ shared_ptr<Node> Host::noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, boo
_a = bi::tcp::endpoint(_a.address(), 0);
}
cnote << "Node:" << _id.abridged() << _a << (_ready ? "ready" : "used") << _oldId.abridged() << (m_nodes.count(_id) ? "[have]" : "[NEW]");
// cnote << "Node:" << _id.abridged() << _a << (_ready ? "ready" : "used") << _oldId.abridged() << (m_nodes.count(_id) ? "[have]" : "[NEW]");
// First check for another node with the same connection credentials, and put it in oldId if found.
if (!_oldId)
@ -422,7 +435,7 @@ shared_ptr<Node> Host::noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, boo
else
m_private -= i;
cnote << m_nodes[_id]->index << ":" << m_ready;
// cnote << m_nodes[_id]->index << ":" << m_ready;
m_hadNewNodes = true;
@ -648,23 +661,26 @@ void Host::prunePeers()
{
RecursiveGuard l(x_peers);
// We'll keep at most twice as many as is ideal, halfing what counts as "too young to kill" until we get there.
for (unsigned old = 15000; m_peers.size() > m_idealPeerCount * 2 && old > 100; old /= 2)
while (m_peers.size() > m_idealPeerCount)
set<NodeId> dc;
for (unsigned old = 15000; m_peers.size() - dc.size() > m_idealPeerCount * 2 && old > 100; old /= 2)
if (m_peers.size() - dc.size() > m_idealPeerCount)
{
// look for worst peer to kick off
// first work out how many are old enough to kick off.
shared_ptr<Session> worst;
unsigned agedPeers = 0;
for (auto i: m_peers)
if (auto p = i.second.lock())
if (/*(m_mode != NodeMode::Host || p->m_caps != 0x01) &&*/ chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers.
{
++agedPeers;
if ((!worst || p->rating() < worst->rating() || (p->rating() == worst->rating() && p->m_connect > worst->m_connect))) // kill older ones
worst = p;
}
if (!dc.count(i.first))
if (auto p = i.second.lock())
if (/*(m_mode != NodeMode::Host || p->m_caps != 0x01) &&*/ chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers.
{
++agedPeers;
if ((!worst || p->rating() < worst->rating() || (p->rating() == worst->rating() && p->m_connect > worst->m_connect))) // kill older ones
worst = p;
}
if (!worst || agedPeers <= m_idealPeerCount)
break;
dc.insert(worst->id());
worst->disconnect(TooManyPeers);
}

2
libp2p/Host.h

@ -203,7 +203,7 @@ private:
/// This won't touch alter the blockchain.
virtual void doWork();
std::shared_ptr<Node> noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = h256());
std::shared_ptr<Node> noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = NodeId());
Nodes potentialPeers(RangeMask<unsigned> const& _known);
std::string m_clientVersion; ///< Our version string.

1
libp2p/Session.cpp

@ -475,7 +475,6 @@ void Session::drop(DisconnectReason _reason)
{
if (m_dropped)
return;
cerr << (void*)this << " dropped" << endl;
if (m_socket.is_open())
try
{

12
libpyserpent/pyserpent.cpp

@ -120,11 +120,15 @@ std::vector<Node> cppifyNodeList(PyObject* o) {
}
PYMETHOD(ps_compile, FROMSTR, compile, pyifyString)
PYMETHOD(ps_compile_chunk, FROMSTR, compileChunk, pyifyString)
PYMETHOD(ps_compile_to_lll, FROMSTR, compileToLLL, pyifyNode)
PYMETHOD(ps_compile_chunk_to_lll, FROMSTR, compileChunkToLLL, pyifyNode)
PYMETHOD(ps_compile_lll, FROMNODE, compileLLL, pyifyString)
PYMETHOD(ps_parse, FROMSTR, parseSerpent, pyifyNode)
PYMETHOD(ps_rewrite, FROMNODE, rewrite, pyifyNode)
PYMETHOD(ps_rewrite_chunk, FROMNODE, rewriteChunk, pyifyNode)
PYMETHOD(ps_pretty_compile, FROMSTR, prettyCompile, pyifyNodeList)
PYMETHOD(ps_pretty_compile_chunk, FROMSTR, prettyCompileChunk, pyifyNodeList)
PYMETHOD(ps_pretty_compile_lll, FROMNODE, prettyCompileLLL, pyifyNodeList)
PYMETHOD(ps_serialize, FROMLIST, serialize, pyifyString)
PYMETHOD(ps_deserialize, FROMSTR, deserialize, pyifyNodeList)
@ -134,16 +138,24 @@ PYMETHOD(ps_parse_lll, FROMSTR, parseLLL, pyifyNode)
static PyMethodDef PyextMethods[] = {
{"compile", ps_compile, METH_VARARGS,
"Compile code."},
{"compile_chunk", ps_compile_chunk, METH_VARARGS,
"Compile code chunk (no wrappers)."},
{"compile_to_lll", ps_compile_to_lll, METH_VARARGS,
"Compile code to LLL."},
{"compile_chunk_to_lll", ps_compile_chunk_to_lll, METH_VARARGS,
"Compile code chunk to LLL (no wrappers)."},
{"compile_lll", ps_compile_lll, METH_VARARGS,
"Compile LLL to EVM."},
{"parse", ps_parse, METH_VARARGS,
"Parse serpent"},
{"rewrite", ps_rewrite, METH_VARARGS,
"Rewrite parsed serpent to LLL"},
{"rewrite_chunk", ps_rewrite_chunk, METH_VARARGS,
"Rewrite parsed serpent to LLL (no wrappers)"},
{"pretty_compile", ps_pretty_compile, METH_VARARGS,
"Compile to EVM opcodes"},
{"pretty_compile_chunk", ps_pretty_compile_chunk, METH_VARARGS,
"Compile chunk to EVM opcodes (no wrappers)"},
{"pretty_compile_lll", ps_pretty_compile_lll, METH_VARARGS,
"Compile LLL to EVM opcodes"},
{"serialize", ps_serialize, METH_VARARGS,

275
libqethereum/QEthereum.cpp

@ -1,3 +1,25 @@
/*
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 QEthereum.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <boost/filesystem.hpp>
#include <QtCore/QtCore>
#include <QtWebKitWidgets/QWebFrame>
#include <libdevcrypto/FileSystem.h>
@ -5,6 +27,8 @@
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
#include <libethereum/EthereumHost.h>
#include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.h>
#include "QEthereum.h"
using namespace std;
using namespace dev;
@ -21,7 +45,7 @@ dev::bytes toBytes(QString const& _s)
else
{
// Binary
cwarn << "THIS FUNCTIONALITY IS DEPRECATED. DO NOT ASSUME ASCII/BINARY-STRINGS WILL BE ACCEPTED. USE eth.fromAscii().";
cwarn << "'" << _s.toStdString() << "': Unrecognised format for number/hash. USE eth.fromAscii() if you mean to convert from ASCII.";
return asBytes(_s);
}
}
@ -448,11 +472,10 @@ QString QEthereum::doTransact(QString _json)
TransactionSkeleton t = toTransaction(_json);
if (!t.from && m_accounts.size())
{
auto b = m_accounts.begin()->first;
u256 b = 0;
for (auto a: m_accounts)
if (client()->balanceAt(a.first) > client()->balanceAt(b))
b = a.first;
t.from = b;
if (client()->balanceAt(a.first) > b)
t.from = a.first, b = client()->balanceAt(a.first);
}
if (!m_accounts.count(t.from))
return QString();
@ -466,7 +489,7 @@ QString QEthereum::doTransact(QString _json)
// TODO: insert validification hook here.
client()->transact(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice);
else
ret = toQJS(client()->transact(t.from, t.value, t.data, t.gas, t.gasPrice));
ret = toQJS(client()->transact(m_accounts[t.from].secret(), t.value, t.data, t.gas, t.gasPrice));
client()->flushTransactions();
return ret;
}
@ -535,8 +558,9 @@ void QEthereum::poll()
// TODO: repot and hook all these up.
QWhisper::QWhisper(QObject* _p, std::shared_ptr<dev::shh::Interface> const& _c): QObject(_p), m_face(_c)
QWhisper::QWhisper(QObject* _p, std::shared_ptr<dev::shh::Interface> const& _c, QList<dev::KeyPair> _ids): QObject(_p), m_face(_c)
{
setIdentities(_ids);
}
QWhisper::~QWhisper()
@ -559,37 +583,250 @@ void QWhisper::faceDieing()
}
void QWhisper::send(QString /*dev::Address*/ _dest, QString /*ev::KeyPair*/ _from, QString /*dev::h256 const&*/ _topic, QString /*dev::bytes const&*/ _payload)
static shh::Message toMessage(QString _json)
{
(void)_dest;
(void)_from;
(void)_topic;
(void)_payload;
shh::Message ret;
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
if (f.contains("from"))
ret.setFrom(toPublic(f["from"].toString()));
if (f.contains("to"))
ret.setTo(toPublic(f["to"].toString()));
if (f.contains("payload"))
ret.setPayload(toBytes(f["payload"].toString()));
return ret;
}
unsigned QWhisper::newWatch(QString _json)
static shh::Envelope toSealed(QString _json, shh::Message const& _m, Secret _from)
{
(void)_json;
return 0;
unsigned ttl = 50;
unsigned workToProve = 50;
shh::BuildTopic bt;
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
if (f.contains("ttl"))
ttl = f["ttl"].toInt();
if (f.contains("workToProve"))
workToProve = f["workToProve"].toInt();
if (f.contains("topic"))
{
if (f["topic"].isString())
bt.shift(asBytes(padded(f["topic"].toString(), 32)));
else if (f["topic"].isArray())
for (auto i: f["topic"].toArray())
bt.shift(asBytes(padded(i.toString(), 32)));
}
return _m.seal(_from, bt, ttl, workToProve);
}
QString QWhisper::watchMessages(unsigned _w)
void QWhisper::doPost(QString _json)
{
(void)_w;
return "";
shh::Message m = toMessage(_json);
Secret from;
if (m.from() && m_ids.count(m.from()))
{
cwarn << "Silently signing message from identity" << m.from().abridged() << ": User validation hook goes here.";
// TODO: insert validification hook here.
from = m_ids[m.from()];
}
face()->inject(toSealed(_json, m, from));
}
void QWhisper::setIdentities(QList<dev::KeyPair> const& _l)
{
m_ids.clear();
for (auto i: _l)
m_ids[i.pub()] = i.secret();
emit idsChanged();
}
static pair<shh::TopicMask, Public> toWatch(QString _json)
{
shh::BuildTopicMask bt(shh::BuildTopicMask::Empty);
Public to;
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
if (f.contains("to"))
to = toPublic(f["to"].toString());
if (f.contains("topic"))
{
if (f["topic"].isString())
bt.shift(asBytes(padded(f["topic"].toString(), 32)));
else if (f["topic"].isArray())
for (auto i: f["topic"].toArray())
if (i.isString())
bt.shift(asBytes(padded(i.toString(), 32)));
else
bt.shift();
}
return make_pair(bt.toTopicMask(), to);
}
// _json contains
// topic: the topic as an array of components, some may be null.
// to: specifies the id to which the message is encrypted. null if broadcast.
unsigned QWhisper::newWatch(QString _json)
{
auto w = toWatch(_json);
auto ret = face()->installWatch(w.first);
m_watches.insert(make_pair(ret, w.second));
return ret;
}
void QWhisper::killWatch(unsigned _w)
{
(void)_w;
face()->uninstallWatch(_w);
m_watches.erase(_w);
}
void QWhisper::clearWatches()
{
for (auto i: m_watches)
face()->uninstallWatch(i.first);
m_watches.clear();
}
static QString toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m)
{
QJsonObject v;
v["hash"] = toQJS(_h);
v["expiry"] = (int)_e.expiry();
v["sent"] = (int)_e.sent();
v["ttl"] = (int)_e.ttl();
v["workProved"] = (int)_e.workProved();
v["topic"] = toQJS(_e.topic());
v["payload"] = toQJS(_m.payload());
v["from"] = toQJS(_m.from());
v["to"] = toQJS(_m.to());
return QString::fromUtf8(QJsonDocument(v).toJson());
}
QString QWhisper::watchMessages(unsigned _w)
{
QString ret = "[";
auto wit = m_watches.find(_w);
if (wit == m_watches.end())
{
cwarn << "watchMessages called with invalid watch id" << _w;
return "";
}
Public p = wit->second;
if (!p || m_ids.count(p))
for (h256 const& h: face()->watchMessages(_w))
{
auto e = face()->envelope(h);
shh::Message m;
if (p)
{
cwarn << "Silently decrypting message from identity" << p.abridged() << ": User validation hook goes here.";
m = e.open(m_ids[p]);
}
else
m = e.open();
ret.append((ret == "[" ? "" : ",") + toJson(h, e, m));
}
return ret + "]";
}
QString QWhisper::newIdentity()
{
return toQJS(makeIdentity());
}
Public QWhisper::makeIdentity()
{
KeyPair kp = KeyPair::create();
emit newIdToAdd(toQJS(kp.sec()));
return kp.pub();
}
QString QWhisper::newGroup(QString _me, QString _others)
{
(void)_me;
(void)_others;
return "";
}
QString QWhisper::addToGroup(QString _group, QString _who)
{
(void)_group;
(void)_who;
return "";
}
void QWhisper::poll()
{
for (auto const& w: m_watches)
if (!w.second || m_ids.count(w.second))
for (h256 const& h: face()->checkWatch(w.first))
{
auto e = face()->envelope(h);
shh::Message m;
if (w.second)
{
cwarn << "Silently decrypting message from identity" << w.second.abridged() << ": User validation hook goes here.";
m = e.open(m_ids[w.second]);
if (!m)
continue;
}
else
m = e.open();
emit watchChanged(w.first, toJson(h, e, m));
}
}
#include <libdevcrypto/FileSystem.h>
QLDB::QLDB(QObject* _p): QObject(_p)
{
auto path = getDataDir() + "/.web3";
boost::filesystem::create_directories(path);
ldb::Options o;
o.create_if_missing = true;
ldb::DB::Open(o, path, &m_db);
}
QLDB::~QLDB()
{
}
void QLDB::put(QString _p, QString _k, QString _v)
{
bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes();
bytes v = toBytes(_v);
m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size()));
}
QString QLDB::get(QString _p, QString _k)
{
bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes();
string ret;
m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret);
return toQJS(dev::asBytes(ret));
}
void QLDB::putString(QString _p, QString _k, QString _v)
{
bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes();
string v = _v.toStdString();
m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size()));
}
QString QLDB::getString(QString _p, QString _k)
{
bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes();
string ret;
m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret);
return QString::fromStdString(ret);
}
// extra bits needed to link on VS

117
libqethereum/QEthereum.h

@ -1,11 +1,39 @@
/*
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 QEthereum.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#pragma warning(push)
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <QtCore/QObject>
#include <QtCore/QStringList>
#include <QtCore/QList>
#include <libdevcore/CommonIO.h>
#include <libethcore/CommonEth.h>
namespace ldb = leveldb;
namespace dev {
namespace eth {
class Interface;
@ -59,8 +87,9 @@ template <unsigned N> dev::FixedHash<N> toFixed(QString const& _s)
template <unsigned N> inline boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> toInt(QString const& _s);
inline dev::Address toAddress(QString const& _s) { return toFixed<20>(_s); }
inline dev::Secret toSecret(QString const& _s) { return toFixed<32>(_s); }
inline dev::Address toAddress(QString const& _s) { return toFixed<sizeof(dev::Address)>(_s); }
inline dev::Public toPublic(QString const& _s) { return toFixed<sizeof(dev::Public)>(_s); }
inline dev::Secret toSecret(QString const& _s) { return toFixed<sizeof(dev::Secret)>(_s); }
inline dev::u256 toU256(QString const& _s) { return toInt<32>(_s); }
template <unsigned S> QString toQJS(dev::FixedHash<S> const& _h) { return QString::fromStdString("0x" + toHex(_h.ref())); }
@ -213,26 +242,36 @@ class QWhisper: public QObject
Q_OBJECT
public:
QWhisper(QObject* _p, std::shared_ptr<dev::shh::Interface> const& _c);
QWhisper(QObject* _p, std::shared_ptr<dev::shh::Interface> const& _c, QList<dev::KeyPair> _ids);
virtual ~QWhisper();
std::shared_ptr<dev::shh::Interface> face() const;
void setFace(std::shared_ptr<dev::shh::Interface> const& _c) { m_face = _c; }
void setIdentities(QList<dev::KeyPair> const& _l);
/// Call when the face() is going to be deleted to make this object useless but safe.
void faceDieing();
Q_INVOKABLE QWhisper* self() { return this; }
/// Basic message send.
Q_INVOKABLE void send(QString /*dev::Address*/ _dest, QString /*ev::KeyPair*/ _from, QString /*dev::h256 const&*/ _topic, QString /*dev::bytes const&*/ _payload);
Q_INVOKABLE void doPost(QString _json);
// Watches interface
Q_INVOKABLE QString newIdentity();
Q_INVOKABLE bool haveIdentity(QString _id) { return m_ids.count(toPublic(_id)); }
Q_INVOKABLE QString newGroup(QString _id, QString _who);
Q_INVOKABLE QString addToGroup(QString _group, QString _who);
// Watches interface
Q_INVOKABLE unsigned newWatch(QString _json);
Q_INVOKABLE QString watchMessages(unsigned _w);
Q_INVOKABLE void killWatch(unsigned _w);
void clearWatches();
Q_INVOKABLE void clearWatches();
Q_INVOKABLE QString watchMessages(unsigned _w);
dev::Public makeIdentity();
std::map<dev::Public, dev::Secret> const& ids() const { return m_ids; }
public slots:
/// Check to see if anything has changed, fire off signals if so.
@ -240,36 +279,68 @@ public slots:
void poll();
signals:
void watchChanged(unsigned _w);
void watchChanged(unsigned _w, QString _envelopeJson);
void idsChanged();
void newIdToAdd(QString _id);
private:
std::weak_ptr<dev::shh::Interface> m_face;
std::vector<unsigned> m_watches;
std::map<unsigned, dev::Public> m_watches;
std::map<dev::Public, dev::Secret> m_ids;
};
class QLDB: public QObject
{
Q_OBJECT
public:
QLDB(QObject* _p);
~QLDB();
Q_INVOKABLE void put(QString _name, QString _key, QString _value);
Q_INVOKABLE QString get(QString _name, QString _key);
Q_INVOKABLE void putString(QString _name, QString _key, QString _value);
Q_INVOKABLE QString getString(QString _name, QString _key);
private:
ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions;
ldb::DB* m_db;
};
// TODO: add p2p object
#define QETH_INSTALL_JS_NAMESPACE(_frame, _env, _dev, _eth, _shh) [_frame, _env, _dev, _eth, _shh]() \
#define QETH_INSTALL_JS_NAMESPACE(_frame, _env, _web3, _eth, _shh, _ldb) [_frame, _env, _web3, _eth, _shh, _ldb]() \
{ \
_frame->disconnect(); \
_frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \
_frame->addToJavaScriptWindowObject("dev", _dev, QWebFrame::ScriptOwnership); \
_frame->addToJavaScriptWindowObject("web3", _web3, QWebFrame::ScriptOwnership); \
if (_ldb) \
{ \
_frame->addToJavaScriptWindowObject("_web3_dot_db", _ldb, QWebFrame::QtOwnership); \
_frame->evaluateJavaScript("web3.db = _web3_dot_db"); \
} \
if (_eth) \
{ \
_frame->addToJavaScriptWindowObject("eth", _eth, QWebFrame::ScriptOwnership); \
_frame->evaluateJavaScript("eth.makeWatch = function(a) { var ww = eth.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { eth.killWatch(w); }; ret.changed = function(f) { eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(eth.watchMessages(this.w)) }; return ret; }"); \
_frame->evaluateJavaScript("eth.watch = function(a) { return eth.makeWatch(JSON.stringify(a)) }"); \
_frame->evaluateJavaScript("eth.transact = function(a, f) { var r = eth.doTransact(JSON.stringify(a)); if (f) f(r); }"); \
_frame->evaluateJavaScript("eth.call = function(a, f) { var ret = eth.doCallJson(JSON.stringify(a)); if (f) f(ret); return ret; }"); \
_frame->evaluateJavaScript("eth.messages = function(a) { return JSON.parse(eth.getMessages(JSON.stringify(a))); }"); \
_frame->evaluateJavaScript("eth.block = function(a) { return JSON.parse(eth.getBlock(a)); }"); \
_frame->evaluateJavaScript("eth.transaction = function(a) { return JSON.parse(eth.getTransaction(a)); }"); \
_frame->evaluateJavaScript("eth.uncle = function(a) { return JSON.parse(eth.getUncle(a)); }"); \
_frame->addToJavaScriptWindowObject("_web3_dot_eth", _eth, QWebFrame::ScriptOwnership); \
_frame->evaluateJavaScript("_web3_dot_eth.makeWatch = function(a) { var ww = _web3_dot_eth.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_eth.killWatch(this.w); }; ret.changed = function(f) { _web3_dot_eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(_web3_dot_eth.watchMessages(this.w)) }; return ret; }"); \
_frame->evaluateJavaScript("_web3_dot_eth.watch = function(a) { return _web3_dot_eth.makeWatch(JSON.stringify(a)) }"); \
_frame->evaluateJavaScript("_web3_dot_eth.transact = function(a, f) { var r = _web3_dot_eth.doTransact(JSON.stringify(a)); if (f) f(r); }"); \
_frame->evaluateJavaScript("_web3_dot_eth.call = function(a, f) { var ret = _web3_dot_eth.doCallJson(JSON.stringify(a)); if (f) f(ret); return ret; }"); \
_frame->evaluateJavaScript("_web3_dot_eth.messages = function(a) { return JSON.parse(_web3_dot_eth.getMessages(JSON.stringify(a))); }"); \
_frame->evaluateJavaScript("_web3_dot_eth.block = function(a) { return JSON.parse(_web3_dot_eth.getBlock(a)); }"); \
_frame->evaluateJavaScript("_web3_dot_eth.transaction = function(a) { return JSON.parse(_web3_dot_eth.getTransaction(a)); }"); \
_frame->evaluateJavaScript("_web3_dot_eth.uncle = function(a) { return JSON.parse(_web3_dot_eth.getUncle(a)); }"); \
_frame->evaluateJavaScript("web3.eth = _web3_dot_eth"); \
} \
if (_shh) \
{ \
_frame->addToJavaScriptWindowObject("shh", _shh, QWebFrame::ScriptOwnership); \
_frame->evaluateJavaScript("shh.makeWatch = function(a) { var ww = shh.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { shh.killWatch(w); }; ret.changed = function(f) { shh.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(shh.watchMessages(this.w)) }; return ret; }"); \
_frame->evaluateJavaScript("shh.watch = function(a) { return shh.makeWatch(JSON.stringify(a)) }"); \
_frame->addToJavaScriptWindowObject("_web3_dot_shh", _shh, QWebFrame::ScriptOwnership); \
_frame->evaluateJavaScript("_web3_dot_shh.makeWatch = function(json) { var ww = _web3_dot_shh.newWatch(json); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_shh.killWatch(this.w); }; ret.arrived = function(f) { _web3_dot_shh.watchChanged.connect(function(nw, envelope) { if (nw == ww) f(JSON.parse(envelope)) }); var existing = JSON.parse(_web3_dot_shh.watchMessages(this.w)); for (var e in existing) f(existing[e]) }; return ret; }"); \
_frame->evaluateJavaScript("_web3_dot_shh.watch = function(filter) { return _web3_dot_shh.makeWatch(JSON.stringify(filter)) }"); \
_frame->evaluateJavaScript("_web3_dot_shh.post = function(message) { return _web3_dot_shh.doPost(JSON.stringify(message)) }"); \
_frame->evaluateJavaScript("web3.shh = _web3_dot_shh"); \
} \
}

32
libserpent/compiler.cpp

@ -123,9 +123,10 @@ programData opcodeify(Node node,
token("$begincode"+symb+".endcode"+symb, m), token("DUP1", m),
token("$begincode"+symb, m), sub.code, token("CODECOPY", m),
token("$endcode"+symb, m), token("JUMP", m),
token("~begincode"+symb, m), code, token("~endcode"+symb, m)
token("~begincode"+symb, m), code, token("~endcode"+symb, m),
token("JUMPDEST", m)
};
return pd(sub.aux, multiToken(nodelist, 10, m), 1);
return pd(sub.aux, multiToken(nodelist, 11, m), 1);
}
// Stack variables
if (node.val == "with") {
@ -171,9 +172,9 @@ programData opcodeify(Node node,
cond.code,
token("$endif"+symb, m), token("JUMPI", m),
action.code,
token("~endif"+symb, m)
token("~endif"+symb, m), token("JUMPDEST", m)
};
return pd(aux, multiToken(nodelist, 5, m), 0);
return pd(aux, multiToken(nodelist, 6, m), 0);
}
// 3-part conditional
else if (node.val == "if" && node.args.size() == 3) {
@ -190,13 +191,15 @@ programData opcodeify(Node node,
if (elsed.outs > outs) elsed.code = popwrap(elsed.code);
Node nodelist[] = {
ifd.code,
token("NOT", m), token("$else"+symb, m), token("JUMPI", m),
token("NOT", m),
token("$else"+symb, m), token("JUMPI", m),
thend.code,
token("$endif"+symb, m), token("JUMP", m), token("~else"+symb, m),
token("$endif"+symb, m), token("JUMP", m),
token("~else"+symb, m), token("JUMPDEST", m),
elsed.code,
token("~endif"+symb, m)
token("~endif"+symb, m), token("JUMPDEST", m)
};
return pd(aux, multiToken(nodelist, 10, m), outs);
return pd(aux, multiToken(nodelist, 12, m), outs);
}
// While (rewritten to this in rewrites)
else if (node.val == "until") {
@ -207,13 +210,14 @@ programData opcodeify(Node node,
err("Condition of while/until loop has arity 0", m);
if (action.outs) action.code = popwrap(action.code);
Node nodelist[] = {
token("~beg"+symb, m),
token("~beg"+symb, m), token("JUMPDEST", m),
cond.code,
token("$end"+symb, m), token("JUMPI", m),
action.code,
token("$beg"+symb, m), token("JUMP", m), token("~end"+symb, m)
token("$beg"+symb, m), token("JUMP", m),
token("~end"+symb, m), token("JUMPDEST", m)
};
return pd(aux, multiToken(nodelist, 8, m));
return pd(aux, multiToken(nodelist, 10, m));
}
// Memory allocations
else if (node.val == "alloc") {
@ -298,7 +302,7 @@ Node finalize(programData c) {
Metadata m = c.code.metadata;
// If we are using both alloc and variables, we need to pre-zfill
// some memory
if (c.aux.allocUsed && c.aux.vars.size() > 0) {
if ((c.aux.allocUsed || c.aux.calldataUsed) && c.aux.vars.size() > 0) {
Node nodelist[] = {
token("0", m),
token(unsignedToDecimal(c.aux.vars.size() * 32 - 1)),
@ -309,8 +313,8 @@ Node finalize(programData c) {
// If msg.data is being used as an array, then we need to copy it
if (c.aux.calldataUsed) {
Node nodelist[] = {
token("MSIZE", m), token("CALLDATASIZE", m), token("MSIZE", m),
token("0", m), token("CALLDATACOPY", m),
token("MSIZE", m), token("CALLDATASIZE", m), token("0", m),
token("MSIZE", m), token("CALLDATACOPY", m),
token(c.aux.vars["'msg.data"], m), token("MSTORE", m)
};
bottom.push_back(multiToken(nodelist, 7, m));

12
libserpent/funcs.cpp

@ -14,6 +14,10 @@ Node compileToLLL(std::string input) {
return rewrite(parseSerpent(input));
}
Node compileChunkToLLL(std::string input) {
return rewriteChunk(parseSerpent(input));
}
std::string compile(std::string input) {
return compileLLL(compileToLLL(input));
}
@ -21,3 +25,11 @@ std::string compile(std::string input) {
std::vector<Node> prettyCompile(std::string input) {
return prettyCompileLLL(compileToLLL(input));
}
std::string compileChunk(std::string input) {
return compileLLL(compileChunkToLLL(input));
}
std::vector<Node> prettyCompileChunk(std::string input) {
return prettyCompileLLL(compileChunkToLLL(input));
}

6
libserpent/funcs.h

@ -24,6 +24,12 @@
Node compileToLLL(std::string input);
Node compileChunkToLLL(std::string input);
std::string compile(std::string input);
std::vector<Node> prettyCompile(std::string input);
std::string compileChunk(std::string input);
std::vector<Node> prettyCompileChunk(std::string input);

4
libserpent/opcodes.h

@ -72,11 +72,11 @@ Mapping mapping[] = {
Mapping("PC", 0x5a, 0, 1),
Mapping("MSIZE", 0x5b, 0, 1),
Mapping("GAS", 0x5c, 0, 1),
Mapping("JUMPDEST", 0x5d, 0, 0),
Mapping("CREATE", 0xf0, 3, 1),
Mapping("CALL", 0xf1, 7, 1),
Mapping("RETURN", 0xf2, 2, 0),
Mapping("POST", 0xf3, 5, 0),
Mapping("CALL_STATELESS", 0xf4, 7, 1),
Mapping("CALL_CODE", 0xf3, 7, 1),
Mapping("SUICIDE", 0xff, 1, 0),
Mapping("---END---", 0x00, 0, 0),
};

24
libserpent/parser.cpp

@ -32,7 +32,7 @@ int toktype(Node tok) {
if (v == "(" || v == "[" || v == "{") return LPAREN;
else if (v == ")" || v == "]" || v == "}") return RPAREN;
else if (v == ",") return COMMA;
else if (v == "!" || v == "not") return UNARY_OP;
else if (v == "!" || v == "not" || v == "neg") return UNARY_OP;
else if (precedence(tok) >= 0) return BINARY_OP;
if (tok.val[0] != '"' && tok.val[0] != '\'') {
for (unsigned i = 0; i < tok.val.length(); i++) {
@ -91,17 +91,19 @@ std::vector<Node> shuntingYard(std::vector<Node> tokens) {
// If binary op, keep popping from stack while higher bedmas precedence
else if (toktyp == BINARY_OP) {
if (tok.val == "-" && prevtyp != ALPHANUM && prevtyp != RPAREN) {
oq.push_back(token("0", tok.metadata));
stack.push_back(token("neg", tok.metadata));
}
int prec = precedence(tok);
while (stack.size()
&& (toktype(stack.back()) == BINARY_OP
|| toktype(stack.back()) == UNARY_OP)
&& precedence(stack.back()) <= prec) {
oq.push_back(stack.back());
stack.pop_back();
else {
int prec = precedence(tok);
while (stack.size()
&& (toktype(stack.back()) == BINARY_OP
|| toktype(stack.back()) == UNARY_OP)
&& precedence(stack.back()) <= prec) {
oq.push_back(stack.back());
stack.pop_back();
}
stack.push_back(tok);
}
stack.push_back(tok);
}
// Comma means finish evaluating the argument
else if (toktyp == COMMA) {
@ -153,10 +155,10 @@ Node treefy(std::vector<Node> stream) {
else if (typ == RPAREN) {
std::vector<Node> args;
while (1) {
if (!oq.size()) err("Bracket without matching", tok.metadata);
if (toktype(oq.back()) == LPAREN) break;
args.push_back(oq.back());
oq.pop_back();
if (!oq.size()) err("Bracket without matching", tok.metadata);
}
oq.pop_back();
args.push_back(oq.back());

60
libserpent/rewriter.cpp

@ -17,9 +17,7 @@ std::string valid[][3] = {
{ "alloc", "1", "1" },
{ "array", "1", "1" },
{ "call", "2", "4" },
{ "call_stateless", "2", "4" },
{ "post", "4", "5" },
{ "postcall", "3", "4" },
{ "call_code", "2", "4" },
{ "create", "1", "4" },
{ "msg", "4", "6" },
{ "msg_stateless", "4", "6" },
@ -28,6 +26,8 @@ std::string valid[][3] = {
{ "sha3", "1", "2" },
{ "return", "1", "2" },
{ "inset", "1", "1" },
{ "min", "2", "2" },
{ "max", "2", "2" },
{ "array_lit", "0", tt256 },
{ "seq", "0", tt256 },
{ "---END---", "", "" } //Keep this line at the end of the list
@ -70,6 +70,14 @@ std::string macros[][2] = {
"(!= $a $b)",
"(not (eq $a $b))"
},
{
"(min a b)",
"(with $1 a (with $2 b (if (lt $1 $2) $1 $2)))"
},
{
"(max a b)",
"(with $1 a (with $2 b (if (lt $1 $2) $2 $1)))"
},
{
"(if $cond $do (else $else))",
"(if $cond $do $else)"
@ -134,22 +142,6 @@ std::string macros[][2] = {
"(send $to $value)",
"(call (sub (gas) 25) $to $value 0 0 0 0)"
},
{
"(post $gas $to $value $datain $datainsz)",
"(~post $gas $to $value $datain (mul $datainsz 32))"
},
{
"(post $gas $to $value $datain)",
"(seq (set $1 $datain) (~post $gas $to $value (ref $1) 32))"
},
{
"(postcall $gas $to $datain)",
"(post $gas $to 0 $datain)",
},
{
"(postcall $gas $to $datain $datainsz)",
"(post $gas $to 0 $datain $datainsz)",
},
{
"(send $gas $to $value)",
"(call $gas $to $value 0 0 0 0)"
@ -229,32 +221,32 @@ std::string macros[][2] = {
},
{
"(msg $gas $to $val $inp $inpsz $outsz)",
"(with $1 (mul 32 $outsz) (with $2 (alloc (get $1)) (call $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1)) (get $2)))"
"(with $1 (mul 32 $outsz) (with $2 (alloc (get $1)) (seq (call $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1)) (get $2))))"
},
// Call stateless and msg stateless
{
"(call_stateless $f $dataval)",
"(msg_stateless (sub (gas) 45) $f 0 $dataval)"
"(call_code $f $dataval)",
"(msg_code (sub (gas) 45) $f 0 $dataval)"
},
{
"(call_stateless $f $inp $inpsz)",
"(msg_stateless (sub (gas) 25) $f 0 $inp $inpsz)"
"(call_code $f $inp $inpsz)",
"(msg_code (sub (gas) 25) $f 0 $inp $inpsz)"
},
{
"(call_stateless $f $inp $inpsz $outsz)",
"(with $1 $outsz (with $2 (alloc (mul 32 (get $1))) (seq (call_stateless (sub (gas) (add 25 (get $1))) $f 0 $inp (mul 32 $inpsz) (get $2) (mul 32 (get $1))) (get $2))))"
"(call_code $f $inp $inpsz $outsz)",
"(with $1 $outsz (with $2 (alloc (mul 32 (get $1))) (seq (call_code (sub (gas) (add 25 (get $1))) $f 0 $inp (mul 32 $inpsz) (get $2) (mul 32 (get $1))) (get $2))))"
},
{
"(msg_stateless $gas $to $val $inp $inpsz)",
"(seq (call_stateless $gas $to $val $inp (mul 32 $inpsz) (ref $1) 32) (get $1))"
"(msg_code $gas $to $val $inp $inpsz)",
"(seq (call_code $gas $to $val $inp (mul 32 $inpsz) (ref $1) 32) (get $1))"
},
{
"(msg_stateless $gas $to $val $dataval)",
"(seq (set $1 $dataval) (call_stateless $gas $to $val (ref $1) 32 (ref $2) 32) (get $2))"
"(msg_code $gas $to $val $dataval)",
"(seq (set $1 $dataval) (call_code $gas $to $val (ref $1) 32 (ref $2) 32) (get $2))"
},
{
"(msg_stateless $gas $to $val $inp $inpsz $outsz)",
"(with $1 (mul 32 $outsz) (with $2 (alloc (get $1)) (call_stateless $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1)) (get $2)))"
"(msg_code $gas $to $val $inp $inpsz $outsz)",
"(with $1 (mul 32 $outsz) (with $2 (alloc (get $1)) (call_code $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1)) (get $2)))"
},
// Wrappers
{
@ -561,4 +553,8 @@ Node rewrite(Node inp) {
return optimize(apply_rules(validate(preprocess(inp))));
}
Node rewriteChunk(Node inp) {
return optimize(apply_rules(validate(inp)));
}
using namespace std;

3
libserpent/rewriter.h

@ -10,4 +10,7 @@
// Applies rewrite rules
Node rewrite(Node inp);
// Applies rewrite rules adding without wrapper
Node rewriteChunk(Node inp);
#endif

6
libserpent/util.cpp

@ -137,7 +137,7 @@ std::string strToNumeric(std::string inp) {
}
else if (inp.substr(0,2) == "0x") {
for (unsigned i = 2; i < inp.length(); i++) {
int dig = std::string("0123456789abcdef").find(inp[i]);
int dig = std::string("0123456789abcdef0123456789ABCDEF").find(inp[i]) % 16;
if (dig < 0) return "";
o = decimalAdd(decimalMul(o,"16"), unsignedToDecimal(dig));
}
@ -219,8 +219,8 @@ void err(std::string errtext, Metadata met) {
std::string err = "Error (file \"" + met.file + "\", line " +
unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) +
"): " + errtext;
std::cerr << err << "\n";
throw(err);
std::cerr << err << "\n";
throw(err);
}
//Bin to hex

481
libsolidity/AST.cpp

@ -0,0 +1,481 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity abstract syntax tree.
*/
#include <algorithm>
#include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h>
namespace dev
{
namespace solidity
{
void ContractDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
}
void StructDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_members, _visitor);
_visitor.endVisit(*this);
}
void ParameterList::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_parameters, _visitor);
_visitor.endVisit(*this);
}
void FunctionDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_parameters->accept(_visitor);
if (m_returnParameters)
m_returnParameters->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void VariableDeclaration::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_typeName)
m_typeName->accept(_visitor);
_visitor.endVisit(*this);
}
void TypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ElementaryTypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void UserDefinedTypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Mapping::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_keyType->accept(_visitor);
m_valueType->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Statement::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Block::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_statements, _visitor);
_visitor.endVisit(*this);
}
void IfStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_condition->accept(_visitor);
m_trueBody->accept(_visitor);
if (m_falseBody)
m_falseBody->accept(_visitor);
}
_visitor.endVisit(*this);
}
void BreakableStatement::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void WhileStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_condition->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Continue::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Break::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Return::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_expression)
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void VariableDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_variable->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Assignment::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_leftHandSide->accept(_visitor);
m_rightHandSide->accept(_visitor);
}
_visitor.endVisit(*this);
}
void UnaryOperation::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_subExpression->accept(_visitor);
_visitor.endVisit(*this);
}
void BinaryOperation::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_left->accept(_visitor);
m_right->accept(_visitor);
}
_visitor.endVisit(*this);
}
void FunctionCall::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_expression->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void MemberAccess::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void IndexAccess::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_base->accept(_visitor);
m_index->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Identifier::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Literal::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
TypeError ASTNode::createTypeError(std::string const& _description)
{
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
void Statement::expectType(Expression& _expression, const Type& _expectedType)
{
_expression.checkTypeRequirements();
if (!_expression.getType()->isImplicitlyConvertibleTo(_expectedType))
BOOST_THROW_EXCEPTION(_expression.createTypeError("Type not implicitly convertible to expected type."));
//@todo provide more information to the exception
}
void Block::checkTypeRequirements()
{
for (std::shared_ptr<Statement> const& statement: m_statements)
statement->checkTypeRequirements();
}
void IfStatement::checkTypeRequirements()
{
expectType(*m_condition, BoolType());
m_trueBody->checkTypeRequirements();
if (m_falseBody)
m_falseBody->checkTypeRequirements();
}
void WhileStatement::checkTypeRequirements()
{
expectType(*m_condition, BoolType());
m_body->checkTypeRequirements();
}
void Continue::checkTypeRequirements()
{
}
void Break::checkTypeRequirements()
{
}
void Return::checkTypeRequirements()
{
BOOST_ASSERT(m_returnParameters);
if (m_returnParameters->getParameters().size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement "
"than in returns declaration."));
// this could later be changed such that the paramaters type is an anonymous struct type,
// but for now, we only allow one return parameter
expectType(*m_expression, *m_returnParameters->getParameters().front()->getType());
}
void VariableDefinition::checkTypeRequirements()
{
// Variables can be declared without type (with "var"), in which case the first assignment
// setsthe type.
// Note that assignments before the first declaration are legal because of the special scoping
// rules inherited from JavaScript.
if (m_value)
{
if (m_variable->getType())
expectType(*m_value, *m_variable->getType());
else
{
// no type declared and no previous assignment, infer the type
m_value->checkTypeRequirements();
m_variable->setType(m_value->getType());
}
}
}
void Assignment::checkTypeRequirements()
{
//@todo lefthandside actually has to be assignable
// add a feature to the type system to check that
m_leftHandSide->checkTypeRequirements();
expectType(*m_rightHandSide, *m_leftHandSide->getType());
m_type = m_leftHandSide->getType();
if (m_assigmentOperator != Token::ASSIGN)
{
// complex assignment
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator)))
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
}
}
void UnaryOperation::checkTypeRequirements()
{
// INC, DEC, NOT, BIT_NOT, DELETE
m_subExpression->checkTypeRequirements();
m_type = m_subExpression->getType();
if (m_type->acceptsUnaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
}
void BinaryOperation::checkTypeRequirements()
{
m_right->checkTypeRequirements();
m_left->checkTypeRequirements();
if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType()))
m_commonType = m_left->getType();
else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType()))
m_commonType = m_right->getType();
else
BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation."));
if (Token::isCompareOp(m_operator))
m_type = std::make_shared<BoolType>();
else
{
BOOST_ASSERT(Token::isBinaryOp(m_operator));
m_type = m_commonType;
if (!m_commonType->acceptsBinaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
}
}
void FunctionCall::checkTypeRequirements()
{
m_expression->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
Type const& expressionType = *m_expression->getType();
Type::Category const category = expressionType.getCategory();
if (category == Type::Category::TYPE)
{
TypeType const* type = dynamic_cast<TypeType const*>(&expressionType);
BOOST_ASSERT(type);
//@todo for structs, we have to check the number of arguments to be equal to the
// number of non-mapping members
if (m_arguments.size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for "
"explicit type conersion."));
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type->getActualType()))
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type->getActualType();
}
else if (category == Type::Category::FUNCTION)
{
//@todo would be nice to create a struct type from the arguments
// and then ask if that is implicitly convertible to the struct represented by the
// function parameters
FunctionType const* function = dynamic_cast<FunctionType const*>(&expressionType);
BOOST_ASSERT(function);
FunctionDefinition const& fun = function->getFunction();
std::vector<ASTPointer<VariableDeclaration>> const& parameters = fun.getParameters();
if (parameters.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType()))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
// @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs
if (fun.getReturnParameterList()->getParameters().empty())
m_type = std::make_shared<VoidType>();
else
m_type = fun.getReturnParameterList()->getParameters().front()->getType();
}
else
BOOST_THROW_EXCEPTION(createTypeError("Type does not support invocation."));
}
void MemberAccess::checkTypeRequirements()
{
BOOST_ASSERT(false); // not yet implemented
// m_type = ;
}
void IndexAccess::checkTypeRequirements()
{
BOOST_ASSERT(false); // not yet implemented
// m_type = ;
}
void Identifier::checkTypeRequirements()
{
BOOST_ASSERT(m_referencedDeclaration);
//@todo these dynamic casts here are not really nice...
// is i useful to have an AST visitor here?
// or can this already be done in NameAndTypeResolver?
// the only problem we get there is that in
// var x;
// x = 2;
// var y = x;
// the type of x is not yet determined.
VariableDeclaration* variable = dynamic_cast<VariableDeclaration*>(m_referencedDeclaration);
if (variable)
{
if (!variable->getType())
BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type "
"could be determined."));
m_type = variable->getType();
return;
}
//@todo can we unify these with TypeName::toType()?
StructDefinition* structDef = dynamic_cast<StructDefinition*>(m_referencedDeclaration);
if (structDef)
{
// note that we do not have a struct type here
m_type = std::make_shared<TypeType>(std::make_shared<StructType>(*structDef));
return;
}
FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration);
if (functionDef)
{
// a function reference is not a TypeType, because calling a TypeType converts to the type.
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion.
m_type = std::make_shared<FunctionType>(*functionDef);
return;
}
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
if (contractDef)
{
m_type = std::make_shared<TypeType>(std::make_shared<ContractType>(*contractDef));
return;
}
BOOST_ASSERT(false); // declaration reference of unknown/forbidden type
}
void ElementaryTypeNameExpression::checkTypeRequirements()
{
m_type = std::make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));
}
void Literal::checkTypeRequirements()
{
m_type = Type::forLiteral(*this);
}
}
}

537
libsolidity/AST.h

@ -0,0 +1,537 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity abstract syntax tree.
*/
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <boost/noncopyable.hpp>
#include <libsolidity/ASTForward.h>
#include <libsolidity/BaseTypes.h>
#include <libsolidity/Token.h>
#include <libsolidity/Types.h>
#include <libsolidity/Exceptions.h>
namespace dev
{
namespace solidity
{
class ASTVisitor;
class ASTNode: private boost::noncopyable
{
public:
explicit ASTNode(Location const& _location): m_location(_location) {}
virtual ~ASTNode() {}
virtual void accept(ASTVisitor& _visitor) = 0;
template <class T>
static void listAccept(std::vector<ASTPointer<T>>& _list, ASTVisitor& _visitor)
{
for (ASTPointer<T>& element: _list)
element->accept(_visitor);
}
Location const& getLocation() const { return m_location; }
/// Creates a @ref TypeError exception and decorates it with the location of the node and
/// the given description
TypeError createTypeError(std::string const& _description);
private:
Location m_location;
};
class Declaration: public ASTNode
{
public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name) {}
const ASTString& getName() const { return *m_name; }
private:
ASTPointer<ASTString> m_name;
};
class ContractDefinition: public Declaration
{
public:
ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
Declaration(_location, _name),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions)
{}
virtual void accept(ASTVisitor& _visitor) override;
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() { return m_definedStructs; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() { return m_definedFunctions; }
private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
};
class StructDefinition: public Declaration
{
public:
StructDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
std::vector<ASTPointer<VariableDeclaration>> const& _members):
Declaration(_location, _name), m_members(_members) {}
virtual void accept(ASTVisitor& _visitor) override;
private:
std::vector<ASTPointer<VariableDeclaration>> m_members;
};
/// Used as function parameter list and return list
/// None of the parameters is allowed to contain mappings (not even recursively
/// inside structs)
class ParameterList: public ASTNode
{
public:
ParameterList(Location const& _location,
std::vector<ASTPointer<VariableDeclaration>> const& _parameters):
ASTNode(_location), m_parameters(_parameters) {}
virtual void accept(ASTVisitor& _visitor) override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() { return m_parameters; }
private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters;
};
class FunctionDefinition: public Declaration
{
public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name, bool _isPublic,
ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters),
m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override;
bool isPublic() const { return m_isPublic; }
bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList& getParameterList() { return *m_parameters; }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block& getBody() { return *m_body; }
private:
bool m_isPublic;
ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst;
ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body;
};
class VariableDeclaration: public Declaration
{
public:
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name):
Declaration(_location, _name), m_typeName(_type) {}
virtual void accept(ASTVisitor& _visitor) override;
bool isTypeGivenExplicitly() const { return bool(m_typeName); }
TypeName* getTypeName() const { return m_typeName.get(); }
//! Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
//! declared and there is no assignment to the variable that fixes the type.
std::shared_ptr<Type const> const& getType() const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
std::shared_ptr<Type const> m_type;
};
/// types
/// @{
class TypeName: public ASTNode
{
public:
explicit TypeName(Location const& _location): ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() = 0;
};
/// any pre-defined type that is not a mapping
class ElementaryTypeName: public TypeName
{
public:
explicit ElementaryTypeName(Location const& _location, Token::Value _type):
TypeName(_location), m_type(_type) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() override { return Type::fromElementaryTypeName(m_type); }
Token::Value getType() const { return m_type; }
private:
Token::Value m_type;
};
class UserDefinedTypeName: public TypeName
{
public:
UserDefinedTypeName(Location const& _location, ASTPointer<ASTString> const& _name):
TypeName(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() override { return Type::fromUserDefinedTypeName(*this); }
const ASTString& getName() const { return *m_name; }
void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; }
StructDefinition const* getReferencedStruct() const { return m_referencedStruct; }
private:
ASTPointer<ASTString> m_name;
StructDefinition* m_referencedStruct;
};
class Mapping: public TypeName
{
public:
Mapping(Location const& _location, ASTPointer<ElementaryTypeName> const& _keyType,
ASTPointer<TypeName> const& _valueType):
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() override { return Type::fromMapping(*this); }
private:
ASTPointer<ElementaryTypeName> m_keyType;
ASTPointer<TypeName> m_valueType;
};
/// @}
/// Statements
/// @{
class Statement: public ASTNode
{
public:
explicit Statement(Location const& _location): ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
//! Check all type requirements, throws exception if some requirement is not met.
//! For expressions, this also returns the inferred type of the expression. For other
//! statements, returns the empty pointer.
virtual void checkTypeRequirements() = 0;
protected:
//! Check that the inferred type for _expression is _expectedType or at least implicitly
//! convertible to _expectedType. If not, throw exception.
void expectType(Expression& _expression, Type const& _expectedType);
};
class Block: public Statement
{
public:
Block(Location const& _location, std::vector<ASTPointer<Statement>> const& _statements):
Statement(_location), m_statements(_statements) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
private:
std::vector<ASTPointer<Statement>> m_statements;
};
class IfStatement: public Statement
{
public:
IfStatement(Location const& _location, ASTPointer<Expression> const& _condition,
ASTPointer<Statement> const& _trueBody, ASTPointer<Statement> const& _falseBody):
Statement(_location),
m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
private:
ASTPointer<Expression> m_condition;
ASTPointer<Statement> m_trueBody;
ASTPointer<Statement> m_falseBody; //< "else" part, optional
};
class BreakableStatement: public Statement
{
public:
BreakableStatement(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
};
class WhileStatement: public BreakableStatement
{
public:
WhileStatement(Location const& _location, ASTPointer<Expression> const& _condition,
ASTPointer<Statement> const& _body):
BreakableStatement(_location), m_condition(_condition), m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
private:
ASTPointer<Expression> m_condition;
ASTPointer<Statement> m_body;
};
class Continue: public Statement
{
public:
Continue(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
};
class Break: public Statement
{
public:
Break(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
};
class Return: public Statement
{
public:
Return(Location const& _location, ASTPointer<Expression> _expression):
Statement(_location), m_expression(_expression) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
private:
ASTPointer<Expression> m_expression; //< value to return, optional
ParameterList* m_returnParameters; //< extracted from the function declaration
};
class VariableDefinition: public Statement
{
public:
VariableDefinition(Location const& _location, ASTPointer<VariableDeclaration> _variable,
ASTPointer<Expression> _value):
Statement(_location), m_variable(_variable), m_value(_value) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
private:
ASTPointer<VariableDeclaration> m_variable;
ASTPointer<Expression> m_value; ///< can be missing
};
class Expression: public Statement
{
public:
Expression(Location const& _location): Statement(_location) {}
std::shared_ptr<Type const> const& getType() const { return m_type; }
protected:
//! Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type;
};
/// @}
/// Expressions
/// @{
class Assignment: public Expression
{
public:
Assignment(Location const& _location, ASTPointer<Expression> const& _leftHandSide,
Token::Value _assignmentOperator, ASTPointer<Expression> const& _rightHandSide):
Expression(_location), m_leftHandSide(_leftHandSide),
m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
Token::Value getAssignmentOperator() const { return m_assigmentOperator; }
private:
ASTPointer<Expression> m_leftHandSide;
Token::Value m_assigmentOperator;
ASTPointer<Expression> m_rightHandSide;
};
class UnaryOperation: public Expression
{
public:
UnaryOperation(Location const& _location, Token::Value _operator,
ASTPointer<Expression> const& _subExpression, bool _isPrefix):
Expression(_location), m_operator(_operator),
m_subExpression(_subExpression), m_isPrefix(_isPrefix) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
Token::Value getOperator() const { return m_operator; }
bool isPrefixOperation() const { return m_isPrefix; }
private:
Token::Value m_operator;
ASTPointer<Expression> m_subExpression;
bool m_isPrefix;
};
class BinaryOperation: public Expression
{
public:
BinaryOperation(Location const& _location, ASTPointer<Expression> const& _left,
Token::Value _operator, ASTPointer<Expression> const& _right):
Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
Token::Value getOperator() const { return m_operator; }
private:
ASTPointer<Expression> m_left;
Token::Value m_operator;
ASTPointer<Expression> m_right;
std::shared_ptr<Type const> m_commonType;
};
/// Can be ordinary function call, type cast or struct construction.
class FunctionCall: public Expression
{
public:
FunctionCall(Location const& _location, ASTPointer<Expression> const& _expression,
std::vector<ASTPointer<Expression>> const& _arguments):
Expression(_location), m_expression(_expression), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
private:
ASTPointer<Expression> m_expression;
std::vector<ASTPointer<Expression>> m_arguments;
};
class MemberAccess: public Expression
{
public:
MemberAccess(Location const& _location, ASTPointer<Expression> _expression,
ASTPointer<ASTString> const& _memberName):
Expression(_location), m_expression(_expression), m_memberName(_memberName) {}
virtual void accept(ASTVisitor& _visitor) override;
const ASTString& getMemberName() const { return *m_memberName; }
virtual void checkTypeRequirements() override;
private:
ASTPointer<Expression> m_expression;
ASTPointer<ASTString> m_memberName;
};
class IndexAccess: public Expression
{
public:
IndexAccess(Location const& _location, ASTPointer<Expression> const& _base,
ASTPointer<Expression> const& _index):
Expression(_location), m_base(_base), m_index(_index) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
private:
ASTPointer<Expression> m_base;
ASTPointer<Expression> m_index;
};
class PrimaryExpression: public Expression
{
public:
PrimaryExpression(Location const& _location): Expression(_location) {}
};
class Identifier: public PrimaryExpression
{
public:
Identifier(Location const& _location, ASTPointer<ASTString> const& _name):
PrimaryExpression(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration* getReferencedDeclaration() { return m_referencedDeclaration; }
private:
ASTPointer<ASTString> m_name;
//! Declaration the name refers to.
Declaration* m_referencedDeclaration;
};
class ElementaryTypeNameExpression: public PrimaryExpression
{
public:
ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken):
PrimaryExpression(_location), m_typeToken(_typeToken) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
Token::Value getTypeToken() const { return m_typeToken; }
private:
Token::Value m_typeToken;
};
class Literal: public PrimaryExpression
{
public:
Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value):
PrimaryExpression(_location), m_token(_token), m_value(_value) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
Token::Value getToken() const { return m_token; }
ASTString const& getValue() const { return *m_value; }
private:
Token::Value m_token;
ASTPointer<ASTString> m_value;
};
/// @}
}
}

78
libsolidity/ASTForward.h

@ -0,0 +1,78 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Forward-declarations of AST classes.
*/
#pragma once
#include <string>
#include <memory>
#include <vector>
// Forward-declare all AST node types
namespace dev
{
namespace solidity
{
class ASTNode;
class Declaration;
class ContractDefinition;
class StructDefinition;
class ParameterList;
class FunctionDefinition;
class VariableDeclaration;
class TypeName;
class ElementaryTypeName;
class UserDefinedTypeName;
class Mapping;
class Statement;
class Block;
class IfStatement;
class BreakableStatement;
class WhileStatement;
class Continue;
class Break;
class Return;
class VariableDefinition;
class Expression;
class Assignment;
class UnaryOperation;
class BinaryOperation;
class FunctionCall;
class MemberAccess;
class IndexAccess;
class PrimaryExpression;
class Identifier;
class ElementaryTypeNameExpression;
class Literal;
// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do
// not do reference counting but point to a special memory area that is completely released
// explicitly.
template <class T>
using ASTPointer = std::shared_ptr<T>;
using ASTString = std::string;
}
}

443
libsolidity/ASTPrinter.cpp

@ -0,0 +1,443 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging.
*/
#include <libsolidity/ASTPrinter.h>
#include <libsolidity/AST.h>
namespace dev
{
namespace solidity
{
ASTPrinter::ASTPrinter(ASTPointer<ASTNode> const& _ast, std::string const& _source):
m_indentation(0), m_source(_source), m_ast(_ast)
{
}
void ASTPrinter::print(std::ostream& _stream)
{
m_ostream = &_stream;
m_ast->accept(*this);
m_ostream = nullptr;
}
bool ASTPrinter::visit(ContractDefinition& _node)
{
writeLine("ContractDefinition \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(StructDefinition& _node)
{
writeLine("StructDefinition \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(ParameterList& _node)
{
writeLine("ParameterList");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(FunctionDefinition& _node)
{
writeLine("FunctionDefinition \"" + _node.getName() + "\"" +
(_node.isPublic() ? " - public" : "") +
(_node.isDeclaredConst() ? " - const" : ""));
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(VariableDeclaration& _node)
{
writeLine("VariableDeclaration \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(TypeName& _node)
{
writeLine("TypeName");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(ElementaryTypeName& _node)
{
writeLine(std::string("ElementaryTypeName ") + Token::toString(_node.getType()));
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(UserDefinedTypeName& _node)
{
writeLine("UserDefinedTypeName \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Mapping& _node)
{
writeLine("Mapping");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Statement& _node)
{
writeLine("Statement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Block& _node)
{
writeLine("Block");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(IfStatement& _node)
{
writeLine("IfStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(BreakableStatement& _node)
{
writeLine("BreakableStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(WhileStatement& _node)
{
writeLine("WhileStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Continue& _node)
{
writeLine("Continue");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Break& _node)
{
writeLine("Break");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Return& _node)
{
writeLine("Return");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(VariableDefinition& _node)
{
writeLine("VariableDefinition");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Expression& _node)
{
writeLine("Expression");
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Assignment& _node)
{
writeLine(std::string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(UnaryOperation& _node)
{
writeLine(std::string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
") " + Token::toString(_node.getOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(BinaryOperation& _node)
{
writeLine(std::string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(FunctionCall& _node)
{
writeLine("FunctionCall");
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(MemberAccess& _node)
{
writeLine("MemberAccess to member " + _node.getMemberName());
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(IndexAccess& _node)
{
writeLine("IndexAccess");
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(PrimaryExpression& _node)
{
writeLine("PrimaryExpression");
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Identifier& _node)
{
writeLine(std::string("Identifier ") + _node.getName());
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node)
{
writeLine(std::string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken()));
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Literal& _node)
{
char const* tokenString = Token::toString(_node.getToken());
if (!tokenString)
tokenString = "[no token]";
writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue());
printType(_node);
printSourcePart(_node);
return goDeeper();
}
void ASTPrinter::endVisit(ASTNode&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ContractDefinition&)
{
m_indentation--;
}
void ASTPrinter::endVisit(StructDefinition&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ParameterList&)
{
m_indentation--;
}
void ASTPrinter::endVisit(FunctionDefinition&)
{
m_indentation--;
}
void ASTPrinter::endVisit(VariableDeclaration&)
{
m_indentation--;
}
void ASTPrinter::endVisit(TypeName&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ElementaryTypeName&)
{
m_indentation--;
}
void ASTPrinter::endVisit(UserDefinedTypeName&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Mapping&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Statement&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Block&)
{
m_indentation--;
}
void ASTPrinter::endVisit(IfStatement&)
{
m_indentation--;
}
void ASTPrinter::endVisit(BreakableStatement&)
{
m_indentation--;
}
void ASTPrinter::endVisit(WhileStatement&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Continue&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Break&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Return&)
{
m_indentation--;
}
void ASTPrinter::endVisit(VariableDefinition&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Expression&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Assignment&)
{
m_indentation--;
}
void ASTPrinter::endVisit(UnaryOperation&)
{
m_indentation--;
}
void ASTPrinter::endVisit(BinaryOperation&)
{
m_indentation--;
}
void ASTPrinter::endVisit(FunctionCall&)
{
m_indentation--;
}
void ASTPrinter::endVisit(MemberAccess&)
{
m_indentation--;
}
void ASTPrinter::endVisit(IndexAccess&)
{
m_indentation--;
}
void ASTPrinter::endVisit(PrimaryExpression&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Identifier&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ElementaryTypeNameExpression&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Literal&)
{
m_indentation--;
}
void ASTPrinter::printSourcePart(ASTNode const& _node)
{
if (!m_source.empty())
{
Location const& location(_node.getLocation());
*m_ostream << getIndentation() << " Source: |"
<< m_source.substr(location.start, location.end - location.start) << "|" << std::endl;
}
}
void ASTPrinter::printType(Expression const& _expression)
{
if (_expression.getType())
*m_ostream << getIndentation() << " Type: " << _expression.getType()->toString() << "\n";
else
*m_ostream << getIndentation() << " Type unknown.\n";
}
std::string ASTPrinter::getIndentation() const
{
return std::string(m_indentation * 2, ' ');
}
void ASTPrinter::writeLine(std::string const& _line)
{
*m_ostream << getIndentation() << _line << std::endl;
}
}
}

117
libsolidity/ASTPrinter.h

@ -0,0 +1,117 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging.
*/
#pragma once
#include <ostream>
#include <libsolidity/ASTVisitor.h>
namespace dev
{
namespace solidity
{
class ASTPrinter: public ASTVisitor
{
public:
/// Create a printer for the given abstract syntax tree. If the source is specified,
/// the corresponding parts of the source are printed with each node.
ASTPrinter(ASTPointer<ASTNode> const& _ast, const std::string& _source = std::string());
/// Output the string representation of the AST to _stream.
void print(std::ostream& _stream);
bool visit(ContractDefinition& _node) override;
bool visit(StructDefinition& _node) override;
bool visit(ParameterList& _node) override;
bool visit(FunctionDefinition& _node) override;
bool visit(VariableDeclaration& _node) override;
bool visit(TypeName& _node) override;
bool visit(ElementaryTypeName& _node) override;
bool visit(UserDefinedTypeName& _node) override;
bool visit(Mapping& _node) override;
bool visit(Statement& _node) override;
bool visit(Block& _node) override;
bool visit(IfStatement& _node) override;
bool visit(BreakableStatement& _node) override;
bool visit(WhileStatement& _node) override;
bool visit(Continue& _node) override;
bool visit(Break& _node) override;
bool visit(Return& _node) override;
bool visit(VariableDefinition& _node) override;
bool visit(Expression& _node) override;
bool visit(Assignment& _node) override;
bool visit(UnaryOperation& _node) override;
bool visit(BinaryOperation& _node) override;
bool visit(FunctionCall& _node) override;
bool visit(MemberAccess& _node) override;
bool visit(IndexAccess& _node) override;
bool visit(PrimaryExpression& _node) override;
bool visit(Identifier& _node) override;
bool visit(ElementaryTypeNameExpression& _node) override;
bool visit(Literal& _node) override;
void endVisit(ASTNode& _node) override;
void endVisit(ContractDefinition&) override;
void endVisit(StructDefinition&) override;
void endVisit(ParameterList&) override;
void endVisit(FunctionDefinition&) override;
void endVisit(VariableDeclaration&) override;
void endVisit(TypeName&) override;
void endVisit(ElementaryTypeName&) override;
void endVisit(UserDefinedTypeName&) override;
void endVisit(Mapping&) override;
void endVisit(Statement&) override;
void endVisit(Block&) override;
void endVisit(IfStatement&) override;
void endVisit(BreakableStatement&) override;
void endVisit(WhileStatement&) override;
void endVisit(Continue&) override;
void endVisit(Break&) override;
void endVisit(Return&) override;
void endVisit(VariableDefinition&) override;
void endVisit(Expression&) override;
void endVisit(Assignment&) override;
void endVisit(UnaryOperation&) override;
void endVisit(BinaryOperation&) override;
void endVisit(FunctionCall&) override;
void endVisit(MemberAccess&) override;
void endVisit(IndexAccess&) override;
void endVisit(PrimaryExpression&) override;
void endVisit(Identifier&) override;
void endVisit(ElementaryTypeNameExpression&) override;
void endVisit(Literal&) override;
private:
void printSourcePart(ASTNode const& _node);
void printType(Expression const& _expression);
std::string getIndentation() const;
void writeLine(std::string const& _line);
bool goDeeper() { m_indentation++; return true; }
int m_indentation;
std::string m_source;
ASTPointer<ASTNode> m_ast;
std::ostream* m_ostream;
};
}
}

104
libsolidity/ASTVisitor.h

@ -0,0 +1,104 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* AST visitor base class.
*/
#pragma once
#include <libsolidity/ASTForward.h>
#include <string>
namespace dev
{
namespace solidity
{
class ASTVisitor
{
public:
/// These functions are called after a call to ASTNode::accept,
/// first visit, then (if visit returns true) recursively for all
/// child nodes in document order (exception for contracts) and then
/// endVisit.
virtual bool visit(ASTNode&) { return true; }
virtual bool visit(ContractDefinition&) { return true; }
virtual bool visit(StructDefinition&) { return true; }
virtual bool visit(ParameterList&) { return true; }
virtual bool visit(FunctionDefinition&) { return true; }
virtual bool visit(VariableDeclaration&) { return true; }
virtual bool visit(TypeName&) { return true; }
virtual bool visit(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; }
virtual bool visit(Mapping&) { return true; }
virtual bool visit(Statement&) { return true; }
virtual bool visit(Block&) { return true; }
virtual bool visit(IfStatement&) { return true; }
virtual bool visit(BreakableStatement&) { return true; }
virtual bool visit(WhileStatement&) { return true; }
virtual bool visit(Continue&) { return true; }
virtual bool visit(Break&) { return true; }
virtual bool visit(Return&) { return true; }
virtual bool visit(VariableDefinition&) { return true; }
virtual bool visit(Expression&) { return true; }
virtual bool visit(Assignment&) { return true; }
virtual bool visit(UnaryOperation&) { return true; }
virtual bool visit(BinaryOperation&) { return true; }
virtual bool visit(FunctionCall&) { return true; }
virtual bool visit(MemberAccess&) { return true; }
virtual bool visit(IndexAccess&) { return true; }
virtual bool visit(PrimaryExpression&) { return true; }
virtual bool visit(Identifier&) { return true; }
virtual bool visit(ElementaryTypeNameExpression&) { return true; }
virtual bool visit(Literal&) { return true; }
virtual void endVisit(ASTNode&) { }
virtual void endVisit(ContractDefinition&) { }
virtual void endVisit(StructDefinition&) { }
virtual void endVisit(ParameterList&) { }
virtual void endVisit(FunctionDefinition&) { }
virtual void endVisit(VariableDeclaration&) { }
virtual void endVisit(TypeName&) { }
virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { }
virtual void endVisit(Mapping&) { }
virtual void endVisit(Statement&) { }
virtual void endVisit(Block&) { }
virtual void endVisit(IfStatement&) { }
virtual void endVisit(BreakableStatement&) { }
virtual void endVisit(WhileStatement&) { }
virtual void endVisit(Continue&) { }
virtual void endVisit(Break&) { }
virtual void endVisit(Return&) { }
virtual void endVisit(VariableDefinition&) { }
virtual void endVisit(Expression&) { }
virtual void endVisit(Assignment&) { }
virtual void endVisit(UnaryOperation&) { }
virtual void endVisit(BinaryOperation&) { }
virtual void endVisit(FunctionCall&) { }
virtual void endVisit(MemberAccess&) { }
virtual void endVisit(IndexAccess&) { }
virtual void endVisit(PrimaryExpression&) { }
virtual void endVisit(Identifier&) { }
virtual void endVisit(ElementaryTypeNameExpression&) { }
virtual void endVisit(Literal&) { }
};
}
}

52
libsolidity/BaseTypes.h

@ -0,0 +1,52 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Some elementary types for the parser.
*/
#pragma once
#include <ostream>
namespace dev
{
namespace solidity
{
/// Representation of an interval of source positions.
/// The interval includes start and excludes end.
struct Location
{
Location(int _start, int _end): start(_start), end(_end) { }
Location(): start(-1), end(-1) { }
bool IsValid() const { return start >= 0 && end >= start; }
int start;
int end;
};
/// Stream output for Location (used e.g. in boost exceptions).
inline std::ostream& operator<<(std::ostream& _out, Location const& _location)
{
return _out << "[" << _location.start << "," << _location.end << ")";
}
}
}

24
libsolidity/CMakeLists.txt

@ -0,0 +1,24 @@
cmake_policy(SET CMP0015 NEW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST)
set(EXECUTABLE solidity)
if(ETH_STATIC)
add_library(${EXECUTABLE} STATIC ${SRC_LIST})
else()
add_library(${EXECUTABLE} SHARED ${SRC_LIST})
endif()
file(GLOB HEADERS "*.h")
include_directories(..)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} evmface)
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

42
libsolidity/Exceptions.h

@ -0,0 +1,42 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity exception hierarchy.
*/
#pragma once
#include <string>
#include <libdevcore/Exceptions.h>
#include <libsolidity/BaseTypes.h>
namespace dev
{
namespace solidity
{
struct ParserError: virtual Exception {};
struct TypeError: virtual Exception {};
struct DeclarationError: virtual Exception {};
typedef boost::error_info<struct tag_sourcePosition, int> errinfo_sourcePosition;
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation;
}
}

202
libsolidity/NameAndTypeResolver.cpp

@ -0,0 +1,202 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Parser part that determines the declarations corresponding to names and the types of expressions.
*/
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/AST.h>
#include <libsolidity/Exceptions.h>
#include <boost/assert.hpp>
namespace dev
{
namespace solidity
{
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{
reset();
DeclarationRegistrationHelper registrar(m_scopes, _contract);
m_currentScope = &m_scopes[&_contract];
//@todo structs
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, nullptr);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
{
m_currentScope = &m_scopes[function.get()];
ReferencesResolver referencesResolver(*function, *this,
function->getReturnParameterList().get());
}
// First, the parameter types of all functions need to be resolved before we can check
// the types, since it is possible to call functions that are only defined later
// in the source.
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
{
m_currentScope = &m_scopes[function.get()];
function->getBody().checkTypeRequirements();
}
}
void NameAndTypeResolver::reset()
{
m_scopes.clear();
m_currentScope = nullptr;
}
Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
{
return m_currentScope->resolveName(_name, _recursive);
}
DeclarationRegistrationHelper::DeclarationRegistrationHelper(std::map<ASTNode*, Scope>& _scopes,
ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(&m_scopes[nullptr])
{
_astRoot.accept(*this);
}
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
{
registerDeclaration(_contract, true);
return true;
}
void DeclarationRegistrationHelper::endVisit(ContractDefinition&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(StructDefinition& _struct)
{
registerDeclaration(_struct, true);
return true;
}
void DeclarationRegistrationHelper::endVisit(StructDefinition&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function)
{
registerDeclaration(_function, true);
return true;
}
void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
{
registerDeclaration(_declaration, false);
return true;
}
void DeclarationRegistrationHelper::endVisit(VariableDeclaration&)
{
}
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node)
{
std::map<ASTNode*, Scope>::iterator iter;
bool newlyAdded;
std::tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope));
BOOST_ASSERT(newlyAdded);
m_currentScope = &iter->second;
}
void DeclarationRegistrationHelper::closeCurrentScope()
{
BOOST_ASSERT(m_currentScope);
m_currentScope = m_currentScope->getOuterScope();
}
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{
BOOST_ASSERT(m_currentScope);
if (!m_currentScope->registerDeclaration(_declaration))
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation())
<< errinfo_comment("Identifier already declared."));
//@todo the exception should also contain the location of the first declaration
if (_opensScope)
enterNewSubScope(_declaration);
}
ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters):
m_resolver(_resolver), m_returnParameters(_returnParameters)
{
_root.accept(*this);
}
void ReferencesResolver::endVisit(VariableDeclaration& _variable)
{
// endVisit because the internal type needs resolving if it is a user defined type
// or mapping
if (_variable.getTypeName())
_variable.setType(_variable.getTypeName()->toType());
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
}
bool ReferencesResolver::visit(Return& _return)
{
BOOST_ASSERT(m_returnParameters);
_return.setFunctionReturnParameters(*m_returnParameters);
return true;
}
bool ReferencesResolver::visit(Mapping&)
{
// @todo
return true;
}
bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
{
Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName());
if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation())
<< errinfo_comment("Undeclared identifier."));
StructDefinition* referencedStruct = dynamic_cast<StructDefinition*>(declaration);
//@todo later, contracts are also valid types
if (!referencedStruct)
BOOST_THROW_EXCEPTION(_typeName.createTypeError("Identifier does not name a type name."));
_typeName.setReferencedStruct(*referencedStruct);
return false;
}
bool ReferencesResolver::visit(Identifier& _identifier)
{
Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName());
if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Undeclared identifier."));
_identifier.setReferencedDeclaration(*declaration);
return false;
}
}
}

100
libsolidity/NameAndTypeResolver.h

@ -0,0 +1,100 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Parser part that determines the declarations corresponding to names and the types of expressions.
*/
#pragma once
#include <map>
#include <boost/noncopyable.hpp>
#include <libsolidity/Scope.h>
#include <libsolidity/ASTVisitor.h>
namespace dev
{
namespace solidity
{
//! Resolves name references, resolves all types and checks that all operations are valid for the
//! inferred types. An exception is throw on the first error.
class NameAndTypeResolver: private boost::noncopyable
{
public:
NameAndTypeResolver() {}
void resolveNamesAndTypes(ContractDefinition& _contract);
Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
private:
void reset();
//! Maps nodes declaring a scope to scopes, i.e. ContractDefinition, FunctionDeclaration and
//! StructDefinition (@todo not yet implemented), where nullptr denotes the global scope.
std::map<ASTNode*, Scope> m_scopes;
Scope* m_currentScope;
};
//! Traverses the given AST upon construction and fills _scopes with all declarations inside the
//! AST.
class DeclarationRegistrationHelper: private ASTVisitor
{
public:
DeclarationRegistrationHelper(std::map<ASTNode*, Scope>& _scopes, ASTNode& _astRoot);
private:
bool visit(ContractDefinition& _contract);
void endVisit(ContractDefinition& _contract);
bool visit(StructDefinition& _struct);
void endVisit(StructDefinition& _struct);
bool visit(FunctionDefinition& _function);
void endVisit(FunctionDefinition& _function);
bool visit(VariableDeclaration& _declaration);
void endVisit(VariableDeclaration& _declaration);
void enterNewSubScope(ASTNode& _node);
void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope);
std::map<ASTNode*, Scope>& m_scopes;
Scope* m_currentScope;
};
//! Resolves references to declarations (of variables and types) and also establishes the link
//! between a return statement and the return parameter list.
class ReferencesResolver: private ASTVisitor
{
public:
ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ParameterList* _returnParameters);
private:
virtual void endVisit(VariableDeclaration& _variable) override;
virtual bool visit(Identifier& _identifier) override;
virtual bool visit(UserDefinedTypeName& _typeName) override;
virtual bool visit(Mapping&) override;
virtual bool visit(Return& _return) override;
NameAndTypeResolver& m_resolver;
ParameterList* m_returnParameters;
};
}
}

544
libsolidity/Parser.cpp

@ -0,0 +1,544 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity parser.
*/
#include <libdevcore/Log.h>
#include <libsolidity/BaseTypes.h>
#include <libsolidity/Parser.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h>
namespace dev
{
namespace solidity
{
ASTPointer<ContractDefinition> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
{
m_scanner = _scanner;
return parseContractDefinition();
}
/// AST node factory that also tracks the begin and end position of an AST node
/// while it is being parsed
class Parser::ASTNodeFactory
{
public:
ASTNodeFactory(Parser const& _parser): m_parser(_parser), m_location(_parser.getPosition(), -1) {}
void markEndPosition() { m_location.end = m_parser.getEndPosition(); }
void setLocationEmpty() { m_location.end = m_location.start; }
/// Set the end position to the one of the given node.
void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; }
template <class NodeType, typename... Args>
ASTPointer<NodeType> createNode(Args&& ... _args)
{
if (m_location.end < 0)
markEndPosition();
return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...);
}
private:
Parser const& m_parser;
Location m_location;
};
int Parser::getPosition() const
{
return m_scanner->getCurrentLocation().start;
}
int Parser::getEndPosition() const
{
return m_scanner->getCurrentLocation().end;
}
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::CONTRACT);
ASTPointer<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE);
std::vector<ASTPointer<StructDefinition>> structs;
std::vector<ASTPointer<VariableDeclaration>> stateVariables;
std::vector<ASTPointer<FunctionDefinition>> functions;
bool visibilityIsPublic = true;
while (true)
{
Token::Value currentToken = m_scanner->getCurrentToken();
if (currentToken == Token::RBRACE)
break;
else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE)
{
visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC);
m_scanner->next();
expectToken(Token::COLON);
}
else if (currentToken == Token::FUNCTION)
functions.push_back(parseFunctionDefinition(visibilityIsPublic));
else if (currentToken == Token::STRUCT)
structs.push_back(parseStructDefinition());
else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
Token::isElementaryTypeName(currentToken))
{
bool const allowVar = false;
stateVariables.push_back(parseVariableDeclaration(allowVar));
expectToken(Token::SEMICOLON);
}
else
BOOST_THROW_EXCEPTION(createParserError("Function, variable or struct declaration expected."));
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
expectToken(Token::EOS);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions);
}
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::FUNCTION);
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false;
if (m_scanner->getCurrentToken() == Token::CONST)
{
isDeclaredConst = true;
m_scanner->next();
}
ASTPointer<ParameterList> returnParameters;
if (m_scanner->getCurrentToken() == Token::RETURNS)
{
bool const permitEmptyParameterList = false;
m_scanner->next();
returnParameters = parseParameterList(permitEmptyParameterList);
}
else
{
// create an empty parameter list at a zero-length location
ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty();
returnParameters = nodeFactory.createNode<ParameterList>(std::vector<ASTPointer<VariableDeclaration>>());
}
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, parameters,
isDeclaredConst, returnParameters, block);
}
ASTPointer<StructDefinition> Parser::parseStructDefinition()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::STRUCT);
ASTPointer<ASTString> name = expectIdentifierToken();
std::vector<ASTPointer<VariableDeclaration>> members;
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE)
{
bool const allowVar = false;
members.push_back(parseVariableDeclaration(allowVar));
expectToken(Token::SEMICOLON);
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<StructDefinition>(name, members);
}
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type = parseTypeName(_allowVar);
nodeFactory.markEndPosition();
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken());
}
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
{
ASTPointer<TypeName> type;
Token::Value token = m_scanner->getCurrentToken();
if (Token::isElementaryTypeName(token))
{
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
m_scanner->next();
}
else if (token == Token::VAR)
{
if (!_allowVar)
BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name."));
m_scanner->next();
}
else if (token == Token::MAPPING)
{
type = parseMapping();
}
else if (token == Token::IDENTIFIER)
{
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
type = nodeFactory.createNode<UserDefinedTypeName>(expectIdentifierToken());
}
else
BOOST_THROW_EXCEPTION(createParserError("Expected type name"));
return type;
}
ASTPointer<Mapping> Parser::parseMapping()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::MAPPING);
expectToken(Token::LPAREN);
if (!Token::isElementaryTypeName(m_scanner->getCurrentToken()))
BOOST_THROW_EXCEPTION(createParserError("Expected elementary type name for mapping key type"));
ASTPointer<ElementaryTypeName> keyType;
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken());
m_scanner->next();
expectToken(Token::ARROW);
bool const allowVar = false;
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
return nodeFactory.createNode<Mapping>(keyType, valueType);
}
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
{
ASTNodeFactory nodeFactory(*this);
std::vector<ASTPointer<VariableDeclaration>> parameters;
expectToken(Token::LPAREN);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
{
bool const allowVar = false;
parameters.push_back(parseVariableDeclaration(allowVar));
while (m_scanner->getCurrentToken() != Token::RPAREN)
{
expectToken(Token::COMMA);
parameters.push_back(parseVariableDeclaration(allowVar));
}
}
nodeFactory.markEndPosition();
m_scanner->next();
return nodeFactory.createNode<ParameterList>(parameters);
}
ASTPointer<Block> Parser::parseBlock()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBRACE);
std::vector<ASTPointer<Statement>> statements;
while (m_scanner->getCurrentToken() != Token::RBRACE)
statements.push_back(parseStatement());
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<Block>(statements);
}
ASTPointer<Statement> Parser::parseStatement()
{
ASTPointer<Statement> statement;
switch (m_scanner->getCurrentToken())
{
case Token::IF:
return parseIfStatement();
case Token::WHILE:
return parseWhileStatement();
case Token::LBRACE:
return parseBlock();
// starting from here, all statements must be terminated by a semicolon
case Token::CONTINUE:
statement = ASTNodeFactory(*this).createNode<Continue>();
break;
case Token::BREAK:
statement = ASTNodeFactory(*this).createNode<Break>();
break;
case Token::RETURN:
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression;
if (m_scanner->next() != Token::SEMICOLON)
{
expression = parseExpression();
nodeFactory.setEndPositionFromNode(expression);
}
statement = nodeFactory.createNode<Return>(expression);
}
break;
default:
// distinguish between variable definition (and potentially assignment) and expressions
// (which include assignments to other expressions and pre-declared variables)
// We have a variable definition if we ge a keyword that specifies a type name, or
// in the case of a user-defined type, we have two identifiers following each other.
if (m_scanner->getCurrentToken() == Token::MAPPING ||
m_scanner->getCurrentToken() == Token::VAR ||
Token::isElementaryTypeName(m_scanner->getCurrentToken()) ||
(m_scanner->getCurrentToken() == Token::IDENTIFIER &&
m_scanner->peekNextToken() == Token::IDENTIFIER))
statement = parseVariableDefinition();
else // "ordinary" expression
statement = parseExpression();
}
expectToken(Token::SEMICOLON);
return statement;
}
ASTPointer<IfStatement> Parser::parseIfStatement()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::IF);
expectToken(Token::LPAREN);
ASTPointer<Expression> condition = parseExpression();
expectToken(Token::RPAREN);
ASTPointer<Statement> trueBody = parseStatement();
ASTPointer<Statement> falseBody;
if (m_scanner->getCurrentToken() == Token::ELSE)
{
m_scanner->next();
falseBody = parseStatement();
nodeFactory.setEndPositionFromNode(falseBody);
}
else
nodeFactory.setEndPositionFromNode(trueBody);
return nodeFactory.createNode<IfStatement>(condition, trueBody, falseBody);
}
ASTPointer<WhileStatement> Parser::parseWhileStatement()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::WHILE);
expectToken(Token::LPAREN);
ASTPointer<Expression> condition = parseExpression();
expectToken(Token::RPAREN);
ASTPointer<Statement> body = parseStatement();
nodeFactory.setEndPositionFromNode(body);
return nodeFactory.createNode<WhileStatement>(condition, body);
}
ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
{
ASTNodeFactory nodeFactory(*this);
bool const allowVar = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(allowVar);
ASTPointer<Expression> value;
if (m_scanner->getCurrentToken() == Token::ASSIGN)
{
m_scanner->next();
value = parseExpression();
nodeFactory.setEndPositionFromNode(value);
}
else
nodeFactory.setEndPositionFromNode(variable);
return nodeFactory.createNode<VariableDefinition>(variable, value);
}
ASTPointer<Expression> Parser::parseExpression()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression = parseBinaryExpression();
if (!Token::isAssignmentOp(m_scanner->getCurrentToken()))
return expression;
Token::Value assignmentOperator = expectAssignmentOperator();
ASTPointer<Expression> rightHandSide = parseExpression();
nodeFactory.setEndPositionFromNode(rightHandSide);
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
}
ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression = parseUnaryExpression();
int precedence = Token::precedence(m_scanner->getCurrentToken());
for (; precedence >= _minPrecedence; --precedence)
{
while (Token::precedence(m_scanner->getCurrentToken()) == precedence)
{
Token::Value op = m_scanner->getCurrentToken();
m_scanner->next();
ASTPointer<Expression> right = parseBinaryExpression(precedence + 1);
nodeFactory.setEndPositionFromNode(right);
expression = nodeFactory.createNode<BinaryOperation>(expression, op, right);
}
}
return expression;
}
ASTPointer<Expression> Parser::parseUnaryExpression()
{
ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken();
if (Token::isUnaryOp(token) || Token::isCountOp(token))
{
// prefix expression
m_scanner->next();
ASTPointer<Expression> subExpression = parseUnaryExpression();
nodeFactory.setEndPositionFromNode(subExpression);
return nodeFactory.createNode<UnaryOperation>(token, subExpression, true);
}
else
{
// potential postfix expression
ASTPointer<Expression> subExpression = parseLeftHandSideExpression();
token = m_scanner->getCurrentToken();
if (!Token::isCountOp(token))
return subExpression;
nodeFactory.markEndPosition();
m_scanner->next();
return nodeFactory.createNode<UnaryOperation>(token, subExpression, false);
}
}
ASTPointer<Expression> Parser::parseLeftHandSideExpression()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression = parsePrimaryExpression();
while (true)
{
switch (m_scanner->getCurrentToken())
{
case Token::LBRACK:
{
m_scanner->next();
ASTPointer<Expression> index = parseExpression();
nodeFactory.markEndPosition();
expectToken(Token::RBRACK);
expression = nodeFactory.createNode<IndexAccess>(expression, index);
}
break;
case Token::PERIOD:
{
m_scanner->next();
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken());
}
break;
case Token::LPAREN:
{
m_scanner->next();
std::vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments();
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
expression = nodeFactory.createNode<FunctionCall>(expression, arguments);
}
break;
default:
return expression;
}
}
}
ASTPointer<Expression> Parser::parsePrimaryExpression()
{
ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken();
ASTPointer<Expression> expression;
switch (token)
{
case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL:
expression = nodeFactory.createNode<Literal>(token, ASTPointer<ASTString>());
m_scanner->next();
break;
case Token::NUMBER:
case Token::STRING_LITERAL:
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break;
case Token::IDENTIFIER:
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
break;
case Token::LPAREN:
{
m_scanner->next();
ASTPointer<Expression> expression = parseExpression();
expectToken(Token::RPAREN);
return expression;
}
default:
if (Token::isElementaryTypeName(token))
{
// used for casts
expression = nodeFactory.createNode<ElementaryTypeNameExpression>(token);
m_scanner->next();
}
else
{
BOOST_THROW_EXCEPTION(createParserError("Expected primary expression."));
return ASTPointer<Expression>(); // this is not reached
}
break;
}
return expression;
}
std::vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
{
std::vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() != Token::RPAREN)
{
arguments.push_back(parseExpression());
while (m_scanner->getCurrentToken() != Token::RPAREN)
{
expectToken(Token::COMMA);
arguments.push_back(parseExpression());
}
}
return arguments;
}
void Parser::expectToken(Token::Value _value)
{
if (m_scanner->getCurrentToken() != _value)
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected token ") + std::string(Token::getName(_value))));
m_scanner->next();
}
Token::Value Parser::expectAssignmentOperator()
{
Token::Value op = m_scanner->getCurrentToken();
if (!Token::isAssignmentOp(op))
BOOST_THROW_EXCEPTION(createParserError("Expected assignment operator"));
m_scanner->next();
return op;
}
ASTPointer<ASTString> Parser::expectIdentifierToken()
{
if (m_scanner->getCurrentToken() != Token::IDENTIFIER)
BOOST_THROW_EXCEPTION(createParserError("Expected identifier"));
return getLiteralAndAdvance();
}
ASTPointer<ASTString> Parser::getLiteralAndAdvance()
{
ASTPointer<ASTString> identifier = std::make_shared<ASTString>(m_scanner->getCurrentLiteral());
m_scanner->next();
return identifier;
}
ParserError Parser::createParserError(std::string const& _description) const
{
return ParserError() << errinfo_sourcePosition(getPosition()) << errinfo_comment(_description);
}
}
}

86
libsolidity/Parser.h

@ -0,0 +1,86 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity parser.
*/
#pragma once
#include "libsolidity/AST.h"
namespace dev
{
namespace solidity
{
class Scanner;
class Parser
{
public:
ASTPointer<ContractDefinition> parse(std::shared_ptr<Scanner> const& _scanner);
private:
class ASTNodeFactory;
/// Start position of the current token
int getPosition() const;
/// End position of the current token
int getEndPosition() const;
/// Parsing functions for the AST nodes
/// @{
ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar);
ASTPointer<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true);
ASTPointer<Block> parseBlock();
ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement();
ASTPointer<WhileStatement> parseWhileStatement();
ASTPointer<VariableDefinition> parseVariableDefinition();
ASTPointer<Expression> parseExpression();
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4);
ASTPointer<Expression> parseUnaryExpression();
ASTPointer<Expression> parseLeftHandSideExpression();
ASTPointer<Expression> parsePrimaryExpression();
std::vector<ASTPointer<Expression>> parseFunctionCallArguments();
/// @}
/// Helper functions
/// @{
/// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value);
Token::Value expectAssignmentOperator();
ASTPointer<ASTString> expectIdentifierToken();
ASTPointer<ASTString> getLiteralAndAdvance();
/// @}
/// Creates a @ref ParserError exception and annotates it with the current position and the
/// given @a _description.
ParserError createParserError(std::string const& _description) const;
std::shared_ptr<Scanner> m_scanner;
};
}
}

702
libsolidity/Scanner.cpp

@ -0,0 +1,702 @@
/*
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/>.
This file is derived from the file "scanner.cc", which was part of the
V8 project. The original copyright header follows:
Copyright 2006-2012, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity scanner.
*/
#include <algorithm>
#include <tuple>
#include <libsolidity/Scanner.h>
namespace dev
{
namespace solidity
{
namespace
{
bool IsDecimalDigit(char c)
{
return '0' <= c && c <= '9';
}
bool IsHexDigit(char c)
{
return IsDecimalDigit(c)
|| ('a' <= c && c <= 'f')
|| ('A' <= c && c <= 'F');
}
bool IsLineTerminator(char c)
{
return c == '\n';
}
bool IsWhiteSpace(char c)
{
return c == ' ' || c == '\n' || c == '\t';
}
bool IsIdentifierStart(char c)
{
return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}
bool IsIdentifierPart(char c)
{
return IsIdentifierStart(c) || IsDecimalDigit(c);
}
int HexValue(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else return -1;
}
} // end anonymous namespace
Scanner::Scanner(CharStream const& _source)
{
reset(_source);
}
void Scanner::reset(CharStream const& _source)
{
m_source = _source;
m_char = m_source.get();
skipWhitespace();
scanToken();
next();
}
bool Scanner::scanHexNumber(char& o_scannedNumber, int _expectedLength)
{
BOOST_ASSERT(_expectedLength <= 4); // prevent overflow
char x = 0;
for (int i = 0; i < _expectedLength; i++)
{
int d = HexValue(m_char);
if (d < 0)
{
rollback(i);
return false;
}
x = x * 16 + d;
advance();
}
o_scannedNumber = x;
return true;
}
// Ensure that tokens can be stored in a byte.
BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100);
Token::Value Scanner::next()
{
m_current_token = m_next_token;
scanToken();
return m_current_token.token;
}
Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else)
{
advance();
if (m_char == _next)
return selectToken(_then);
else
return _else;
}
bool Scanner::skipWhitespace()
{
int const start_position = getSourcePos();
while (IsWhiteSpace(m_char))
advance();
// Return whether or not we skipped any characters.
return getSourcePos() != start_position;
}
Token::Value Scanner::skipSingleLineComment()
{
// The line terminator at the end of the line is not considered
// to be part of the single-line comment; it is recognized
// separately by the lexical grammar and becomes part of the
// stream of input elements for the syntactic grammar
while (advance() && !IsLineTerminator(m_char)) { };
return Token::WHITESPACE;
}
Token::Value Scanner::skipMultiLineComment()
{
BOOST_ASSERT(m_char == '*');
advance();
while (!isSourcePastEndOfInput())
{
char ch = m_char;
advance();
// If we have reached the end of the multi-line comment, we
// consume the '/' and insert a whitespace. This way all
// multi-line comments are treated as whitespace.
if (ch == '*' && m_char == '/')
{
m_char = ' ';
return Token::WHITESPACE;
}
}
// Unterminated multi-line comment.
return Token::ILLEGAL;
}
void Scanner::scanToken()
{
m_next_token.literal.clear();
Token::Value token;
do
{
// Remember the position of the next token
m_next_token.location.start = getSourcePos();
switch (m_char)
{
case '\n': // fall-through
case ' ':
case '\t':
token = selectToken(Token::WHITESPACE);
break;
case '"':
case '\'':
token = scanString();
break;
case '<':
// < <= << <<=
advance();
if (m_char == '=')
token = selectToken(Token::LTE);
else if (m_char == '<')
token = selectToken('=', Token::ASSIGN_SHL, Token::SHL);
else
token = Token::LT;
break;
case '>':
// > >= >> >>= >>> >>>=
advance();
if (m_char == '=')
token = selectToken(Token::GTE);
else if (m_char == '>')
{
// >> >>= >>> >>>=
advance();
if (m_char == '=')
token = selectToken(Token::ASSIGN_SAR);
else if (m_char == '>')
token = selectToken('=', Token::ASSIGN_SHR, Token::SHR);
else
token = Token::SAR;
}
else
token = Token::GT;
break;
case '=':
// = == =>
advance();
if (m_char == '=')
token = selectToken(Token::EQ);
else if (m_char == '>')
token = selectToken(Token::ARROW);
else
token = Token::ASSIGN;
break;
case '!':
// ! !=
advance();
if (m_char == '=')
token = selectToken(Token::NE);
else
token = Token::NOT;
break;
case '+':
// + ++ +=
advance();
if (m_char == '+')
token = selectToken(Token::INC);
else if (m_char == '=')
token = selectToken(Token::ASSIGN_ADD);
else
token = Token::ADD;
break;
case '-':
// - -- -=
advance();
if (m_char == '-')
{
advance();
token = Token::DEC;
}
else if (m_char == '=')
token = selectToken(Token::ASSIGN_SUB);
else
token = Token::SUB;
break;
case '*':
// * *=
token = selectToken('=', Token::ASSIGN_MUL, Token::MUL);
break;
case '%':
// % %=
token = selectToken('=', Token::ASSIGN_MOD, Token::MOD);
break;
case '/':
// / // /* /=
advance();
if (m_char == '/')
token = skipSingleLineComment();
else if (m_char == '*')
token = skipMultiLineComment();
else if (m_char == '=')
token = selectToken(Token::ASSIGN_DIV);
else
token = Token::DIV;
break;
case '&':
// & && &=
advance();
if (m_char == '&')
token = selectToken(Token::AND);
else if (m_char == '=')
token = selectToken(Token::ASSIGN_BIT_AND);
else
token = Token::BIT_AND;
break;
case '|':
// | || |=
advance();
if (m_char == '|')
token = selectToken(Token::OR);
else if (m_char == '=')
token = selectToken(Token::ASSIGN_BIT_OR);
else
token = Token::BIT_OR;
break;
case '^':
// ^ ^=
token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);
break;
case '.':
// . Number
advance();
if (IsDecimalDigit(m_char))
token = scanNumber(true);
else
token = Token::PERIOD;
break;
case ':':
token = selectToken(Token::COLON);
break;
case ';':
token = selectToken(Token::SEMICOLON);
break;
case ',':
token = selectToken(Token::COMMA);
break;
case '(':
token = selectToken(Token::LPAREN);
break;
case ')':
token = selectToken(Token::RPAREN);
break;
case '[':
token = selectToken(Token::LBRACK);
break;
case ']':
token = selectToken(Token::RBRACK);
break;
case '{':
token = selectToken(Token::LBRACE);
break;
case '}':
token = selectToken(Token::RBRACE);
break;
case '?':
token = selectToken(Token::CONDITIONAL);
break;
case '~':
token = selectToken(Token::BIT_NOT);
break;
default:
if (IsIdentifierStart(m_char))
token = scanIdentifierOrKeyword();
else if (IsDecimalDigit(m_char))
token = scanNumber(false);
else if (skipWhitespace())
token = Token::WHITESPACE;
else if (isSourcePastEndOfInput())
token = Token::EOS;
else
token = selectToken(Token::ILLEGAL);
break;
}
// Continue scanning for tokens as long as we're just skipping
// whitespace.
}
while (token == Token::WHITESPACE);
m_next_token.location.end = getSourcePos();
m_next_token.token = token;
}
bool Scanner::scanEscape()
{
char c = m_char;
advance();
// Skip escaped newlines.
if (IsLineTerminator(c))
return true;
switch (c)
{
case '\'': // fall through
case '"': // fall through
case '\\':
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'u':
if (!scanHexNumber(c, 4))
return false;
break;
case 'v':
c = '\v';
break;
case 'x':
if (!scanHexNumber(c, 2))
return false;
break;
}
addLiteralChar(c);
return true;
}
Token::Value Scanner::scanString()
{
char const quote = m_char;
advance(); // consume quote
LiteralScope literal(this);
while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char))
{
char c = m_char;
advance();
if (c == '\\')
{
if (isSourcePastEndOfInput() || !scanEscape())
return Token::ILLEGAL;
}
else
addLiteralChar(c);
}
if (m_char != quote) return Token::ILLEGAL;
literal.Complete();
advance(); // consume quote
return Token::STRING_LITERAL;
}
void Scanner::scanDecimalDigits()
{
while (IsDecimalDigit(m_char))
addLiteralCharAndAdvance();
}
Token::Value Scanner::scanNumber(bool _periodSeen)
{
BOOST_ASSERT(IsDecimalDigit(m_char)); // the first digit of the number or the fraction
enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL;
LiteralScope literal(this);
if (_periodSeen)
{
// we have already seen a decimal point of the float
addLiteralChar('.');
scanDecimalDigits(); // we know we have at least one digit
}
else
{
// if the first character is '0' we must check for octals and hex
if (m_char == '0')
{
addLiteralCharAndAdvance();
// either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or
// an octal number.
if (m_char == 'x' || m_char == 'X')
{
// hex number
kind = HEX;
addLiteralCharAndAdvance();
if (!IsHexDigit(m_char))
return Token::ILLEGAL; // we must have at least one hex digit after 'x'/'X'
while (IsHexDigit(m_char))
addLiteralCharAndAdvance();
}
}
// Parse decimal digits and allow trailing fractional part.
if (kind == DECIMAL)
{
scanDecimalDigits(); // optional
if (m_char == '.')
{
addLiteralCharAndAdvance();
scanDecimalDigits(); // optional
}
}
}
// scan exponent, if any
if (m_char == 'e' || m_char == 'E')
{
BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
if (kind != DECIMAL) return Token::ILLEGAL;
// scan exponent
addLiteralCharAndAdvance();
if (m_char == '+' || m_char == '-')
addLiteralCharAndAdvance();
if (!IsDecimalDigit(m_char))
return Token::ILLEGAL; // we must have at least one decimal digit after 'e'/'E'
scanDecimalDigits();
}
// The source character immediately following a numeric literal must
// not be an identifier start or a decimal digit; see ECMA-262
// section 7.8.3, page 17 (note that we read only one decimal digit
// if the value is 0).
if (IsDecimalDigit(m_char) || IsIdentifierStart(m_char))
return Token::ILLEGAL;
literal.Complete();
return Token::NUMBER;
}
// ----------------------------------------------------------------------------
// Keyword Matcher
#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
KEYWORD_GROUP('a') \
KEYWORD("address", Token::ADDRESS) \
KEYWORD_GROUP('b') \
KEYWORD("break", Token::BREAK) \
KEYWORD("bool", Token::BOOL) \
KEYWORD_GROUP('c') \
KEYWORD("case", Token::CASE) \
KEYWORD("const", Token::CONST) \
KEYWORD("continue", Token::CONTINUE) \
KEYWORD("contract", Token::CONTRACT) \
KEYWORD_GROUP('d') \
KEYWORD("default", Token::DEFAULT) \
KEYWORD("delete", Token::DELETE) \
KEYWORD("do", Token::DO) \
KEYWORD_GROUP('e') \
KEYWORD("else", Token::ELSE) \
KEYWORD("extends", Token::EXTENDS) \
KEYWORD_GROUP('f') \
KEYWORD("false", Token::FALSE_LITERAL) \
KEYWORD("for", Token::FOR) \
KEYWORD("function", Token::FUNCTION) \
KEYWORD_GROUP('h') \
KEYWORD("hash", Token::HASH) \
KEYWORD("hash32", Token::HASH32) \
KEYWORD("hash64", Token::HASH64) \
KEYWORD("hash128", Token::HASH128) \
KEYWORD("hash256", Token::HASH256) \
KEYWORD_GROUP('i') \
KEYWORD("if", Token::IF) \
KEYWORD("in", Token::IN) \
KEYWORD("int", Token::INT) \
KEYWORD("int32", Token::INT32) \
KEYWORD("int64", Token::INT64) \
KEYWORD("int128", Token::INT128) \
KEYWORD("int256", Token::INT256) \
KEYWORD_GROUP('l') \
KEYWORD_GROUP('m') \
KEYWORD("mapping", Token::MAPPING) \
KEYWORD_GROUP('n') \
KEYWORD("new", Token::NEW) \
KEYWORD("null", Token::NULL_LITERAL) \
KEYWORD_GROUP('p') \
KEYWORD("private", Token::PRIVATE) \
KEYWORD("public", Token::PUBLIC) \
KEYWORD_GROUP('r') \
KEYWORD("real", Token::REAL) \
KEYWORD("return", Token::RETURN) \
KEYWORD("returns", Token::RETURNS) \
KEYWORD_GROUP('s') \
KEYWORD("string", Token::STRING_TYPE) \
KEYWORD("struct", Token::STRUCT) \
KEYWORD("switch", Token::SWITCH) \
KEYWORD_GROUP('t') \
KEYWORD("text", Token::TEXT) \
KEYWORD("this", Token::THIS) \
KEYWORD("true", Token::TRUE_LITERAL) \
KEYWORD_GROUP('u') \
KEYWORD("uint", Token::UINT) \
KEYWORD("uint32", Token::UINT32) \
KEYWORD("uint64", Token::UINT64) \
KEYWORD("uint128", Token::UINT128) \
KEYWORD("uint256", Token::UINT256) \
KEYWORD("ureal", Token::UREAL) \
KEYWORD_GROUP('v') \
KEYWORD("var", Token::VAR) \
KEYWORD_GROUP('w') \
KEYWORD("while", Token::WHILE) \
static Token::Value KeywordOrIdentifierToken(std::string const& input)
{
BOOST_ASSERT(!input.empty());
int const kMinLength = 2;
int const kMaxLength = 10;
if (input.size() < kMinLength || input.size() > kMaxLength)
return Token::IDENTIFIER;
switch (input[0])
{
default:
#define KEYWORD_GROUP_CASE(ch) \
break; \
case ch:
#define KEYWORD(keyword, token) \
{ \
/* 'keyword' is a char array, so sizeof(keyword) is */ \
/* strlen(keyword) plus 1 for the NUL char. */ \
int const keyword_length = sizeof(keyword) - 1; \
BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \
BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \
if (input == keyword) \
return token; \
}
KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
}
return Token::IDENTIFIER;
}
Token::Value Scanner::scanIdentifierOrKeyword()
{
BOOST_ASSERT(IsIdentifierStart(m_char));
LiteralScope literal(this);
addLiteralCharAndAdvance();
// Scan the rest of the identifier characters.
while (IsIdentifierPart(m_char))
addLiteralCharAndAdvance();
literal.Complete();
return KeywordOrIdentifierToken(m_next_token.literal);
}
char CharStream::advanceAndGet()
{
if (isPastEndOfInput())
return 0;
++m_pos;
if (isPastEndOfInput())
return 0;
return get();
}
char CharStream::rollback(size_t _amount)
{
BOOST_ASSERT(m_pos >= _amount);
m_pos -= _amount;
return get();
}
std::string CharStream::getLineAtPosition(int _position) const
{
// if _position points to \n, it returns the line before the \n
using size_type = std::string::size_type;
size_type searchStart = std::min<size_type>(m_source.size(), _position);
if (searchStart > 0)
searchStart--;
size_type lineStart = m_source.rfind('\n', searchStart);
if (lineStart == std::string::npos)
lineStart = 0;
else
lineStart++;
return m_source.substr(lineStart,
std::min(m_source.find('\n', lineStart),
m_source.size()) - lineStart);
}
std::tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
{
using size_type = std::string::size_type;
size_type searchPosition = std::min<size_type>(m_source.size(), _position);
int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n');
size_type lineStart;
if (searchPosition == 0)
lineStart = 0;
else
{
lineStart = m_source.rfind('\n', searchPosition - 1);
lineStart = lineStart == std::string::npos ? 0 : lineStart + 1;
}
return std::tuple<int, int>(lineNumber, searchPosition - lineStart);
}
}
}

204
libsolidity/Scanner.h

@ -0,0 +1,204 @@
/*
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/>.
This file is derived from the file "scanner.h", which was part of the
V8 project. The original copyright header follows:
Copyright 2006-2012, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity scanner.
*/
#pragma once
#include <boost/assert.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libdevcore/CommonData.h>
#include <libsolidity/BaseTypes.h>
#include <libsolidity/Token.h>
namespace dev
{
namespace solidity
{
class AstRawString;
class AstValueFactory;
class ParserRecorder;
class CharStream
{
public:
CharStream(): m_pos(0) {}
explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {}
int getPos() const { return m_pos; }
bool isPastEndOfInput() const { return m_pos >= m_source.size(); }
char get() const { return m_source[m_pos]; }
char advanceAndGet();
char rollback(size_t _amount);
/// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive.
/// @{
std::string getLineAtPosition(int _position) const;
std::tuple<int, int> translatePositionToLineColumn(int _position) const;
/// @}
private:
std::string m_source;
size_t m_pos;
};
class Scanner
{
public:
// Scoped helper for literal recording. Automatically drops the literal
// if aborting the scanning before it's complete.
class LiteralScope
{
public:
explicit LiteralScope(Scanner* self): scanner_(self), complete_(false) { scanner_->startNewLiteral(); }
~LiteralScope() { if (!complete_) scanner_->dropLiteral(); }
void Complete() { complete_ = true; }
private:
Scanner* scanner_;
bool complete_;
};
explicit Scanner(CharStream const& _source);
/// Resets the scanner as if newly constructed with _input as input.
void reset(CharStream const& _source);
/// Returns the next token and advances input.
Token::Value next();
/// Information about the current token
/// @{
/// Returns the current token
Token::Value getCurrentToken() { return m_current_token.token; }
Location getCurrentLocation() const { return m_current_token.location; }
const std::string& getCurrentLiteral() const { return m_current_token.literal; }
/// @}
/// Information about the next token
/// @{
/// Returns the next token without advancing input.
Token::Value peekNextToken() const { return m_next_token.token; }
Location peekLocation() const { return m_next_token.location; }
const std::string& peekLiteral() const { return m_next_token.literal; }
/// @}
/// Functions that help pretty-printing parse errors.
/// Do only use in error cases, they are quite expensive.
/// @{
std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); }
std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); }
/// @}
private:
// Used for the current and look-ahead token.
struct TokenDesc
{
Token::Value token;
Location location;
std::string literal;
};
/// Literal buffer support
/// @{
inline void startNewLiteral() { m_next_token.literal.clear(); }
inline void addLiteralChar(char c) { m_next_token.literal.push_back(c); }
inline void dropLiteral() { m_next_token.literal.clear(); }
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
/// @}
bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); }
void rollback(int _amount) { m_char = m_source.rollback(_amount); }
inline Token::Value selectToken(Token::Value _tok) { advance(); return _tok; }
/// If the next character is _next, advance and return _then, otherwise return _else.
inline Token::Value selectToken(char _next, Token::Value _then, Token::Value _else);
bool scanHexNumber(char& o_scannedNumber, int _expectedLength);
// Scans a single JavaScript token.
void scanToken();
bool skipWhitespace();
Token::Value skipSingleLineComment();
Token::Value skipMultiLineComment();
void scanDecimalDigits();
Token::Value scanNumber(bool _periodSeen);
Token::Value scanIdentifierOrKeyword();
Token::Value scanString();
/// Scans an escape-sequence which is part of a string and adds the
/// decoded character to the current literal. Returns true if a pattern
/// is scanned.
bool scanEscape();
/// Return the current source position.
int getSourcePos() { return m_source.getPos(); }
bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); }
TokenDesc m_current_token; // desc for current token (as returned by Next())
TokenDesc m_next_token; // desc for next token (one token look-ahead)
CharStream m_source;
/// one character look-ahead, equals 0 at end of input
char m_char;
};
}
}

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

Loading…
Cancel
Save