Browse Source

Merge branch 'develop' of https://github.com/ethereum/cpp-ethereum into build_enhancement

Conflicts:
	alethzero/CMakeLists.txt
	cmake/EthDependenciesDeprecated.cmake
	libdevcrypto/CryptoPP.h
	libdevcrypto/EC.cpp
	third/CMakeLists.txt
cl-refactor
sveneh 10 years ago
parent
commit
791c8e3070
  1. 2
      CMakeLists.txt
  2. 2
      alethzero/CMakeLists.txt
  3. 170
      alethzero/MainWin.cpp
  4. 6
      alethzero/MainWin.h
  5. 36
      alethzero/OurWebThreeStubServer.cpp
  6. 38
      alethzero/OurWebThreeStubServer.h
  7. 2
      eth/main.cpp
  8. 96
      exp/main.cpp
  9. 2
      iethxi/MainWin.cpp
  10. 2
      libdevcore/Common.cpp
  11. 3
      libdevcore/CommonJS.cpp
  12. 4
      libdevcore/Exceptions.h
  13. 5
      libdevcore/FixedHash.h
  14. 1
      libdevcore/Log.h
  15. 6
      libdevcore/RLP.h
  16. 145
      libdevcrypto/Common.cpp
  17. 34
      libdevcrypto/Common.h
  18. 47
      libdevcrypto/CryptoPP.cpp
  19. 42
      libdevcrypto/CryptoPP.h
  20. 167
      libdevcrypto/EC.cpp
  21. 23
      libdevcrypto/EC.h
  22. 1
      libdevcrypto/FileSystem.h
  23. 3
      libdevcrypto/SHA3.h
  24. 3
      libdevcrypto/SHA3MAC.cpp
  25. 4
      libethcore/CommonEth.cpp
  26. 42
      libethereum/Client.cpp
  27. 55
      libethereum/Executive.cpp
  28. 2
      libethereum/Executive.h
  29. 2
      libethereum/ExtVM.h
  30. 57
      libethereum/MessageFilter.cpp
  31. 34
      libethereum/MessageFilter.h
  32. 57
      libethereum/State.cpp
  33. 5
      libethereum/State.h
  34. 38
      libethereum/Transaction.cpp
  35. 132
      libethereum/Transaction.h
  36. 2
      libevm/CMakeLists.txt
  37. 2
      libevm/ExtVMFace.cpp
  38. 27
      libevm/ExtVMFace.h
  39. 10
      libevm/VM.h
  40. 81
      libevmcore/Assembly.cpp
  41. 14
      libevmcore/Assembly.h
  42. 2
      libevmcore/CMakeLists.txt
  43. 36
      libevmcore/Exceptions.h
  44. 337
      libevmcore/Instruction.cpp
  45. 30
      libevmcore/Instruction.h
  46. 337
      libevmface/Instruction.cpp
  47. 117
      libjsqrc/main.js
  48. 8
      libjsqrc/qt.js
  49. 2
      libjsqrc/setup.js
  50. 1
      liblll/All.h
  51. 2
      liblll/CMakeLists.txt
  52. 2
      liblll/CodeFragment.cpp
  53. 4
      liblll/CodeFragment.h
  54. 3
      liblll/Exceptions.h
  55. 2
      libpyserpent/CMakeLists.txt
  56. 2
      libqethereum/CMakeLists.txt
  57. 30
      libqethereum/QEthereum.cpp
  58. 2
      libqethereum/QmlEthereum.cpp
  59. 2
      libserpent/CMakeLists.txt
  60. 14
      libserpent/bignum.cpp
  61. 12
      libserpent/bignum.h
  62. 212
      libserpent/compiler.cpp
  63. 224
      libserpent/opcodes.h
  64. 53
      libserpent/parser.cpp
  65. 870
      libserpent/rewriter.cpp
  66. 2
      libserpent/tokenize.cpp
  67. 22
      libserpent/util.cpp
  68. 6
      libserpent/util.h
  69. 115
      libsolidity/AST.cpp
  70. 119
      libsolidity/AST.h
  71. 1
      libsolidity/ASTForward.h
  72. 12
      libsolidity/ASTPrinter.cpp
  73. 2
      libsolidity/ASTPrinter.h
  74. 2
      libsolidity/ASTVisitor.h
  75. 10
      libsolidity/CMakeLists.txt
  76. 525
      libsolidity/Compiler.cpp
  77. 138
      libsolidity/Compiler.h
  78. 87
      libsolidity/CompilerContext.cpp
  79. 102
      libsolidity/CompilerContext.h
  80. 50
      libsolidity/CompilerStack.cpp
  81. 43
      libsolidity/CompilerStack.h
  82. 2
      libsolidity/Exceptions.h
  83. 436
      libsolidity/ExpressionCompiler.cpp
  84. 117
      libsolidity/ExpressionCompiler.h
  85. 46
      libsolidity/NameAndTypeResolver.cpp
  86. 17
      libsolidity/NameAndTypeResolver.h
  87. 27
      libsolidity/Parser.cpp
  88. 1
      libsolidity/Parser.h
  89. 138
      libsolidity/Scanner.cpp
  90. 7
      libsolidity/Scanner.h
  91. 148
      libsolidity/Token.h
  92. 105
      libsolidity/Types.cpp
  93. 40
      libsolidity/Types.h
  94. 201
      libweb3jsonrpc/WebThreeStubServer.cpp
  95. 93
      libweb3jsonrpc/WebThreeStubServer.h
  96. 344
      libweb3jsonrpc/abstractwebthreestubserver.h
  97. 100
      libweb3jsonrpc/spec.json
  98. 35
      libwhisper/Common.cpp
  99. 8
      libwhisper/Common.h
  100. 7
      libwhisper/Interface.cpp

2
CMakeLists.txt

@ -105,7 +105,7 @@ include(EthDependenciesDeprecated)
createBuildInfo()
add_subdirectory(libdevcore)
add_subdirectory(libevmface)
add_subdirectory(libevmcore)
add_subdirectory(liblll)
add_subdirectory(libserpent)
add_subdirectory(libsolidity)

2
alethzero/CMakeLists.txt

@ -53,7 +53,7 @@ else ()
endif ()
qt5_use_modules(${EXECUTEABLE} Core)# Gui Widgets Network WebKit WebKitWidgets)
target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore devcrypto secp256k1 gmp serpent lll evmface devcore web3jsonrpc jsqrc)
target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore devcrypto secp256k1 gmp serpent lll solidity evmcore devcore web3jsonrpc jsqrc)
if (APPLE)
# First have qt5 install plugins and frameworks

170
alethzero/MainWin.cpp

@ -36,6 +36,9 @@
#include <libdevcore/CommonJS.h>
#include <liblll/Compiler.h>
#include <liblll/CodeFragment.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libevm/VM.h>
#include <libethereum/BlockChain.h>
#include <libethereum/ExtVM.h>
@ -47,6 +50,7 @@
#include "MiningView.h"
#include "BuildInfo.h"
#include "MainWin.h"
#include "OurWebThreeStubServer.h"
#include "ui_Main.h"
using namespace std;
using namespace dev;
@ -149,7 +153,10 @@ Main::Main(QWidget *parent) :
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_server = unique_ptr<WebThreeStubServer>(new WebThreeStubServer(&m_qwebConnector, *web3(), keysAsVector(m_myKeys)));
// w3stubserver, on dealloc, deletes m_qwebConnector
m_qwebConnector = new QWebThreeConnector(); // owned by WebThreeStubServer
m_server.reset(new OurWebThreeStubServer(m_qwebConnector, *web3(), keysAsVector(m_myKeys)));
connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString)));
m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening();
@ -158,7 +165,7 @@ Main::Main(QWidget *parent) :
// NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it.
m_qweb = new QWebThree(this);
auto qweb = m_qweb;
m_qwebConnector.setQWeb(qweb);
m_qwebConnector->setQWeb(qweb);
QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
QWebFrame* f = ui->webView->page()->mainFrame();
@ -200,12 +207,28 @@ Main::~Main()
writeSettings();
}
void Main::on_newIdentity_triggered()
{
KeyPair kp = KeyPair::create();
m_myIdentities.append(kp);
m_server->setIdentities(keysAsVector(owned()));
refreshWhisper();
}
void Main::refreshWhisper()
{
ui->shhFrom->clear();
for (auto i: m_server->ids())
ui->shhFrom->addItem(QString::fromStdString(toHex(i.first.ref())));
}
void Main::addNewId(QString _ids)
{
Secret _id = jsToSecret(_ids.toStdString());
KeyPair kp(_id);
m_myIdentities.push_back(kp);
m_server->setIdentities(keysAsVector(owned()));
refreshWhisper();
}
dev::p2p::NetworkPreferences Main::netPrefs() const
@ -587,6 +610,7 @@ void Main::readSettings(bool _skipGeometry)
}
}
ethereum()->setAddress(m_myKeys.back().address());
m_server->setAccounts(keysAsVector(m_myKeys));
}
{
@ -868,18 +892,18 @@ void Main::refreshPending()
ui->transactionQueue->clear();
for (Transaction const& t: ethereum()->pending())
{
QString s = t.receiveAddress ?
QString s = t.receiveAddress() ?
QString("%2 %5> %3: %1 [%4]")
.arg(formatBalance(t.value).c_str())
.arg(formatBalance(t.value()).c_str())
.arg(render(t.safeSender()))
.arg(render(t.receiveAddress))
.arg((unsigned)t.nonce)
.arg(ethereum()->codeAt(t.receiveAddress).size() ? '*' : '-') :
.arg(render(t.receiveAddress()))
.arg((unsigned)t.nonce())
.arg(ethereum()->codeAt(t.receiveAddress()).size() ? '*' : '-') :
QString("%2 +> %3: %1 [%4]")
.arg(formatBalance(t.value).c_str())
.arg(formatBalance(t.value()).c_str())
.arg(render(t.safeSender()))
.arg(render(right160(sha3(rlpList(t.safeSender(), t.nonce)))))
.arg((unsigned)t.nonce);
.arg(render(right160(sha3(rlpList(t.safeSender(), t.nonce())))))
.arg((unsigned)t.nonce());
ui->transactionQueue->addItem(s);
}
}
@ -946,7 +970,7 @@ static bool blockMatch(string const& _f, dev::eth::BlockDetails const& _b, h256
static bool transactionMatch(string const& _f, Transaction const& _t)
{
string info = toHex(_t.receiveAddress.ref()) + " " + toHex(_t.sha3(true).ref()) + " " + toHex(_t.sha3(false).ref()) + " " + toHex(_t.sender().ref());
string info = toHex(_t.receiveAddress().ref()) + " " + toHex(_t.sha3().ref()) + " " + toHex(_t.sha3(eth::WithoutSignature).ref()) + " " + toHex(_t.sender().ref());
if (info.find(_f) != string::npos)
return true;
return false;
@ -986,18 +1010,18 @@ void Main::refreshBlockChain()
Transaction t(i.data());
if (bm || transactionMatch(filter, t))
{
QString s = t.receiveAddress ?
QString s = t.receiveAddress() ?
QString(" %2 %5> %3: %1 [%4]")
.arg(formatBalance(t.value).c_str())
.arg(formatBalance(t.value()).c_str())
.arg(render(t.safeSender()))
.arg(render(t.receiveAddress))
.arg((unsigned)t.nonce)
.arg(ethereum()->codeAt(t.receiveAddress).size() ? '*' : '-') :
.arg(render(t.receiveAddress()))
.arg((unsigned)t.nonce())
.arg(ethereum()->codeAt(t.receiveAddress()).size() ? '*' : '-') :
QString(" %2 +> %3: %1 [%4]")
.arg(formatBalance(t.value).c_str())
.arg(formatBalance(t.value()).c_str())
.arg(render(t.safeSender()))
.arg(render(right160(sha3(rlpList(t.safeSender(), t.nonce)))))
.arg((unsigned)t.nonce);
.arg(render(right160(sha3(rlpList(t.safeSender(), t.nonce())))))
.arg((unsigned)t.nonce());
QListWidgetItem* txItem = new QListWidgetItem(s, ui->blocks);
auto hba = QByteArray((char const*)h.data(), h.size);
txItem->setData(Qt::UserRole, hba);
@ -1144,26 +1168,26 @@ void Main::on_transactionQueue_currentItemChanged()
{
Transaction tx(ethereum()->pending()[i]);
auto ss = tx.safeSender();
h256 th = sha3(rlpList(ss, tx.nonce));
h256 th = sha3(rlpList(ss, tx.nonce()));
s << "<h3>" << th << "</h3>";
s << "From: <b>" << pretty(ss).toStdString() << "</b> " << ss;
if (tx.isCreation())
s << "<br/>Creates: <b>" << pretty(right160(th)).toStdString() << "</b> " << right160(th);
else
s << "<br/>To: <b>" << pretty(tx.receiveAddress).toStdString() << "</b> " << tx.receiveAddress;
s << "<br/>Value: <b>" << formatBalance(tx.value) << "</b>";
s << "&nbsp;&emsp;&nbsp;#<b>" << tx.nonce << "</b>";
s << "<br/>Gas price: <b>" << formatBalance(tx.gasPrice) << "</b>";
s << "<br/>Gas: <b>" << tx.gas << "</b>";
s << "<br/>To: <b>" << pretty(tx.receiveAddress()).toStdString() << "</b> " << tx.receiveAddress();
s << "<br/>Value: <b>" << formatBalance(tx.value()) << "</b>";
s << "&nbsp;&emsp;&nbsp;#<b>" << tx.nonce() << "</b>";
s << "<br/>Gas price: <b>" << formatBalance(tx.gasPrice()) << "</b>";
s << "<br/>Gas: <b>" << tx.gas() << "</b>";
if (tx.isCreation())
{
if (tx.data.size())
s << "<h4>Code</h4>" << disassemble(tx.data);
if (tx.data().size())
s << "<h4>Code</h4>" << disassemble(tx.data());
}
else
{
if (tx.data.size())
s << dev::memDump(tx.data, 16, true);
if (tx.data().size())
s << dev::memDump(tx.data(), 16, true);
}
s << "<hr/>";
@ -1238,6 +1262,13 @@ void Main::on_blocks_currentItemChanged()
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>";
for (auto u: block[2])
{
BlockInfo uncle = BlockInfo::fromHeader(u.data());
s << "<br/><span style=\"margin-left: 2em\">&nbsp;</span>Hash: <b>" << uncle.hash << "</b>";
s << "<br/><span style=\"margin-left: 2em\">&nbsp;</span>Parent: <b>" << uncle.parentHash << "</b>";
s << "<br/><span style=\"margin-left: 2em\">&nbsp;</span>Number: <b>" << uncle.number << "</b>";
}
if (info.parentHash)
s << "<br/>Pre: <b>" << BlockInfo(ethereum()->blockChain().block(info.parentHash)).stateRoot << "</b>";
else
@ -1251,31 +1282,31 @@ void Main::on_blocks_currentItemChanged()
unsigned txi = item->data(Qt::UserRole + 1).toInt();
Transaction tx(block[1][txi].data());
auto ss = tx.safeSender();
h256 th = sha3(rlpList(ss, tx.nonce));
h256 th = sha3(rlpList(ss, tx.nonce()));
s << "<h3>" << th << "</h3>";
s << "<h4>" << h << "[<b>" << txi << "</b>]</h4>";
s << "<br/>From: <b>" << pretty(ss).toHtmlEscaped().toStdString() << "</b> " << ss;
if (tx.isCreation())
s << "<br/>Creates: <b>" << pretty(right160(th)).toHtmlEscaped().toStdString() << "</b> " << right160(th);
else
s << "<br/>To: <b>" << pretty(tx.receiveAddress).toHtmlEscaped().toStdString() << "</b> " << tx.receiveAddress;
s << "<br/>Value: <b>" << formatBalance(tx.value) << "</b>";
s << "&nbsp;&emsp;&nbsp;#<b>" << tx.nonce << "</b>";
s << "<br/>Gas price: <b>" << formatBalance(tx.gasPrice) << "</b>";
s << "<br/>Gas: <b>" << tx.gas << "</b>";
s << "<br/>V: <b>" << hex << nouppercase << (int)tx.vrs.v << "</b>";
s << "<br/>R: <b>" << hex << nouppercase << tx.vrs.r << "</b>";
s << "<br/>S: <b>" << hex << nouppercase << tx.vrs.s << "</b>";
s << "<br/>Msg: <b>" << tx.sha3(false) << "</b>";
s << "<br/>To: <b>" << pretty(tx.receiveAddress()).toHtmlEscaped().toStdString() << "</b> " << tx.receiveAddress();
s << "<br/>Value: <b>" << formatBalance(tx.value()) << "</b>";
s << "&nbsp;&emsp;&nbsp;#<b>" << tx.nonce() << "</b>";
s << "<br/>Gas price: <b>" << formatBalance(tx.gasPrice()) << "</b>";
s << "<br/>Gas: <b>" << tx.gas() << "</b>";
s << "<br/>V: <b>" << hex << nouppercase << (int)tx.signature().v << " + 27</b>";
s << "<br/>R: <b>" << hex << nouppercase << tx.signature().r << "</b>";
s << "<br/>S: <b>" << hex << nouppercase << tx.signature().s << "</b>";
s << "<br/>Msg: <b>" << tx.sha3(eth::WithoutSignature) << "</b>";
if (tx.isCreation())
{
if (tx.data.size())
s << "<h4>Code</h4>" << disassemble(tx.data);
if (tx.data().size())
s << "<h4>Code</h4>" << disassemble(tx.data());
}
else
{
if (tx.data.size())
s << dev::memDump(tx.data, 16, true);
if (tx.data().size())
s << dev::memDump(tx.data(), 16, true);
}
s << renderDiff(ethereum()->diff(txi, h));
ui->debugCurrent->setEnabled(true);
@ -1344,7 +1375,7 @@ void Main::populateDebugger(dev::bytesConstRef _r)
debugFinished();
vector<WorldState const*> levels;
m_codes.clear();
bytesConstRef lastExtCode;
bytes lastExtCode;
bytesConstRef lastData;
h256 lastHash;
h256 lastDataHash;
@ -1357,7 +1388,7 @@ void Main::populateDebugger(dev::bytesConstRef _r)
lastExtCode = ext.code;
lastHash = sha3(lastExtCode);
if (!m_codes.count(lastHash))
m_codes[lastHash] = ext.code.toBytes();
m_codes[lastHash] = ext.code;
}
if (ext.data != lastData)
{
@ -1564,10 +1595,29 @@ void Main::on_data_textChanged()
string src = ui->data->toPlainText().toStdString();
vector<string> errors;
QString lll;
QString solidity;
if (src.find_first_not_of("1234567890abcdefABCDEF") == string::npos && src.size() % 2 == 0)
{
m_data = fromHex(src);
}
else if (src.substr(0, 8) == "contract") // improve this heuristic
{
shared_ptr<solidity::Scanner> scanner = make_shared<solidity::Scanner>();
try
{
m_data = dev::solidity::CompilerStack::compile(src, scanner, m_enableOptimizer);
}
catch (dev::Exception const& exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", *scanner);
solidity = "<h4>Solidity</h4><pre>" + QString::fromStdString(error.str()).toHtmlEscaped() + "</pre>";
}
catch (...)
{
solidity = "<h4>Solidity</h4><pre>Uncaught exception.</pre>";
}
}
else
{
m_data = dev::eth::compileLLL(src, m_enableOptimizer, &errors);
@ -1602,7 +1652,7 @@ void Main::on_data_textChanged()
for (auto const& i: errors)
errs.append("<div style=\"border-left: 6px solid #c00; margin-top: 2px\">" + QString::fromStdString(i).toHtmlEscaped() + "</div>");
}
ui->code->setHtml(errs + lll + "<h4>Code</h4>" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped());
ui->code->setHtml(errs + lll + solidity + "<h4>Code</h4>" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped());
ui->gas->setMinimum((qint64)Client::txGas(m_data.size(), 0));
if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas);
@ -1772,6 +1822,7 @@ void Main::on_send_clicked()
void Main::keysChanged()
{
onBalancesChange();
m_server->setAccounts(keysAsVector(m_myKeys));
}
void Main::on_debug_clicked()
@ -1786,14 +1837,9 @@ void Main::on_debug_clicked()
Secret s = i.secret();
m_executiveState = ethereum()->postState();
m_currentExecution = unique_ptr<Executive>(new Executive(m_executiveState));
Transaction t;
t.nonce = m_executiveState.transactionsFrom(dev::toAddress(s));
t.value = value();
t.gasPrice = gasPrice();
t.gas = ui->gas->value();
t.data = m_data;
t.receiveAddress = isCreation() ? Address() : fromString(ui->destination->currentText());
t.sign(s);
Transaction t = isCreation() ?
Transaction(value(), gasPrice(), ui->gas->value(), m_data, m_executiveState.transactionsFrom(dev::toAddress(s)), s) :
Transaction(value(), gasPrice(), ui->gas->value(), fromString(ui->destination->currentText()), m_data, m_executiveState.transactionsFrom(dev::toAddress(s)), s);
auto r = t.rlp();
populateDebugger(&r);
m_currentExecution.reset();
@ -2150,20 +2196,6 @@ void Main::on_post_clicked()
whisper()->inject(m.seal(from, topicFromText(ui->shhTopic->toPlainText()), ui->shhTtl->value(), ui->shhWork->value()));
}
void Main::on_newIdentity_triggered()
{
KeyPair kp = KeyPair::create();
m_myIdentities.append(kp);
m_server->setIdentities(keysAsVector(owned()));
}
void Main::refreshWhisper()
{
ui->shhFrom->clear();
for (auto i: m_server ->ids())
ui->shhFrom->addItem(QString::fromStdString(toHex(i.first.ref())));
}
void Main::refreshWhispers()
{
ui->whispers->clear();
@ -2188,7 +2220,7 @@ void Main::refreshWhispers()
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);
QString item = QString("[%1 - %2s] *%3 %5 %4").arg(t).arg(e.ttl()).arg(e.workProved()).arg(toString(e.topics()).c_str()).arg(msg);
ui->whispers->addItem(item);
}
}

6
alethzero/MainWin.h

@ -47,7 +47,7 @@ class MessageFilter;
}}
class QQuickView;
class WebThreeStubServer;
class OurWebThreeStubServer;
struct WorldState
{
@ -255,7 +255,7 @@ private:
QString m_logHistory;
bool m_logChanged = true;
QWebThreeConnector m_qwebConnector;
std::unique_ptr<WebThreeStubServer> m_server;
QWebThreeConnector* m_qwebConnector;
std::unique_ptr<OurWebThreeStubServer> m_server;
QWebThree* m_qweb = nullptr;
};

36
alethzero/OurWebThreeStubServer.cpp

@ -0,0 +1,36 @@
/*
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 OurWebThreeStubServer.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "OurWebThreeStubServer.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector* _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts):
WebThreeStubServer(_conn, _web3, _accounts)
{}
std::string OurWebThreeStubServer::shh_newIdentity()
{
dev::KeyPair kp = dev::KeyPair::create();
emit onNewId(QString::fromStdString(toJS(kp.sec())));
return toJS(kp.pub());
}

38
alethzero/OurWebThreeStubServer.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 OurWebThreeStubServer.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <QtCore/QObject>
#include <libdevcore/CommonJS.h>
#include <libdevcrypto/Common.h>
#include <libweb3jsonrpc/WebThreeStubServer.h>
class OurWebThreeStubServer: public QObject, public WebThreeStubServer
{
Q_OBJECT
public:
OurWebThreeStubServer(jsonrpc::AbstractServerConnector* _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts);
virtual std::string shh_newIdentity() override;
signals:
void onNewId(QString _s);
};

2
eth/main.cpp

@ -32,7 +32,7 @@
#include <libweb3jsonrpc/CorsHttpServer.h>
#endif
#include <libdevcrypto/FileSystem.h>
#include <libevmface/Instruction.h>
#include <libevmcore/Instruction.h>
#include <libevm/VM.h>
#include <libethereum/All.h>
#include <libwebthree/WebThree.h>

96
exp/main.cpp

@ -36,7 +36,7 @@ using namespace dev::eth;
using namespace dev::p2p;
using namespace dev::shh;
#if 0
#if 1
int main()
{
DownloadMan man;
@ -44,18 +44,19 @@ int main()
DownloadSub s1(man);
DownloadSub s2(man);
man.resetToChain(h256s({u256(0), u256(1), u256(2), u256(3), u256(4), u256(5), u256(6), u256(7), u256(8)}));
cnote << s0.nextFetch(2);
cnote << s1.nextFetch(2);
cnote << s2.nextFetch(2);
s0.noteBlock(u256(0));
assert((s0.nextFetch(2) == h256Set{(u256)7, (u256)8}));
assert((s1.nextFetch(2) == h256Set{(u256)5, (u256)6}));
assert((s2.nextFetch(2) == h256Set{(u256)3, (u256)4}));
s0.noteBlock(u256(8));
s0.doneFetch();
cnote << s0.nextFetch(2);
s1.noteBlock(u256(2));
s1.noteBlock(u256(3));
assert((s0.nextFetch(2) == h256Set{(u256)2, (u256)7}));
s1.noteBlock(u256(6));
s1.noteBlock(u256(5));
s1.doneFetch();
cnote << s1.nextFetch(2);
s0.doneFetch();
cnote << s0.nextFetch(2);
assert((s1.nextFetch(2) == h256Set{(u256)0, (u256)1}));
s0.doneFetch(); // TODO: check exact semantics of doneFetch & nextFetch. Not sure if they're right -> doneFetch calls resetFetch which kills all the info of past fetches.
cdebug << s0.nextFetch(2);
assert((s0.nextFetch(2) == h256Set{(u256)3, (u256)4}));
/* RangeMask<unsigned> m(0, 100);
cnote << m;
@ -73,49 +74,70 @@ int main()
}
#endif
int main(int argc, char** argv)
/*int other(bool& o_started)
{
g_logVerbosity = 20;
short listenPort = 30303;
string remoteHost;
short remotePort = 30303;
setThreadName("other");
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
if (arg == "-l" && i + 1 < argc)
listenPort = (short)atoi(argv[++i]);
else if (arg == "-r" && i + 1 < argc)
remoteHost = argv[++i];
else if (arg == "-p" && i + 1 < argc)
remotePort = (short)atoi(argv[++i]);
else
remoteHost = argv[i];
}
short listenPort = 30300;
Host ph("Test", NetworkPreferences(listenPort, "", false, true));
auto wh = ph.registerCapability(new WhisperHost());
ph.start();
if (!remoteHost.empty())
ph.connect(remoteHost, remotePort);
o_started = true;
/// Only interested in odd packets
auto w = wh->installWatch(BuildTopicMask()("odd"));
KeyPair us = KeyPair::create();
for (int i = 0; ; ++i)
unsigned last = 0;
unsigned total = 0;
for (int i = 0; i < 100 && last < 81; ++i)
{
wh->post(us.sec(), RLPStream().append(i * i).out(), BuildTopic(i)(i % 2 ? "odd" : "even"));
for (auto i: wh->checkWatch(w))
{
Message msg = wh->envelope(i).open();
last = RLP(msg.payload()).toInt<unsigned>();
cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>();
total += last;
}
this_thread::sleep_for(chrono::seconds(1));
this_thread::sleep_for(chrono::milliseconds(50));
}
return 0;
return total;
}
int main(int, char**)
{
g_logVerbosity = 0;
bool started = false;
unsigned result;
std::thread listener([&](){ return (result = other(started)); });
while (!started)
this_thread::sleep_for(chrono::milliseconds(50));
short listenPort = 30303;
string remoteHost = "127.0.0.1";
short remotePort = 30300;
Host ph("Test", NetworkPreferences(listenPort, "", false, true));
auto wh = ph.registerCapability(new WhisperHost());
ph.start();
if (!remoteHost.empty())
ph.connect(remoteHost, remotePort);
KeyPair us = KeyPair::create();
for (int i = 0; i < 10; ++i)
{
wh->post(us.sec(), RLPStream().append(i * i).out(), BuildTopic(i)(i % 2 ? "odd" : "even"));
this_thread::sleep_for(chrono::milliseconds(250));
}
listener.join();
assert(result == 1 + 9 + 25 + 49 + 81);
return 0;
}*/

2
iethxi/MainWin.cpp

@ -9,7 +9,7 @@
#include <QtCore/QtCore>
#include <libethcore/FileSystem.h>
#include <libethcore/Dagger.h>
#include <libevmface/Instruction.h>
#include <libevmcore/Instruction.h>
#include <libethereum/Client.h>
#include <libethereum/EthereumHost.h>
#include "BuildInfo.h"

2
libdevcore/Common.cpp

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

3
libdevcore/CommonJS.cpp

@ -35,8 +35,7 @@ bytes jsToBytes(std::string const& _s)
// Decimal
return toCompactBigEndian(bigint(_s));
else
// Binary
return asBytes(_s);
return bytes();
}
std::string jsPadded(std::string const& _s, unsigned _l, unsigned _r)

4
libdevcore/Exceptions.h

@ -24,7 +24,6 @@
#include <exception>
#include <boost/exception/all.hpp>
#include <boost/throw_exception.hpp>
#include <libdevcrypto/Common.h>
#include "CommonData.h"
#include "FixedHash.h"
@ -46,4 +45,7 @@ struct FileError: virtual Exception {};
typedef boost::error_info<struct tag_invalidSymbol, char> errinfo_invalidSymbol;
typedef boost::error_info<struct tag_address, std::string> errinfo_wrongAddress;
typedef boost::error_info<struct tag_comment, std::string> errinfo_comment;
typedef boost::error_info<struct tag_required, size_t> errinfo_required;
typedef boost::error_info<struct tag_got, size_t> errinfo_got;
typedef boost::tuple<errinfo_required, errinfo_got> RequirementError;
}

5
libdevcore/FixedHash.h

@ -163,6 +163,11 @@ public:
return (*this |= _h.template nbloom<P, N>());
}
template <unsigned P, unsigned M> inline bool containsBloom(FixedHash<M> const& _h)
{
return contains(_h.template nbloom<P, N>());
}
template <unsigned P, unsigned M> inline FixedHash<M> nbloom() const
{
static const unsigned c_bloomBits = M * 8;

1
libdevcore/Log.h

@ -27,6 +27,7 @@
#include <chrono>
#include <boost/thread.hpp>
#include "vector_ref.h"
#include "CommonIO.h"
namespace dev
{

6
libdevcore/RLP.h

@ -159,7 +159,11 @@ public:
/// Best-effort conversion operators.
explicit operator std::string() const { return toString(); }
explicit operator RLPs() const { return toList(); }
explicit operator byte() const { return toInt<byte>(); }
explicit operator uint8_t() const { return toInt<uint8_t>(); }
explicit operator uint16_t() const { return toInt<uint16_t>(); }
explicit operator uint32_t() const { return toInt<uint32_t>(); }
explicit operator uint64_t() const { return toInt<uint64_t>(); }
explicit operator u160() const { return toInt<u160>(); }
explicit operator u256() const { return toInt<u256>(); }
explicit operator bigint() const { return toInt<bigint>(); }
template <unsigned _N> explicit operator FixedHash<_N>() const { return toHash<FixedHash<_N>>(); }

145
libdevcrypto/Common.cpp

@ -14,53 +14,34 @@
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 CommonEth.cpp
/** @file Common.cpp
* @author Gav Wood <i@gavwood.com>
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
#include "Common.h"
#include <random>
#include <secp256k1/secp256k1.h>
#include <chrono>
#include <mutex>
#include "EC.h"
#include "SHA3.h"
#include "FileSystem.h"
#include "Common.h"
using namespace std;
using namespace dev;
using namespace crypto;
//#define ETH_ADDRESS_DEBUG 1
Address dev::toAddress(Secret _private)
Address dev::toAddress(Secret _secret)
{
secp256k1_start();
byte pubkey[65];
int pubkeylen = 65;
int ok = secp256k1_ecdsa_seckey_verify(_private.data());
if (!ok)
return Address();
ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, _private.data(), 0);
if (asserts(pubkeylen == 65))
return Address();
if (!ok)
return Address();
ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65);
if (!ok)
return Address();
auto ret = right160(dev::sha3(bytesConstRef(&(pubkey[1]), 64)));
#if ETH_ADDRESS_DEBUG
cout << "---- ADDRESS -------------------------------" << endl;
cout << "SEC: " << _private << endl;
cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl;
cout << "ADR: " << ret << endl;
#endif
return ret;
return KeyPair(_secret).address();
}
KeyPair KeyPair::create()
{
secp256k1_start();
static std::mt19937_64 s_eng(time(0));
std::uniform_int_distribution<uint16_t> d(0, 255);
static mt19937_64 s_eng(time(0) + chrono::high_resolution_clock::now().time_since_epoch().count());
uniform_int_distribution<uint16_t> d(0, 255);
for (int i = 0; i < 100; ++i)
{
@ -78,24 +59,10 @@ KeyPair KeyPair::create()
KeyPair::KeyPair(h256 _sec):
m_secret(_sec)
{
int ok = secp256k1_ecdsa_seckey_verify(m_secret.data());
if (!ok)
return;
byte pubkey[65];
int pubkeylen = 65;
ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, m_secret.data(), 0);
if (!ok || pubkeylen != 65)
return;
ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65);
if (!ok)
return;
m_secret = m_secret;
memcpy(m_public.data(), &(pubkey[1]), 64);
m_address = right160(dev::sha3(bytesConstRef(&(pubkey[1]), 64)));
toPublic(m_secret, m_public);
if (verifySecret(m_secret, m_public))
m_address = right160(dev::sha3(m_public.ref()));
#if ETH_ADDRESS_DEBUG
cout << "---- ADDRESS -------------------------------" << endl;
cout << "SEC: " << m_secret << endl;
@ -128,51 +95,55 @@ bool dev::decrypt(Secret _k, bytesConstRef _cipher, bytes& o_plaintext)
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;
return crypto::recover(_sig, _message.ref());
}
inline h256 kFromMessage(h256 _msg, h256 _priv)
Signature dev::sign(Secret _k, h256 _hash)
{
return _msg ^ _priv;
return crypto::sign(_k, _hash);
}
Signature dev::sign(Secret _k, h256 _message)
bool dev::verify(Public _p, Signature _s, h256 _hash)
{
int v = 0;
secp256k1_start();
SignatureStruct ret;
h256 nonce = kFromMessage(_message, _k);
return crypto::verify(_p, _s, bytesConstRef(_hash.data(), 32), true);
}
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
h256 Nonce::get(bool _commit)
{
// todo: atomic efface bit, periodic save, kdf, rr, rng
// todo: encrypt
static h256 s_seed;
static string s_seedFile(getDataDir() + "/seed");
static mutex s_x;
lock_guard<mutex> l(s_x);
if (!s_seed)
{
static Nonce s_nonce;
bytes b = contents(s_seedFile);
if (b.size() == 32)
memcpy(s_seed.data(), b.data(), 32);
else
{
// todo: replace w/entropy from user and system
std::mt19937_64 s_eng(time(0));
std::uniform_int_distribution<uint16_t> d(0, 255);
for (unsigned i = 0; i < 32; ++i)
s_seed[i] = (byte)d(s_eng);
}
if (!s_seed)
BOOST_THROW_EXCEPTION(InvalidState());
// prevent seed reuse if process terminates abnormally
writeFile(s_seedFile, bytes());
}
h256 prev(s_seed);
sha3(prev.ref(), s_seed.ref());
if (_commit)
writeFile(s_seedFile, s_seed.asBytes());
return std::move(s_seed);
}
ret.v = v;
return *(Signature const*)&ret;
Nonce::~Nonce()
{
Nonce::get(true);
}

34
libdevcrypto/Common.h

@ -14,8 +14,9 @@
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 CommonEth.h
/** @file Common.h
* @author Gav Wood <i@gavwood.com>
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* Ethereum-specific data structures & algorithms.
@ -25,6 +26,7 @@
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
#include <libdevcore/Exceptions.h>
namespace dev
{
@ -50,6 +52,9 @@ using Address = h160;
/// A vector of Ethereum addresses.
using Addresses = h160s;
/// A vector of Ethereum addresses.
using AddressSet = std::set<h160>;
/// A vector of secrets.
using Secrets = h256s;
@ -59,15 +64,18 @@ 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);
/// Recovers Public key from signed message hash.
Public recover(Signature _sig, h256 _hash);
/// Returns siganture of message hash.
Signature sign(Secret _k, h256 _message);
Signature sign(Secret _k, h256 _hash);
/// Verify signature.
bool verify(Public _k, Signature _s, h256 _hash);
/// Simple class that represents a "key pair".
/// All of the data of the class can be regenerated from the secret key (m_secret) alone.
@ -107,4 +115,20 @@ private:
Address m_address;
};
namespace crypto
{
struct InvalidState: public dev::Exception {};
/**
* @brief Generator for nonce material
*/
struct Nonce
{
static h256 get(bool _commit = false);
private:
Nonce() {}
~Nonce();
};
}
}

47
libdevcrypto/CryptoPP.cpp

@ -25,50 +25,21 @@ 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));
}
/// Integer and Point Conversion:
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)
void pp::exportPublicKey(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.");
secp256k1Params.GetCurve().EncodePoint(prefixedKey.data(), _k.GetPublicElement(), false);
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)
void pp::exponentToPublic(Integer const& _e, Public& _p)
{
_k.GetPrivateExponent().Encode(_s.data(), Secret::size);
}
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> pk;
pk.Initialize(secp256k1Params, secp256k1Params.ExponentiateBase(_e));
pp::exportPublicKey(pk, _p);
}

42
libdevcrypto/CryptoPP.h

@ -18,7 +18,7 @@
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* CryptoPP headers and helper methods
* CryptoPP headers and primitive helper methods
*/
#pragma once
@ -45,7 +45,7 @@
#include <cryptopp/files.h>
#include <cryptopp/osrng.h>
#include <cryptopp/oids.h>
#include <secp256k1/secp256k1.h>
#include <cryptopp/dsa.h>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include "Common.h"
@ -54,30 +54,34 @@ namespace dev
{
namespace crypto
{
namespace pp
{
using namespace CryptoPP;
/// CryptoPP random number pool
static CryptoPP::AutoSeededRandomPool PRNG;
/// CryptoPP EC Cruve
static const CryptoPP::OID secp256k1Curve = CryptoPP::ASN1::secp256k1();
static const CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP> secp256k1Params(secp256k1Curve);
static ECP::Point publicToPoint(Public const& _p) { Integer x(_p.data(), 32); Integer y(_p.data() + 32, 32); return std::move(ECP::Point(x,y)); }
static Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s.data(), Secret::size)); }
/// 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);
void exportPublicKey(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p);
/// Conversion from bytes to cryptopp exponent
CryptoPP::Integer ExponentFromSecret(Secret const& _s);
static void exportPrivateKey(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s) { _k.GetPrivateExponent().Encode(_s.data(), Secret::size); }
/// Conversion from cryptopp exponent Integer to bytes
void PublicFromExponent(CryptoPP::Integer const& _k, Public& _s);
void exponentToPublic(Integer const& _e, Public& _p);
/// Conversion from cryptopp public key to bytes
void PublicFromDL_PublicKey_EC(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p);
template <class T>
void initializeDLScheme(Secret const& _s, T& io_operator) { io_operator.AccessKey().Initialize(pp::secp256k1Params, secretToExponent(_s)); }
/// Conversion from cryptopp private key to bytes
void SecretFromDL_PrivateKey_EC(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s);
template <class T>
void initializeDLScheme(Public const& _p, T& io_operator) { io_operator.AccessKey().Initialize(pp::secp256k1Params, publicToPoint(_p)); }
}
}

167
libdevcrypto/EC.cpp

@ -19,53 +19,65 @@
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* Shared EC classes and functions.
* ECDSA, ECIES
*/
#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"
#pragma warning(pop)
#pragma GCC diagnostic pop
#include <secp256k1/secp256k1.h>
#include "CryptoPP.h"
#include "SHA3.h"
#include "SHA3MAC.h"
#include "EC.h"
// CryptoPP and dev conflict so dev and pp namespace are used explicitly
static_assert(dev::Secret::size == 32, "Secret key must be 32 bytes.");
static_assert(dev::Public::size == 64, "Public key must be 64 bytes.");
static_assert(dev::Signature::size == 65, "Signature must be 65 bytes.");
using namespace std;
using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
using namespace pp;
void crypto::toPublic(Secret const& _s, Public& o_public)
{
exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public);
}
void dev::crypto::encrypt(Public const& _key, bytes& io_cipher)
h256 crypto::kdf(Secret const& _priv, h256 const& _hash)
{
// H(H(r||k)^h)
h256 s;
sha3mac(Nonce::get().ref(), _priv.ref(), s.ref());
s ^= _hash;
sha3(s.ref(), s.ref());
if (!s || !_hash || !_priv)
BOOST_THROW_EXCEPTION(InvalidState());
return std::move(s);
}
void crypto::encrypt(Public const& _k, bytes& io_cipher)
{
ECIES<ECP>::Encryptor e;
e.AccessKey().AccessGroupParameters().Initialize(pp::secp256k1());
e.AccessKey().SetPublicElement(pp::PointFromPublic(_key));
initializeDLScheme(_k, e);
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());
// todo: use StringSource with io_cipher as input and output.
e.Encrypt(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)
void 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));
initializeDLScheme(_k, d);
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());
// todo: use StringSource with io_text as input and output.
DecodingResult r = d.Decrypt(PRNG, io_text.data(), clen, p.data());
if (!r.isValidCoding)
{
io_text.clear();
@ -75,3 +87,114 @@ void dev::crypto::decrypt(Secret const& _k, bytes& io_text)
io_text = std::move(p);
}
Signature crypto::sign(Secret const& _k, bytesConstRef _message)
{
return crypto::sign(_k, sha3(_message));
}
Signature crypto::sign(Secret const& _key, h256 const& _hash)
{
ECDSA<ECP,SHA3_256>::Signer signer;
initializeDLScheme(_key, signer);
Integer const& q = secp256k1Params.GetGroupOrder();
Integer const& qs = secp256k1Params.GetSubgroupOrder();
Integer e(_hash.asBytes().data(), 32);
Integer k(kdf(_key, _hash).data(), 32);
if (k == 0)
BOOST_THROW_EXCEPTION(InvalidState());
k = 1 + (k % (qs - 1));
ECP::Point rp = secp256k1Params.ExponentiateBase(k);
Integer r = secp256k1Params.ConvertElementToInteger(rp);
int recid = ((r >= q) ? 2 : 0) | (rp.y.IsOdd() ? 1 : 0);
Integer kInv = k.InverseMod(q);
Integer s = (kInv * (Integer(_key.asBytes().data(), 32)*r + e)) % q;
assert(!!r && !!s);
if (s > qs)
{
s = q - s;
if (recid)
recid ^= 1;
}
Signature sig;
r.Encode(sig.data(), 32);
s.Encode(sig.data() + 32, 32);
sig[64] = recid;
return sig;
}
bool crypto::verify(Signature const& _signature, bytesConstRef _message)
{
return crypto::verify(crypto::recover(_signature, _message), _signature, _message);
}
bool crypto::verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed)
{
static size_t derMaxEncodingLength = 72;
if (_hashed)
{
assert(_message.size() == 32);
byte encpub[65] = {0x04};
memcpy(&encpub[1], _p.data(), 64);
byte dersig[derMaxEncodingLength];
size_t cssz = DSAConvertSignatureFormat(dersig, derMaxEncodingLength, DSA_DER, _sig.data(), 64, DSA_P1363);
assert(cssz <= derMaxEncodingLength);
return (1 == secp256k1_ecdsa_verify(_message.data(), _message.size(), dersig, cssz, encpub, 65));
}
ECDSA<ECP, SHA3_256>::Verifier verifier;
initializeDLScheme(_p, verifier);
return verifier.VerifyMessage(_message.data(), _message.size(), _sig.data(), sizeof(Signature) - 1);
}
Public crypto::recover(Signature _signature, bytesConstRef _message)
{
secp256k1_start();
int pubkeylen = 65;
byte pubkey[pubkeylen];
if (!secp256k1_ecdsa_recover_compact(_message.data(), 32, _signature.data(), pubkey, &pubkeylen, 0, (int)_signature[64]))
return Public();
#if ETH_CRYPTO_TRACE
h256* sig = (h256 const*)_signature.data();
cout << "---- RECOVER -------------------------------" << endl;
cout << "MSG: " << _message << endl;
cout << "R S V: " << sig[0] << " " << sig[1] << " " << (int)(_signature[64] - 27) << "+27" << endl;
cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl;
#endif
Public ret;
memcpy(&ret, &(pubkey[1]), sizeof(Public));
return ret;
}
bool crypto::verifySecret(Secret const& _s, Public const& _p)
{
secp256k1_start();
int ok = secp256k1_ecdsa_seckey_verify(_s.data());
if (!ok)
return false;
int pubkeylen = 65;
byte pubkey[pubkeylen];
ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, _s.data(), 0);
if (!ok || pubkeylen != 65)
return false;
ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65);
if (!ok)
return false;
for (int i = 0; i < 32; i++)
if (pubkey[i+1]!=_p[i])
return false;
return true;
}

23
libdevcrypto/EC.h

@ -18,7 +18,7 @@
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* Shared EC classes and functions.
* ECDSA, ECIES
*/
#pragma once
@ -30,12 +30,33 @@ namespace dev
namespace crypto
{
void toPublic(Secret const& _s, Public& o_public);
h256 kdf(Secret const& _priv, h256 const& _hash);
/// Encrypts text (in place).
void encrypt(Public const& _k, bytes& io_cipher);
/// Decrypts text (in place).
void decrypt(Secret const& _k, bytes& io_text);
/// Returns siganture of message.
Signature sign(Secret const& _k, bytesConstRef _message);
/// Returns compact siganture of message hash.
Signature sign(Secret const& _k, h256 const& _hash);
/// Verify compact signature (public key is extracted from message).
bool verify(Signature const& _signature, bytesConstRef _message);
/// Verify signature.
bool verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed = false);
/// Recovers public key from compact signature. Uses libsecp256k1.
Public recover(Signature _signature, bytesConstRef _message);
bool verifySecret(Secret const& _s, Public const& _p);
}
}

1
libdevcrypto/FileSystem.h

@ -24,6 +24,7 @@
#pragma once
#include <string>
#include <libdevcore/CommonIO.h>
namespace dev
{

3
libdevcrypto/SHA3.h

@ -56,6 +56,9 @@ inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef((bytes*)&_inpu
/// Calculate SHA3-256 hash of the given input (presented as a binary-filled string), returning as a 256-bit hash.
inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)); }
/// Calculate SHA3-256 hash of the given input (presented as a FixedHash), returns a 256-bit hash.
template<unsigned N> inline h256 sha3(FixedHash<N> const& _input) { return sha3(_input.ref()); }
extern h256 EmptySHA3;
extern h256 EmptyListSHA3;

3
libdevcrypto/SHA3MAC.cpp

@ -28,9 +28,10 @@ using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output)
void crypto::sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output)
{
CryptoPP::SHA3_256 ctx;
assert(_secret.size() > 0);
ctx.Update((byte*)_secret.data(), _secret.size());
ctx.Update((byte*)_plain.data(), _plain.size());
assert(_output.size() >= 32);

4
libethcore/CommonEth.cpp

@ -34,8 +34,8 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 38;
const unsigned c_databaseVersion = 3;
const unsigned c_protocolVersion = 39;
const unsigned c_databaseVersion = 4;
static const vector<pair<u256, string>> g_units =
{

42
libethereum/Client.cpp

@ -316,19 +316,13 @@ void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _
{
startWorking();
Transaction t;
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
u256 n;
{
ReadGuard l(x_stateDB);
t.nonce = m_postMine.transactionsFrom(toAddress(_secret));
n = m_postMine.transactionsFrom(toAddress(_secret));
}
t.value = _value;
t.gasPrice = _gasPrice;
t.gas = _gas;
t.type = Transaction::MessageCall;
t.receiveAddress = _dest;
t.data = _data;
t.sign(_secret);
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
cnote << "New transaction " << t;
m_tq.attemptImport(t.rlp());
}
@ -338,22 +332,16 @@ bytes Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _dat
bytes out;
try
{
u256 n;
State temp;
Transaction t;
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
{
ReadGuard l(x_stateDB);
temp = m_postMine;
t.nonce = temp.transactionsFrom(toAddress(_secret));
n = temp.transactionsFrom(toAddress(_secret));
}
t.value = _value;
t.gasPrice = _gasPrice;
t.gas = _gas;
t.type = Transaction::ContractCreation;
t.receiveAddress = _dest;
t.data = _data;
t.sign(_secret);
u256 gasUsed = temp.execute(t.data, &out, false);
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
u256 gasUsed = temp.execute(t.data(), &out, false);
(void)gasUsed; // TODO: do something with gasused which it returns.
}
catch (...)
@ -367,21 +355,15 @@ Address Client::transact(Secret _secret, u256 _endowment, bytes const& _init, u2
{
startWorking();
Transaction t;
u256 n;
{
ReadGuard l(x_stateDB);
t.nonce = m_postMine.transactionsFrom(toAddress(_secret));
n = m_postMine.transactionsFrom(toAddress(_secret));
}
t.value = _endowment;
t.gasPrice = _gasPrice;
t.gas = _gas;
t.type = Transaction::ContractCreation;
t.receiveAddress = Address();
t.data = _init;
t.sign(_secret);
Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
cnote << "New transaction " << t;
m_tq.attemptImport(t.rlp());
return right160(sha3(rlpList(t.sender(), t.nonce)));
return right160(sha3(rlpList(t.sender(), t.nonce())));
}
void Client::inject(bytesConstRef _rlp)

55
libethereum/Executive.cpp

@ -40,7 +40,7 @@ Executive::~Executive()
u256 Executive::gasUsed() const
{
return m_t.gas - m_endGas;
return m_t.gas() - m_endGas;
}
bool Executive::setup(bytesConstRef _rlp)
@ -52,63 +52,63 @@ bool Executive::setup(bytesConstRef _rlp)
// Avoid invalid transactions.
auto nonceReq = m_s.transactionsFrom(m_sender);
if (m_t.nonce != nonceReq)
if (m_t.nonce() != nonceReq)
{
clog(StateDetail) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce;
BOOST_THROW_EXCEPTION(InvalidNonce(nonceReq, m_t.nonce));
clog(StateDetail) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce();
BOOST_THROW_EXCEPTION(InvalidNonce(nonceReq, m_t.nonce()));
}
// Don't like transactions whose gas price is too low. NOTE: this won't stay here forever - it's just until we get a proper gas price discovery protocol going.
if (m_t.gasPrice < m_s.m_currentBlock.minGasPrice)
if (m_t.gasPrice() < m_s.m_currentBlock.minGasPrice)
{
clog(StateDetail) << "Offered gas-price is too low: Require >" << m_s.m_currentBlock.minGasPrice << " Got" << m_t.gasPrice;
clog(StateDetail) << "Offered gas-price is too low: Require >" << m_s.m_currentBlock.minGasPrice << " Got" << m_t.gasPrice();
BOOST_THROW_EXCEPTION(GasPriceTooLow());
}
// Check gas cost is enough.
u256 gasCost = m_t.data.size() * c_txDataGas + c_txGas;
u256 gasCost = m_t.data().size() * c_txDataGas + c_txGas;
if (m_t.gas < gasCost)
if (m_t.gas() < gasCost)
{
clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << gasCost << " Got" << m_t.gas;
clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << gasCost << " Got" << m_t.gas();
BOOST_THROW_EXCEPTION(OutOfGas());
}
u256 cost = m_t.value + m_t.gas * m_t.gasPrice;
u256 cost = m_t.value() + m_t.gas() * m_t.gasPrice();
// Avoid unaffordable transactions.
if (m_s.balance(m_sender) < cost)
{
clog(StateDetail) << "Not enough cash: Require >" << cost << " Got" << m_s.balance(m_sender);
BOOST_THROW_EXCEPTION(NotEnoughCash());
BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError((int)cost, (int)m_s.balance(m_sender)));
}
u256 startGasUsed = m_s.gasUsed();
if (startGasUsed + m_t.gas > m_s.m_currentBlock.gasLimit)
if (startGasUsed + m_t.gas() > m_s.m_currentBlock.gasLimit)
{
clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas;
BOOST_THROW_EXCEPTION(BlockGasLimitReached());
clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas();
BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((int)(m_s.m_currentBlock.gasLimit - startGasUsed), (int)m_t.gas()));
}
// Increment associated nonce for sender.
m_s.noteSending(m_sender);
// Pay...
clog(StateDetail) << "Paying" << formatBalance(cost) << "from sender (includes" << m_t.gas << "gas at" << formatBalance(m_t.gasPrice) << ")";
clog(StateDetail) << "Paying" << formatBalance(cost) << "from sender (includes" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")";
m_s.subBalance(m_sender, cost);
if (m_ms)
{
m_ms->from = m_sender;
m_ms->to = m_t.receiveAddress;
m_ms->value = m_t.value;
m_ms->input = m_t.data;
m_ms->to = m_t.receiveAddress();
m_ms->value = m_t.value();
m_ms->input = m_t.data();
}
if (m_t.isCreation())
return create(m_sender, m_t.value, m_t.gasPrice, m_t.gas - gasCost, &m_t.data, m_sender);
return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - gasCost, &m_t.data(), m_sender);
else
return call(m_t.receiveAddress, m_sender, m_t.value, m_t.gasPrice, bytesConstRef(&m_t.data), m_t.gas - gasCost, m_sender);
return call(m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - gasCost, m_sender);
}
bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
@ -177,22 +177,17 @@ bool Executive::go(OnOpFunc const& _onOp)
{
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 += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);
m_endGas = m_vm->gas();
}
catch (StepsDone const&)
{
return false;
}
catch (OutOfGas const& /*_e*/)
{
clog(StateChat) << "Out of Gas! Reverting.";
revert = true;
}
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
m_endGas = m_vm->gas();
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
m_endGas = 0;//m_vm->gas();
revert = true;
}
catch (Exception const& _e)
@ -234,9 +229,9 @@ void Executive::finalize(OnOpFunc const&)
m_s.m_cache[m_newAddress].setCode(m_out);
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_sender, m_endGas * m_t.gasPrice);
m_s.addBalance(m_sender, m_endGas * m_t.gasPrice());
u256 feesEarned = (m_t.gas - m_endGas) * m_t.gasPrice;
u256 feesEarned = (m_t.gas() - m_endGas) * m_t.gasPrice();
// cnote << "Transferring" << formatBalance(gasSpent) << "to miner.";
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);

2
libethereum/Executive.h

@ -23,7 +23,7 @@
#include <functional>
#include <libdevcore/Log.h>
#include <libevmface/Instruction.h>
#include <libevmcore/Instruction.h>
#include <libethcore/CommonEth.h>
#include <libevm/ExtVMFace.h>
#include "Transaction.h"

2
libethereum/ExtVM.h

@ -40,7 +40,7 @@ class ExtVM: public ExtVMFace
public:
/// Full constructor.
ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, Manifest* o_ms, unsigned _depth = 0):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code, _s.m_previousBlock, _s.m_currentBlock, _depth), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms)
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _s.m_previousBlock, _s.m_currentBlock, _depth), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms)
{
m_s.ensureCached(_myAddress, true, true);
}

57
libethereum/MessageFilter.cpp

@ -79,7 +79,7 @@ bool MessageFilter::matches(State const& _s, unsigned _i) const
return false;
Transaction t = _s.pending()[_i];
if (!m_to.empty() && !m_to.count(t.receiveAddress))
if (!m_to.empty() && !m_to.count(t.receiveAddress()))
return false;
if (!m_from.empty() && !m_from.count(t.sender()))
return false;
@ -149,3 +149,58 @@ bool MessageFilter::matches(Manifest const& _m, vector<unsigned> _p, Address _o,
return ret;
}
void LogFilter::streamRLP(RLPStream& _s) const
{
_s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip;
}
h256 LogFilter::sha3() const
{
RLPStream s;
streamRLP(s);
return dev::sha3(s.out());
}
bool LogFilter::matches(LogBloom _bloom) const
{
if (m_addresses.size())
{
for (auto i: m_addresses)
if (_bloom.containsBloom<3>(dev::sha3(i)))
goto OK1;
return false;
}
OK1:
if (m_topics.size())
{
for (auto i: m_topics)
if (_bloom.containsBloom<3>(dev::sha3(i)))
goto OK2;
return false;
}
OK2:
return true;
}
bool LogFilter::matches(State const& _s, unsigned _i) const
{
return matches(_s.receipt(_i)).size() > 0;
}
LogEntries LogFilter::matches(TransactionReceipt const& _m) const
{
LogEntries ret;
for (LogEntry const& e: _m.log())
{
if (!m_addresses.empty() && !m_addresses.count(e.address))
continue;
for (auto const& t: m_topics)
if (!e.topics.count(t))
continue;
ret.push_back(e);
}
return ret;
}

34
libethereum/MessageFilter.h

@ -25,6 +25,7 @@
#include <libdevcore/RLP.h>
#include <libethcore/CommonEth.h>
#include "PastMessage.h"
#include "TransactionReceipt.h"
namespace dev
{
@ -72,5 +73,38 @@ private:
unsigned m_skip;
};
class LogFilter
{
public:
LogFilter(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 streamRLP(RLPStream& _s) const;
h256 sha3() const;
int earliest() const { return m_earliest; }
int latest() const { return m_latest; }
unsigned max() const { return m_max; }
unsigned skip() const { return m_skip; }
bool matches(LogBloom _bloom) const;
bool matches(State const& _s, unsigned _i) const;
LogEntries matches(TransactionReceipt const& _r) const;
LogFilter address(Address _a) { m_addresses.insert(_a); return *this; }
LogFilter from(Address _a) { return topic(u256((u160)_a) + 1); }
LogFilter topic(h256 const& _t) { m_topics.insert(_t); return *this; }
LogFilter withMax(unsigned _m) { m_max = _m; return *this; }
LogFilter withSkip(unsigned _m) { m_skip = _m; return *this; }
LogFilter withEarliest(int _e) { m_earliest = _e; return *this; }
LogFilter withLatest(int _e) { m_latest = _e; return *this; }
private:
AddressSet m_addresses;
h256Set m_topics;
int m_earliest = 0;
int m_latest = -1;
unsigned m_max;
unsigned m_skip;
};
}
}

57
libethereum/State.cpp

@ -26,7 +26,7 @@
#include <random>
#include <secp256k1/secp256k1.h>
#include <libdevcore/CommonIO.h>
#include <libevmface/Instruction.h>
#include <libevmcore/Instruction.h>
#include <libethcore/Exceptions.h>
#include <libevm/VM.h>
#include "BlockChain.h"
@ -60,6 +60,7 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out)
if (secp256k1_ecdsa_recover_compact(in.hash.data(), 32, in.r.data(), pubkey, &pubkeylen, 0, (int)(u256)in.v - 27))
ret = dev::sha3(bytesConstRef(&(pubkey[1]), 64));
memset(ret.data(), 0, 12);
memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret)));
}
@ -520,7 +521,7 @@ bool State::cull(TransactionQueue& _tq) const
try
{
Transaction t(i.second);
if (t.nonce <= transactionsFrom(t.sender()))
if (t.nonce() <= transactionsFrom(t.sender()))
{
_tq.drop(i.first);
ret = true;
@ -791,7 +792,8 @@ h256 State::oldBloom() const
LogBloom State::logBloom() const
{
LogBloom ret;
ret.shiftBloom<3>(sha3(m_currentBlock.coinbaseAddress.ref()));
auto sa = sha3(m_currentBlock.coinbaseAddress.ref());
ret.shiftBloom<3>(sa);
for (TransactionReceipt const& i: m_receipts)
ret |= i.bloom();
return ret;
@ -1125,10 +1127,10 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
#if ETH_PARANOIA
ctrace << "Executing" << e.t() << "on" << h;
ctrace << toHex(e.t().rlp(true));
ctrace << toHex(e.t().rlp());
#endif
e.go();
e.go(e.simpleTrace());
e.finalize();
#if ETH_PARANOIA
@ -1153,10 +1155,10 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
paranoia("after execution commit.", true);
if (e.t().receiveAddress)
if (e.t().receiveAddress())
{
EnforceRefs r(m_db, true);
if (storageRoot(e.t().receiveAddress) && m_db.lookup(storageRoot(e.t().receiveAddress)).empty())
if (storageRoot(e.t().receiveAddress()) && m_db.lookup(storageRoot(e.t().receiveAddress())).empty())
{
cwarn << "TRIE immediately after execution; no node for receiveAddress";
BOOST_THROW_EXCEPTION(InvalidTrie());
@ -1192,11 +1194,14 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
auto it = !(_codeAddress & ~h160(0xffffffff)) ? c_precompiled.find((unsigned)(u160)_codeAddress) : c_precompiled.end();
if (it != c_precompiled.end())
{
if (*_gas >= it->second.gas)
if (*_gas < it->second.gas)
{
*_gas -= it->second.gas;
it->second.exec(_data, _out);
*_gas = 0;
return false;
}
*_gas -= it->second.gas;
it->second.exec(_data, _out);
}
else if (addressHasCode(_codeAddress))
{
@ -1212,32 +1217,29 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
*o_sub += evm.sub;
if (o_ms)
o_ms->output = out.toBytes();
}
catch (OutOfGas const& /*_e*/)
{
clog(StateChat) << "Out of Gas! Reverting.";
revert = true;
*_gas = vm.gas();
}
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
revert = true;
*_gas = 0;
}
catch (Exception const& _e)
{
clog(StateChat) << "Exception in VM: " << diagnostic_information(_e);
cwarn << "Unexpected exception in VM: " << diagnostic_information(_e) << ". This is exceptionally bad.";
// TODO: use fallback known-safe VM.
}
catch (std::exception const& _e)
{
clog(StateChat) << "std::exception in VM: " << _e.what();
cwarn << "Unexpected exception in VM: " << _e.what() << ". This is exceptionally bad.";
// TODO: use fallback known-safe VM.
}
// Write state out only in the case of a non-excepted transaction.
if (revert)
evm.revert();
*_gas = vm.gas();
return !revert;
}
else
@ -1280,16 +1282,13 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
o_ms->output = out.toBytes();
if (o_sub)
*o_sub += evm.sub;
}
catch (OutOfGas const& /*_e*/)
{
clog(StateChat) << "Out of Gas! Reverting.";
revert = true;
*_gas = vm.gas();
}
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
revert = true;
*_gas = 0;
}
catch (Exception const& _e)
{
@ -1316,8 +1315,6 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
if (addressInUse(newAddress))
m_cache[newAddress].setCode(out);
*_gas = vm.gas();
return newAddress;
}
@ -1379,7 +1376,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s)
stringstream contout;
if ((cache && cache->codeBearing()) || (!cache && r && !r[3].isEmpty()))
if ((cache && cache->codeBearing()) || (!cache && r && (h256)r[3] != EmptySHA3))
{
std::map<u256, u256> mem;
std::set<u256> back;
@ -1408,7 +1405,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s)
else
contout << r[2].toHash<h256>();
if (cache && cache->isFreshCode())
contout << " $" << cache->code();
contout << " $" << toHex(cache->code());
else
contout << " $" << (cache ? cache->codeHash() : r[3].toHash<h256>());

5
libethereum/State.h

@ -42,7 +42,7 @@
namespace dev
{
namespace test { class FakeExtVM; class FakeState; }
namespace test { class ImportTest; }
namespace eth
{
@ -67,8 +67,7 @@ struct PrecompiledAddress
class State
{
friend class ExtVM;
friend class test::FakeExtVM;
friend class test::FakeState;
friend class dev::test::ImportTest;
friend class Executive;
public:

38
libethereum/Transaction.cpp

@ -36,18 +36,18 @@ Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender)
RLP rlp(_rlpData);
try
{
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 = SignatureStruct{ rlp[field = 7].toInt<u256>(), rlp[field = 8].toInt<u256>(), byte(rlp[field = 6].toInt<byte>() - 27) };
m_nonce = rlp[field = 0].toInt<u256>();
m_gasPrice = rlp[field = 1].toInt<u256>();
m_gas = rlp[field = 2].toInt<u256>();
m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
m_receiveAddress = rlp[field = 3].toHash<Address>();
m_value = rlp[field = 4].toInt<u256>();
m_data = rlp[field = 5].toBytes();
m_vrs = SignatureStruct{ rlp[field = 7].toInt<u256>(), rlp[field = 8].toInt<u256>(), byte(rlp[field = 6].toInt<byte>() - 27) };
if (_checkSender)
m_sender = sender();
}
catch (Exception & _e)
catch (Exception& _e)
{
_e << errinfo_name("invalid transaction format") << BadFieldError(field,toHex(rlp[field].data().toBytes()));
throw;
@ -71,7 +71,7 @@ Address Transaction::sender() const
{
if (!m_sender)
{
auto p = recover(*(Signature const*)&vrs, sha3(false));
auto p = recover(*(Signature const*)&m_vrs, sha3(WithoutSignature));
if (!p)
BOOST_THROW_EXCEPTION(InvalidSignature());
m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));
@ -81,19 +81,21 @@ Address Transaction::sender() const
void Transaction::sign(Secret _priv)
{
auto sig = dev::sign(_priv, sha3(false));
vrs = *(SignatureStruct const*)&sig;
auto sig = dev::sign(_priv, sha3(WithoutSignature));
m_vrs = *(SignatureStruct const*)&sig;
}
void Transaction::streamRLP(RLPStream& _s, bool _sig) const
void Transaction::streamRLP(RLPStream& _s, IncludeSignature _sig) const
{
if (m_type == NullTransaction)
return;
_s.appendList((_sig ? 3 : 0) + 6);
_s << nonce << gasPrice << gas;
if (type == MessageCall)
_s << receiveAddress;
_s << m_nonce << m_gasPrice << m_gas;
if (m_type == MessageCall)
_s << m_receiveAddress;
else
_s << "";
_s << value << data;
_s << m_value << m_data;
if (_sig)
_s << (vrs.v + 27) << (u256)vrs.r << (u256)vrs.s;
_s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s;
}

132
libethereum/Transaction.h

@ -30,68 +30,134 @@ namespace dev
namespace eth
{
struct Transaction
/// Named-boolean type to encode whether a signature be included in the serialisation process.
enum IncludeSignature
{
enum Type
{
ContractCreation,
MessageCall
};
WithoutSignature = 0, ///< Do not include a signature.
WithSignature = 1, ///< Do include a signature.
};
/// Encodes a transaction, ready to be exported to or freshly imported from RLP.
class Transaction
{
public:
/// Constructs a null transaction.
Transaction() {}
Transaction(bytesConstRef _rlp, bool _checkSender = false);
Transaction(bytes const& _rlp, bool _checkSender = false): Transaction(&_rlp, _checkSender) {}
bool operator==(Transaction const& _c) const { return type == _c.type && (type == ContractCreation || receiveAddress == _c.receiveAddress) && value == _c.value && data == _c.data; }
/// Constructs a signed message-call transaction.
Transaction(u256 _value, u256 _gasPrice, u256 _gas, Address const& _dest, bytes const& _data, u256 _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
/// Constructs a signed contract-creation transaction.
Transaction(u256 _value, u256 _gasPrice, u256 _gas, bytes const& _data, u256 _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
/// Constructs an unsigned message-call transaction.
Transaction(u256 _value, u256 _gasPrice, u256 _gas, Address const& _dest, bytes const& _data): m_type(MessageCall), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs an unsigned contract-creation transaction.
Transaction(u256 _value, u256 _gasPrice, u256 _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs a transaction from the given RLP.
explicit Transaction(bytesConstRef _rlp, bool _checkSender = false);
/// Constructs a transaction from the given RLP.
explicit Transaction(bytes const& _rlp, bool _checkSender = false): Transaction(&_rlp, _checkSender) {}
/// Checks equality of transactions.
bool operator==(Transaction const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; }
/// Checks inequality of transactions.
bool operator!=(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.
u256 gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS.
u256 gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
/// @returns sender of the transaction from the signature (and hash).
Address sender() const;
/// Like sender() but will never throw. @returns a null Address if the signature is invalid.
Address safeSender() const noexcept;
/// @returns true if transaction is non-null.
operator bool() const { return m_type != NullTransaction; }
bytes data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction.
/// @returns true if transaction is contract-creation.
bool isCreation() const { return m_type == ContractCreation; }
SignatureStruct vrs; ///< The signature of the transaction. Encodes the sender.
/// @returns true if transaction is message-call.
bool isMessageCall() const { return m_type == MessageCall; }
Address safeSender() const noexcept; ///< Like sender() but will never throw.
Address sender() const; ///< Determine the sender of the transaction from the signature (and hash).
void sign(Secret _priv); ///< Sign the transaction.
/// Serialises this transaction to an RLPStream.
void streamRLP(RLPStream& _s, IncludeSignature _sig = WithSignature) const;
bool isCreation() const { return !receiveAddress; }
/// @returns the RLP serialisation of this transaction.
bytes rlp(IncludeSignature _sig = WithSignature) const { RLPStream s; streamRLP(s, _sig); return s.out(); }
static h256 kFromMessage(h256 _msg, h256 _priv);
/// @returns the SHA3 hash of the RLP serialisation of this transaction.
h256 sha3(IncludeSignature _sig = WithSignature) const { RLPStream s; streamRLP(s, _sig); return dev::sha3(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; streamRLP(s, _sig); return dev::sha3(s.out()); }
bytes sha3Bytes(bool _sig = true) const { RLPStream s; streamRLP(s, _sig); return dev::sha3Bytes(s.out()); }
/// @returns the amount of ETH to be transferred by this (message-call) transaction, in Wei. Synonym for endowment().
u256 value() const { return m_value; }
/// @returns the amount of ETH to be endowed by this (contract-creation) transaction, in Wei. Synonym for value().
u256 endowment() const { return m_value; }
/// @returns the base fee and thus the implied exchange rate of ETH to GAS.
u256 gasPrice() const { return m_gasPrice; }
/// @returns the total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
u256 gas() const { return m_gas; }
/// @returns the receiving address of the message-call transaction (undefined for contract-creation transactions).
Address receiveAddress() const { return m_receiveAddress; }
/// @returns the data associated with this (message-call) transaction. Synonym for initCode().
bytes const& data() const { return m_data; }
/// @returns the initialisation code associated with this (contract-creation) transaction. Synonym for data().
bytes const& initCode() const { return m_data; }
/// @returns the transaction-count of the sender.
u256 nonce() const { return m_nonce; }
/// @returns the signature of the transaction. Encodes the sender.
SignatureStruct const& signature() const { return m_vrs; }
private:
mutable Address m_sender;
/// Type of transaction.
enum Type
{
NullTransaction, ///< Null transaction.
ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored.
MessageCall ///< Transaction to invoke a message call - receiveAddress() is used.
};
void sign(Secret _priv); ///< Sign the transaction.
Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction?
u256 m_nonce; ///< The transaction-count of the sender.
u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
Address m_receiveAddress; ///< The receiving address of the transaction.
u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS.
u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction.
SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender.
mutable Address m_sender; ///< Cached sender, determined from signature.
};
/// Nice name for vector of Transaction.
using Transactions = std::vector<Transaction>;
/// Simple human-readable stream-shift operator.
inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t)
{
_out << "{";
if (_t.receiveAddress)
_out << _t.receiveAddress.abridged();
if (_t.receiveAddress())
_out << _t.receiveAddress().abridged();
else
_out << "[CREATE]";
_out << "/" << _t.nonce << "$" << _t.value << "+" << _t.gas << "@" << _t.gasPrice;
Address s;
_out << "/" << _t.nonce() << "$" << _t.value() << "+" << _t.gas() << "@" << _t.gasPrice();
try
{
_out << "<-" << _t.sender().abridged();
}
catch (...) {}
_out << " #" << _t.data.size() << "}";
_out << " #" << _t.data().size() << "}";
return _out;
}

2
libevm/CMakeLists.txt

@ -17,7 +17,7 @@ include_directories(..)
target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} evmface)
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} secp256k1)
target_link_libraries(${EXECUTABLE} gmp)

2
libevm/ExtVMFace.cpp

@ -25,7 +25,7 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, unsigned _depth):
ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes const& _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, unsigned _depth):
myAddress(_myAddress),
caller(_caller),
origin(_origin),

27
libevm/ExtVMFace.h

@ -27,7 +27,7 @@
#include <libdevcore/CommonData.h>
#include <libdevcore/RLP.h>
#include <libdevcrypto/SHA3.h>
#include <libevmface/Instruction.h>
#include <libevmcore/Instruction.h>
#include <libethcore/CommonEth.h>
#include <libethcore/BlockInfo.h>
@ -36,27 +36,35 @@ namespace dev
namespace eth
{
template <class T> inline std::set<T> toSet(std::vector<T> const& _ts)
{
std::set<T> ret;
for (auto const& t: _ts)
ret.insert(t);
return ret;
}
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)) {}
LogEntry(RLP const& _r) { address = (Address)_r[0]; topics = (h256Set)_r[1]; data = (bytes)_r[2]; }
LogEntry(Address const& _address, h256s const& _ts, bytes&& _d): address(_address), topics(toSet(_ts)), data(std::move(_d)) {}
void streamRLP(RLPStream& _s) const { _s.appendList(3) << from << topics << data; }
void streamRLP(RLPStream& _s) const { _s.appendList(3) << address << topics << data; }
LogBloom bloom() const
{
LogBloom ret;
ret.shiftBloom<3, 32>(sha3(from.ref()));
ret.shiftBloom<3, 32>(sha3(address.ref()));
for (auto t: topics)
ret.shiftBloom<3, 32>(sha3(t.ref()));
return ret;
}
Address from;
h256s topics;
Address address;
h256Set topics;
bytes data;
};
@ -80,7 +88,6 @@ struct SubState
{
suicides += _s.suicides;
refunds += _s.refunds;
suicides += _s.suicides;
return *this;
}
};
@ -97,7 +104,7 @@ public:
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);
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes const& _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, unsigned _depth);
virtual ~ExtVMFace() = default;
@ -146,7 +153,7 @@ public:
u256 value; ///< Value (in Wei) that was passed to this address.
u256 gasPrice; ///< Price of gas (that we already paid).
bytesConstRef data; ///< Current input data.
bytesConstRef code; ///< Current code that is executing.
bytes code; ///< Current code that is executing.
BlockInfo previousBlock; ///< The previous block's information.
BlockInfo currentBlock; ///< The current block's information.
SubState sub; ///< Sub-band VM state (suicides, refund counter, logs).

10
libevm/VM.h

@ -24,7 +24,7 @@
#include <unordered_map>
#include <libdevcore/Exceptions.h>
#include <libethcore/CommonEth.h>
#include <libevmface/Instruction.h>
#include <libevmcore/Instruction.h>
#include <libdevcrypto/SHA3.h>
#include <libethcore/BlockInfo.h>
#include "FeeStructure.h"
@ -41,7 +41,7 @@ struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {};
struct StackTooSmall: virtual public VMException { StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
struct StackTooSmall: virtual public VMException {};
// 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.
@ -71,7 +71,7 @@ public:
template <class Ext>
bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1);
void require(u256 _n) { if (m_stack.size() < _n) BOOST_THROW_EXCEPTION(StackTooSmall(_n, m_stack.size())); }
void require(u256 _n) { if (m_stack.size() < _n) BOOST_THROW_EXCEPTION(StackTooSmall() << RequirementError(int(_n), m_stack.size())); }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 gas() const { return m_gas; }
u256 curPC() const { return m_curPC; }
@ -701,7 +701,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::JUMP:
nextPC = m_stack.back();
if (!m_jumpDests.count((unsigned)nextPC))
if (!m_jumpDests.count(nextPC))
BOOST_THROW_EXCEPTION(BadJumpDestination());
m_stack.pop_back();
break;
@ -709,7 +709,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
if (m_stack[m_stack.size() - 2])
{
nextPC = m_stack.back();
if (!m_jumpDests.count((unsigned)nextPC))
if (!m_jumpDests.count(nextPC))
BOOST_THROW_EXCEPTION(BadJumpDestination());
}
m_stack.pop_back();

81
liblll/Assembly.cpp → libevmcore/Assembly.cpp

@ -70,7 +70,12 @@ unsigned Assembly::bytesRequired() const
case PushData:
case PushSub:
ret += 1 + br;
default:;
break;
case NoOptimizeBegin:
case NoOptimizeEnd:
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
if (dev::bytesRequired(ret) <= br)
return ret;
@ -140,9 +145,17 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i)
case PushSubSize:
_out << " PUSHss[" << hex << h256(i.data()).abridged() << "]";
break;
case NoOptimizeBegin:
_out << " DoNotOptimze{{";
break;
case NoOptimizeEnd:
_out << " DoNotOptimze}}";
break;
case UndefinedItem:
_out << " ???";
default:;
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
return _out;
}
@ -177,7 +190,14 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix) const
case PushData:
_out << _prefix << " PUSH [" << hex << (unsigned)i.m_data << "]" << endl;
break;
default:;
case NoOptimizeBegin:
_out << _prefix << "DoNotOptimze{{" << endl;
break;
case NoOptimizeEnd:
_out << _prefix << "DoNotOptimze}}" << endl;
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
if (m_data.size() || m_subs.size())
@ -217,6 +237,12 @@ inline bool matches(AssemblyItemsConstRef _a, AssemblyItemsConstRef _b)
return true;
}
inline bool popCountIncreased(AssemblyItemsConstRef _pre, AssemblyItems const& _post)
{
auto isPop = [](AssemblyItem const& _item) -> bool { return _item.match(AssemblyItem(Instruction::POP)); };
return count_if(begin(_post), end(_post), isPop) > count_if(begin(_pre), end(_pre), isPop);
}
struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; };
#define copt dev::LogOutputStream<OptimiserChannel, true>()
@ -224,6 +250,14 @@ Assembly& Assembly::optimise(bool _enable)
{
if (!_enable)
return *this;
auto signextend = [](u256 a, u256 b) -> u256
{
if (a >= 31)
return b;
unsigned testBit = unsigned(a) * 8 + 7;
u256 mask = (u256(1) << testBit) - 1;
return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask;
};
map<Instruction, function<u256(u256, u256)>> c_simple =
{
{ Instruction::SUB, [](u256 a, u256 b)->u256{return a - b;} },
@ -232,6 +266,7 @@ Assembly& Assembly::optimise(bool _enable)
{ Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} },
{ Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} },
{ Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(2) << 256);} },
{ Instruction::SIGNEXTEND, signextend },
{ Instruction::LT, [](u256 a, u256 b)->u256{return a < b ? 1 : 0;} },
{ Instruction::GT, [](u256 a, u256 b)->u256{return a > b ? 1 : 0;} },
{ Instruction::SLT, [](u256 a, u256 b)->u256{return u2s(a) < u2s(b) ? 1 : 0;} },
@ -242,6 +277,9 @@ Assembly& Assembly::optimise(bool _enable)
{
{ Instruction::ADD, [](u256 a, u256 b)->u256{return a + b;} },
{ Instruction::MUL, [](u256 a, u256 b)->u256{return a * b;} },
{ Instruction::AND, [](u256 a, u256 b)->u256{return a & b;} },
{ Instruction::OR, [](u256 a, u256 b)->u256{return a | b;} },
{ Instruction::XOR, [](u256 a, u256 b)->u256{return a ^ b;} },
};
std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules =
{
@ -260,8 +298,23 @@ Assembly& Assembly::optimise(bool _enable)
{
rules.push_back({ { Push, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[1].data(), m[0].data()) }; } });
rules.push_back({ { Push, i.first, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[2].data(), m[0].data()), i.first }; } });
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [&](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {}; else return m.toVector(); }});
}
// jump to next instruction
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [&](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }});
// pop optimization, do not compute values that are popped again anyway
rules.push_back({ { AssemblyItem(UndefinedItem), Instruction::POP }, [](AssemblyItemsConstRef m) -> AssemblyItems
{
if (m[0].type() != Operation)
return m.toVector();
Instruction instr = Instruction(byte(m[0].data()));
if (Instruction::DUP1 <= instr && instr <= Instruction::DUP16)
return {};
InstructionInfo info = instructionInfo(instr);
if (info.sideEffects || info.additional != 0 || info.ret != 1)
return m.toVector();
return AssemblyItems(info.args, Instruction::POP);
} });
copt << *this;
@ -269,16 +322,21 @@ Assembly& Assembly::optimise(bool _enable)
for (unsigned count = 1; count > 0; total += count)
{
count = 0;
map<u256, unsigned> tags;
for (unsigned i = 0; i < m_items.size(); ++i)
{
if (m_items[i].type() == NoOptimizeBegin)
{
while (i < m_items.size() && m_items[i].type() != NoOptimizeEnd)
++i;
continue;
}
for (auto const& r: rules)
{
auto vr = AssemblyItemsConstRef(&m_items).cropped(i, r.first.size());
if (matches(&r.first, vr))
if (matches(vr, &r.first))
{
auto rw = r.second(vr);
if (rw.size() < vr.size())
if (rw.size() < vr.size() || (rw.size() == vr.size() && popCountIncreased(vr, rw)))
{
copt << vr << "matches" << AssemblyItemsConstRef(&r.first) << "becomes...";
for (unsigned j = 0; j < vr.size(); ++j)
@ -297,6 +355,8 @@ Assembly& Assembly::optimise(bool _enable)
bool o = false;
while (m_items.size() > i + 1 && m_items[i + 1].type() != Tag)
{
if (m_items[i + 1].type() == NoOptimizeBegin)
break;
m_items.erase(m_items.begin() + i + 1);
o = true;
}
@ -308,6 +368,7 @@ Assembly& Assembly::optimise(bool _enable)
}
}
map<u256, unsigned> tags;
for (unsigned i = 0; i < m_items.size(); ++i)
if (m_items[i].type() == Tag)
tags.insert(make_pair(m_items[i].data(), i));
@ -416,7 +477,11 @@ bytes Assembly::assemble() const
tagPos[(unsigned)i.m_data] = ret.size();
ret.push_back((byte)Instruction::JUMPDEST);
break;
default:;
case NoOptimizeBegin:
case NoOptimizeEnd:
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
for (auto const& i: tagRef)

14
liblll/Assembly.h → libevmcore/Assembly.h

@ -24,7 +24,7 @@
#include <iostream>
#include <sstream>
#include <libdevcore/Common.h>
#include <libevmface/Instruction.h>
#include <libevmcore/Instruction.h>
#include "Exceptions.h"
namespace dev
@ -32,7 +32,7 @@ namespace dev
namespace eth
{
enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, Tag, PushData };
enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, Tag, PushData, NoOptimizeBegin, NoOptimizeEnd };
class Assembly;
@ -45,8 +45,8 @@ public:
AssemblyItem(Instruction _i): m_type(Operation), m_data((byte)_i) {}
AssemblyItem(AssemblyItemType _type, u256 _data = 0): m_type(_type), m_data(_data) {}
AssemblyItem tag() const { assert(m_type == PushTag || m_type == Tag); return AssemblyItem(Tag, m_data); }
AssemblyItem pushTag() const { assert(m_type == PushTag || m_type == Tag); return AssemblyItem(PushTag, m_data); }
AssemblyItem tag() const { if (asserts(m_type == PushTag || m_type == Tag)) BOOST_THROW_EXCEPTION(Exception()); return AssemblyItem(Tag, m_data); }
AssemblyItem pushTag() const { if (asserts(m_type == PushTag || m_type == Tag)) BOOST_THROW_EXCEPTION(Exception()); return AssemblyItem(PushTag, m_data); }
AssemblyItemType type() const { return m_type; }
u256 data() const { return m_data; }
@ -94,7 +94,7 @@ public:
AssemblyItem const& back() { return m_items.back(); }
std::string backString() const { return m_items.size() && m_items.back().m_type == PushString ? m_strings.at((h256)m_items.back().m_data) : std::string(); }
void onePath() { assert(!m_totalDeposit && !m_baseDeposit); m_baseDeposit = m_deposit; m_totalDeposit = INT_MAX; }
void onePath() { if (asserts(!m_totalDeposit && !m_baseDeposit)) BOOST_THROW_EXCEPTION(InvalidDeposit()); m_baseDeposit = m_deposit; m_totalDeposit = INT_MAX; }
void otherPath() { donePath(); m_totalDeposit = m_deposit; m_deposit = m_baseDeposit; }
void donePaths() { donePath(); m_totalDeposit = m_baseDeposit = 0; }
void ignored() { m_baseDeposit = m_deposit; }
@ -105,7 +105,11 @@ public:
void injectStart(AssemblyItem const& _i);
std::string out() const { std::stringstream ret; streamRLP(ret); return ret.str(); }
int deposit() const { return m_deposit; }
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
bytes assemble() const;
Assembly& optimise(bool _enable);
std::ostream& streamRLP(std::ostream& _out, std::string const& _prefix = "") const;

2
libevmface/CMakeLists.txt → libevmcore/CMakeLists.txt

@ -4,7 +4,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST)
set(EXECUTABLE evmface)
set(EXECUTABLE evmcore)
file(GLOB HEADERS "*.h")
if(ETH_STATIC)

36
libevmcore/Exceptions.h

@ -0,0 +1,36 @@
/*
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 Exceptions.h
* @author Christian <c@ethdev.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Exceptions.h>
namespace dev
{
namespace eth
{
struct AssemblyException: virtual Exception {};
struct InvalidDeposit: virtual AssemblyException {};
struct InvalidOpcode: virtual AssemblyException {};
}
}

337
libevmcore/Instruction.cpp

@ -0,0 +1,337 @@
/*
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 Instruction.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Instruction.h"
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
const std::map<std::string, Instruction> dev::eth::c_instructions =
{
{ "STOP", Instruction::STOP },
{ "ADD", Instruction::ADD },
{ "SUB", Instruction::SUB },
{ "MUL", Instruction::MUL },
{ "DIV", Instruction::DIV },
{ "SDIV", Instruction::SDIV },
{ "MOD", Instruction::MOD },
{ "SMOD", Instruction::SMOD },
{ "EXP", Instruction::EXP },
{ "BNOT", Instruction::NOT },
{ "LT", Instruction::LT },
{ "GT", Instruction::GT },
{ "SLT", Instruction::SLT },
{ "SGT", Instruction::SGT },
{ "EQ", Instruction::EQ },
{ "NOT", Instruction::ISZERO },
{ "AND", Instruction::AND },
{ "OR", Instruction::OR },
{ "XOR", Instruction::XOR },
{ "BYTE", Instruction::BYTE },
{ "ADDMOD", Instruction::ADDMOD },
{ "MULMOD", Instruction::MULMOD },
{ "SIGNEXTEND", Instruction::SIGNEXTEND },
{ "SHA3", Instruction::SHA3 },
{ "ADDRESS", Instruction::ADDRESS },
{ "BALANCE", Instruction::BALANCE },
{ "ORIGIN", Instruction::ORIGIN },
{ "CALLER", Instruction::CALLER },
{ "CALLVALUE", Instruction::CALLVALUE },
{ "CALLDATALOAD", Instruction::CALLDATALOAD },
{ "CALLDATASIZE", Instruction::CALLDATASIZE },
{ "CALLDATACOPY", Instruction::CALLDATACOPY },
{ "CODESIZE", Instruction::CODESIZE },
{ "CODECOPY", Instruction::CODECOPY },
{ "GASPRICE", Instruction::GASPRICE },
{ "EXTCODESIZE", Instruction::EXTCODESIZE },
{ "EXTCODECOPY", Instruction::EXTCODECOPY },
{ "PREVHASH", Instruction::PREVHASH },
{ "COINBASE", Instruction::COINBASE },
{ "TIMESTAMP", Instruction::TIMESTAMP },
{ "NUMBER", Instruction::NUMBER },
{ "DIFFICULTY", Instruction::DIFFICULTY },
{ "GASLIMIT", Instruction::GASLIMIT },
{ "POP", Instruction::POP },
{ "MLOAD", Instruction::MLOAD },
{ "MSTORE", Instruction::MSTORE },
{ "MSTORE8", Instruction::MSTORE8 },
{ "SLOAD", Instruction::SLOAD },
{ "SSTORE", Instruction::SSTORE },
{ "JUMP", Instruction::JUMP },
{ "JUMPI", Instruction::JUMPI },
{ "PC", Instruction::PC },
{ "MSIZE", Instruction::MSIZE },
{ "GAS", Instruction::GAS },
{ "JUMPDEST", Instruction::JUMPDEST },
{ "PUSH1", Instruction::PUSH1 },
{ "PUSH2", Instruction::PUSH2 },
{ "PUSH3", Instruction::PUSH3 },
{ "PUSH4", Instruction::PUSH4 },
{ "PUSH5", Instruction::PUSH5 },
{ "PUSH6", Instruction::PUSH6 },
{ "PUSH7", Instruction::PUSH7 },
{ "PUSH8", Instruction::PUSH8 },
{ "PUSH9", Instruction::PUSH9 },
{ "PUSH10", Instruction::PUSH10 },
{ "PUSH11", Instruction::PUSH11 },
{ "PUSH12", Instruction::PUSH12 },
{ "PUSH13", Instruction::PUSH13 },
{ "PUSH14", Instruction::PUSH14 },
{ "PUSH15", Instruction::PUSH15 },
{ "PUSH16", Instruction::PUSH16 },
{ "PUSH17", Instruction::PUSH17 },
{ "PUSH18", Instruction::PUSH18 },
{ "PUSH19", Instruction::PUSH19 },
{ "PUSH20", Instruction::PUSH20 },
{ "PUSH21", Instruction::PUSH21 },
{ "PUSH22", Instruction::PUSH22 },
{ "PUSH23", Instruction::PUSH23 },
{ "PUSH24", Instruction::PUSH24 },
{ "PUSH25", Instruction::PUSH25 },
{ "PUSH26", Instruction::PUSH26 },
{ "PUSH27", Instruction::PUSH27 },
{ "PUSH28", Instruction::PUSH28 },
{ "PUSH29", Instruction::PUSH29 },
{ "PUSH30", Instruction::PUSH30 },
{ "PUSH31", Instruction::PUSH31 },
{ "PUSH32", Instruction::PUSH32 },
{ "DUP1", Instruction::DUP1 },
{ "DUP2", Instruction::DUP2 },
{ "DUP3", Instruction::DUP3 },
{ "DUP4", Instruction::DUP4 },
{ "DUP5", Instruction::DUP5 },
{ "DUP6", Instruction::DUP6 },
{ "DUP7", Instruction::DUP7 },
{ "DUP8", Instruction::DUP8 },
{ "DUP9", Instruction::DUP9 },
{ "DUP10", Instruction::DUP10 },
{ "DUP11", Instruction::DUP11 },
{ "DUP12", Instruction::DUP12 },
{ "DUP13", Instruction::DUP13 },
{ "DUP14", Instruction::DUP14 },
{ "DUP15", Instruction::DUP15 },
{ "DUP16", Instruction::DUP16 },
{ "SWAP1", Instruction::SWAP1 },
{ "SWAP2", Instruction::SWAP2 },
{ "SWAP3", Instruction::SWAP3 },
{ "SWAP4", Instruction::SWAP4 },
{ "SWAP5", Instruction::SWAP5 },
{ "SWAP6", Instruction::SWAP6 },
{ "SWAP7", Instruction::SWAP7 },
{ "SWAP8", Instruction::SWAP8 },
{ "SWAP9", Instruction::SWAP9 },
{ "SWAP10", Instruction::SWAP10 },
{ "SWAP11", Instruction::SWAP11 },
{ "SWAP12", Instruction::SWAP12 },
{ "SWAP13", Instruction::SWAP13 },
{ "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 },
{ "RETURN", Instruction::RETURN },
{ "SUICIDE", Instruction::SUICIDE }
};
static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ // Add, Args, Ret, SideEffects
{ Instruction::STOP, { "STOP", 0, 0, 0, true } },
{ Instruction::ADD, { "ADD", 0, 2, 1, false } },
{ Instruction::SUB, { "SUB", 0, 2, 1, false } },
{ Instruction::MUL, { "MUL", 0, 2, 1, false } },
{ Instruction::DIV, { "DIV", 0, 2, 1, false } },
{ Instruction::SDIV, { "SDIV", 0, 2, 1, false } },
{ Instruction::MOD, { "MOD", 0, 2, 1, false } },
{ Instruction::SMOD, { "SMOD", 0, 2, 1, false } },
{ Instruction::EXP, { "EXP", 0, 2, 1, false } },
{ Instruction::NOT, { "NOT", 0, 1, 1, false } },
{ Instruction::LT, { "LT", 0, 2, 1, false } },
{ Instruction::GT, { "GT", 0, 2, 1, false } },
{ Instruction::SLT, { "SLT", 0, 2, 1, false } },
{ Instruction::SGT, { "SGT", 0, 2, 1, false } },
{ Instruction::EQ, { "EQ", 0, 2, 1, false } },
{ Instruction::ISZERO, { "ISZERO", 0, 1, 1, false } },
{ Instruction::AND, { "AND", 0, 2, 1, false } },
{ Instruction::OR, { "OR", 0, 2, 1, false } },
{ Instruction::XOR, { "XOR", 0, 2, 1, false } },
{ Instruction::BYTE, { "BYTE", 0, 2, 1, false } },
{ Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false } },
{ Instruction::MULMOD, { "MULMOD", 0, 3, 1, false } },
{ Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false } },
{ Instruction::SHA3, { "SHA3", 0, 2, 1, false } },
{ Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false } },
{ Instruction::BALANCE, { "BALANCE", 0, 1, 1, false } },
{ Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false } },
{ Instruction::CALLER, { "CALLER", 0, 0, 1, false } },
{ Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1, false } },
{ Instruction::CALLDATALOAD,{ "CALLDATALOAD", 0, 1, 1, false } },
{ Instruction::CALLDATASIZE,{ "CALLDATASIZE", 0, 0, 1, false } },
{ Instruction::CALLDATACOPY,{ "CALLDATACOPY", 0, 3, 0, true } },
{ Instruction::CODESIZE, { "CODESIZE", 0, 0, 1, false } },
{ Instruction::CODECOPY, { "CODECOPY", 0, 3, 0, true } },
{ Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, false } },
{ Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false } },
{ Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true } },
{ Instruction::PREVHASH, { "PREVHASH", 0, 0, 1, false } },
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false } },
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false } },
{ Instruction::NUMBER, { "NUMBER", 0, 0, 1, false } },
{ Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1, false } },
{ Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1, false } },
{ Instruction::POP, { "POP", 0, 1, 0, false } },
{ Instruction::MLOAD, { "MLOAD", 0, 1, 1, false } },
{ Instruction::MSTORE, { "MSTORE", 0, 2, 0, true } },
{ Instruction::MSTORE8, { "MSTORE8", 0, 2, 0, true } },
{ Instruction::SLOAD, { "SLOAD", 0, 1, 1, false } },
{ Instruction::SSTORE, { "SSTORE", 0, 2, 0, true } },
{ Instruction::JUMP, { "JUMP", 0, 1, 0, true } },
{ Instruction::JUMPI, { "JUMPI", 0, 2, 0, true } },
{ Instruction::PC, { "PC", 0, 0, 1, false } },
{ Instruction::MSIZE, { "MSIZE", 0, 0, 1, false } },
{ Instruction::GAS, { "GAS", 0, 0, 1, false } },
{ Instruction::JUMPDEST, { "JUMPDEST", 0, 1, 0, true } },
{ Instruction::PUSH1, { "PUSH1", 1, 0, 1, false } },
{ Instruction::PUSH2, { "PUSH2", 2, 0, 1, false } },
{ Instruction::PUSH3, { "PUSH3", 3, 0, 1, false } },
{ Instruction::PUSH4, { "PUSH4", 4, 0, 1, false } },
{ Instruction::PUSH5, { "PUSH5", 5, 0, 1, false } },
{ Instruction::PUSH6, { "PUSH6", 6, 0, 1, false } },
{ Instruction::PUSH7, { "PUSH7", 7, 0, 1, false } },
{ Instruction::PUSH8, { "PUSH8", 8, 0, 1, false } },
{ Instruction::PUSH9, { "PUSH9", 9, 0, 1, false } },
{ Instruction::PUSH10, { "PUSH10", 10, 0, 1, false } },
{ Instruction::PUSH11, { "PUSH11", 11, 0, 1, false } },
{ Instruction::PUSH12, { "PUSH12", 12, 0, 1, false } },
{ Instruction::PUSH13, { "PUSH13", 13, 0, 1, false } },
{ Instruction::PUSH14, { "PUSH14", 14, 0, 1, false } },
{ Instruction::PUSH15, { "PUSH15", 15, 0, 1, false } },
{ Instruction::PUSH16, { "PUSH16", 16, 0, 1, false } },
{ Instruction::PUSH17, { "PUSH17", 17, 0, 1, false } },
{ Instruction::PUSH18, { "PUSH18", 18, 0, 1, false } },
{ Instruction::PUSH19, { "PUSH19", 19, 0, 1, false } },
{ Instruction::PUSH20, { "PUSH20", 20, 0, 1, false } },
{ Instruction::PUSH21, { "PUSH21", 21, 0, 1, false } },
{ Instruction::PUSH22, { "PUSH22", 22, 0, 1, false } },
{ Instruction::PUSH23, { "PUSH23", 23, 0, 1, false } },
{ Instruction::PUSH24, { "PUSH24", 24, 0, 1, false } },
{ Instruction::PUSH25, { "PUSH25", 25, 0, 1, false } },
{ Instruction::PUSH26, { "PUSH26", 26, 0, 1, false } },
{ Instruction::PUSH27, { "PUSH27", 27, 0, 1, false } },
{ Instruction::PUSH28, { "PUSH28", 28, 0, 1, false } },
{ Instruction::PUSH29, { "PUSH29", 29, 0, 1, false } },
{ Instruction::PUSH30, { "PUSH30", 30, 0, 1, false } },
{ Instruction::PUSH31, { "PUSH31", 31, 0, 1, false } },
{ Instruction::PUSH32, { "PUSH32", 32, 0, 1, false } },
{ Instruction::DUP1, { "DUP1", 0, 1, 2, false } },
{ Instruction::DUP2, { "DUP2", 0, 2, 3, false } },
{ Instruction::DUP3, { "DUP3", 0, 3, 4, false } },
{ Instruction::DUP4, { "DUP4", 0, 4, 5, false } },
{ Instruction::DUP5, { "DUP5", 0, 5, 6, false } },
{ Instruction::DUP6, { "DUP6", 0, 6, 7, false } },
{ Instruction::DUP7, { "DUP7", 0, 7, 8, false } },
{ Instruction::DUP8, { "DUP8", 0, 8, 9, false } },
{ Instruction::DUP9, { "DUP9", 0, 9, 10, false } },
{ Instruction::DUP10, { "DUP10", 0, 10, 11, false } },
{ Instruction::DUP11, { "DUP11", 0, 11, 12, false } },
{ Instruction::DUP12, { "DUP12", 0, 12, 13, false } },
{ Instruction::DUP13, { "DUP13", 0, 13, 14, false } },
{ Instruction::DUP14, { "DUP14", 0, 14, 15, false } },
{ Instruction::DUP15, { "DUP15", 0, 15, 16, false } },
{ Instruction::DUP16, { "DUP16", 0, 16, 17, false } },
{ Instruction::SWAP1, { "SWAP1", 0, 2, 2, false } },
{ Instruction::SWAP2, { "SWAP2", 0, 3, 3, false } },
{ Instruction::SWAP3, { "SWAP3", 0, 4, 4, false } },
{ Instruction::SWAP4, { "SWAP4", 0, 5, 5, false } },
{ Instruction::SWAP5, { "SWAP5", 0, 6, 6, false } },
{ Instruction::SWAP6, { "SWAP6", 0, 7, 7, false } },
{ Instruction::SWAP7, { "SWAP7", 0, 8, 8, false } },
{ Instruction::SWAP8, { "SWAP8", 0, 9, 9, false } },
{ Instruction::SWAP9, { "SWAP9", 0, 10, 10, false } },
{ Instruction::SWAP10, { "SWAP10", 0, 11, 11, false } },
{ Instruction::SWAP11, { "SWAP11", 0, 12, 12, false } },
{ Instruction::SWAP12, { "SWAP12", 0, 13, 13, false } },
{ Instruction::SWAP13, { "SWAP13", 0, 14, 14, false } },
{ Instruction::SWAP14, { "SWAP14", 0, 15, 15, false } },
{ Instruction::SWAP15, { "SWAP15", 0, 16, 16, false } },
{ Instruction::SWAP16, { "SWAP16", 0, 17, 17, false } },
{ Instruction::LOG0, { "LOG0", 0, 1, 0, true } },
{ Instruction::LOG1, { "LOG1", 0, 2, 0, true } },
{ Instruction::LOG2, { "LOG2", 0, 3, 0, true } },
{ Instruction::LOG3, { "LOG3", 0, 4, 0, true } },
{ Instruction::LOG4, { "LOG4", 0, 5, 0, true } },
{ Instruction::CREATE, { "CREATE", 0, 3, 1, true } },
{ Instruction::CALL, { "CALL", 0, 7, 1, true } },
{ Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true } },
{ Instruction::RETURN, { "RETURN", 0, 2, 0, true } },
{ Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true } }
};
string dev::eth::disassemble(bytes const& _mem)
{
stringstream ret;
unsigned numerics = 0;
for (auto it = _mem.begin(); it != _mem.end(); ++it)
{
byte n = *it;
auto iit = c_instructionInfo.find((Instruction)n);
if (numerics || iit == c_instructionInfo.end() || (byte)iit->first != n) // not an instruction or expecting an argument...
{
if (numerics)
numerics--;
ret << "0x" << hex << (int)n << " ";
}
else
{
auto const& ii = iit->second;
ret << ii.name << " ";
numerics = ii.additional;
}
}
return ret.str();
}
InstructionInfo dev::eth::instructionInfo(Instruction _inst)
{
try
{
return c_instructionInfo.at(_inst);
}
catch (...)
{
cwarn << "<INVALID_INSTRUCTION: " << toString((unsigned)_inst) << ">\n" << boost::current_exception_diagnostic_information();
return InstructionInfo({"<INVALID_INSTRUCTION: " + toString((unsigned)_inst) + ">", 0, 0, 0, false});
}
}
bool dev::eth::isValidInstruction(Instruction _inst)
{
return !!c_instructionInfo.count(_inst);
}

30
libevmface/Instruction.h → libevmcore/Instruction.h

@ -22,10 +22,7 @@
#pragma once
#include <libdevcore/Common.h>
#include <libdevcore/Exceptions.h>
namespace boost { namespace spirit { class utree; } }
namespace sp = boost::spirit;
#include <libevmcore/Exceptions.h>
namespace dev
{
@ -176,6 +173,30 @@ enum class Instruction: uint8_t
SUICIDE = 0xff ///< halt execution and register account for later deletion
};
/// @returns the PUSH<_number> instruction
inline Instruction pushInstruction(unsigned _number)
{
if (asserts(1 <= _number && _number <= 32))
BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("Invalid PUSH instruction requested."));
return Instruction(unsigned(Instruction::PUSH1) + _number - 1);
}
/// @returns the DUP<_number> instruction
inline Instruction dupInstruction(unsigned _number)
{
if (asserts(1 <= _number && _number <= 16))
BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("Invalid DUP instruction requested."));
return Instruction(unsigned(Instruction::DUP1) + _number - 1);
}
/// @returns the SWAP<_number> instruction
inline Instruction swapInstruction(unsigned _number)
{
if (asserts(1 <= _number && _number <= 16))
BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("Invalid SWAP instruction requested."));
return Instruction(unsigned(Instruction::SWAP1) + _number - 1);
}
/// Information structure for a particular instruction.
struct InstructionInfo
{
@ -183,6 +204,7 @@ struct InstructionInfo
int additional; ///< Additional items required in memory for this instructions (only for PUSH).
int args; ///< Number of items required on the stack for this instruction (and, for the purposes of ret, the number taken from the stack).
int ret; ///< Number of items placed (back) on the stack by this instruction, assuming args items were removed.
bool sideEffects; ///< false if the only effect on the execution environment (apart from gas usage) is a change to a topmost segment of the stack
};
/// Information on all the instructions.

337
libevmface/Instruction.cpp

@ -1,337 +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 Instruction.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Instruction.h"
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
const std::map<std::string, Instruction> dev::eth::c_instructions =
{
{ "STOP", Instruction::STOP },
{ "ADD", Instruction::ADD },
{ "SUB", Instruction::SUB },
{ "MUL", Instruction::MUL },
{ "DIV", Instruction::DIV },
{ "SDIV", Instruction::SDIV },
{ "MOD", Instruction::MOD },
{ "SMOD", Instruction::SMOD },
{ "EXP", Instruction::EXP },
{ "BNOT", Instruction::NOT },
{ "LT", Instruction::LT },
{ "GT", Instruction::GT },
{ "SLT", Instruction::SLT },
{ "SGT", Instruction::SGT },
{ "EQ", Instruction::EQ },
{ "NOT", Instruction::ISZERO },
{ "AND", Instruction::AND },
{ "OR", Instruction::OR },
{ "XOR", Instruction::XOR },
{ "BYTE", Instruction::BYTE },
{ "ADDMOD", Instruction::ADDMOD },
{ "MULMOD", Instruction::MULMOD },
{ "SIGNEXTEND", Instruction::SIGNEXTEND },
{ "SHA3", Instruction::SHA3 },
{ "ADDRESS", Instruction::ADDRESS },
{ "BALANCE", Instruction::BALANCE },
{ "ORIGIN", Instruction::ORIGIN },
{ "CALLER", Instruction::CALLER },
{ "CALLVALUE", Instruction::CALLVALUE },
{ "CALLDATALOAD", Instruction::CALLDATALOAD },
{ "CALLDATASIZE", Instruction::CALLDATASIZE },
{ "CALLDATACOPY", Instruction::CALLDATACOPY },
{ "CODESIZE", Instruction::CODESIZE },
{ "CODECOPY", Instruction::CODECOPY },
{ "GASPRICE", Instruction::GASPRICE },
{ "EXTCODESIZE", Instruction::EXTCODESIZE },
{ "EXTCODECOPY", Instruction::EXTCODECOPY },
{ "PREVHASH", Instruction::PREVHASH },
{ "COINBASE", Instruction::COINBASE },
{ "TIMESTAMP", Instruction::TIMESTAMP },
{ "NUMBER", Instruction::NUMBER },
{ "DIFFICULTY", Instruction::DIFFICULTY },
{ "GASLIMIT", Instruction::GASLIMIT },
{ "POP", Instruction::POP },
{ "MLOAD", Instruction::MLOAD },
{ "MSTORE", Instruction::MSTORE },
{ "MSTORE8", Instruction::MSTORE8 },
{ "SLOAD", Instruction::SLOAD },
{ "SSTORE", Instruction::SSTORE },
{ "JUMP", Instruction::JUMP },
{ "JUMPI", Instruction::JUMPI },
{ "PC", Instruction::PC },
{ "MSIZE", Instruction::MSIZE },
{ "GAS", Instruction::GAS },
{ "JUMPDEST", Instruction::JUMPDEST },
{ "PUSH1", Instruction::PUSH1 },
{ "PUSH2", Instruction::PUSH2 },
{ "PUSH3", Instruction::PUSH3 },
{ "PUSH4", Instruction::PUSH4 },
{ "PUSH5", Instruction::PUSH5 },
{ "PUSH6", Instruction::PUSH6 },
{ "PUSH7", Instruction::PUSH7 },
{ "PUSH8", Instruction::PUSH8 },
{ "PUSH9", Instruction::PUSH9 },
{ "PUSH10", Instruction::PUSH10 },
{ "PUSH11", Instruction::PUSH11 },
{ "PUSH12", Instruction::PUSH12 },
{ "PUSH13", Instruction::PUSH13 },
{ "PUSH14", Instruction::PUSH14 },
{ "PUSH15", Instruction::PUSH15 },
{ "PUSH16", Instruction::PUSH16 },
{ "PUSH17", Instruction::PUSH17 },
{ "PUSH18", Instruction::PUSH18 },
{ "PUSH19", Instruction::PUSH19 },
{ "PUSH20", Instruction::PUSH20 },
{ "PUSH21", Instruction::PUSH21 },
{ "PUSH22", Instruction::PUSH22 },
{ "PUSH23", Instruction::PUSH23 },
{ "PUSH24", Instruction::PUSH24 },
{ "PUSH25", Instruction::PUSH25 },
{ "PUSH26", Instruction::PUSH26 },
{ "PUSH27", Instruction::PUSH27 },
{ "PUSH28", Instruction::PUSH28 },
{ "PUSH29", Instruction::PUSH29 },
{ "PUSH30", Instruction::PUSH30 },
{ "PUSH31", Instruction::PUSH31 },
{ "PUSH32", Instruction::PUSH32 },
{ "DUP1", Instruction::DUP1 },
{ "DUP2", Instruction::DUP2 },
{ "DUP3", Instruction::DUP3 },
{ "DUP4", Instruction::DUP4 },
{ "DUP5", Instruction::DUP5 },
{ "DUP6", Instruction::DUP6 },
{ "DUP7", Instruction::DUP7 },
{ "DUP8", Instruction::DUP8 },
{ "DUP9", Instruction::DUP9 },
{ "DUP10", Instruction::DUP10 },
{ "DUP11", Instruction::DUP11 },
{ "DUP12", Instruction::DUP12 },
{ "DUP13", Instruction::DUP13 },
{ "DUP14", Instruction::DUP14 },
{ "DUP15", Instruction::DUP15 },
{ "DUP16", Instruction::DUP16 },
{ "SWAP1", Instruction::SWAP1 },
{ "SWAP2", Instruction::SWAP2 },
{ "SWAP3", Instruction::SWAP3 },
{ "SWAP4", Instruction::SWAP4 },
{ "SWAP5", Instruction::SWAP5 },
{ "SWAP6", Instruction::SWAP6 },
{ "SWAP7", Instruction::SWAP7 },
{ "SWAP8", Instruction::SWAP8 },
{ "SWAP9", Instruction::SWAP9 },
{ "SWAP10", Instruction::SWAP10 },
{ "SWAP11", Instruction::SWAP11 },
{ "SWAP12", Instruction::SWAP12 },
{ "SWAP13", Instruction::SWAP13 },
{ "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 },
{ "RETURN", Instruction::RETURN },
{ "SUICIDE", Instruction::SUICIDE }
};
static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ // Add, Args, Ret
{ Instruction::STOP, { "STOP", 0, 0, 0 } },
{ Instruction::ADD, { "ADD", 0, 2, 1 } },
{ Instruction::SUB, { "SUB", 0, 2, 1 } },
{ Instruction::MUL, { "MUL", 0, 2, 1 } },
{ Instruction::DIV, { "DIV", 0, 2, 1 } },
{ Instruction::SDIV, { "SDIV", 0, 2, 1 } },
{ Instruction::MOD, { "MOD", 0, 2, 1 } },
{ Instruction::SMOD, { "SMOD", 0, 2, 1 } },
{ Instruction::EXP, { "EXP", 0, 2, 1 } },
{ Instruction::NOT, { "BNOT", 0, 1, 1 } },
{ Instruction::LT, { "LT", 0, 2, 1 } },
{ Instruction::GT, { "GT", 0, 2, 1 } },
{ Instruction::SLT, { "SLT", 0, 2, 1 } },
{ Instruction::SGT, { "SGT", 0, 2, 1 } },
{ Instruction::EQ, { "EQ", 0, 2, 1 } },
{ Instruction::ISZERO, { "NOT", 0, 1, 1 } },
{ Instruction::AND, { "AND", 0, 2, 1 } },
{ Instruction::OR, { "OR", 0, 2, 1 } },
{ Instruction::XOR, { "XOR", 0, 2, 1 } },
{ 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 } },
{ Instruction::ORIGIN, { "ORIGIN", 0, 0, 1 } },
{ Instruction::CALLER, { "CALLER", 0, 0, 1 } },
{ Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1 } },
{ Instruction::CALLDATALOAD,{ "CALLDATALOAD", 0, 1, 1 } },
{ Instruction::CALLDATASIZE,{ "CALLDATASIZE", 0, 0, 1 } },
{ Instruction::CALLDATACOPY,{ "CALLDATACOPY", 0, 3, 0 } },
{ Instruction::CODESIZE, { "CODESIZE", 0, 0, 1 } },
{ Instruction::CODECOPY, { "CODECOPY", 0, 3, 0 } },
{ Instruction::GASPRICE, { "GASPRICE", 0, 0, 1 } },
{ Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1 } },
{ Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0 } },
{ Instruction::PREVHASH, { "PREVHASH", 0, 0, 1 } },
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1 } },
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1 } },
{ Instruction::NUMBER, { "NUMBER", 0, 0, 1 } },
{ Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1 } },
{ Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1 } },
{ Instruction::POP, { "POP", 0, 1, 0 } },
{ Instruction::MLOAD, { "MLOAD", 0, 1, 1 } },
{ Instruction::MSTORE, { "MSTORE", 0, 2, 0 } },
{ Instruction::MSTORE8, { "MSTORE8", 0, 2, 0 } },
{ Instruction::SLOAD, { "SLOAD", 0, 1, 1 } },
{ Instruction::SSTORE, { "SSTORE", 0, 2, 0 } },
{ Instruction::JUMP, { "JUMP", 0, 1, 0 } },
{ Instruction::JUMPI, { "JUMPI", 0, 2, 0 } },
{ Instruction::PC, { "PC", 0, 0, 1 } },
{ Instruction::MSIZE, { "MSIZE", 0, 0, 1 } },
{ Instruction::GAS, { "GAS", 0, 0, 1 } },
{ Instruction::JUMPDEST, { "JUMPDEST", 0, 1, 0 } },
{ Instruction::PUSH1, { "PUSH1", 1, 0, 1 } },
{ Instruction::PUSH2, { "PUSH2", 2, 0, 1 } },
{ Instruction::PUSH3, { "PUSH3", 3, 0, 1 } },
{ Instruction::PUSH4, { "PUSH4", 4, 0, 1 } },
{ Instruction::PUSH5, { "PUSH5", 5, 0, 1 } },
{ Instruction::PUSH6, { "PUSH6", 6, 0, 1 } },
{ Instruction::PUSH7, { "PUSH7", 7, 0, 1 } },
{ Instruction::PUSH8, { "PUSH8", 8, 0, 1 } },
{ Instruction::PUSH9, { "PUSH9", 9, 0, 1 } },
{ Instruction::PUSH10, { "PUSH10", 10, 0, 1 } },
{ Instruction::PUSH11, { "PUSH11", 11, 0, 1 } },
{ Instruction::PUSH12, { "PUSH12", 12, 0, 1 } },
{ Instruction::PUSH13, { "PUSH13", 13, 0, 1 } },
{ Instruction::PUSH14, { "PUSH14", 14, 0, 1 } },
{ Instruction::PUSH15, { "PUSH15", 15, 0, 1 } },
{ Instruction::PUSH16, { "PUSH16", 16, 0, 1 } },
{ Instruction::PUSH17, { "PUSH17", 17, 0, 1 } },
{ Instruction::PUSH18, { "PUSH18", 18, 0, 1 } },
{ Instruction::PUSH19, { "PUSH19", 19, 0, 1 } },
{ Instruction::PUSH20, { "PUSH20", 20, 0, 1 } },
{ Instruction::PUSH21, { "PUSH21", 21, 0, 1 } },
{ Instruction::PUSH22, { "PUSH22", 22, 0, 1 } },
{ Instruction::PUSH23, { "PUSH23", 23, 0, 1 } },
{ Instruction::PUSH24, { "PUSH24", 24, 0, 1 } },
{ Instruction::PUSH25, { "PUSH25", 25, 0, 1 } },
{ Instruction::PUSH26, { "PUSH26", 26, 0, 1 } },
{ Instruction::PUSH27, { "PUSH27", 27, 0, 1 } },
{ Instruction::PUSH28, { "PUSH28", 28, 0, 1 } },
{ Instruction::PUSH29, { "PUSH29", 29, 0, 1 } },
{ Instruction::PUSH30, { "PUSH30", 30, 0, 1 } },
{ Instruction::PUSH31, { "PUSH31", 31, 0, 1 } },
{ Instruction::PUSH32, { "PUSH32", 32, 0, 1 } },
{ Instruction::DUP1, { "DUP1", 0, 1, 2 } },
{ Instruction::DUP2, { "DUP2", 0, 2, 3 } },
{ Instruction::DUP3, { "DUP3", 0, 3, 4 } },
{ Instruction::DUP4, { "DUP4", 0, 4, 5 } },
{ Instruction::DUP5, { "DUP5", 0, 5, 6 } },
{ Instruction::DUP6, { "DUP6", 0, 6, 7 } },
{ Instruction::DUP7, { "DUP7", 0, 7, 8 } },
{ Instruction::DUP8, { "DUP8", 0, 8, 9 } },
{ Instruction::DUP9, { "DUP9", 0, 9, 10 } },
{ Instruction::DUP10, { "DUP10", 0, 10, 11 } },
{ Instruction::DUP11, { "DUP11", 0, 11, 12 } },
{ Instruction::DUP12, { "DUP12", 0, 12, 13 } },
{ Instruction::DUP13, { "DUP13", 0, 13, 14 } },
{ Instruction::DUP14, { "DUP14", 0, 14, 15 } },
{ Instruction::DUP15, { "DUP15", 0, 15, 16 } },
{ Instruction::DUP16, { "DUP16", 0, 16, 17 } },
{ Instruction::SWAP1, { "SWAP1", 0, 2, 2 } },
{ Instruction::SWAP2, { "SWAP2", 0, 3, 3 } },
{ Instruction::SWAP3, { "SWAP3", 0, 4, 4 } },
{ Instruction::SWAP4, { "SWAP4", 0, 5, 5 } },
{ Instruction::SWAP5, { "SWAP5", 0, 6, 6 } },
{ Instruction::SWAP6, { "SWAP6", 0, 7, 7 } },
{ Instruction::SWAP7, { "SWAP7", 0, 8, 8 } },
{ Instruction::SWAP8, { "SWAP8", 0, 9, 9 } },
{ Instruction::SWAP9, { "SWAP9", 0, 10, 10 } },
{ Instruction::SWAP10, { "SWAP10", 0, 11, 11 } },
{ Instruction::SWAP11, { "SWAP11", 0, 12, 12 } },
{ Instruction::SWAP12, { "SWAP12", 0, 13, 13 } },
{ Instruction::SWAP13, { "SWAP13", 0, 14, 14 } },
{ 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 } },
{ Instruction::RETURN, { "RETURN", 0, 2, 0 } },
{ Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} }
};
string dev::eth::disassemble(bytes const& _mem)
{
stringstream ret;
unsigned numerics = 0;
for (auto it = _mem.begin(); it != _mem.end(); ++it)
{
byte n = *it;
auto iit = c_instructionInfo.find((Instruction)n);
if (numerics || iit == c_instructionInfo.end() || (byte)iit->first != n) // not an instruction or expecting an argument...
{
if (numerics)
numerics--;
ret << "0x" << hex << (int)n << " ";
}
else
{
auto const& ii = iit->second;
ret << ii.name << " ";
numerics = ii.additional;
}
}
return ret.str();
}
InstructionInfo dev::eth::instructionInfo(Instruction _inst)
{
try
{
return c_instructionInfo.at(_inst);
}
catch (...)
{
cwarn << "<INVALID_INSTRUCTION: " << toString((unsigned)_inst) << ">\n" << boost::current_exception_diagnostic_information();
return InstructionInfo({"<INVALID_INSTRUCTION: " + toString((unsigned)_inst) + ">", 0, 0, 0});
}
}
bool dev::eth::isValidInstruction(Instruction _inst)
{
return !!c_instructionInfo.count(_inst);
}

117
libjsqrc/main.js

@ -2,19 +2,19 @@
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
GNU Lesser General Public License for more details.
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ethereum.js
/** @file main.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2014
@ -66,82 +66,83 @@
var ethMethods = function () {
var blockCall = function (args) {
return typeof args[0] === "string" ? "blockByHash" : "blockByNumber";
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
};
var transactionCall = function (args) {
return typeof args[0] === "string" ? 'transactionByHash' : 'transactionByNumber';
return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber';
};
var uncleCall = function (args) {
return typeof args[0] === "string" ? 'uncleByHash' : 'uncleByNumber';
return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber';
};
var methods = [
{ name: 'balanceAt', call: 'balanceAt' },
{ name: 'stateAt', call: 'stateAt' },
{ name: 'countAt', call: 'countAt'},
{ name: 'codeAt', call: 'codeAt' },
{ name: 'transact', call: 'transact' },
{ name: 'call', call: 'call' },
{ name: 'balanceAt', call: 'eth_balanceAt' },
{ name: 'stateAt', call: 'eth_stateAt' },
{ name: 'countAt', call: 'eth_countAt'},
{ name: 'codeAt', call: 'eth_codeAt' },
{ name: 'transact', call: 'eth_transact' },
{ name: 'call', call: 'eth_call' },
{ name: 'block', call: blockCall },
{ name: 'transaction', call: transactionCall },
{ name: 'uncle', call: uncleCall },
{ name: 'compile', call: 'compile' }
{ name: 'compile', call: 'eth_compile' },
{ name: 'lll', call: 'eth_lll' }
];
return methods;
};
var ethProperties = function () {
return [
{ name: 'coinbase', getter: 'coinbase', setter: 'setCoinbase' },
{ name: 'listening', getter: 'listening', setter: 'setListening' },
{ name: 'mining', getter: 'mining', setter: 'setMining' },
{ name: 'gasPrice', getter: 'gasPrice' },
{ name: 'account', getter: 'account' },
{ name: 'accounts', getter: 'accounts' },
{ name: 'peerCount', getter: 'peerCount' },
{ name: 'defaultBlock', getter: 'defaultBlock', setter: 'setDefaultBlock' },
{ name: 'number', getter: 'number'}
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
{ name: 'gasPrice', getter: 'eth_gasPrice' },
{ name: 'account', getter: 'eth_account' },
{ name: 'accounts', getter: 'eth_accounts' },
{ name: 'peerCount', getter: 'eth_peerCount' },
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
{ name: 'number', getter: 'eth_number'}
];
};
var dbMethods = function () {
return [
{ name: 'put', call: 'put' },
{ name: 'get', call: 'get' },
{ name: 'putString', call: 'putString' },
{ name: 'getString', call: 'getString' }
{ name: 'put', call: 'db_put' },
{ name: 'get', call: 'db_get' },
{ name: 'putString', call: 'db_putString' },
{ name: 'getString', call: 'db_getString' }
];
};
var shhMethods = function () {
return [
{ name: 'post', call: 'post' },
{ name: 'newIdentity', call: 'newIdentity' },
{ name: 'haveIdentity', call: 'haveIdentity' },
{ name: 'newGroup', call: 'newGroup' },
{ name: 'addToGroup', call: 'addToGroup' }
{ name: 'post', call: 'shh_post' },
{ name: 'newIdentity', call: 'shh_newIdentity' },
{ name: 'haveIdentity', call: 'shh_haveIdentity' },
{ name: 'newGroup', call: 'shh_newGroup' },
{ name: 'addToGroup', call: 'shh_addToGroup' }
];
};
var ethWatchMethods = function () {
var newFilter = function (args) {
return typeof args[0] === 'string' ? 'newFilterString' : 'newFilter';
return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';
};
return [
{ name: 'newFilter', call: newFilter },
{ name: 'uninstallFilter', call: 'uninstallFilter' },
{ name: 'getMessages', call: 'getMessages' }
{ name: 'uninstallFilter', call: 'eth_uninstallFilter' },
{ name: 'getMessages', call: 'eth_getMessages' }
];
};
var shhWatchMethods = function () {
return [
{ name: 'newFilter', call: 'shhNewFilter' },
{ name: 'uninstallFilter', call: 'shhUninstallFilter' },
{ name: 'getMessage', call: 'shhGetMessages' }
{ name: 'newFilter', call: 'shh_newFilter' },
{ name: 'uninstallFilter', call: 'shh_uninstallFilter' },
{ name: 'getMessage', call: 'shh_getMessages' }
];
};
@ -153,15 +154,15 @@
return {call: call, args: args};
}).then(function (request) {
return new Promise(function (resolve, reject) {
web3.provider.send(request, function (result) {
if (result || typeof result === "boolean") {
web3.provider.send(request, function (err, result) {
if (!err) {
resolve(result);
return;
}
reject(result);
reject(err);
});
});
}).catch(function( err) {
}).catch(function(err) {
console.error(err);
});
};
@ -173,8 +174,12 @@
var proto = {};
proto.get = function () {
return new Promise(function(resolve, reject) {
web3.provider.send({call: property.getter}, function(result) {
resolve(result);
web3.provider.send({call: property.getter}, function(err, result) {
if (!err) {
resolve(result);
return;
}
reject(err);
});
});
};
@ -182,12 +187,12 @@
proto.set = function (val) {
return flattenPromise([val]).then(function (args) {
return new Promise(function (resolve) {
web3.provider.send({call: property.setter, args: args}, function (result) {
if (result) {
web3.provider.send({call: property.setter, args: args}, function (err, result) {
if (!err) {
resolve(result);
} else {
reject(result);
return;
}
reject(err);
});
});
}).catch(function (err) {
@ -217,6 +222,8 @@
// Find termination
var str = "";
var i = 0, l = hex.length;
if (hex.substring(0, 2) == '0x')
i = 2;
for(; i < l; i+=2) {
var code = hex.charCodeAt(i)
if(code == 0) {
@ -238,7 +245,7 @@
var hex = this.toHex(str);
while(hex.length < pad*2)
hex += "00";
return hex
return "0x" + hex;
},
eth: {
@ -293,11 +300,11 @@
setupMethods(web3.shh, shhMethods());
var ethWatch = {
changed: 'changed'
changed: 'eth_changed'
};
setupMethods(ethWatch, ethWatchMethods());
var shhWatch = {
changed: 'shhChanged'
changed: 'shh_changed'
};
setupMethods(shhWatch, shhWatchMethods());
@ -408,8 +415,10 @@
};
Filter.prototype.trigger = function(messages) {
for(var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i].call(this, messages);
if (!(messages instanceof Array) || messages.length) {
for(var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i].call(this, messages);
}
}
};
@ -438,7 +447,7 @@
if(data._id) {
var cb = web3._callbacks[data._id];
if (cb) {
cb.call(this, data.data)
cb.call(this, data.error, data.data);
delete web3._callbacks[data._id];
}
}

8
libjsqrc/qt.js

@ -2,19 +2,19 @@
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
GNU Lesser General Public License for more details.
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ethereum.js
/** @file qt.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2014

2
libjsqrc/setup.js

@ -14,7 +14,7 @@
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
/** @file setup.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2014

1
liblll/All.h

@ -1,6 +1,5 @@
#pragma once
#include "Assembly.h"
#include "CodeFragment.h"
#include "Compiler.h"
#include "CompilerState.h"

2
liblll/CMakeLists.txt

@ -15,7 +15,7 @@ endif()
include_directories(..)
target_link_libraries(${EXECUTABLE} evmface)
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)

2
liblll/CodeFragment.cpp

@ -25,7 +25,7 @@
#include <boost/spirit/include/support_utree.hpp>
#include <libdevcore/Log.h>
#include <libdevcore/CommonIO.h>
#include <libevmface/Instruction.h>
#include <libevmcore/Instruction.h>
#include "CompilerState.h"
#include "Parser.h"
using namespace std;

4
liblll/CodeFragment.h

@ -22,8 +22,8 @@
#pragma once
#include <libdevcore/Common.h>
#include <libevmface/Instruction.h>
#include "Assembly.h"
#include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h>
#include "Exceptions.h"
namespace boost { namespace spirit { class utree; } }

3
liblll/Exceptions.h

@ -32,16 +32,13 @@ namespace eth
class CompilerException: public dev::Exception {};
class InvalidOperation: public CompilerException {};
class IntegerOutOfRange: public CompilerException {};
class StringTooLong: public CompilerException {};
class EmptyList: public CompilerException {};
class DataNotExecutable: public CompilerException {};
class IncorrectParameterCount: public CompilerException {};
class InvalidDeposit: public CompilerException {};
class InvalidName: public CompilerException {};
class InvalidMacroArgs: public CompilerException {};
class InvalidLiteral: public CompilerException {};
class BareSymbol: public CompilerException {};
class ExpectedLiteral: public CompilerException {};
}
}

2
libpyserpent/CMakeLists.txt

@ -13,7 +13,7 @@ include_directories(..)
target_link_libraries(${EXECUTABLE} serpent)
target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} evmface)
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} ${PYTHON_LS})

2
libqethereum/CMakeLists.txt

@ -59,7 +59,7 @@ endif()
include_directories(/)
qt5_use_modules(${EXECUTABLE} Core Gui WebKit WebKitWidgets Widgets Network Quick Qml)
target_link_libraries(${EXECUTABLE} ethereum secp256k1 ${JSONRPC_LS})
target_link_libraries(${EXECUTABLE} ethereum secp256k1 ${JSON_RPC_CPP_LIBRARIES})
if (APPLE)
if (${ADDFRAMEWORKS})

30
libqethereum/QEthereum.cpp

@ -59,13 +59,13 @@ void QWebThree::poll()
{
if (m_watches.size() > 0)
{
QString batch = toJsonRpcBatch(m_watches, "changed");
emit processData(batch, "changed");
QString batch = toJsonRpcBatch(m_watches, "eth_changed");
emit processData(batch, "eth_changed");
}
if (m_shhWatches.size() > 0)
{
QString batch = toJsonRpcBatch(m_shhWatches, "shhChanged");
emit processData(batch, "shhChanged");
QString batch = toJsonRpcBatch(m_shhWatches, "shh_changed");
emit processData(batch, "shh_changed");
}
}
@ -73,13 +73,13 @@ void QWebThree::clearWatches()
{
if (m_watches.size() > 0)
{
QString batch = toJsonRpcBatch(m_watches, "uninstallFilter");
QString batch = toJsonRpcBatch(m_watches, "eth_uninstallFilter");
m_watches.clear();
emit processData(batch, "internal");
}
if (m_shhWatches.size() > 0)
{
QString batch = toJsonRpcBatch(m_shhWatches, "shhUninstallFilter");
QString batch = toJsonRpcBatch(m_shhWatches, "shh_uninstallFilter");
m_shhWatches.clear();
emit processData(batch, "internal");
}
@ -106,7 +106,12 @@ void QWebThree::postMessage(QString _json)
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
QString method = f["call"].toString();
if (!method.compare("uninstallFilter") && f["args"].isArray() && f["args"].toArray().size())
if (!method.compare("eth_uninstallFilter") && f["args"].isArray() && f["args"].toArray().size())
{
int idToRemove = f["args"].toArray()[0].toInt();
m_watches.erase(std::remove(m_watches.begin(), m_watches.end(), idToRemove), m_watches.end());
}
else if (!method.compare("eth_uninstallFilter") && f["args"].isArray() && f["args"].toArray().size())
{
int idToRemove = f["args"].toArray()[0].toInt();
m_watches.erase(std::remove(m_watches.begin(), m_watches.end(), idToRemove), m_watches.end());
@ -120,6 +125,7 @@ static QString formatOutput(QJsonObject const& _object)
QJsonObject res;
res["_id"] = _object["id"];
res["data"] = _object["result"];
res["error"] = _object["error"];
return QString::fromUtf8(QJsonDocument(res).toJson());
}
@ -128,7 +134,7 @@ void QWebThree::onDataProcessed(QString _json, QString _addInfo)
if (!_addInfo.compare("internal"))
return;
if (!_addInfo.compare("changed"))
if (!_addInfo.compare("eth_changed"))
{
QJsonArray resultsArray = QJsonDocument::fromJson(_json.toUtf8()).array();
for (int i = 0; i < resultsArray.size(); i++)
@ -145,7 +151,7 @@ void QWebThree::onDataProcessed(QString _json, QString _addInfo)
return;
}
if (!_addInfo.compare("shhChanged"))
if (!_addInfo.compare("shh_changed"))
{
QJsonArray resultsArray = QJsonDocument::fromJson(_json.toUtf8()).array();
for (int i = 0; i < resultsArray.size(); i++)
@ -164,11 +170,11 @@ void QWebThree::onDataProcessed(QString _json, QString _addInfo)
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
if ((!_addInfo.compare("newFilter") || !_addInfo.compare("newFilterString")) && f.contains("result"))
if ((!_addInfo.compare("eth_newFilter") || !_addInfo.compare("eth_newFilterString")) && f.contains("result"))
m_watches.push_back(f["result"].toInt());
if (!_addInfo.compare("shhNewFilter") && f.contains("result"))
else if (!_addInfo.compare("shh_newFilter") && f.contains("result"))
m_shhWatches.push_back(f["result"].toInt());
if (!_addInfo.compare("newIdentity") && f.contains("result"))
else if (!_addInfo.compare("shh_newIdentity") && f.contains("result"))
emit onNewId(f["result"].toString());
response(formatOutput(f));

2
libqethereum/QmlEthereum.cpp

@ -4,7 +4,7 @@
#include <QtCore/QtCore>
#include <QtWebKitWidgets/QWebFrame>
#include <libdevcrypto/FileSystem.h>
#include <libevmface/Instruction.h>
#include <libevmcore/Instruction.h>
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
#include <libethereum/EthereumHost.h>

2
libserpent/CMakeLists.txt

@ -16,7 +16,7 @@ endif()
include_directories(..)
target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} evmface)
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
if("${TARGET_PLATFORM}" STREQUAL "w64")

14
libserpent/bignum.cpp

@ -48,6 +48,20 @@ std::string decimalMul(std::string a, std::string b) {
return o;
}
//Modexp
std::string decimalModExp(std::string b, std::string e, std::string m) {
if (e == "0") return "1";
else if (e == "1") return b;
else if (decimalMod(e, "2") == "0") {
std::string o = decimalModExp(b, decimalDiv(e, "2"), m);
return decimalMod(decimalMul(o, o), m);
}
else {
std::string o = decimalModExp(b, decimalDiv(e, "2"), m);
return decimalMod(decimalMul(decimalMul(o, o), b), m);
}
}
//Is a greater than b? Flag allows equality
bool decimalGt(std::string a, std::string b, bool eqAllowed) {
if (a == b) return eqAllowed;

12
libserpent/bignum.h

@ -7,10 +7,16 @@ const std::string tt256 =
"115792089237316195423570985008687907853269984665640564039457584007913129639936"
;
const std::string tt255 =
"57896044618658097711785492504343953926634992332820282019728792003956564819968"
const std::string tt256m1 =
"115792089237316195423570985008687907853269984665640564039457584007913129639935"
;
const std::string tt255 =
"57896044618658097711785492504343953926634992332820282019728792003956564819968";
const std::string tt176 =
"95780971304118053647396689196894323976171195136475136";
std::string unsignedToDecimal(unsigned branch);
std::string decimalAdd(std::string a, std::string b);
@ -23,6 +29,8 @@ std::string decimalDiv(std::string a, std::string b);
std::string decimalMod(std::string a, std::string b);
std::string decimalModExp(std::string b, std::string e, std::string m);
bool decimalGt(std::string a, std::string b, bool eqAllowed=false);
unsigned decimalToUnsigned(std::string a);

212
libserpent/compiler.cpp

@ -8,10 +8,18 @@
struct programAux {
std::map<std::string, std::string> vars;
int nextVarMem;
bool allocUsed;
bool calldataUsed;
int step;
int labelLength;
int functionCount;
};
struct programVerticalAux {
int height;
std::map<std::string, int> dupvars;
std::map<std::string, int> funvars;
};
struct programData {
@ -25,6 +33,16 @@ programAux Aux() {
o.allocUsed = false;
o.calldataUsed = false;
o.step = 0;
o.nextVarMem = 32;
o.functionCount = 0;
return o;
}
programVerticalAux verticalAux() {
programVerticalAux o;
o.height = 0;
o.dupvars = std::map<std::string, int>();
o.funvars = std::map<std::string, int>();
return o;
}
@ -57,28 +75,28 @@ Node popwrap(Node node) {
// Turns LLL tree into tree of code fragments
programData opcodeify(Node node,
programAux aux=Aux(),
int height=0,
std::map<std::string, int> dupvars=
std::map<std::string, int>()) {
programVerticalAux vaux=verticalAux()) {
std::string symb = "_"+mkUniqueToken();
Metadata m = node.metadata;
// Numbers
if (node.type == TOKEN) {
return pd(aux, nodeToNumeric(node), 1);
}
else if (node.val == "ref" || node.val == "get" || node.val == "set") {
else if (node.val == "ref" || node.val == "get" ||
node.val == "set" || node.val == "declare") {
std::string varname = node.args[0].val;
if (!aux.vars.count(varname)) {
aux.vars[varname] = unsignedToDecimal(aux.vars.size() * 32);
aux.vars[varname] = unsignedToDecimal(aux.nextVarMem);
aux.nextVarMem += 32;
}
if (varname == "'msg.data") aux.calldataUsed = true;
// Set variable
if (node.val == "set") {
programData sub = opcodeify(node.args[1], aux, height, dupvars);
programData sub = opcodeify(node.args[1], aux, vaux);
if (!sub.outs)
err("Value to set variable must have nonzero arity!", m);
if (dupvars.count(node.args[0].val)) {
int h = height - dupvars[node.args[0].val];
if (vaux.dupvars.count(node.args[0].val)) {
int h = vaux.height - vaux.dupvars[node.args[0].val];
if (h > 16) err("Too deep for stack variable (max 16)", m);
Node nodelist[] = {
sub.code,
@ -96,8 +114,8 @@ programData opcodeify(Node node,
}
// Get variable
else if (node.val == "get") {
if (dupvars.count(node.args[0].val)) {
int h = height - dupvars[node.args[0].val];
if (vaux.dupvars.count(node.args[0].val)) {
int h = vaux.height - vaux.dupvars[node.args[0].val];
if (h > 16) err("Too deep for stack variable (max 16)", m);
return pd(aux, token("DUP"+unsignedToDecimal(h)), 1);
}
@ -106,36 +124,157 @@ programData opcodeify(Node node,
return pd(aux, multiToken(nodelist, 2, m), 1);
}
// Refer variable
else {
if (dupvars.count(node.args[0].val))
else if (node.val == "ref") {
if (vaux.dupvars.count(node.args[0].val))
err("Cannot ref stack variable!", m);
return pd(aux, token(aux.vars[varname], m), 1);
}
// Declare variable
else {
Node nodelist[] = { };
return pd(aux, multiToken(nodelist, 0, m), 0);
}
}
// Define functions (TODO: eventually move to rewriter.cpp, keep
// compiler pure LLL)
if (node.val == "def") {
std::vector<std::string> varNames;
std::vector<int> varSizes;
bool useLt32 = false;
int totalSz = 0;
if (node.args.size() != 2)
err("Malformed def!", m);
// Collect the list of variable names and variable byte counts
for (unsigned i = 0; i < node.args[0].args.size(); i++) {
if (node.args[0].args[i].val == "kv") {
if (node.args[0].args[i].args.size() != 2)
err("Malformed def!", m);
varNames.push_back(node.args[0].args[i].args[0].val);
varSizes.push_back(
decimalToUnsigned(node.args[0].args[i].args[1].val));
if (varSizes.back() > 32)
err("Max argument width: 32 bytes", m);
useLt32 = true;
}
else {
varNames.push_back(node.args[0].args[i].val);
varSizes.push_back(32);
}
aux.vars[varNames.back()] = unsignedToDecimal(aux.nextVarMem + 32 * i);
totalSz += varSizes.back();
}
int functionCount = aux.functionCount;
int nextVarMem = aux.nextVarMem;
aux.nextVarMem += 32 * varNames.size();
aux.functionCount += 1;
programData inner;
// If we're only using 32-byte variables, then great, just copy
// over the calldata!
if (!useLt32) {
programData sub = opcodeify(node.args[1], aux, vaux);
Node nodelist[] = {
token(unsignedToDecimal(totalSz), m),
token("1", m),
token(unsignedToDecimal(nextVarMem), m),
token("CALLDATACOPY", m),
sub.code
};
inner = pd(sub.aux, multiToken(nodelist, 5, m), 0);
}
else {
std::vector<Node> innerList;
int cum = 1;
for (unsigned i = 0; i < varNames.size();) {
// If we get a series of 32-byte values, we calldatacopy them
if (varSizes[i] == 32) {
unsigned until = i+1;
while (until < varNames.size() && varSizes[until] == 32)
until += 1;
innerList.push_back(token(unsignedToDecimal((until - i) * 32), m));
innerList.push_back(token(unsignedToDecimal(cum), m));
innerList.push_back(token(unsignedToDecimal(nextVarMem + i * 32), m));
innerList.push_back(token("CALLDATACOPY", m));
cum += (until - i) * 32;
i = until;
}
// Otherwise, we do a clever trick to extract the value
else {
innerList.push_back(token(unsignedToDecimal(32 - varSizes[i]), m));
innerList.push_back(token("256", m));
innerList.push_back(token("EXP", m));
innerList.push_back(token(unsignedToDecimal(cum), m));
innerList.push_back(token("CALLDATALOAD", m));
innerList.push_back(token("DIV", m));
innerList.push_back(token(unsignedToDecimal(nextVarMem + i * 32), m));
innerList.push_back(token("MSTORE", m));
cum += varSizes[i];
i += 1;
}
}
// If caller == origin, then it's from a tx, so unpack, otherwise
// plain copy
programData sub = opcodeify(node.args[1], aux, vaux);
Node ilnode = astnode("", innerList, m);
Node nodelist[] = {
token(unsignedToDecimal(32 * varNames.size()), m),
token("1", m),
token(unsignedToDecimal(nextVarMem), m),
token("CALLDATACOPY", m),
token("CALLER", m),
token("ORIGIN", m),
token("EQ", m),
token("ISZERO", m),
token("$maincode"+symb, m),
token("JUMPI", m),
ilnode,
token("~maincode"+symb, m),
token("JUMPDEST", m),
sub.code
};
inner = pd(sub.aux, multiToken(nodelist, 14, m), 0);
}
// Check if the function call byte is the same
Node nodelist2[] = {
token("0", m),
token("CALLDATALOAD", m),
token("0", m),
token("BYTE", m),
token(unsignedToDecimal(functionCount), m),
token("EQ", m),
token("ISZERO", m),
token("$endcode"+symb, m),
token("JUMPI", m),
inner.code,
token("~endcode"+symb, m),
token("JUMPDEST", m),
};
return pd(inner.aux, multiToken(nodelist2, 12, m), 0);
}
// Code blocks
if (node.val == "lll" && node.args.size() == 2) {
if (node.args[1].val != "0") aux.allocUsed = true;
std::vector<Node> o;
o.push_back(finalize(opcodeify(node.args[0])));
programData sub = opcodeify(node.args[1], aux, height, dupvars);
programData sub = opcodeify(node.args[1], aux, vaux);
Node code = astnode("____CODE", o, m);
Node nodelist[] = {
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("JUMPDEST", m)
token("~begincode"+symb, m), code,
token("~endcode"+symb, m), token("JUMPDEST", m)
};
return pd(sub.aux, multiToken(nodelist, 11, m), 1);
}
// Stack variables
if (node.val == "with") {
std::map<std::string, int> dupvars2 = dupvars;
dupvars2[node.args[0].val] = height;
programData initial = opcodeify(node.args[1], aux, height, dupvars);
programData initial = opcodeify(node.args[1], aux, vaux);
programVerticalAux vaux2 = vaux;
vaux2.dupvars[node.args[0].val] = vaux.height;
vaux2.height += 1;
if (!initial.outs)
err("Initial variable value must have nonzero arity!", m);
programData sub = opcodeify(node.args[2], initial.aux, height + 1, dupvars2);
programData sub = opcodeify(node.args[2], initial.aux, vaux2);
Node nodelist[] = {
initial.code,
sub.code
@ -151,7 +290,7 @@ programData opcodeify(Node node,
std::vector<Node> children;
int lastOut = 0;
for (unsigned i = 0; i < node.args.size(); i++) {
programData sub = opcodeify(node.args[i], aux, height, dupvars);
programData sub = opcodeify(node.args[i], aux, vaux);
aux = sub.aux;
if (sub.outs == 1) {
if (i < node.args.size() - 1) sub.code = popwrap(sub.code);
@ -163,8 +302,8 @@ programData opcodeify(Node node,
}
// 2-part conditional (if gets rewritten to unless in rewrites)
else if (node.val == "unless" && node.args.size() == 2) {
programData cond = opcodeify(node.args[0], aux, height, dupvars);
programData action = opcodeify(node.args[1], cond.aux, height, dupvars);
programData cond = opcodeify(node.args[0], aux, vaux);
programData action = opcodeify(node.args[1], cond.aux, vaux);
aux = action.aux;
if (!cond.outs) err("Condition of if/unless statement has arity 0", m);
if (action.outs) action.code = popwrap(action.code);
@ -178,9 +317,9 @@ programData opcodeify(Node node,
}
// 3-part conditional
else if (node.val == "if" && node.args.size() == 3) {
programData ifd = opcodeify(node.args[0], aux, height, dupvars);
programData thend = opcodeify(node.args[1], ifd.aux, height, dupvars);
programData elsed = opcodeify(node.args[2], thend.aux, height, dupvars);
programData ifd = opcodeify(node.args[0], aux, vaux);
programData thend = opcodeify(node.args[1], ifd.aux, vaux);
programData elsed = opcodeify(node.args[2], thend.aux, vaux);
aux = elsed.aux;
if (!ifd.outs)
err("Condition of if/unless statement has arity 0", m);
@ -191,7 +330,7 @@ programData opcodeify(Node node,
if (elsed.outs > outs) elsed.code = popwrap(elsed.code);
Node nodelist[] = {
ifd.code,
token("NOT", m),
token("ISZERO", m),
token("$else"+symb, m), token("JUMPI", m),
thend.code,
token("$endif"+symb, m), token("JUMP", m),
@ -203,8 +342,8 @@ programData opcodeify(Node node,
}
// While (rewritten to this in rewrites)
else if (node.val == "until") {
programData cond = opcodeify(node.args[0], aux, height, dupvars);
programData action = opcodeify(node.args[1], cond.aux, height, dupvars);
programData cond = opcodeify(node.args[0], aux, vaux);
programData action = opcodeify(node.args[1], cond.aux, vaux);
aux = action.aux;
if (!cond.outs)
err("Condition of while/until loop has arity 0", m);
@ -215,13 +354,13 @@ programData opcodeify(Node node,
token("$end"+symb, m), token("JUMPI", m),
action.code,
token("$beg"+symb, m), token("JUMP", m),
token("~end"+symb, m), token("JUMPDEST", m)
token("~end"+symb, m), token("JUMPDEST", m),
};
return pd(aux, multiToken(nodelist, 10, m));
}
// Memory allocations
else if (node.val == "alloc") {
programData bytez = opcodeify(node.args[0], aux, height, dupvars);
programData bytez = opcodeify(node.args[0], aux, vaux);
aux = bytez.aux;
if (!bytez.outs)
err("Alloc input has arity 0", m);
@ -251,7 +390,9 @@ programData opcodeify(Node node,
for (unsigned i = 0; i < node.args.size(); i++) {
Metadata m2 = node.args[i].metadata;
nodes.push_back(token("DUP1", m2));
programData sub = opcodeify(node.args[i], aux, height + 2, dupvars);
programVerticalAux vaux2 = vaux;
vaux2.height += 2;
programData sub = opcodeify(node.args[i], aux, vaux2);
if (!sub.outs)
err("Array_lit item " + unsignedToDecimal(i) + " has zero arity", m2);
aux = sub.aux;
@ -276,10 +417,9 @@ programData opcodeify(Node node,
err("Invalid arity for "+node.val, m);
}
for (int i = node.args.size() - 1; i >= 0; i--) {
programData sub = opcodeify(node.args[i],
aux,
height - i - 1 + node.args.size(),
dupvars);
programVerticalAux vaux2 = vaux;
vaux2.height = vaux.height - i - 1 + node.args.size();
programData sub = opcodeify(node.args[i], aux, vaux2);
aux = sub.aux;
if (!sub.outs)
err("Input "+unsignedToDecimal(i)+" has arity 0", sub.code.metadata);
@ -305,7 +445,7 @@ Node finalize(programData c) {
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)),
token(unsignedToDecimal(c.aux.nextVarMem - 1)),
token("MSTORE8", m)
};
bottom.push_back(multiToken(nodelist, 3, m));

224
libserpent/opcodes.h

@ -1,20 +1,3 @@
/*
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/>.
*/
#ifndef ETHSERP_OPCODES
#define ETHSERP_OPCODES
@ -24,128 +7,131 @@
#include <map>
class Mapping {
public:
Mapping(std::string Op, int Opcode, int In, int Out) {
op = Op;
opcode = Opcode;
in = In;
out = Out;
}
std::string op;
int opcode;
int in;
int out;
public:
Mapping(std::string Op, int Opcode, int In, int Out) {
op = Op;
opcode = Opcode;
in = In;
out = Out;
}
std::string op;
int opcode;
int in;
int out;
};
Mapping mapping[] = {
Mapping("STOP", 0x00, 0, 0),
Mapping("ADD", 0x01, 2, 1),
Mapping("MUL", 0x02, 2, 1),
Mapping("SUB", 0x03, 2, 1),
Mapping("DIV", 0x04, 2, 1),
Mapping("SDIV", 0x05, 2, 1),
Mapping("MOD", 0x06, 2, 1),
Mapping("SMOD", 0x07, 2, 1),
Mapping("EXP", 0x08, 2, 1),
Mapping("NEG", 0x09, 1, 1),
Mapping("LT", 0x0a, 2, 1),
Mapping("GT", 0x0b, 2, 1),
Mapping("SLT", 0x0c, 2, 1),
Mapping("SGT", 0x0d, 2, 1),
Mapping("EQ", 0x0e, 2, 1),
Mapping("NOT", 0x0f, 1, 1),
Mapping("AND", 0x10, 2, 1),
Mapping("OR", 0x11, 2, 1),
Mapping("XOR", 0x12, 2, 1),
Mapping("BYTE", 0x13, 2, 1),
Mapping("ADDMOD", 0x14, 3, 1),
Mapping("MULMOD", 0x15, 3, 1),
Mapping("SIGNEXTEND", 0x16, 2, 1),
Mapping("SHA3", 0x20, 2, 1),
Mapping("ADDRESS", 0x30, 0, 1),
Mapping("BALANCE", 0x31, 1, 1),
Mapping("ORIGIN", 0x32, 0, 1),
Mapping("CALLER", 0x33, 0, 1),
Mapping("CALLVALUE", 0x34, 0, 1),
Mapping("CALLDATALOAD", 0x35, 1, 1),
Mapping("CALLDATASIZE", 0x36, 0, 1),
Mapping("CALLDATACOPY", 0x37, 3, 1),
Mapping("CODESIZE", 0x38, 0, 1),
Mapping("CODECOPY", 0x39, 3, 1),
Mapping("GASPRICE", 0x3a, 0, 1),
Mapping("PREVHASH", 0x40, 0, 1),
Mapping("COINBASE", 0x41, 0, 1),
Mapping("TIMESTAMP", 0x42, 0, 1),
Mapping("NUMBER", 0x43, 0, 1),
Mapping("DIFFICULTY", 0x44, 0, 1),
Mapping("GASLIMIT", 0x45, 0, 1),
Mapping("POP", 0x50, 1, 0),
Mapping("MLOAD", 0x53, 1, 1),
Mapping("MSTORE", 0x54, 2, 0),
Mapping("MSTORE8", 0x55, 2, 0),
Mapping("SLOAD", 0x56, 1, 1),
Mapping("SSTORE", 0x57, 2, 0),
Mapping("JUMP", 0x58, 1, 0),
Mapping("JUMPI", 0x59, 2, 0),
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("CALL_CODE", 0xf3, 7, 1),
Mapping("SUICIDE", 0xff, 1, 0),
Mapping("---END---", 0x00, 0, 0),
Mapping("STOP", 0x00, 0, 0),
Mapping("ADD", 0x01, 2, 1),
Mapping("MUL", 0x02, 2, 1),
Mapping("SUB", 0x03, 2, 1),
Mapping("DIV", 0x04, 2, 1),
Mapping("SDIV", 0x05, 2, 1),
Mapping("MOD", 0x06, 2, 1),
Mapping("SMOD", 0x07, 2, 1),
Mapping("ADDMOD", 0x08, 3, 1),
Mapping("MULMOD", 0x09, 3, 1),
Mapping("EXP", 0x0a, 2, 1),
Mapping("SIGNEXTEND", 0x0b, 2, 1),
Mapping("LT", 0x10, 2, 1),
Mapping("GT", 0x11, 2, 1),
Mapping("SLT", 0x12, 2, 1),
Mapping("SGT", 0x13, 2, 1),
Mapping("EQ", 0x14, 2, 1),
Mapping("ISZERO", 0x15, 1, 1),
Mapping("AND", 0x16, 2, 1),
Mapping("OR", 0x17, 2, 1),
Mapping("XOR", 0x18, 2, 1),
Mapping("NOT", 0x19, 1, 1),
Mapping("BYTE", 0x1a, 2, 1),
Mapping("ADDMOD", 0x14, 3, 1),
Mapping("MULMOD", 0x15, 3, 1),
Mapping("SIGNEXTEND", 0x16, 2, 1),
Mapping("SHA3", 0x20, 2, 1),
Mapping("ADDRESS", 0x30, 0, 1),
Mapping("BALANCE", 0x31, 1, 1),
Mapping("ORIGIN", 0x32, 0, 1),
Mapping("CALLER", 0x33, 0, 1),
Mapping("CALLVALUE", 0x34, 0, 1),
Mapping("CALLDATALOAD", 0x35, 1, 1),
Mapping("CALLDATASIZE", 0x36, 0, 1),
Mapping("CALLDATACOPY", 0x37, 3, 1),
Mapping("CODESIZE", 0x38, 0, 1),
Mapping("CODECOPY", 0x39, 3, 1),
Mapping("GASPRICE", 0x3a, 0, 1),
Mapping("PREVHASH", 0x40, 0, 1),
Mapping("COINBASE", 0x41, 0, 1),
Mapping("TIMESTAMP", 0x42, 0, 1),
Mapping("NUMBER", 0x43, 0, 1),
Mapping("DIFFICULTY", 0x44, 0, 1),
Mapping("GASLIMIT", 0x45, 0, 1),
Mapping("POP", 0x50, 1, 0),
Mapping("MLOAD", 0x51, 1, 1),
Mapping("MSTORE", 0x52, 2, 0),
Mapping("MSTORE8", 0x53, 2, 0),
Mapping("SLOAD", 0x54, 1, 1),
Mapping("SSTORE", 0x55, 2, 0),
Mapping("JUMP", 0x56, 1, 0),
Mapping("JUMPI", 0x57, 2, 0),
Mapping("PC", 0x58, 0, 1),
Mapping("MSIZE", 0x59, 0, 1),
Mapping("GAS", 0x5a, 0, 1),
Mapping("JUMPDEST", 0x5b, 0, 0),
Mapping("LOG0", 0xa0, 2, 0),
Mapping("LOG1", 0xa1, 3, 0),
Mapping("LOG2", 0xa2, 4, 0),
Mapping("LOG3", 0xa3, 5, 0),
Mapping("LOG4", 0xa4, 6, 0),
Mapping("CREATE", 0xf0, 3, 1),
Mapping("CALL", 0xf1, 7, 1),
Mapping("RETURN", 0xf2, 2, 0),
Mapping("CALL_CODE", 0xf3, 7, 1),
Mapping("SUICIDE", 0xff, 1, 0),
Mapping("---END---", 0x00, 0, 0),
};
std::map<std::string, std::vector<int> > opcodes;
std::map<int, std::string> reverseOpcodes;
// Fetches everything EXCEPT PUSH1..32
std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi)
{
if (!opcodes.size())
{
int i = 0;
while (mapping[i].op != "---END---")
{
Mapping mi = mapping[i];
opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out);
i++;
}
for (i = 1; i <= 16; i++)
{
opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1);
opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1);
}
for (std::map<std::string, std::vector<int> >::iterator it=opcodes.begin(); it != opcodes.end(); it++)
reverseOpcodes[(*it).second[0]] = (*it).first;
}
std::string op;
std::vector<int> opdata;
op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : "";
opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1);
return std::pair<std::string, std::vector<int> >(op, opdata);
std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi) {
if (!opcodes.size()) {
int i = 0;
while (mapping[i].op != "---END---") {
Mapping mi = mapping[i];
opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out);
i++;
}
for (i = 1; i <= 16; i++) {
opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1);
opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1);
}
for (std::map<std::string, std::vector<int> >::iterator it=opcodes.begin();
it != opcodes.end();
it++) {
reverseOpcodes[(*it).second[0]] = (*it).first;
}
}
std::string op;
std::vector<int> opdata;
op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : "";
opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1);
return std::pair<std::string, std::vector<int> >(op, opdata);
}
int opcode(std::string op)
{
int opcode(std::string op) {
return _opdata(op, -1).second[0];
}
int opinputs(std::string op)
{
int opinputs(std::string op) {
return _opdata(op, -1).second[1];
}
int opoutputs(std::string op)
{
int opoutputs(std::string op) {
return _opdata(op, -1).second[2];
}
std::string op(int opcode)
{
std::string op(int opcode) {
return _opdata("", opcode).first;
}

53
libserpent/parser.cpp

@ -9,20 +9,21 @@
// Extended BEDMAS precedence order
int precedence(Node tok) {
std::string v = tok.val;
if (v == "!" || v == "not") return 0;
else if (v=="^" || v == "**") return 1;
else if (v=="*" || v=="/" || v=="@/" || v=="%" || v=="@%") return 2;
else if (v=="+" || v=="-") return 3;
else if (v=="<" || v==">" || v=="<=" || v==">=") return 4;
else if (v=="@<" || v=="@>" || v=="@<=" || v=="@>=") return 4;
else if (v=="&" || v=="|" || v=="xor" || v=="==" || v == "!=") return 5;
else if (v=="&&" || v=="and") return 6;
else if (v=="||" || v=="or") return 7;
else if (v==":") return 8;
if (v == ".") return -1;
else if (v == "!" || v == "not") return 1;
else if (v=="^" || v == "**") return 2;
else if (v=="*" || v=="/" || v=="@/" || v=="%" || v=="@%") return 3;
else if (v=="+" || v=="-") return 4;
else if (v=="<" || v==">" || v=="<=" || v==">=") return 5;
else if (v=="@<" || v=="@>" || v=="@<=" || v=="@>=") return 5;
else if (v=="&" || v=="|" || v=="xor" || v=="==" || v == "!=") return 6;
else if (v=="&&" || v=="and") return 7;
else if (v=="||" || v=="or") return 8;
else if (v==":") return 9;
else if (v=="=") return 10;
else if (v=="+=" || v=="-=" || v=="*=" || v=="/=" || v=="%=") return 10;
else if (v=="@/=" || v=="@%=") return 10;
else return -1;
else return 0;
}
// Token classification for shunting-yard purposes
@ -32,8 +33,9 @@ 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" || v == "neg") return UNARY_OP;
else if (precedence(tok) >= 0) return BINARY_OP;
else if (v == "!" || v == "~" || v == "not") return UNARY_OP;
else if (precedence(tok) > 0) return BINARY_OP;
else if (precedence(tok) < 0) return TOKEN_SPLITTER;
if (tok.val[0] != '"' && tok.val[0] != '\'') {
for (unsigned i = 0; i < tok.val.length(); i++) {
if (chartype(tok.val[i]) == SYMB) {
@ -68,6 +70,10 @@ std::vector<Node> shuntingYard(std::vector<Node> tokens) {
}
// Left parens go on stack and output queue
else if (toktyp == LPAREN) {
while (stack.size() && toktype(stack.back()) == TOKEN_SPLITTER) {
oq.push_back(stack.back());
stack.pop_back();
}
if (prevtyp != ALPHANUM && prevtyp != RPAREN) {
oq.push_back(token("id", tok.metadata));
}
@ -88,16 +94,26 @@ std::vector<Node> shuntingYard(std::vector<Node> tokens) {
else if (toktyp == UNARY_OP) {
stack.push_back(tok);
}
// If token splitter, just push it to the stack
else if (toktyp == TOKEN_SPLITTER) {
while (stack.size() && toktype(stack.back()) == TOKEN_SPLITTER) {
oq.push_back(stack.back());
stack.pop_back();
}
stack.push_back(tok);
}
// If binary op, keep popping from stack while higher bedmas precedence
else if (toktyp == BINARY_OP) {
if (tok.val == "-" && prevtyp != ALPHANUM && prevtyp != RPAREN) {
stack.push_back(token("neg", tok.metadata));
stack.push_back(tok);
oq.push_back(token("0", tok.metadata));
}
else {
int prec = precedence(tok);
while (stack.size()
&& (toktype(stack.back()) == BINARY_OP
|| toktype(stack.back()) == UNARY_OP)
|| toktype(stack.back()) == UNARY_OP
|| toktype(stack.back()) == TOKEN_SPLITTER)
&& precedence(stack.back()) <= prec) {
oq.push_back(stack.back());
stack.pop_back();
@ -133,9 +149,9 @@ Node treefy(std::vector<Node> stream) {
int typ = toktype(tok);
// If unary, take node off end of oq and wrap it with the operator
// If binary, do the same with two nodes
if (typ == UNARY_OP || typ == BINARY_OP) {
if (typ == UNARY_OP || typ == BINARY_OP || typ == TOKEN_SPLITTER) {
std::vector<Node> args;
int rounds = (typ == BINARY_OP) ? 2 : 1;
int rounds = (typ == UNARY_OP) ? 1 : 2;
for (int i = 0; i < rounds; i++) {
if (oq.size() == 0) {
err("Line malformed, not enough args for "+tok.val,
@ -245,7 +261,8 @@ int spaceCount(std::string s) {
// Is this a command that takes an argument on the same line?
bool bodied(std::string tok) {
return tok == "if" || tok == "elif" || tok == "while"
|| tok == "with" || tok == "def";
|| tok == "with" || tok == "def" || tok == "extern"
|| tok == "data";
}
// Is this a command that takes an argument as a child block?

870
libserpent/rewriter.cpp

File diff suppressed because it is too large

2
libserpent/tokenize.cpp

@ -13,7 +13,7 @@ int chartype(char c) {
if (c >= '0' && c <= '9') return ALPHANUM;
else if (c >= 'a' && c <= 'z') return ALPHANUM;
else if (c >= 'A' && c <= 'Z') return ALPHANUM;
else if (std::string("~._$").find(c) != std::string::npos) return ALPHANUM;
else if (std::string("~_$").find(c) != std::string::npos) return ALPHANUM;
else if (c == '\t' || c == ' ' || c == '\n') return SPACE;
else if (std::string("()[]{}").find(c) != std::string::npos) return BRACK;
else if (c == '"') return DQUOTE;

22
libserpent/util.cpp

@ -26,6 +26,28 @@ Node astnode(std::string val, std::vector<Node> args, Metadata met) {
return o;
}
//AST node constructors for a specific number of children
Node astnode(std::string val, Node a, Metadata met) {
std::vector<Node> args;
args.push_back(a);
return astnode(val, args, met);
}
Node astnode(std::string val, Node a, Node b, Metadata met) {
std::vector<Node> args;
args.push_back(a);
args.push_back(b);
return astnode(val, args, met);
}
Node astnode(std::string val, Node a, Node b, Node c, Metadata met) {
std::vector<Node> args;
args.push_back(a);
args.push_back(b);
args.push_back(c);
return astnode(val, args, met);
}
// Print token list
std::string printTokens(std::vector<Node> tokens) {
std::string s = "";

6
libserpent/util.h

@ -22,7 +22,8 @@ const int TOKEN = 0,
COLON = 11,
UNARY_OP = 12,
BINARY_OP = 13,
COMPOUND = 14;
COMPOUND = 14,
TOKEN_SPLITTER = 15;
// Stores metadata about each token
class Metadata {
@ -48,6 +49,9 @@ struct Node {
};
Node token(std::string val, Metadata met=Metadata());
Node astnode(std::string val, std::vector<Node> args, Metadata met=Metadata());
Node astnode(std::string val, Node a, Metadata met=Metadata());
Node astnode(std::string val, Node a, Node b, Metadata met=Metadata());
Node astnode(std::string val, Node a, Node b, Node c, Metadata met=Metadata());
// Number of tokens in a tree
int treeSize(Node prog);

115
libsolidity/AST.cpp

@ -167,6 +167,14 @@ void Return::accept(ASTVisitor& _visitor)
_visitor.endVisit(*this);
}
void ExpressionStatement::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))
@ -255,14 +263,6 @@ TypeError ASTNode::createTypeError(string const& _description)
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
void Statement::expectType(Expression& _expression, Type const& _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 (shared_ptr<Statement> const& statement: m_statements)
@ -271,7 +271,7 @@ void Block::checkTypeRequirements()
void IfStatement::checkTypeRequirements()
{
expectType(*m_condition, BoolType());
m_condition->expectType(BoolType());
m_trueBody->checkTypeRequirements();
if (m_falseBody)
m_falseBody->checkTypeRequirements();
@ -279,27 +279,22 @@ void IfStatement::checkTypeRequirements()
void WhileStatement::checkTypeRequirements()
{
expectType(*m_condition, BoolType());
m_condition->expectType(BoolType());
m_body->checkTypeRequirements();
}
void Continue::checkTypeRequirements()
{
}
void Break::checkTypeRequirements()
{
}
void Return::checkTypeRequirements()
{
assert(m_returnParameters);
if (!m_expression)
return;
if (asserts(m_returnParameters))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Return parameters not assigned."));
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());
m_expression->expectType(*m_returnParameters->getParameters().front()->getType());
}
void VariableDefinition::checkTypeRequirements()
@ -311,7 +306,7 @@ void VariableDefinition::checkTypeRequirements()
if (m_value)
{
if (m_variable->getType())
expectType(*m_value, *m_variable->getType());
m_value->expectType(*m_variable->getType());
else
{
// no type declared and no previous assignment, infer the type
@ -323,23 +318,39 @@ void VariableDefinition::checkTypeRequirements()
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());
if (!m_leftHandSide->isLvalue())
BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue."));
m_rightHandSide->expectType(*m_leftHandSide->getType());
m_type = m_leftHandSide->getType();
if (m_assigmentOperator != Token::ASSIGN)
{
// compound assignment
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator)))
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
}
}
void ExpressionStatement::checkTypeRequirements()
{
m_expression->checkTypeRequirements();
}
void Expression::expectType(Type const& _expectedType)
{
checkTypeRequirements();
Type const& type = *getType();
if (!type.isImplicitlyConvertibleTo(_expectedType))
BOOST_THROW_EXCEPTION(createTypeError("Type " + type.toString() +
" not implicitly convertible to expected type "
+ _expectedType.toString() + "."));
}
void UnaryOperation::checkTypeRequirements()
{
// INC, DEC, NOT, BIT_NOT, DELETE
// INC, DEC, ADD, SUB, NOT, BIT_NOT, DELETE
m_subExpression->checkTypeRequirements();
if (m_operator == Token::Value::INC || m_operator == Token::Value::DEC || m_operator == Token::Value::DELETE)
if (!m_subExpression->isLvalue())
BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue."));
m_type = m_subExpression->getType();
if (!m_type->acceptsUnaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
@ -347,22 +358,25 @@ void UnaryOperation::checkTypeRequirements()
void BinaryOperation::checkTypeRequirements()
{
m_right->checkTypeRequirements();
m_left->checkTypeRequirements();
m_right->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."));
BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation: " +
m_left->getType()->toString() + " vs. " +
m_right->getType()->toString()));
if (Token::isCompareOp(m_operator))
m_type = make_shared<BoolType>();
else
{
assert(Token::isBinaryOp(m_operator));
m_type = m_commonType;
if (!m_commonType->acceptsBinaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) +
" not compatible with type " +
m_commonType->toString()));
}
}
@ -375,25 +389,22 @@ void FunctionCall::checkTypeRequirements()
Type const* expressionType = m_expression->getType().get();
if (isTypeConversion())
{
TypeType const* type = dynamic_cast<TypeType const*>(expressionType);
assert(type);
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
//@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()))
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type->getActualType();
m_type = type.getActualType();
}
else
{
//@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);
assert(function);
FunctionDefinition const& fun = function->getFunction();
FunctionDefinition const& fun = dynamic_cast<FunctionType const&>(*expressionType).getFunction();
vector<ASTPointer<VariableDeclaration>> const& parameters = fun.getParameters();
if (parameters.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
@ -402,10 +413,10 @@ void FunctionCall::checkTypeRequirements()
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())
if (fun.getReturnParameters().empty())
m_type = make_shared<VoidType>();
else
m_type = fun.getReturnParameterList()->getParameters().front()->getType();
m_type = fun.getReturnParameters().front()->getType();
}
}
@ -416,34 +427,28 @@ bool FunctionCall::isTypeConversion() const
void MemberAccess::checkTypeRequirements()
{
assert(false); // not yet implemented
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access not yet implemented."));
// m_type = ;
}
void IndexAccess::checkTypeRequirements()
{
assert(false); // not yet implemented
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index access not yet implemented."));
// m_type = ;
}
void Identifier::checkTypeRequirements()
{
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.
if (asserts(m_referencedDeclaration))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier not resolved."));
VariableDeclaration* variable = dynamic_cast<VariableDeclaration*>(m_referencedDeclaration);
if (variable)
{
if (!variable->getType())
BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type "
"could be determined."));
BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined."));
m_type = variable->getType();
m_isLvalue = true;
return;
}
//@todo can we unify these with TypeName::toType()?
@ -469,7 +474,7 @@ void Identifier::checkTypeRequirements()
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef));
return;
}
assert(false); // declaration reference of unknown/forbidden type
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
}
void ElementaryTypeNameExpression::checkTypeRequirements()
@ -480,6 +485,8 @@ void ElementaryTypeNameExpression::checkTypeRequirements()
void Literal::checkTypeRequirements()
{
m_type = Type::forLiteral(*this);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Literal value too large."));
}
}

119
libsolidity/AST.h

@ -116,9 +116,9 @@ public:
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; }
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
@ -135,6 +135,8 @@ public:
Declaration(_location, _name), m_members(_members) {}
virtual void accept(ASTVisitor& _visitor) override;
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
private:
std::vector<ASTPointer<VariableDeclaration>> m_members;
};
@ -152,7 +154,7 @@ public:
ASTNode(_location), m_parameters(_parameters) {}
virtual void accept(ASTVisitor& _visitor) override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() { return m_parameters; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters; }
private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters;
@ -175,15 +177,21 @@ public:
bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList& getParameterList() { return *m_parameters; }
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block& getBody() { return *m_body; }
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
private:
bool m_isPublic;
ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst;
ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body;
std::vector<VariableDeclaration const*> m_localVariables;
};
/**
@ -237,7 +245,10 @@ class ElementaryTypeName: public TypeName
{
public:
explicit ElementaryTypeName(Location const& _location, Token::Value _type):
TypeName(_location), m_type(_type) {}
TypeName(_location), m_type(_type)
{
if (asserts(Token::isElementaryTypeName(_type))) BOOST_THROW_EXCEPTION(InternalCompilerError());
}
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() override { return Type::fromElementaryTypeName(m_type); }
@ -305,11 +316,6 @@ public:
/// This includes checking that operators are applicable to their arguments but also that
/// the number of function call arguments matches the number of formal parameters and so forth.
virtual void checkTypeRequirements() = 0;
protected:
/// Helper function, check that the inferred type for @a _expression is @a _expectedType or at
/// least implicitly convertible to @a _expectedType. If not, throw exception.
void expectType(Expression& _expression, Type const& _expectedType);
};
/**
@ -342,6 +348,11 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
Expression& getCondition() const { return *m_condition; }
Statement& getTrueStatement() const { return *m_trueBody; }
/// @returns the "else" part of the if statement or nullptr if there is no "else" part.
Statement* getFalseStatement() const { return m_falseBody.get(); }
private:
ASTPointer<Expression> m_condition;
ASTPointer<Statement> m_trueBody;
@ -368,6 +379,9 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
Expression& getCondition() const { return *m_condition; }
Statement& getBody() const { return *m_body; }
private:
ASTPointer<Expression> m_condition;
ASTPointer<Statement> m_body;
@ -378,7 +392,7 @@ class Continue: public Statement
public:
Continue(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
virtual void checkTypeRequirements() override {}
};
class Break: public Statement
@ -386,7 +400,7 @@ class Break: public Statement
public:
Break(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
virtual void checkTypeRequirements() override {}
};
class Return: public Statement
@ -398,6 +412,13 @@ public:
virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
ParameterList const& getFunctionReturnParameters() const
{
if (asserts(m_returnParameters))
BOOST_THROW_EXCEPTION(InternalCompilerError());
return *m_returnParameters;
}
Expression* getExpression() const { return m_expression.get(); }
private:
ASTPointer<Expression> m_expression; ///< value to return, optional
@ -420,25 +441,29 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
VariableDeclaration const& getDeclaration() const { return *m_variable; }
Expression* getExpression() const { return m_value.get(); }
private:
ASTPointer<VariableDeclaration> m_variable;
ASTPointer<Expression> m_value; ///< the assigned value, can be missing
};
/**
* An expression, i.e. something that has a value (which can also be of type "void" in case
* of function calls).
* A statement that contains only an expression (i.e. an assignment, function call, ...).
*/
class Expression: public Statement
class ExpressionStatement: public Statement
{
public:
Expression(Location const& _location): Statement(_location) {}
ExpressionStatement(Location const& _location, ASTPointer<Expression> _expression):
Statement(_location), m_expression(_expression) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
std::shared_ptr<Type const> const& getType() const { return m_type; }
Expression& getExpression() const { return *m_expression; }
protected:
/// Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type;
private:
ASTPointer<Expression> m_expression;
};
/// @}
@ -447,16 +472,43 @@ protected:
/// @{
/**
* Assignment, can also be a compound assignment.
* Examples: (a = 7 + 8) or (a *= 2)
* An expression, i.e. something that has a value (which can also be of type "void" in case
* of some function calls).
* @abstract
*/
class Expression: public ASTNode
{
public:
Expression(Location const& _location): ASTNode(_location), m_isLvalue(false) {}
virtual void checkTypeRequirements() = 0;
std::shared_ptr<Type const> const& getType() const { return m_type; }
bool isLvalue() const { return m_isLvalue; }
/// Helper function, infer the type via @ref checkTypeRequirements and then check that it
/// is implicitly convertible to @a _expectedType. If not, throw exception.
void expectType(Type const& _expectedType);
protected:
//! Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type;
//! Whether or not this expression is an lvalue, i.e. something that can be assigned to.
//! This is set during calls to @a checkTypeRequirements()
bool m_isLvalue;
};
/// Assignment, can also be a compound assignment.
/// Examples: (a = 7 + 8) or (a *= 2)
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) {}
m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide)
{
if (asserts(Token::isAssignmentOp(_assignmentOperator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
@ -480,7 +532,10 @@ 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) {}
m_subExpression(_subExpression), m_isPrefix(_isPrefix)
{
if (asserts(Token::isUnaryOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
@ -502,19 +557,25 @@ 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) {}
Expression(_location), m_left(_left), m_operator(_operator), m_right(_right)
{
if (asserts(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
Expression& getLeftExpression() const { return *m_left; }
Expression& getRightExpression() const { return *m_right; }
Token::Value getOperator() const { return m_operator; }
Type const& getCommonType() const { return *m_commonType; }
private:
ASTPointer<Expression> m_left;
Token::Value m_operator;
ASTPointer<Expression> m_right;
/// The common type that is used for the operation, not necessarily the result type (e.g. for
/// comparisons, this is always bool).
std::shared_ptr<Type const> m_commonType;
};
@ -530,6 +591,9 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
Expression& getExpression() const { return *m_expression; }
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
/// Returns true if this is not an actual function call, but an explicit type conversion
/// or constructor call.
bool isTypeConversion() const;
@ -616,7 +680,10 @@ class ElementaryTypeNameExpression: public PrimaryExpression
{
public:
ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken):
PrimaryExpression(_location), m_typeToken(_typeToken) {}
PrimaryExpression(_location), m_typeToken(_typeToken)
{
if (asserts(Token::isElementaryTypeName(_typeToken))) BOOST_THROW_EXCEPTION(InternalCompilerError());
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;

1
libsolidity/ASTForward.h

@ -53,6 +53,7 @@ class Continue;
class Break;
class Return;
class VariableDefinition;
class ExpressionStatement;
class Expression;
class Assignment;
class UnaryOperation;

12
libsolidity/ASTPrinter.cpp

@ -171,6 +171,13 @@ bool ASTPrinter::visit(VariableDefinition& _node)
return goDeeper();
}
bool ASTPrinter::visit(ExpressionStatement& _node)
{
writeLine("ExpressionStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Expression& _node)
{
writeLine("Expression");
@ -358,6 +365,11 @@ void ASTPrinter::endVisit(VariableDefinition&)
m_indentation--;
}
void ASTPrinter::endVisit(ExpressionStatement&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Expression&)
{
m_indentation--;

2
libsolidity/ASTPrinter.h

@ -60,6 +60,7 @@ public:
bool visit(Break& _node) override;
bool visit(Return& _node) override;
bool visit(VariableDefinition& _node) override;
bool visit(ExpressionStatement& _node) override;
bool visit(Expression& _node) override;
bool visit(Assignment& _node) override;
bool visit(UnaryOperation& _node) override;
@ -91,6 +92,7 @@ public:
void endVisit(Break&) override;
void endVisit(Return&) override;
void endVisit(VariableDefinition&) override;
void endVisit(ExpressionStatement&) override;
void endVisit(Expression&) override;
void endVisit(Assignment&) override;
void endVisit(UnaryOperation&) override;

2
libsolidity/ASTVisitor.h

@ -60,6 +60,7 @@ public:
virtual bool visit(Break&) { return true; }
virtual bool visit(Return&) { return true; }
virtual bool visit(VariableDefinition&) { return true; }
virtual bool visit(ExpressionStatement&) { return true; }
virtual bool visit(Expression&) { return true; }
virtual bool visit(Assignment&) { return true; }
virtual bool visit(UnaryOperation&) { return true; }
@ -91,6 +92,7 @@ public:
virtual void endVisit(Break&) { }
virtual void endVisit(Return&) { }
virtual void endVisit(VariableDefinition&) { }
virtual void endVisit(ExpressionStatement&) { }
virtual void endVisit(Expression&) { }
virtual void endVisit(Assignment&) { }
virtual void endVisit(UnaryOperation&) { }

10
libsolidity/CMakeLists.txt

@ -6,18 +6,16 @@ aux_source_directory(. SRC_LIST)
set(EXECUTABLE solidity)
file(GLOB HEADERS "*.h")
if(ETH_STATIC)
add_library(${EXECUTABLE} STATIC ${SRC_LIST})
add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS})
else()
add_library(${EXECUTABLE} SHARED ${SRC_LIST})
add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS})
endif()
file(GLOB HEADERS "*.h")
include_directories(..)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} evmface)
target_link_libraries(${EXECUTABLE} evmcore devcore)
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

525
libsolidity/Compiler.cpp

@ -17,387 +17,298 @@
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity AST to EVM bytecode compiler.
* Solidity compiler.
*/
#include <cassert>
#include <utility>
#include <algorithm>
#include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h>
#include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h>
using namespace std;
namespace dev {
namespace solidity {
void CompilerContext::setLabelPosition(uint32_t _label, uint32_t _position)
bytes Compiler::compile(ContractDefinition& _contract, bool _optimize)
{
assert(m_labelPositions.find(_label) == m_labelPositions.end());
m_labelPositions[_label] = _position;
Compiler compiler;
compiler.compileContract(_contract);
return compiler.m_context.getAssembledBytecode(_optimize);
}
uint32_t CompilerContext::getLabelPosition(uint32_t _label) const
void Compiler::compileContract(ContractDefinition& _contract)
{
auto iter = m_labelPositions.find(_label);
assert(iter != m_labelPositions.end());
return iter->second;
m_context = CompilerContext(); // clear it just in case
//@todo constructor
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
m_context.addFunction(*function);
//@todo sort them?
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
m_context.addStateVariable(*variable);
appendFunctionSelector(_contract.getDefinedFunctions());
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
function->accept(*this);
packIntoContractCreator();
}
void ExpressionCompiler::compile(Expression& _expression)
void Compiler::packIntoContractCreator()
{
m_assemblyItems.clear();
_expression.accept(*this);
CompilerContext creatorContext;
eth::AssemblyItem sub = creatorContext.addSubroutine(m_context.getAssembly());
// stack contains sub size
creatorContext << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
creatorContext << u256(0) << eth::Instruction::RETURN;
swap(m_context, creatorContext);
}
bytes ExpressionCompiler::getAssembledBytecode() const
void Compiler::appendFunctionSelector(vector<ASTPointer<FunctionDefinition>> const& _functions)
{
bytes assembled;
assembled.reserve(m_assemblyItems.size());
// resolve label references
for (uint32_t pos = 0; pos < m_assemblyItems.size(); ++pos)
// sort all public functions and store them together with a tag for their argument decoding section
map<string, pair<FunctionDefinition const*, eth::AssemblyItem>> publicFunctions;
for (ASTPointer<FunctionDefinition> const& f: _functions)
if (f->isPublic())
publicFunctions.insert(make_pair(f->getName(), make_pair(f.get(), m_context.newTag())));
//@todo remove constructor
if (publicFunctions.size() > 255)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract."));
//@todo check for calldatasize?
// retrieve the first byte of the call data
m_context << u256(0) << eth::Instruction::CALLDATALOAD << u256(0) << eth::Instruction::BYTE;
// check that it is not too large
m_context << eth::Instruction::DUP1 << u256(publicFunctions.size() - 1) << eth::Instruction::LT;
eth::AssemblyItem returnTag = m_context.appendConditionalJump();
// otherwise, jump inside jump table (each entry of the table has size 4)
m_context << u256(4) << eth::Instruction::MUL;
eth::AssemblyItem jumpTableStart = m_context.pushNewTag();
m_context << eth::Instruction::ADD << eth::Instruction::JUMP;
// jump table, tell the optimizer not to remove the JUMPDESTs
m_context << eth::AssemblyItem(eth::NoOptimizeBegin) << jumpTableStart;
for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
m_context.appendJumpTo(f.second.second) << eth::Instruction::JUMPDEST;
m_context << eth::AssemblyItem(eth::NoOptimizeEnd);
m_context << returnTag << eth::Instruction::STOP;
for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
{
AssemblyItem const& item = m_assemblyItems[pos];
if (item.getType() == AssemblyItem::Type::LABEL)
m_context.setLabelPosition(item.getLabel(), pos + 1);
}
FunctionDefinition const& function = *f.second.first;
m_context << f.second.second;
for (AssemblyItem const& item: m_assemblyItems)
if (item.getType() == AssemblyItem::Type::LABELREF)
assembled.push_back(m_context.getLabelPosition(item.getLabel()));
else
assembled.push_back(item.getData());
eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(function);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
m_context << returnTag;
return assembled;
appendReturnValuePacker(function);
}
}
AssemblyItems ExpressionCompiler::compileExpression(CompilerContext& _context,
Expression& _expression)
void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function)
{
ExpressionCompiler compiler(_context);
compiler.compile(_expression);
return compiler.getAssemblyItems();
}
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1;
void ExpressionCompiler::endVisit(Assignment& _assignment)
{
Expression& rightHandSide = _assignment.getRightHandSide();
Token::Value op = _assignment.getAssignmentOperator();
if (op != Token::ASSIGN)
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{
// compound assignment
// @todo retrieve lvalue value
rightHandSide.accept(*this);
Type const& resultType = *_assignment.getType();
cleanHigherOrderBitsIfNeeded(*rightHandSide.getType(), resultType);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), resultType);
unsigned const numBytes = var->getType()->getCalldataEncodedSize();
if (numBytes == 0)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(var->getLocation())
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
if (numBytes == 32)
m_context << u256(dataOffset) << eth::Instruction::CALLDATALOAD;
else
m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset)
<< eth::Instruction::CALLDATALOAD << eth::Instruction::DIV;
dataOffset += numBytes;
}
else
rightHandSide.accept(*this);
// @todo store value
}
void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
{
//@todo type checking and creating code for an operator should be in the same place:
// the operator should know how to convert itself and to which types it applies, so
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
// represents the operator
switch (_unaryOperation.getOperator())
//@todo this can be also done more efficiently
unsigned dataOffset = 0;
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
for (unsigned i = 0; i < parameters.size(); ++i)
{
case Token::NOT: // !
append(eth::Instruction::ISZERO);
break;
case Token::BIT_NOT: // ~
append(eth::Instruction::NOT);
break;
case Token::DELETE: // delete
// a -> a xor a (= 0).
// @todo this should also be an assignment
// @todo semantics change for complex types
append(eth::Instruction::DUP1);
append(eth::Instruction::XOR);
break;
case Token::INC: // ++ (pre- or postfix)
// @todo this should also be an assignment
if (_unaryOperation.isPrefixOperation())
{
append(eth::Instruction::PUSH1);
append(1);
append(eth::Instruction::ADD);
}
break;
case Token::DEC: // -- (pre- or postfix)
// @todo this should also be an assignment
if (_unaryOperation.isPrefixOperation())
{
append(eth::Instruction::PUSH1);
append(1);
append(eth::Instruction::SWAP1); //@todo avoid this
append(eth::Instruction::SUB);
}
break;
case Token::ADD: // +
// unary add, so basically no-op
break;
case Token::SUB: // -
// unary -x translates into "0-x"
append(eth::Instruction::PUSH1);
append(0);
append(eth::Instruction::SUB);
break;
default:
assert(false); // invalid operation
Type const& paramType = *parameters[i]->getType();
unsigned numBytes = paramType.getCalldataEncodedSize();
if (numBytes == 0)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(parameters[i]->getLocation())
<< errinfo_comment("Type " + paramType.toString() + " not yet supported."));
m_context << eth::dupInstruction(parameters.size() - i);
if (numBytes != 32)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(dataOffset) << eth::Instruction::MSTORE;
dataOffset += numBytes;
}
// note that the stack is not cleaned up here
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
}
bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
bool Compiler::visit(FunctionDefinition& _function)
{
Expression& leftExpression = _binaryOperation.getLeftExpression();
Expression& rightExpression = _binaryOperation.getRightExpression();
Type const& resultType = *_binaryOperation.getType();
Token::Value const op = _binaryOperation.getOperator();
//@todo to simplify this, the calling convention could by changed such that
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
// although note that this reduces the size of the visible stack
if (op == Token::AND || op == Token::OR)
{
// special case: short-circuiting
appendAndOrOperatorCode(_binaryOperation);
}
else if (Token::isCompareOp(op))
{
leftExpression.accept(*this);
rightExpression.accept(*this);
m_context.startNewFunction();
m_returnTag = m_context.newTag();
m_breakTags.clear();
m_continueTags.clear();
// the types to compare have to be the same, but the resulting type is always bool
assert(*leftExpression.getType() == *rightExpression.getType());
appendCompareOperatorCode(op, *leftExpression.getType());
}
else
{
leftExpression.accept(*this);
cleanHigherOrderBitsIfNeeded(*leftExpression.getType(), resultType);
rightExpression.accept(*this);
cleanHigherOrderBitsIfNeeded(*rightExpression.getType(), resultType);
appendOrdinaryBinaryOperatorCode(op, resultType);
}
m_context << m_context.getFunctionEntryLabel(_function);
// do not visit the child nodes, we already did that explicitly
return false;
}
// stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
void ExpressionCompiler::endVisit(FunctionCall& _functionCall)
{
if (_functionCall.isTypeConversion())
{
//@todo binary representation for all supported types (bool and int) is the same, so no-op
// here for now.
}
else
{
//@todo
}
}
unsigned const numArguments = _function.getParameters().size();
unsigned const numReturnValues = _function.getReturnParameters().size();
unsigned const numLocalVariables = _function.getLocalVariables().size();
void ExpressionCompiler::endVisit(MemberAccess&)
{
for (ASTPointer<VariableDeclaration> const& variable: _function.getParameters() + _function.getReturnParameters())
m_context.addVariable(*variable);
for (VariableDeclaration const* localVariable: _function.getLocalVariables())
m_context.addVariable(*localVariable);
m_context.initializeLocalVariables(numReturnValues + numLocalVariables);
}
_function.getBody().accept(*this);
void ExpressionCompiler::endVisit(IndexAccess&)
{
m_context << m_returnTag;
}
// Now we need to re-shuffle the stack. For this we keep a record of the stack layout
// that shows the target positions of the elements, where "-1" denotes that this element needs
// to be removed from the stack.
// Note that the fact that the return arguments are of increasing index is vital for this
// algorithm to work.
void ExpressionCompiler::endVisit(Identifier&)
{
vector<int> stackLayout;
stackLayout.push_back(numReturnValues); // target of return address
stackLayout += vector<int>(numArguments, -1); // discard all arguments
for (unsigned i = 0; i < numReturnValues; ++i)
stackLayout.push_back(i);
stackLayout += vector<int>(numLocalVariables, -1);
}
while (stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0)
{
m_context << eth::Instruction::POP;
stackLayout.pop_back();
}
else
{
m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1);
swap(stackLayout[stackLayout.back()], stackLayout.back());
}
//@todo assert that everything is in place now
void ExpressionCompiler::endVisit(Literal& _literal)
{
switch (_literal.getType()->getCategory())
{
case Type::Category::INTEGER:
case Type::Category::BOOL:
{
bytes value = _literal.getType()->literalToBigEndian(_literal);
assert(value.size() <= 32);
assert(!value.empty());
append(static_cast<byte>(eth::Instruction::PUSH1) + static_cast<byte>(value.size() - 1));
append(value);
break;
}
default:
assert(false); // @todo
}
}
m_context << eth::Instruction::JUMP;
void ExpressionCompiler::cleanHigherOrderBitsIfNeeded(Type const& _typeOnStack, Type const& _targetType)
{
// If the type of one of the operands is extended, we need to remove all
// higher-order bits that we might have ignored in previous operations.
// @todo: store in the AST whether the operand might have "dirty" higher
// order bits
if (_typeOnStack == _targetType)
return;
if (_typeOnStack.getCategory() == Type::Category::INTEGER &&
_targetType.getCategory() == Type::Category::INTEGER)
{
//@todo
}
else
{
// If we get here, there is either an implementation missing to clean higher oder bits
// for non-integer types that are explicitly convertible or we got here in error.
assert(!_typeOnStack.isExplicitlyConvertibleTo(_targetType));
assert(false); // these types should not be convertible.
}
return false;
}
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperation)
bool Compiler::visit(IfStatement& _ifStatement)
{
Token::Value const op = _binaryOperation.getOperator();
assert(op == Token::OR || op == Token::AND);
_binaryOperation.getLeftExpression().accept(*this);
append(eth::Instruction::DUP1);
if (op == Token::AND)
append(eth::Instruction::NOT);
uint32_t endLabel = appendConditionalJump();
_binaryOperation.getRightExpression().accept(*this);
appendLabel(endLabel);
ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
if (_ifStatement.getFalseStatement())
_ifStatement.getFalseStatement()->accept(*this);
eth::AssemblyItem endTag = m_context.appendJump();
m_context << trueTag;
_ifStatement.getTrueStatement().accept(*this);
m_context << endTag;
return false;
}
void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type)
bool Compiler::visit(WhileStatement& _whileStatement)
{
if (_operator == Token::EQ || _operator == Token::NE)
{
append(eth::Instruction::EQ);
if (_operator == Token::NE)
append(eth::Instruction::NOT);
}
else
{
IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
assert(type);
bool const isSigned = type->isSigned();
eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag();
m_continueTags.push_back(loopStart);
m_breakTags.push_back(loopEnd);
// note that EVM opcodes compare like "stack[0] < stack[1]",
// but our left value is at stack[1], so everyhing is reversed.
switch (_operator)
{
case Token::GTE:
append(isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
append(eth::Instruction::NOT);
break;
case Token::LTE:
append(isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
append(eth::Instruction::NOT);
break;
case Token::GT:
append(isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
break;
case Token::LT:
append(isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
break;
default:
assert(false);
}
}
m_context << loopStart;
ExpressionCompiler::compileExpression(m_context, _whileStatement.getCondition());
m_context << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(loopEnd);
_whileStatement.getBody().accept(*this);
m_context.appendJumpTo(loopStart);
m_context << loopEnd;
m_continueTags.pop_back();
m_breakTags.pop_back();
return false;
}
void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type)
bool Compiler::visit(Continue&)
{
if (Token::isArithmeticOp(_operator))
appendArithmeticOperatorCode(_operator, _type);
else if (Token::isBitOp(_operator))
appendBitOperatorCode(_operator);
else if (Token::isShiftOp(_operator))
appendShiftOperatorCode(_operator);
else
assert(false); // unknown binary operator
if (!m_continueTags.empty())
m_context.appendJumpTo(m_continueTags.back());
return false;
}
void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
bool Compiler::visit(Break&)
{
IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
assert(type);
bool const isSigned = type->isSigned();
switch (_operator)
{
case Token::ADD:
append(eth::Instruction::ADD);
break;
case Token::SUB:
append(eth::Instruction::SWAP1);
append(eth::Instruction::SUB);
break;
case Token::MUL:
append(eth::Instruction::MUL);
break;
case Token::DIV:
append(eth::Instruction::SWAP1);
append(isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
break;
case Token::MOD:
append(eth::Instruction::SWAP1);
append(isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
break;
default:
assert(false);
}
if (!m_breakTags.empty())
m_context.appendJumpTo(m_breakTags.back());
return false;
}
void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
bool Compiler::visit(Return& _return)
{
switch (_operator)
//@todo modifications are needed to make this work with functions returning multiple values
if (Expression* expression = _return.getExpression())
{
case Token::BIT_OR:
append(eth::Instruction::OR);
break;
case Token::BIT_AND:
append(eth::Instruction::AND);
break;
case Token::BIT_XOR:
append(eth::Instruction::XOR);
break;
default:
assert(false);
ExpressionCompiler::compileExpression(m_context, *expression);
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());
unsigned stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(firstVariable));
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
}
m_context.appendJumpTo(m_returnTag);
return false;
}
void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
bool Compiler::visit(VariableDefinition& _variableDefinition)
{
switch (_operator)
if (Expression* expression = _variableDefinition.getExpression())
{
case Token::SHL:
assert(false); //@todo
break;
case Token::SAR:
assert(false); //@todo
break;
default:
assert(false);
ExpressionCompiler::compileExpression(m_context, *expression);
ExpressionCompiler::appendTypeConversion(m_context,
*expression->getType(),
*_variableDefinition.getDeclaration().getType());
unsigned baseStackOffset = m_context.getBaseStackOffsetOfVariable(_variableDefinition.getDeclaration());
unsigned stackPosition = m_context.baseToCurrentStackOffset(baseStackOffset);
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
}
return false;
}
uint32_t ExpressionCompiler::appendConditionalJump()
{
uint32_t label = m_context.dispenseNewLabel();
append(eth::Instruction::PUSH1);
appendLabelref(label);
append(eth::Instruction::JUMPI);
return label;
}
void ExpressionCompiler::append(bytes const& _data)
bool Compiler::visit(ExpressionStatement& _expressionStatement)
{
m_assemblyItems.reserve(m_assemblyItems.size() + _data.size());
for (byte b: _data)
append(b);
Expression& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression);
if (expression.getType()->getCategory() != Type::Category::VOID)
m_context << eth::Instruction::POP;
return false;
}
}
}

138
libsolidity/Compiler.h

@ -20,127 +20,47 @@
* Solidity AST to EVM bytecode compiler.
*/
#include <libevmface/Instruction.h>
#include <ostream>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/Types.h>
#include <libsolidity/Token.h>
#include <libsolidity/CompilerContext.h>
namespace dev {
namespace solidity {
/**
* A single item of compiled code that can be assembled to a single byte value in the final
* bytecode. Its main purpose is to inject jump labels and label references into the opcode stream,
* which can be resolved in the final step.
*/
class AssemblyItem
{
public:
enum class Type
{
CODE, ///< m_data is opcode, m_label is empty.
DATA, ///< m_data is actual data, m_label is empty
LABEL, ///< m_data is JUMPDEST opcode, m_label is id of label
LABELREF ///< m_data is empty, m_label is id of label
};
explicit AssemblyItem(eth::Instruction _instruction) : m_type(Type::CODE), m_data(byte(_instruction)) {}
explicit AssemblyItem(byte _data): m_type(Type::DATA), m_data(_data) {}
/// Factory functions
static AssemblyItem labelRef(uint32_t _label) { return AssemblyItem(Type::LABELREF, 0, _label); }
static AssemblyItem label(uint32_t _label) { return AssemblyItem(Type::LABEL, byte(eth::Instruction::JUMPDEST), _label); }
Type getType() const { return m_type; }
byte getData() const { return m_data; }
uint32_t getLabel() const { return m_label; }
private:
AssemblyItem(Type _type, byte _data, uint32_t _label): m_type(_type), m_data(_data), m_label(_label) {}
Type m_type;
byte m_data; ///< data to be written to the bytecode stream (or filled by a label if this is a LABELREF)
uint32_t m_label; ///< the id of a label either referenced or defined by this item
};
using AssemblyItems = std::vector<AssemblyItem>;
/**
* Context to be shared by all units that compile the same contract. Its current usage only
* concerns dispensing unique jump label IDs and storing their actual positions in the bytecode
* stream.
*/
class CompilerContext
class Compiler: private ASTVisitor
{
public:
CompilerContext(): m_nextLabel(0) {}
uint32_t dispenseNewLabel() { return m_nextLabel++; }
void setLabelPosition(uint32_t _label, uint32_t _position);
uint32_t getLabelPosition(uint32_t _label) const;
Compiler(): m_returnTag(m_context.newTag()) {}
private:
uint32_t m_nextLabel;
std::map<uint32_t, uint32_t> m_labelPositions;
};
/**
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
* of EVM instructions. It needs a compiler context that is the same for the whole compilation
* unit.
*/
class ExpressionCompiler: public ASTVisitor
{
public:
ExpressionCompiler(CompilerContext& _compilerContext): m_context(_compilerContext) {}
void compileContract(ContractDefinition& _contract);
bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); }
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
/// Compile the given expression and (re-)populate the assembly item list.
void compile(Expression& _expression);
AssemblyItems const& getAssemblyItems() const { return m_assemblyItems; }
bytes getAssembledBytecode() const;
/// Compile the given expression and return the assembly items right away.
static AssemblyItems compileExpression(CompilerContext& _context, Expression& _expression);
/// Compile the given contract and return the EVM bytecode.
static bytes compile(ContractDefinition& _contract, bool _optimize);
private:
virtual void endVisit(Assignment& _assignment) override;
virtual void endVisit(UnaryOperation& _unaryOperation) override;
virtual bool visit(BinaryOperation& _binaryOperation) override;
virtual void endVisit(FunctionCall& _functionCall) override;
virtual void endVisit(MemberAccess& _memberAccess) override;
virtual void endVisit(IndexAccess& _indexAccess) override;
virtual void endVisit(Identifier& _identifier) override;
virtual void endVisit(Literal& _literal) override;
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
void cleanHigherOrderBitsIfNeeded(Type const& _typeOnStack, Type const& _targetType);
///@{
///@name Append code for various operator types
void appendAndOrOperatorCode(BinaryOperation& _binaryOperation);
void appendCompareOperatorCode(Token::Value _operator, Type const& _type);
void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);
void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type);
void appendBitOperatorCode(Token::Value _operator);
void appendShiftOperatorCode(Token::Value _operator);
/// @}
/// Appends a JUMPI instruction to a new label and returns the label
uint32_t appendConditionalJump();
/// Append elements to the current instruction list.
void append(eth::Instruction const& _instruction) { m_assemblyItems.push_back(AssemblyItem(_instruction)); }
void append(byte _value) { m_assemblyItems.push_back(AssemblyItem(_value)); }
void append(bytes const& _data);
void appendLabelref(byte _label) { m_assemblyItems.push_back(AssemblyItem::labelRef(_label)); }
void appendLabel(byte _label) { m_assemblyItems.push_back(AssemblyItem::label(_label)); }
AssemblyItems m_assemblyItems;
CompilerContext& m_context;
/// Creates a new compiler context / assembly and packs the current code into the data part.
void packIntoContractCreator();
void appendFunctionSelector(std::vector<ASTPointer<FunctionDefinition> > const& _functions);
void appendCalldataUnpacker(FunctionDefinition const& _function);
void appendReturnValuePacker(FunctionDefinition const& _function);
virtual bool visit(FunctionDefinition& _function) override;
virtual bool visit(IfStatement& _ifStatement) override;
virtual bool visit(WhileStatement& _whileStatement) override;
virtual bool visit(Continue& _continue) override;
virtual bool visit(Break& _break) override;
virtual bool visit(Return& _return) override;
virtual bool visit(VariableDefinition& _variableDefinition) override;
virtual bool visit(ExpressionStatement& _expressionStatement) override;
CompilerContext m_context;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
};
}
}

87
libsolidity/CompilerContext.cpp

@ -0,0 +1,87 @@
/*
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
* Utilities for the solidity compiler.
*/
#include <utility>
#include <numeric>
#include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
using namespace std;
namespace dev {
namespace solidity {
void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
{
m_stateVariables[&_declaration] = m_stateVariablesSize;
m_stateVariablesSize += _declaration.getType()->getStorageSize();
}
void CompilerContext::initializeLocalVariables(unsigned _numVariables)
{
if (_numVariables > 0)
{
*this << u256(0);
for (unsigned i = 1; i < _numVariables; ++i)
*this << eth::Instruction::DUP1;
m_asm.adjustDeposit(-_numVariables);
}
}
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
{
return std::find(m_localVariables.begin(), m_localVariables.end(), _declaration) != m_localVariables.end();
}
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
{
auto res = m_functionEntryLabels.find(&_function);
if (asserts(res != m_functionEntryLabels.end()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function entry label not found."));
return res->second.tag();
}
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{
auto res = find(begin(m_localVariables), end(m_localVariables), &_declaration);
if (asserts(res != m_localVariables.end()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack."));
return unsigned(end(m_localVariables) - res - 1);
}
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
{
return _baseOffset + m_asm.deposit();
}
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
{
auto it = m_stateVariables.find(&_declaration);
if (it == m_stateVariables.end())
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found in storage."));
return it->second;
}
}
}

102
libsolidity/CompilerContext.h

@ -0,0 +1,102 @@
/*
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
* Utilities for the solidity compiler.
*/
#pragma once
#include <ostream>
#include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h>
#include <libsolidity/Types.h>
namespace dev {
namespace solidity {
/**
* Context to be shared by all units that compile the same contract.
* It stores the generated bytecode and the position of identifiers in memory and on the stack.
*/
class CompilerContext
{
public:
CompilerContext(): m_stateVariablesSize(0) {}
void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
void initializeLocalVariables(unsigned _numVariables);
void addVariable(VariableDeclaration const& _declaration) { m_localVariables.push_back(&_declaration); }
void addFunction(FunctionDefinition const& _function) { m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); }
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); }
bool isLocalVariable(Declaration const* _declaration) const;
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); }
eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
/// Returns the distance of the given local variable from the top of the local variable stack.
unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
/// the distance of that variable from the current top of the stack.
unsigned baseToCurrentStackOffset(unsigned _baseOffset) const;
u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
/// Appends a JUMPI instruction to a new tag and @returns the tag
eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
/// Appends a JUMPI instruction to @a _tag
CompilerContext& appendConditionalJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJumpI(_tag); return *this; }
/// Appends a JUMP to a new tag and @returns the tag
eth::AssemblyItem appendJump() { return m_asm.appendJump().tag(); }
/// Appends a JUMP to a specific tag
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag.
eth::AssemblyItem pushNewTag() { return m_asm.append(m_asm.newPushTag()).tag(); }
/// @returns a new tag without pushing any opcodes or data
eth::AssemblyItem newTag() { return m_asm.newTag(); }
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); }
/// Append elements to the current instruction list and adjust @a m_stackOffset.
CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
CompilerContext& operator<<(eth::Instruction _instruction) { m_asm.append(_instruction); return *this; }
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
eth::Assembly const& getAssembly() const { return m_asm; }
void streamAssembly(std::ostream& _stream) const { _stream << m_asm; }
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }
private:
eth::Assembly m_asm;
/// Size of the state variables, offset of next variable to be added.
u256 m_stateVariablesSize;
/// Storage offsets of state variables
std::map<Declaration const*, u256> m_stateVariables;
/// Offsets of local variables on the stack.
std::vector<Declaration const*> m_localVariables;
/// Labels pointing to the entry points of funcitons.
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
};
}
}

50
libsolidity/CompilerStack.cpp

@ -0,0 +1,50 @@
/*
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
* Full-stack compiler that converts a source code string to bytecode.
*/
#include <libsolidity/AST.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/CompilerStack.h>
using namespace std;
namespace dev
{
namespace solidity
{
bytes CompilerStack::compile(std::string const& _sourceCode, shared_ptr<Scanner> _scanner,
bool _optimize)
{
if (!_scanner)
_scanner = make_shared<Scanner>();
_scanner->reset(CharStream(_sourceCode));
ASTPointer<ContractDefinition> contract = Parser().parse(_scanner);
NameAndTypeResolver().resolveNamesAndTypes(*contract);
return Compiler::compile(*contract, _optimize);
}
}
}

43
libsolidity/CompilerStack.h

@ -0,0 +1,43 @@
/*
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
* Full-stack compiler that converts a source code string to bytecode.
*/
#pragma once
#include <string>
#include <memory>
#include <libdevcore/Common.h>
namespace dev {
namespace solidity {
class Scanner; // forward
class CompilerStack
{
public:
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
/// scanning the source code - this is useful for printing exception information.
static bytes compile(std::string const& _sourceCode, std::shared_ptr<Scanner> _scanner = std::shared_ptr<Scanner>(), bool _optimize = false);
};
}
}

2
libsolidity/Exceptions.h

@ -34,6 +34,8 @@ namespace solidity
struct ParserError: virtual Exception {};
struct TypeError: virtual Exception {};
struct DeclarationError: virtual Exception {};
struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {};
typedef boost::error_info<struct tag_sourcePosition, int> errinfo_sourcePosition;
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation;

436
libsolidity/ExpressionCompiler.cpp

@ -0,0 +1,436 @@
/*
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 AST to EVM bytecode compiler for expressions.
*/
#include <utility>
#include <numeric>
#include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerContext.h>
using namespace std;
namespace dev {
namespace solidity {
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression& _expression)
{
ExpressionCompiler compiler(_context);
_expression.accept(compiler);
}
void ExpressionCompiler::appendTypeConversion(CompilerContext& _context,
Type const& _typeOnStack, Type const& _targetType)
{
ExpressionCompiler compiler(_context);
compiler.appendTypeConversion(_typeOnStack, _targetType);
}
bool ExpressionCompiler::visit(Assignment& _assignment)
{
_assignment.getRightHandSide().accept(*this);
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
m_currentLValue.reset();
_assignment.getLeftHandSide().accept(*this);
Token::Value op = _assignment.getAssignmentOperator();
if (op != Token::ASSIGN) // compound assignment
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
else
m_context << eth::Instruction::POP;
storeInLValue(_assignment);
return false;
}
void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
{
//@todo type checking and creating code for an operator should be in the same place:
// the operator should know how to convert itself and to which types it applies, so
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
// represents the operator
switch (_unaryOperation.getOperator())
{
case Token::NOT: // !
m_context << eth::Instruction::ISZERO;
break;
case Token::BIT_NOT: // ~
m_context << eth::Instruction::NOT;
break;
case Token::DELETE: // delete
{
// a -> a xor a (= 0).
// @todo semantics change for complex types
m_context << eth::Instruction::DUP1 << eth::Instruction::XOR;
storeInLValue(_unaryOperation);
break;
}
case Token::INC: // ++ (pre- or postfix)
case Token::DEC: // -- (pre- or postfix)
if (!_unaryOperation.isPrefixOperation())
m_context << eth::Instruction::DUP1;
m_context << u256(1);
if (_unaryOperation.getOperator() == Token::INC)
m_context << eth::Instruction::ADD;
else
m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap
storeInLValue(_unaryOperation, !_unaryOperation.isPrefixOperation());
break;
case Token::ADD: // +
// unary add, so basically no-op
break;
case Token::SUB: // -
m_context << u256(0) << eth::Instruction::SUB;
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid unary operator: " +
string(Token::toString(_unaryOperation.getOperator()))));
}
}
bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
{
Expression& leftExpression = _binaryOperation.getLeftExpression();
Expression& rightExpression = _binaryOperation.getRightExpression();
Type const& commonType = _binaryOperation.getCommonType();
Token::Value const op = _binaryOperation.getOperator();
if (op == Token::AND || op == Token::OR) // special case: short-circuiting
appendAndOrOperatorCode(_binaryOperation);
else
{
bool cleanupNeeded = false;
if (commonType.getCategory() == Type::Category::INTEGER)
if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD)
cleanupNeeded = true;
rightExpression.accept(*this);
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
leftExpression.accept(*this);
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
if (Token::isCompareOp(op))
appendCompareOperatorCode(op, commonType);
else
appendOrdinaryBinaryOperatorCode(op, commonType);
}
// do not visit the child nodes, we already did that explicitly
return false;
}
bool ExpressionCompiler::visit(FunctionCall& _functionCall)
{
if (_functionCall.isTypeConversion())
{
//@todo we only have integers and bools for now which cannot be explicitly converted
if (asserts(_functionCall.getArguments().size() == 1))
BOOST_THROW_EXCEPTION(InternalCompilerError());
Expression& firstArgument = *_functionCall.getArguments().front();
firstArgument.accept(*this);
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
}
else
{
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
m_currentLValue.reset();
_functionCall.getExpression().accept(*this);
if (asserts(m_currentLValue.isInCode()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Code reference expected."));
eth::AssemblyItem functionTag(eth::PushTag, m_currentLValue.location);
FunctionDefinition const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()).getFunction();
eth::AssemblyItem returnLabel = m_context.pushNewTag();
std::vector<ASTPointer<Expression>> const& arguments = _functionCall.getArguments();
if (asserts(arguments.size() == function.getParameters().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType());
}
m_context.appendJumpTo(functionTag);
m_context << returnLabel;
// callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(function.getReturnParameters().size() - arguments.size() - 1);
// @todo for now, the return value of a function is its first return value, so remove
// all others
for (unsigned i = 1; i < function.getReturnParameters().size(); ++i)
m_context << eth::Instruction::POP;
}
return false;
}
void ExpressionCompiler::endVisit(MemberAccess&)
{
}
void ExpressionCompiler::endVisit(IndexAccess&)
{
}
void ExpressionCompiler::endVisit(Identifier& _identifier)
{
Declaration const* declaration = _identifier.getReferencedDeclaration();
if (m_context.isLocalVariable(declaration))
m_currentLValue = LValueLocation(LValueLocation::STACK,
m_context.getBaseStackOffsetOfVariable(*declaration));
else if (m_context.isStateVariable(declaration))
m_currentLValue = LValueLocation(LValueLocation::STORAGE,
m_context.getStorageLocationOfVariable(*declaration));
else if (m_context.isFunctionDefinition(declaration))
m_currentLValue = LValueLocation(LValueLocation::CODE,
m_context.getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(*declaration)).data());
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not supported or identifier not found."));
retrieveLValueValue(_identifier);
}
void ExpressionCompiler::endVisit(Literal& _literal)
{
switch (_literal.getType()->getCategory())
{
case Type::Category::INTEGER:
case Type::Category::BOOL:
m_context << _literal.getType()->literalValue(_literal);
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer and boolean literals implemented for now."));
}
}
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperation)
{
Token::Value const op = _binaryOperation.getOperator();
if (asserts(op == Token::OR || op == Token::AND))
BOOST_THROW_EXCEPTION(InternalCompilerError());
_binaryOperation.getLeftExpression().accept(*this);
m_context << eth::Instruction::DUP1;
if (op == Token::AND)
m_context << eth::Instruction::ISZERO;
eth::AssemblyItem endLabel = m_context.appendConditionalJump();
m_context << eth::Instruction::POP;
_binaryOperation.getRightExpression().accept(*this);
m_context << endLabel;
}
void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type)
{
if (_operator == Token::EQ || _operator == Token::NE)
{
m_context << eth::Instruction::EQ;
if (_operator == Token::NE)
m_context << eth::Instruction::ISZERO;
}
else
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
bool const isSigned = type.isSigned();
switch (_operator)
{
case Token::GTE:
m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT)
<< eth::Instruction::ISZERO;
break;
case Token::LTE:
m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT)
<< eth::Instruction::ISZERO;
break;
case Token::GT:
m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
break;
case Token::LT:
m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator."));
}
}
}
void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type)
{
if (Token::isArithmeticOp(_operator))
appendArithmeticOperatorCode(_operator, _type);
else if (Token::isBitOp(_operator))
appendBitOperatorCode(_operator);
else if (Token::isShiftOp(_operator))
appendShiftOperatorCode(_operator);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown binary operator."));
}
void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
bool const isSigned = type.isSigned();
switch (_operator)
{
case Token::ADD:
m_context << eth::Instruction::ADD;
break;
case Token::SUB:
m_context << eth::Instruction::SUB;
break;
case Token::MUL:
m_context << eth::Instruction::MUL;
break;
case Token::DIV:
m_context << (isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
break;
case Token::MOD:
m_context << (isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator."));
}
}
void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
{
switch (_operator)
{
case Token::BIT_OR:
m_context << eth::Instruction::OR;
break;
case Token::BIT_AND:
m_context << eth::Instruction::AND;
break;
case Token::BIT_XOR:
m_context << eth::Instruction::XOR;
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown bit operator."));
}
}
void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Shift operators not yet implemented."));
switch (_operator)
{
case Token::SHL:
break;
case Token::SAR:
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown shift operator."));
}
}
void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
{
// For a type extension, we need to remove all higher-order bits that we might have ignored in
// previous operations.
// @todo: store in the AST whether the operand might have "dirty" higher order bits
if (_typeOnStack == _targetType && !_cleanupNeeded)
return;
if (_typeOnStack.getCategory() == Type::Category::INTEGER)
appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack));
else if (_typeOnStack != _targetType)
// All other types should not be convertible to non-equal types.
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested."));
}
void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
{
if (_typeOnStack.getNumBits() == 256)
return;
else if (_typeOnStack.isSigned())
m_context << u256(_typeOnStack.getNumBits() / 8 - 1) << eth::Instruction::SIGNEXTEND;
else
m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
}
void ExpressionCompiler::retrieveLValueValue(Expression const& _expression)
{
switch (m_currentLValue.locationType)
{
case LValueLocation::CODE:
// not stored on the stack
break;
case LValueLocation::STACK:
{
unsigned stackPos = m_context.baseToCurrentStackOffset(unsigned(m_currentLValue.location));
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
m_context << eth::dupInstruction(stackPos + 1);
break;
}
case LValueLocation::STORAGE:
m_context << m_currentLValue.location << eth::Instruction::SLOAD;
break;
case LValueLocation::MEMORY:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Location type not yet implemented."));
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unsupported location type."));
break;
}
}
void ExpressionCompiler::storeInLValue(Expression const& _expression, bool _move)
{
switch (m_currentLValue.locationType)
{
case LValueLocation::STACK:
{
unsigned stackPos = m_context.baseToCurrentStackOffset(unsigned(m_currentLValue.location));
if (stackPos > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
else if (stackPos > 0)
m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP;
if (!_move)
retrieveLValueValue(_expression);
break;
}
case LValueLocation::STORAGE:
if (!_move)
m_context << eth::Instruction::DUP1;
m_context << m_currentLValue.location << eth::Instruction::SSTORE;
break;
case LValueLocation::CODE:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Location type does not support assignment."));
break;
case LValueLocation::MEMORY:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Location type not yet implemented."));
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unsupported location type."));
break;
}
}
}
}

117
libsolidity/ExpressionCompiler.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
* Solidity AST to EVM bytecode compiler for expressions.
*/
#include <libdevcore/Common.h>
#include <libsolidity/ASTVisitor.h>
namespace dev {
namespace eth
{
class AssemblyItem; // forward
}
namespace solidity {
class CompilerContext; // forward
class Type; // forward
class IntegerType; // forward
/**
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
* of EVM instructions. It needs a compiler context that is the same for the whole compilation
* unit.
*/
class ExpressionCompiler: private ASTVisitor
{
public:
/// Compile the given @a _expression into the @a _context.
static void compileExpression(CompilerContext& _context, Expression& _expression);
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType);
private:
ExpressionCompiler(CompilerContext& _compilerContext): m_context(_compilerContext) {}
virtual bool visit(Assignment& _assignment) override;
virtual void endVisit(UnaryOperation& _unaryOperation) override;
virtual bool visit(BinaryOperation& _binaryOperation) override;
virtual bool visit(FunctionCall& _functionCall) override;
virtual void endVisit(MemberAccess& _memberAccess) override;
virtual void endVisit(IndexAccess& _indexAccess) override;
virtual void endVisit(Identifier& _identifier) override;
virtual void endVisit(Literal& _literal) override;
///@{
///@name Append code for various operator types
void appendAndOrOperatorCode(BinaryOperation& _binaryOperation);
void appendCompareOperatorCode(Token::Value _operator, Type const& _type);
void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);
void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type);
void appendBitOperatorCode(Token::Value _operator);
void appendShiftOperatorCode(Token::Value _operator);
/// @}
/// Appends an implicit or explicit type conversion. For now this comprises only erasing
/// higher-order bits (@see appendHighBitCleanup) when widening integer types.
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
/// necessary.
void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false);
//// Appends code that cleans higher-order bits for integer types.
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
/// Copies the value of the current lvalue to the top of the stack.
void retrieveLValueValue(Expression const& _expression);
/// Stores the value on top of the stack in the current lvalue. Removes it from the stack if
/// @a _move is true.
void storeInLValue(Expression const& _expression, bool _move = false);
/**
* Location of an lvalue, either in code (for a function) on the stack, in the storage or memory.
*/
struct LValueLocation
{
enum LocationType { INVALID, CODE, STACK, MEMORY, STORAGE };
LValueLocation() { reset(); }
LValueLocation(LocationType _type, u256 const& _location): locationType(_type), location(_location) {}
void reset() { locationType = INVALID; location = 0; }
bool isValid() const { return locationType != INVALID; }
bool isInCode() const { return locationType == CODE; }
bool isInOnStack() const { return locationType == STACK; }
bool isInMemory() const { return locationType == MEMORY; }
bool isInStorage() const { return locationType == STORAGE; }
LocationType locationType;
/// Depending on the type, this is the id of a tag (code), the base offset of a stack
/// variable (@see CompilerContext::getBaseStackOffsetOfVariable) or the offset in
/// storage or memory.
u256 location;
};
LValueLocation m_currentLValue;
CompilerContext& m_context;
};
}
}

46
libsolidity/NameAndTypeResolver.cpp

@ -20,7 +20,6 @@
* Parser part that determines the declarations corresponding to names and the types of expressions.
*/
#include <cassert>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/AST.h>
#include <libsolidity/Exceptions.h>
@ -55,12 +54,15 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
m_currentScope = &m_scopes[function.get()];
function->getBody().checkTypeRequirements();
}
m_currentScope = &m_scopes[nullptr];
}
void NameAndTypeResolver::reset()
Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
{
m_scopes.clear();
m_currentScope = nullptr;
auto iterator = m_scopes.find(_scope);
if (iterator == end(m_scopes))
return nullptr;
return iterator->second.resolveName(_name, false);
}
Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
@ -68,8 +70,13 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name
return m_currentScope->resolveName(_name, _recursive);
}
void NameAndTypeResolver::reset()
{
m_scopes.clear();
m_currentScope = nullptr;
}
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode*, Scope>& _scopes,
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, Scope>& _scopes,
ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(&m_scopes[nullptr])
{
@ -101,42 +108,52 @@ void DeclarationRegistrationHelper::endVisit(StructDefinition&)
bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function)
{
registerDeclaration(_function, true);
m_currentFunction = &_function;
return true;
}
void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
{
m_currentFunction = nullptr;
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
void DeclarationRegistrationHelper::endVisit(VariableDefinition& _variableDefinition)
{
registerDeclaration(_declaration, false);
return true;
// Register the local variables with the function
// This does not fit here perfectly, but it saves us another AST visit.
if (asserts(m_currentFunction))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable definition without function."));
m_currentFunction->addLocalVariable(_variableDefinition.getDeclaration());
}
void DeclarationRegistrationHelper::endVisit(VariableDeclaration&)
bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
{
registerDeclaration(_declaration, false);
return true;
}
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node)
{
map<ASTNode*, Scope>::iterator iter;
map<ASTNode const*, Scope>::iterator iter;
bool newlyAdded;
tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope));
assert(newlyAdded);
if (asserts(newlyAdded))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to add new scope."));
m_currentScope = &iter->second;
}
void DeclarationRegistrationHelper::closeCurrentScope()
{
assert(m_currentScope);
if (asserts(m_currentScope))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Closed non-existing scope."));
m_currentScope = m_currentScope->getEnclosingScope();
}
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{
assert(m_currentScope);
if (asserts(m_currentScope))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration registered without scope."));
if (!m_currentScope->registerDeclaration(_declaration))
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation())
<< errinfo_comment("Identifier already declared."));
@ -163,7 +180,8 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
bool ReferencesResolver::visit(Return& _return)
{
assert(m_returnParameters);
if (asserts(m_returnParameters))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Return parameters not set."));
_return.setFunctionReturnParameters(*m_returnParameters);
return true;
}

17
libsolidity/NameAndTypeResolver.h

@ -44,6 +44,14 @@ public:
NameAndTypeResolver() {}
void resolveNamesAndTypes(ContractDefinition& _contract);
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
/// the global scope is used (i.e. the one containing only the contract).
/// @returns a pointer to the declaration on success or nullptr on failure.
Declaration* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
/// Resolves a name in the "current" scope. Should only be called during the initial
/// resolving phase.
Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
private:
@ -51,7 +59,7 @@ private:
/// 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;
std::map<ASTNode const*, Scope> m_scopes;
Scope* m_currentScope;
};
@ -63,7 +71,7 @@ private:
class DeclarationRegistrationHelper: private ASTVisitor
{
public:
DeclarationRegistrationHelper(std::map<ASTNode*, Scope>& _scopes, ASTNode& _astRoot);
DeclarationRegistrationHelper(std::map<ASTNode const*, Scope>& _scopes, ASTNode& _astRoot);
private:
bool visit(ContractDefinition& _contract);
@ -72,15 +80,16 @@ private:
void endVisit(StructDefinition& _struct);
bool visit(FunctionDefinition& _function);
void endVisit(FunctionDefinition& _function);
void endVisit(VariableDefinition& _variableDefinition);
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;
std::map<ASTNode const*, Scope>& m_scopes;
Scope* m_currentScope;
FunctionDefinition* m_currentFunction;
};
/**

27
libsolidity/Parser.cpp

@ -285,18 +285,18 @@ ASTPointer<Statement> Parser::parseStatement()
}
break;
default:
// distinguish between variable definition (and potentially assignment) and expressions
// distinguish between variable definition (and potentially assignment) and expression statement
// (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
// We have a variable definition if we get 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))
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();
else // "ordinary" expression statement
statement = parseExpressionStatement();
}
expectToken(Token::SEMICOLON);
return statement;
@ -351,6 +351,14 @@ ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
return nodeFactory.createNode<VariableDefinition>(variable, value);
}
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression = parseExpression();
nodeFactory.setEndPositionFromNode(expression);
return nodeFactory.createNode<ExpressionStatement>(expression);
}
ASTPointer<Expression> Parser::parseExpression()
{
ASTNodeFactory nodeFactory(*this);
@ -455,8 +463,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
{
case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL:
expression = nodeFactory.createNode<Literal>(token, ASTPointer<ASTString>());
m_scanner->next();
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break;
case Token::NUMBER:
case Token::STRING_LITERAL:

1
libsolidity/Parser.h

@ -58,6 +58,7 @@ private:
ASTPointer<IfStatement> parseIfStatement();
ASTPointer<WhileStatement> parseWhileStatement();
ASTPointer<VariableDefinition> parseVariableDefinition();
ASTPointer<ExpressionStatement> parseExpressionStatement();
ASTPointer<Expression> parseExpression();
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4);
ASTPointer<Expression> parseUnaryExpression();

138
libsolidity/Scanner.cpp

@ -50,7 +50,6 @@
* Solidity scanner.
*/
#include <cassert>
#include <algorithm>
#include <tuple>
#include <libsolidity/Scanner.h>
@ -103,11 +102,6 @@ int HexValue(char c)
}
} // end anonymous namespace
Scanner::Scanner(CharStream const& _source)
{
reset(_source);
}
void Scanner::reset(CharStream const& _source)
{
m_source = _source;
@ -118,11 +112,10 @@ void Scanner::reset(CharStream const& _source)
}
bool Scanner::scanHexNumber(char& o_scannedNumber, int _expectedLength)
bool Scanner::scanHexByte(char& o_scannedByte)
{
assert(_expectedLength <= 4); // prevent overflow
char x = 0;
for (int i = 0; i < _expectedLength; i++)
for (int i = 0; i < 2; i++)
{
int d = HexValue(m_char);
if (d < 0)
@ -133,7 +126,7 @@ bool Scanner::scanHexNumber(char& o_scannedNumber, int _expectedLength)
x = x * 16 + d;
advance();
}
o_scannedNumber = x;
o_scannedByte = x;
return true;
}
@ -180,7 +173,8 @@ Token::Value Scanner::skipSingleLineComment()
Token::Value Scanner::skipMultiLineComment()
{
assert(m_char == '*');
if (asserts(m_char == '*'))
BOOST_THROW_EXCEPTION(InternalCompilerError());
advance();
while (!isSourcePastEndOfInput())
{
@ -277,7 +271,7 @@ void Scanner::scanToken()
token = Token::ADD;
break;
case '-':
// - -- -=
// - -- -= Number
advance();
if (m_char == '-')
{
@ -286,6 +280,8 @@ void Scanner::scanToken()
}
else if (m_char == '=')
token = selectToken(Token::ASSIGN_SUB);
else if (m_char == '.' || IsDecimalDigit(m_char))
token = scanNumber('-');
else
token = Token::SUB;
break;
@ -337,7 +333,7 @@ void Scanner::scanToken()
// . Number
advance();
if (IsDecimalDigit(m_char))
token = scanNumber(true);
token = scanNumber('.');
else
token = Token::PERIOD;
break;
@ -378,7 +374,7 @@ void Scanner::scanToken()
if (IsIdentifierStart(m_char))
token = scanIdentifierOrKeyword();
else if (IsDecimalDigit(m_char))
token = scanNumber(false);
token = scanNumber();
else if (skipWhitespace())
token = Token::WHITESPACE;
else if (isSourcePastEndOfInput())
@ -423,15 +419,11 @@ bool Scanner::scanEscape()
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))
if (!scanHexByte(c))
return false;
break;
}
@ -471,12 +463,11 @@ void Scanner::scanDecimalDigits()
}
Token::Value Scanner::scanNumber(bool _periodSeen)
Token::Value Scanner::scanNumber(char _charSeen)
{
assert(IsDecimalDigit(m_char)); // the first digit of the number or the fraction
enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL;
enum { DECIMAL, HEX, BINARY } kind = DECIMAL;
LiteralScope literal(this);
if (_periodSeen)
if (_charSeen == '.')
{
// we have already seen a decimal point of the float
addLiteralChar('.');
@ -484,12 +475,13 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
}
else
{
if (_charSeen == '-')
addLiteralChar('-');
// 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.
// either 0, 0exxx, 0Exxx, 0.xxx or a hex number
if (m_char == 'x' || m_char == 'X')
{
// hex number
@ -515,7 +507,8 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
// scan exponent, if any
if (m_char == 'e' || m_char == 'E')
{
assert(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
if (asserts(kind != HEX)) // 'e'/'E' must be scanned as part of the hex number
BOOST_THROW_EXCEPTION(InternalCompilerError());
if (kind != DECIMAL) return Token::ILLEGAL;
// scan exponent
addLiteralCharAndAdvance();
@ -563,17 +556,73 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
KEYWORD("function", Token::FUNCTION) \
KEYWORD_GROUP('h') \
KEYWORD("hash", Token::HASH) \
KEYWORD("hash8", Token::HASH8) \
KEYWORD("hash16", Token::HASH16) \
KEYWORD("hash24", Token::HASH24) \
KEYWORD("hash32", Token::HASH32) \
KEYWORD("hash40", Token::HASH40) \
KEYWORD("hash48", Token::HASH48) \
KEYWORD("hash56", Token::HASH56) \
KEYWORD("hash64", Token::HASH64) \
KEYWORD("hash72", Token::HASH72) \
KEYWORD("hash80", Token::HASH80) \
KEYWORD("hash88", Token::HASH88) \
KEYWORD("hash96", Token::HASH96) \
KEYWORD("hash104", Token::HASH104) \
KEYWORD("hash112", Token::HASH112) \
KEYWORD("hash120", Token::HASH120) \
KEYWORD("hash128", Token::HASH128) \
KEYWORD("hash136", Token::HASH136) \
KEYWORD("hash144", Token::HASH144) \
KEYWORD("hash152", Token::HASH152) \
KEYWORD("hash160", Token::HASH160) \
KEYWORD("hash168", Token::HASH168) \
KEYWORD("hash178", Token::HASH176) \
KEYWORD("hash184", Token::HASH184) \
KEYWORD("hash192", Token::HASH192) \
KEYWORD("hash200", Token::HASH200) \
KEYWORD("hash208", Token::HASH208) \
KEYWORD("hash216", Token::HASH216) \
KEYWORD("hash224", Token::HASH224) \
KEYWORD("hash232", Token::HASH232) \
KEYWORD("hash240", Token::HASH240) \
KEYWORD("hash248", Token::HASH248) \
KEYWORD("hash256", Token::HASH256) \
KEYWORD_GROUP('i') \
KEYWORD("if", Token::IF) \
KEYWORD("in", Token::IN) \
KEYWORD("int", Token::INT) \
KEYWORD("int8", Token::INT8) \
KEYWORD("int16", Token::INT16) \
KEYWORD("int24", Token::INT24) \
KEYWORD("int32", Token::INT32) \
KEYWORD("int40", Token::INT40) \
KEYWORD("int48", Token::INT48) \
KEYWORD("int56", Token::INT56) \
KEYWORD("int64", Token::INT64) \
KEYWORD("int72", Token::INT72) \
KEYWORD("int80", Token::INT80) \
KEYWORD("int88", Token::INT88) \
KEYWORD("int96", Token::INT96) \
KEYWORD("int104", Token::INT104) \
KEYWORD("int112", Token::INT112) \
KEYWORD("int120", Token::INT120) \
KEYWORD("int128", Token::INT128) \
KEYWORD("int136", Token::INT136) \
KEYWORD("int144", Token::INT144) \
KEYWORD("int152", Token::INT152) \
KEYWORD("int160", Token::INT160) \
KEYWORD("int168", Token::INT168) \
KEYWORD("int178", Token::INT176) \
KEYWORD("int184", Token::INT184) \
KEYWORD("int192", Token::INT192) \
KEYWORD("int200", Token::INT200) \
KEYWORD("int208", Token::INT208) \
KEYWORD("int216", Token::INT216) \
KEYWORD("int224", Token::INT224) \
KEYWORD("int232", Token::INT232) \
KEYWORD("int240", Token::INT240) \
KEYWORD("int248", Token::INT248) \
KEYWORD("int256", Token::INT256) \
KEYWORD_GROUP('l') \
KEYWORD_GROUP('m') \
@ -598,9 +647,37 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
KEYWORD("true", Token::TRUE_LITERAL) \
KEYWORD_GROUP('u') \
KEYWORD("uint", Token::UINT) \
KEYWORD("uint8", Token::UINT8) \
KEYWORD("uint16", Token::UINT16) \
KEYWORD("uint24", Token::UINT24) \
KEYWORD("uint32", Token::UINT32) \
KEYWORD("uint40", Token::UINT40) \
KEYWORD("uint48", Token::UINT48) \
KEYWORD("uint56", Token::UINT56) \
KEYWORD("uint64", Token::UINT64) \
KEYWORD("uint72", Token::UINT72) \
KEYWORD("uint80", Token::UINT80) \
KEYWORD("uint88", Token::UINT88) \
KEYWORD("uint96", Token::UINT96) \
KEYWORD("uint104", Token::UINT104) \
KEYWORD("uint112", Token::UINT112) \
KEYWORD("uint120", Token::UINT120) \
KEYWORD("uint128", Token::UINT128) \
KEYWORD("uint136", Token::UINT136) \
KEYWORD("uint144", Token::UINT144) \
KEYWORD("uint152", Token::UINT152) \
KEYWORD("uint160", Token::UINT160) \
KEYWORD("uint168", Token::UINT168) \
KEYWORD("uint178", Token::UINT176) \
KEYWORD("uint184", Token::UINT184) \
KEYWORD("uint192", Token::UINT192) \
KEYWORD("uint200", Token::UINT200) \
KEYWORD("uint208", Token::UINT208) \
KEYWORD("uint216", Token::UINT216) \
KEYWORD("uint224", Token::UINT224) \
KEYWORD("uint232", Token::UINT232) \
KEYWORD("uint240", Token::UINT240) \
KEYWORD("uint248", Token::UINT248) \
KEYWORD("uint256", Token::UINT256) \
KEYWORD("ureal", Token::UREAL) \
KEYWORD_GROUP('v') \
@ -611,7 +688,8 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
static Token::Value KeywordOrIdentifierToken(string const& input)
{
assert(!input.empty());
if (asserts(!input.empty()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
int const kMinLength = 2;
int const kMaxLength = 10;
if (input.size() < kMinLength || input.size() > kMaxLength)
@ -639,7 +717,8 @@ case ch:
Token::Value Scanner::scanIdentifierOrKeyword()
{
assert(IsIdentifierStart(m_char));
if (asserts(IsIdentifierStart(m_char)))
BOOST_THROW_EXCEPTION(InternalCompilerError());
LiteralScope literal(this);
addLiteralCharAndAdvance();
// Scan the rest of the identifier characters.
@ -661,7 +740,8 @@ char CharStream::advanceAndGet()
char CharStream::rollback(size_t _amount)
{
assert(m_pos >= _amount);
if (asserts(m_pos >= _amount))
BOOST_THROW_EXCEPTION(InternalCompilerError());
m_pos -= _amount;
return get();
}

7
libsolidity/Scanner.h

@ -110,7 +110,8 @@ public:
bool complete_;
};
explicit Scanner(CharStream const& _source);
Scanner() { reset(CharStream()); }
explicit Scanner(CharStream const& _source) { reset(_source); }
/// Resets the scanner as if newly constructed with _input as input.
void reset(CharStream const& _source);
@ -168,7 +169,7 @@ private:
/// 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);
bool scanHexByte(char& o_scannedByte);
/// Scans a single JavaScript token.
void scanToken();
@ -179,7 +180,7 @@ private:
Token::Value skipMultiLineComment();
void scanDecimalDigits();
Token::Value scanNumber(bool _periodSeen);
Token::Value scanNumber(char _charSeen = 0);
Token::Value scanIdentifierOrKeyword();
Token::Value scanString();

148
libsolidity/Token.h

@ -42,9 +42,9 @@
#pragma once
#include <cassert>
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libsolidity/Exceptions.h>
namespace dev
{
@ -81,8 +81,6 @@ namespace solidity
T(SEMICOLON, ";", 0) \
T(PERIOD, ".", 0) \
T(CONDITIONAL, "?", 3) \
T(INC, "++", 0) \
T(DEC, "--", 0) \
T(ARROW, "=>", 0) \
\
/* Assignment operators. */ \
@ -136,6 +134,8 @@ namespace solidity
/* being contiguous and sorted in the same order! */ \
T(NOT, "!", 0) \
T(BIT_NOT, "~", 0) \
T(INC, "++", 0) \
T(DEC, "--", 0) \
K(DELETE, "delete", 0) \
\
/* Keywords */ \
@ -169,19 +169,103 @@ namespace solidity
* the implementation in Types.cpp has to be synced to this here
* TODO more to be added */ \
K(INT, "int", 0) \
K(INT8, "int8", 0) \
K(INT16, "int16", 0) \
K(INT24, "int24", 0) \
K(INT32, "int32", 0) \
K(INT40, "int40", 0) \
K(INT48, "int48", 0) \
K(INT56, "int56", 0) \
K(INT64, "int64", 0) \
K(INT72, "int72", 0) \
K(INT80, "int80", 0) \
K(INT88, "int88", 0) \
K(INT96, "int96", 0) \
K(INT104, "int104", 0) \
K(INT112, "int112", 0) \
K(INT120, "int120", 0) \
K(INT128, "int128", 0) \
K(INT136, "int136", 0) \
K(INT144, "int144", 0) \
K(INT152, "int152", 0) \
K(INT160, "int160", 0) \
K(INT168, "int168", 0) \
K(INT176, "int178", 0) \
K(INT184, "int184", 0) \
K(INT192, "int192", 0) \
K(INT200, "int200", 0) \
K(INT208, "int208", 0) \
K(INT216, "int216", 0) \
K(INT224, "int224", 0) \
K(INT232, "int232", 0) \
K(INT240, "int240", 0) \
K(INT248, "int248", 0) \
K(INT256, "int256", 0) \
K(UINT, "uint", 0) \
K(UINT8, "uint8", 0) \
K(UINT16, "uint16", 0) \
K(UINT24, "uint24", 0) \
K(UINT32, "uint32", 0) \
K(UINT40, "uint40", 0) \
K(UINT48, "uint48", 0) \
K(UINT56, "uint56", 0) \
K(UINT64, "uint64", 0) \
K(UINT72, "uint72", 0) \
K(UINT80, "uint80", 0) \
K(UINT88, "uint88", 0) \
K(UINT96, "uint96", 0) \
K(UINT104, "uint104", 0) \
K(UINT112, "uint112", 0) \
K(UINT120, "uint120", 0) \
K(UINT128, "uint128", 0) \
K(UINT136, "uint136", 0) \
K(UINT144, "uint144", 0) \
K(UINT152, "uint152", 0) \
K(UINT160, "uint160", 0) \
K(UINT168, "uint168", 0) \
K(UINT176, "uint178", 0) \
K(UINT184, "uint184", 0) \
K(UINT192, "uint192", 0) \
K(UINT200, "uint200", 0) \
K(UINT208, "uint208", 0) \
K(UINT216, "uint216", 0) \
K(UINT224, "uint224", 0) \
K(UINT232, "uint232", 0) \
K(UINT240, "uint240", 0) \
K(UINT248, "uint248", 0) \
K(UINT256, "uint256", 0) \
K(HASH, "hash", 0) \
K(HASH8, "hash8", 0) \
K(HASH16, "hash16", 0) \
K(HASH24, "hash24", 0) \
K(HASH32, "hash32", 0) \
K(HASH40, "hash40", 0) \
K(HASH48, "hash48", 0) \
K(HASH56, "hash56", 0) \
K(HASH64, "hash64", 0) \
K(HASH72, "hash72", 0) \
K(HASH80, "hash80", 0) \
K(HASH88, "hash88", 0) \
K(HASH96, "hash96", 0) \
K(HASH104, "hash104", 0) \
K(HASH112, "hash112", 0) \
K(HASH120, "hash120", 0) \
K(HASH128, "hash128", 0) \
K(HASH136, "hash136", 0) \
K(HASH144, "hash144", 0) \
K(HASH152, "hash152", 0) \
K(HASH160, "hash160", 0) \
K(HASH168, "hash168", 0) \
K(HASH176, "hash178", 0) \
K(HASH184, "hash184", 0) \
K(HASH192, "hash192", 0) \
K(HASH200, "hash200", 0) \
K(HASH208, "hash208", 0) \
K(HASH216, "hash216", 0) \
K(HASH224, "hash224", 0) \
K(HASH232, "hash232", 0) \
K(HASH240, "hash240", 0) \
K(HASH248, "hash248", 0) \
K(HASH256, "hash256", 0) \
K(ADDRESS, "address", 0) \
K(BOOL, "bool", 0) \
@ -224,7 +308,8 @@ public:
// (e.g. "LT" for the token LT).
static char const* getName(Value tok)
{
assert(tok < NUM_TOKENS); // tok is unsigned
if (asserts(tok < NUM_TOKENS))
BOOST_THROW_EXCEPTION(InternalCompilerError());
return m_name[tok];
}
@ -249,55 +334,10 @@ public:
isEqualityOp(op) || isInequalityOp(op);
}
static Value negateCompareOp(Value op)
{
assert(isArithmeticCompareOp(op));
switch (op)
{
case EQ:
return NE;
case NE:
return EQ;
case LT:
return GTE;
case GT:
return LTE;
case LTE:
return GT;
case GTE:
return LT;
default:
assert(false); // should not get here
return op;
}
}
static Value reverseCompareOp(Value op)
{
assert(isArithmeticCompareOp(op));
switch (op)
{
case EQ:
return EQ;
case NE:
return NE;
case LT:
return GT;
case GT:
return LT;
case LTE:
return GTE;
case GTE:
return LTE;
default:
assert(false); // should not get here
return op;
}
}
static Value AssignmentToBinaryOp(Value op)
{
assert(isAssignmentOp(op) && op != ASSIGN);
if (asserts(isAssignmentOp(op) && op != ASSIGN))
BOOST_THROW_EXCEPTION(InternalCompilerError());
return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR));
}
@ -311,7 +351,8 @@ public:
// have a (unique) string (e.g. an IDENTIFIER).
static char const* toString(Value tok)
{
assert(tok < NUM_TOKENS); // tok is unsigned.
if (asserts(tok < NUM_TOKENS))
BOOST_THROW_EXCEPTION(InternalCompilerError());
return m_string[tok];
}
@ -319,7 +360,8 @@ public:
// operators; returns 0 otherwise.
static int precedence(Value tok)
{
assert(tok < NUM_TOKENS); // tok is unsigned.
if (asserts(tok < NUM_TOKENS))
BOOST_THROW_EXCEPTION(InternalCompilerError());
return m_precedence[tok];
}

105
libsolidity/Types.cpp

@ -20,81 +20,90 @@
* Solidity data types
*/
#include <cassert>
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libsolidity/Types.h>
#include <libsolidity/AST.h>
using namespace std;
namespace dev
{
namespace solidity
{
std::shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
{
if (asserts(Token::isElementaryTypeName(_typeToken)))
BOOST_THROW_EXCEPTION(InternalCompilerError());
if (Token::INT <= _typeToken && _typeToken <= Token::HASH256)
{
int offset = _typeToken - Token::INT;
int bits = offset % 5;
if (bits == 0)
bits = 256;
else
bits = (1 << (bits - 1)) * 32;
int modifier = offset / 5;
return std::make_shared<IntegerType>(bits,
modifier == 0 ? IntegerType::Modifier::SIGNED :
modifier == 1 ? IntegerType::Modifier::UNSIGNED :
IntegerType::Modifier::HASH);
int bytes = offset % 33;
if (bytes == 0)
bytes = 32;
int modifier = offset / 33;
return make_shared<IntegerType>(bytes * 8,
modifier == 0 ? IntegerType::Modifier::SIGNED :
modifier == 1 ? IntegerType::Modifier::UNSIGNED :
IntegerType::Modifier::HASH);
}
else if (_typeToken == Token::ADDRESS)
return std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS);
return make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS);
else if (_typeToken == Token::BOOL)
return std::make_shared<BoolType>();
return make_shared<BoolType>();
else
assert(false); // @todo add other tyes
return std::shared_ptr<Type>();
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
}
std::shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{
return std::make_shared<StructType>(*_typeName.getReferencedStruct());
return make_shared<StructType>(*_typeName.getReferencedStruct());
}
std::shared_ptr<Type> Type::fromMapping(Mapping const&)
shared_ptr<Type> Type::fromMapping(Mapping const&)
{
assert(false); //@todo not yet implemented
return std::shared_ptr<Type>();
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Mapping types not yet implemented."));
}
std::shared_ptr<Type> Type::forLiteral(Literal const& _literal)
shared_ptr<Type> Type::forLiteral(Literal const& _literal)
{
switch (_literal.getToken())
{
case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL:
return std::make_shared<BoolType>();
return make_shared<BoolType>();
case Token::NUMBER:
return IntegerType::smallestTypeForLiteral(_literal.getValue());
case Token::STRING_LITERAL:
return std::shared_ptr<Type>(); // @todo
return shared_ptr<Type>(); // @todo
default:
return std::shared_ptr<Type>();
return shared_ptr<Type>();
}
}
std::shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(std::string const&)
shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(string const& _literal)
{
//@todo
return std::make_shared<IntegerType>(256, Modifier::UNSIGNED);
bigint value(_literal);
bool isSigned = value < 0 || (!_literal.empty() && _literal.front() == '-');
if (isSigned)
// convert to positive number of same bit requirements
value = ((-value) - 1) << 1;
unsigned bytes = max(bytesRequired(value), 1u);
if (bytes > 32)
return shared_ptr<IntegerType>();
return make_shared<IntegerType>(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED);
}
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
m_bits(_bits), m_modifier(_modifier)
{
if (isAddress())
_bits = 160;
assert(_bits > 0 && _bits <= 256 && _bits % 8 == 0);
m_bits = 160;
if (asserts(m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid bit number for integer type: " + dev::toString(_bits)));
}
bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@ -151,22 +160,18 @@ bool IntegerType::operator==(Type const& _other) const
return other.m_bits == m_bits && other.m_modifier == m_modifier;
}
std::string IntegerType::toString() const
string IntegerType::toString() const
{
if (isAddress())
return "address";
std::string prefix = isHash() ? "hash" : (isSigned() ? "int" : "uint");
string prefix = isHash() ? "hash" : (isSigned() ? "int" : "uint");
return prefix + dev::toString(m_bits);
}
bytes IntegerType::literalToBigEndian(Literal const& _literal) const
u256 IntegerType::literalValue(Literal const& _literal) const
{
bigint value(_literal.getValue());
if (!isSigned() && value < 0)
return bytes(); // @todo this should already be caught by "smallestTypeforLiteral"
//@todo check that the number of bits is correct
//@todo does "toCompactBigEndian" work for signed numbers?
return toCompactBigEndian(value);
return u256(value);
}
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
@ -182,14 +187,14 @@ bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return isImplicitlyConvertibleTo(_convertTo);
}
bytes BoolType::literalToBigEndian(Literal const& _literal) const
u256 BoolType::literalValue(Literal const& _literal) const
{
if (_literal.getToken() == Token::TRUE_LITERAL)
return bytes(1, 1);
return u256(1);
else if (_literal.getToken() == Token::FALSE_LITERAL)
return bytes(1, 0);
return u256(0);
else
return NullBytes;
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal."));
}
bool ContractType::operator==(Type const& _other) const
@ -200,6 +205,14 @@ bool ContractType::operator==(Type const& _other) const
return other.m_contract == m_contract;
}
u256 ContractType::getStorageSize() const
{
u256 size = 0;
for (ASTPointer<VariableDeclaration> const& variable: m_contract.getStateVariables())
size += variable->getType()->getStorageSize();
return max<u256>(1, size);
}
bool StructType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -208,6 +221,14 @@ bool StructType::operator==(Type const& _other) const
return other.m_struct == m_struct;
}
u256 StructType::getStorageSize() const
{
u256 size = 0;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
size += variable->getType()->getStorageSize();
return max<u256>(1, size);
}
bool FunctionType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())

40
libsolidity/Types.h

@ -26,6 +26,7 @@
#include <string>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/ASTForward.h>
#include <libsolidity/Token.h>
@ -55,7 +56,8 @@ public:
static std::shared_ptr<Type> fromMapping(Mapping const& _typeName);
/// @}
/// Auto-detect the proper type for a literal
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
/// not fit any type.
static std::shared_ptr<Type> forLiteral(Literal const& _literal);
virtual Category getCategory() const = 0;
@ -70,8 +72,19 @@ public:
virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); }
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
/// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding
/// is not a simple big-endian encoding or the type cannot be stored on the stack.
virtual unsigned getCalldataEncodedSize() const { return 0; }
/// @returns number of bytes required to hold this value in storage.
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
virtual u256 getStorageSize() const { return 1; }
virtual std::string toString() const = 0;
virtual bytes literalToBigEndian(Literal const&) const { return NullBytes; }
virtual u256 literalValue(Literal const&) const
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested "
"for type without literals."));
}
};
/**
@ -86,6 +99,8 @@ public:
};
virtual Category getCategory() const override { return Category::INTEGER; }
/// @returns the smallest integer type for the given literal or an empty pointer
/// if no type fits.
static std::shared_ptr<IntegerType> smallestTypeForLiteral(std::string const& _literal);
explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED);
@ -97,8 +112,10 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize() const { return m_bits / 8; }
virtual std::string toString() const override;
virtual bytes literalToBigEndian(Literal const& _literal) const override;
virtual u256 literalValue(Literal const& _literal) const override;
int getNumBits() const { return m_bits; }
bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; }
@ -127,8 +144,10 @@ public:
return _operator == Token::NOT || _operator == Token::DELETE;
}
virtual unsigned getCalldataEncodedSize() const { return 1; }
virtual std::string toString() const override { return "bool"; }
virtual bytes literalToBigEndian(Literal const& _literal) const override;
virtual u256 literalValue(Literal const& _literal) const override;
};
/**
@ -141,7 +160,7 @@ public:
ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const;
virtual std::string toString() const override { return "contract{...}"; }
private:
@ -162,7 +181,7 @@ public:
}
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const;
virtual std::string toString() const override { return "struct{...}"; }
private:
@ -180,9 +199,9 @@ public:
FunctionDefinition const& getFunction() const { return m_function; }
virtual std::string toString() const override { return "function(...)returns(...)"; }
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override { return "function(...)returns(...)"; }
virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
private:
FunctionDefinition const& m_function;
@ -196,9 +215,9 @@ class MappingType: public Type
public:
virtual Category getCategory() const override { return Category::MAPPING; }
MappingType() {}
virtual std::string toString() const override { return "mapping(...=>...)"; }
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override { return "mapping(...=>...)"; }
private:
std::shared_ptr<Type const> m_keyType;
@ -216,6 +235,7 @@ public:
VoidType() {}
virtual std::string toString() const override { return "void"; }
virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); }
};
/**
@ -231,7 +251,7 @@ public:
std::shared_ptr<Type const> const& getActualType() const { return m_actualType; }
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
private:

201
libweb3jsonrpc/WebThreeStubServer.cpp

@ -22,7 +22,7 @@
*/
#include "WebThreeStubServer.h"
#include <libevmface/Instruction.h>
#include <libevmcore/Instruction.h>
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
#include <libwebthree/WebThree.h>
@ -87,13 +87,31 @@ static Json::Value toJson(dev::eth::Transaction const& _t)
{
Json::Value res;
res["hash"] = toJS(_t.sha3());
res["input"] = jsFromBinary(_t.data);
res["to"] = toJS(_t.receiveAddress);
res["input"] = jsFromBinary(_t.data());
res["to"] = toJS(_t.receiveAddress());
res["from"] = toJS(_t.sender());
res["gas"] = (int)_t.gas;
res["gasPrice"] = toJS(_t.gasPrice);
res["nonce"] = toJS(_t.nonce);
res["value"] = toJS(_t.value);
res["gas"] = (int)_t.gas();
res["gasPrice"] = toJS(_t.gasPrice());
res["nonce"] = toJS(_t.nonce());
res["value"] = toJS(_t.value());
return res;
}
static Json::Value toJson(dev::eth::LogEntry const& _e)
{
Json::Value res;
res["data"] = jsFromBinary(_e.data);
res["address"] = toJS(_e.address);
for (auto const& t: _e.topics)
res["topics"].append(toJS(t));
return res;
}
/*static*/ Json::Value toJson(dev::eth::LogEntries const& _es) // commented to avoid warning. Uncomment once in use @ poC-7.
{
Json::Value res;
for (dev::eth::LogEntry const& e: _es)
res.append(toJson(e));
return res;
}
@ -123,9 +141,9 @@ static dev::eth::MessageFilter toMessageFilter(Json::Value const& _json)
{
if (_json["to"].isArray())
for (auto i : _json["to"])
filter.from(jsToAddress(i.asString()));
filter.to(jsToAddress(i.asString()));
else
filter.from(jsToAddress(_json["to"].asString()));
filter.to(jsToAddress(_json["to"].asString()));
}
if (!_json["altered"].empty())
{
@ -143,6 +161,50 @@ static dev::eth::MessageFilter toMessageFilter(Json::Value const& _json)
return filter;
}
/*static*/ dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to avoid warning. Uncomment once in use @ PoC-7.
{
dev::eth::LogFilter filter;
if (!_json.isObject() || _json.empty())
return filter;
if (!_json["earliest"].empty())
filter.withEarliest(_json["earliest"].asInt());
if (!_json["latest"].empty())
filter.withLatest(_json["lastest"].asInt());
if (!_json["max"].empty())
filter.withMax(_json["max"].asInt());
if (!_json["skip"].empty())
filter.withSkip(_json["skip"].asInt());
if (!_json["from"].empty())
{
if (_json["from"].isArray())
for (auto i : _json["from"])
filter.from(jsToAddress(i.asString()));
else
filter.from(jsToAddress(_json["from"].asString()));
}
if (!_json["address"].empty())
{
if (_json["address"].isArray())
for (auto i : _json["address"])
filter.address(jsToAddress(i.asString()));
else
filter.from(jsToAddress(_json["address"].asString()));
}
if (!_json["topics"].empty())
{
if (_json["topics"].isArray())
{
for (auto i: _json["topics"])
if (i.isString())
filter.topic(jsToU256(i.asString()));
}
else if(_json["topics"].isString())
filter.topic(jsToU256(_json["topics"].asString()));
}
return filter;
}
static shh::Message toMessage(Json::Value const& _json)
{
shh::Message ret;
@ -151,7 +213,7 @@ static shh::Message toMessage(Json::Value const& _json)
if (!_json["to"].empty())
ret.setTo(jsToPublic(_json["to"].asString()));
if (!_json["payload"].empty())
ret.setPayload(asBytes(_json["payload"].asString()));
ret.setPayload(jsToBytes(_json["payload"].asString()));
return ret;
}
@ -168,10 +230,10 @@ static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m,
if (!_json["topic"].empty())
{
if (_json["topic"].isString())
bt.shift(asBytes(jsPadded(_json["topic"].asString(), 32)));
bt.shift(jsToBytes(_json["topic"].asString()));
else if (_json["topic"].isArray())
for (auto i: _json["topic"])
bt.shift(asBytes(jsPadded(i.asString(), 32)));
bt.shift(jsToBytes(i.asString()));
}
return _m.seal(_from, bt, ttl, workToProve);
}
@ -187,12 +249,12 @@ static pair<shh::TopicMask, Public> toWatch(Json::Value const& _json)
if (!_json["topic"].empty())
{
if (_json["topic"].isString())
bt.shift(asBytes(jsPadded(_json["topic"].asString(), 32)));
bt.shift(jsToBytes(_json["topic"].asString()));
else if (_json["topic"].isArray())
for (auto i: _json["topic"])
{
if (i.isString())
bt.shift(asBytes(jsPadded(i.asString(), 32)));
bt.shift(jsToBytes(i.asString()));
else
bt.shift();
}
@ -208,8 +270,9 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message
res["sent"] = (int)_e.sent();
res["ttl"] = (int)_e.ttl();
res["workProved"] = (int)_e.workProved();
res["topic"] = toJS(_e.topic());
res["payload"] = asString(_m.payload());
for (auto const& t: _e.topics())
res["topics"].append(toJS((u256)t));
res["payload"] = toJS(_m.payload());
res["from"] = toJS(_m.from());
res["to"] = toJS(_m.to());
return res;
@ -252,14 +315,7 @@ std::shared_ptr<dev::shh::Interface> WebThreeStubServer::face() const
return m_web3.whisper();
}
std::string WebThreeStubServer::account()
{
if (!m_accounts.empty())
return toJS(m_accounts.begin()->first);
return "";
}
Json::Value WebThreeStubServer::accounts()
Json::Value WebThreeStubServer::eth_accounts()
{
Json::Value ret(Json::arrayValue);
for (auto i: m_accounts)
@ -267,27 +323,27 @@ Json::Value WebThreeStubServer::accounts()
return ret;
}
std::string WebThreeStubServer::addToGroup(std::string const& _group, std::string const& _who)
std::string WebThreeStubServer::shh_addToGroup(std::string const& _group, std::string const& _who)
{
(void)_group;
(void)_who;
return "";
}
std::string WebThreeStubServer::balanceAt(string const& _address)
std::string WebThreeStubServer::eth_balanceAt(string const& _address)
{
int block = 0;
return toJS(client()->balanceAt(jsToAddress(_address), block));
}
Json::Value WebThreeStubServer::blockByHash(std::string const& _hash)
Json::Value WebThreeStubServer::eth_blockByHash(std::string const& _hash)
{
if (!client())
return "";
return toJson(client()->blockInfo(jsToFixed<32>(_hash)));
}
Json::Value WebThreeStubServer::blockByNumber(int const& _number)
Json::Value WebThreeStubServer::eth_blockByNumber(int const& _number)
{
if (!client())
return "";
@ -319,10 +375,10 @@ static TransactionSkeleton toTransaction(Json::Value const& _json)
ret.data = jsToBytes(_json["code"].asString());
else if (_json["data"].isArray())
for (auto i: _json["data"])
dev::operator +=(ret.data, asBytes(jsPadded(i.asString(), 32)));
dev::operator +=(ret.data, jsToBytes(jsPadded(i.asString(), 32)));
else if (_json["code"].isArray())
for (auto i: _json["code"])
dev::operator +=(ret.data, asBytes(jsPadded(i.asString(), 32)));
dev::operator +=(ret.data, jsToBytes(jsPadded(i.asString(), 32)));
else if (_json["dataclose"].isArray())
for (auto i: _json["dataclose"])
dev::operator +=(ret.data, jsToBytes(i.asString()));
@ -330,7 +386,7 @@ static TransactionSkeleton toTransaction(Json::Value const& _json)
return ret;
}
std::string WebThreeStubServer::call(Json::Value const& _json)
std::string WebThreeStubServer::eth_call(Json::Value const& _json)
{
std::string ret;
if (!client())
@ -354,41 +410,41 @@ std::string WebThreeStubServer::call(Json::Value const& _json)
return ret;
}
bool WebThreeStubServer::changed(int const& _id)
bool WebThreeStubServer::eth_changed(int const& _id)
{
if (!client())
return false;
return client()->checkWatch(_id);
}
std::string WebThreeStubServer::codeAt(string const& _address)
std::string WebThreeStubServer::eth_codeAt(string const& _address)
{
int block = 0;
return client() ? jsFromBinary(client()->codeAt(jsToAddress(_address), block)) : "";
}
std::string WebThreeStubServer::coinbase()
std::string WebThreeStubServer::eth_coinbase()
{
return client() ? toJS(client()->address()) : "";
}
double WebThreeStubServer::countAt(string const& _address)
double WebThreeStubServer::eth_countAt(string const& _address)
{
int block = 0;
return client() ? (double)(uint64_t)client()->countAt(jsToAddress(_address), block) : 0;
}
int WebThreeStubServer::defaultBlock()
int WebThreeStubServer::eth_defaultBlock()
{
return client() ? client()->getDefault() : 0;
}
std::string WebThreeStubServer::gasPrice()
std::string WebThreeStubServer::eth_gasPrice()
{
return toJS(10 * dev::eth::szabo);
}
std::string WebThreeStubServer::get(std::string const& _name, std::string const& _key)
std::string WebThreeStubServer::db_get(std::string const& _name, std::string const& _key)
{
bytes k = sha3(_name).asBytes() + sha3(_key).asBytes();
string ret;
@ -396,14 +452,14 @@ std::string WebThreeStubServer::get(std::string const& _name, std::string const&
return toJS(dev::asBytes(ret));
}
Json::Value WebThreeStubServer::getMessages(int const& _id)
Json::Value WebThreeStubServer::eth_getMessages(int const& _id)
{
if (!client())
return Json::Value();
return Json::Value();
return toJson(client()->messages(_id));
}
std::string WebThreeStubServer::getString(std::string const& _name, std::string const& _key)
std::string WebThreeStubServer::db_getString(std::string const& _name, std::string const& _key)
{
bytes k = sha3(_name).asBytes() + sha3(_key).asBytes();
string ret;
@ -411,22 +467,22 @@ std::string WebThreeStubServer::getString(std::string const& _name, std::string
return ret;
}
bool WebThreeStubServer::haveIdentity(std::string const& _id)
bool WebThreeStubServer::shh_haveIdentity(std::string const& _id)
{
return m_ids.count(jsToPublic(_id)) > 0;
}
bool WebThreeStubServer::listening()
bool WebThreeStubServer::eth_listening()
{
return m_web3.isNetworkStarted();
}
bool WebThreeStubServer::mining()
bool WebThreeStubServer::eth_mining()
{
return client() ? client()->isMining() : false;
}
int WebThreeStubServer::newFilter(Json::Value const& _json)
int WebThreeStubServer::eth_newFilter(Json::Value const& _json)
{
unsigned ret = -1;
if (!client())
@ -435,7 +491,7 @@ int WebThreeStubServer::newFilter(Json::Value const& _json)
return ret;
}
int WebThreeStubServer::newFilterString(std::string const& _filter)
int WebThreeStubServer::eth_newFilterString(std::string const& _filter)
{
unsigned ret = -1;
if (!client())
@ -447,37 +503,44 @@ int WebThreeStubServer::newFilterString(std::string const& _filter)
return ret;
}
std::string WebThreeStubServer::newGroup(std::string const& _id, std::string const& _who)
std::string WebThreeStubServer::shh_newGroup(std::string const& _id, std::string const& _who)
{
(void)_id;
(void)_who;
return "";
}
std::string WebThreeStubServer::newIdentity()
std::string WebThreeStubServer::shh_newIdentity()
{
cnote << this << m_ids;
KeyPair kp = KeyPair::create();
m_ids[kp.pub()] = kp.secret();
return toJS(kp.pub());
}
std::string WebThreeStubServer::compile(string const& _s)
std::string WebThreeStubServer::eth_compile(string const& _s)
{
return toJS(dev::eth::compileLLL(_s));
}
std::string WebThreeStubServer::eth_lll(string const& _s)
{
return toJS(dev::eth::compileLLL(_s));
}
int WebThreeStubServer::number()
int WebThreeStubServer::eth_number()
{
return client() ? client()->number() + 1 : 0;
}
int WebThreeStubServer::peerCount()
int WebThreeStubServer::eth_peerCount()
{
return m_web3.peerCount();
}
bool WebThreeStubServer::post(Json::Value const& _json)
bool WebThreeStubServer::shh_post(Json::Value const& _json)
{
cnote << this << m_ids;
shh::Message m = toMessage(_json);
Secret from;
@ -492,7 +555,7 @@ bool WebThreeStubServer::post(Json::Value const& _json)
return true;
}
bool WebThreeStubServer::put(std::string const& _name, std::string const& _key, std::string const& _value)
bool WebThreeStubServer::db_put(std::string const& _name, std::string const& _key, std::string const& _value)
{
bytes k = sha3(_name).asBytes() + sha3(_key).asBytes();
bytes v = jsToBytes(_value);
@ -500,7 +563,7 @@ bool WebThreeStubServer::put(std::string const& _name, std::string const& _key,
return true;
}
bool WebThreeStubServer::putString(std::string const& _name, std::string const& _key, std::string const& _value)
bool WebThreeStubServer::db_putString(std::string const& _name, std::string const& _key, std::string const& _value)
{
bytes k = sha3(_name).asBytes() + sha3(_key).asBytes();
string v = _value;
@ -508,7 +571,7 @@ bool WebThreeStubServer::putString(std::string const& _name, std::string const&
return true;
}
bool WebThreeStubServer::setCoinbase(std::string const& _address)
bool WebThreeStubServer::eth_setCoinbase(std::string const& _address)
{
if (!client())
return false;
@ -516,7 +579,7 @@ bool WebThreeStubServer::setCoinbase(std::string const& _address)
return true;
}
bool WebThreeStubServer::setDefaultBlock(int const& _block)
bool WebThreeStubServer::eth_setDefaultBlock(int const& _block)
{
if (!client())
return false;
@ -524,7 +587,7 @@ bool WebThreeStubServer::setDefaultBlock(int const& _block)
return true;
}
bool WebThreeStubServer::setListening(bool const& _listening)
bool WebThreeStubServer::eth_setListening(bool const& _listening)
{
if (_listening)
m_web3.startNetwork();
@ -533,7 +596,7 @@ bool WebThreeStubServer::setListening(bool const& _listening)
return true;
}
bool WebThreeStubServer::setMining(bool const& _mining)
bool WebThreeStubServer::eth_setMining(bool const& _mining)
{
if (!client())
return false;
@ -545,7 +608,7 @@ bool WebThreeStubServer::setMining(bool const& _mining)
return true;
}
Json::Value WebThreeStubServer::shhChanged(int const& _id)
Json::Value WebThreeStubServer::shh_changed(int const& _id)
{
Json::Value ret(Json::arrayValue);
auto pub = m_shhWatches[_id];
@ -563,13 +626,13 @@ Json::Value WebThreeStubServer::shhChanged(int const& _id)
}
else
m = e.open();
ret.append(toJson(h,e,m));
ret.append(toJson(h, e, m));
}
return ret;
}
int WebThreeStubServer::shhNewFilter(Json::Value const& _json)
int WebThreeStubServer::shh_newFilter(Json::Value const& _json)
{
auto w = toWatch(_json);
auto ret = face()->installWatch(w.first);
@ -577,19 +640,19 @@ int WebThreeStubServer::shhNewFilter(Json::Value const& _json)
return ret;
}
bool WebThreeStubServer::shhUninstallFilter(int const& _id)
bool WebThreeStubServer::shh_uninstallFilter(int const& _id)
{
face()->uninstallWatch(_id);
return true;
}
std::string WebThreeStubServer::stateAt(string const& _address, string const& _storage)
std::string WebThreeStubServer::eth_stateAt(string const& _address, string const& _storage)
{
int block = 0;
return client() ? toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), block)) : "";
}
std::string WebThreeStubServer::transact(Json::Value const& _json)
std::string WebThreeStubServer::eth_transact(Json::Value const& _json)
{
std::string ret;
if (!client())
@ -619,35 +682,35 @@ std::string WebThreeStubServer::transact(Json::Value const& _json)
return ret;
}
Json::Value WebThreeStubServer::transactionByHash(std::string const& _hash, int const& _i)
Json::Value WebThreeStubServer::eth_transactionByHash(std::string const& _hash, int const& _i)
{
if (!client())
return "";
return toJson(client()->transaction(jsToFixed<32>(_hash), _i));
}
Json::Value WebThreeStubServer::transactionByNumber(int const& _number, int const& _i)
Json::Value WebThreeStubServer::eth_transactionByNumber(int const& _number, int const& _i)
{
if (!client())
return "";
return toJson(client()->transaction(client()->hashFromNumber(_number), _i));
}
Json::Value WebThreeStubServer::uncleByHash(std::string const& _hash, int const& _i)
Json::Value WebThreeStubServer::eth_uncleByHash(std::string const& _hash, int const& _i)
{
if (!client())
return "";
return toJson(client()->uncle(jsToFixed<32>(_hash), _i));
}
Json::Value WebThreeStubServer::uncleByNumber(int const& _number, int const& _i)
Json::Value WebThreeStubServer::eth_uncleByNumber(int const& _number, int const& _i)
{
if (!client())
return "";
return toJson(client()->uncle(client()->hashFromNumber(_number), _i));
}
bool WebThreeStubServer::uninstallFilter(int const& _id)
bool WebThreeStubServer::eth_uninstallFilter(int const& _id)
{
if (!client())
return false;

93
libweb3jsonrpc/WebThreeStubServer.h

@ -56,59 +56,64 @@ class Interface;
* @brief JSON-RPC api implementation
* @todo filters should work on unsigned instead of int
* unsigned are not supported in json-rpc-cpp and there are bugs with double in json-rpc-cpp version 0.2.1
* @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols.
* @todo modularise everything so additional subprotocols don't need to change this file.
*/
class WebThreeStubServer: public AbstractWebThreeStubServer
{
public:
WebThreeStubServer(jsonrpc::AbstractServerConnector* _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts);
virtual std::string account();
virtual Json::Value accounts();
virtual std::string addToGroup(std::string const& _group, std::string const& _who);
virtual std::string balanceAt(std::string const& _address);
virtual Json::Value blockByHash(std::string const& _hash);
virtual Json::Value blockByNumber(int const& _number);
virtual std::string call(Json::Value const& _json);
virtual bool changed(int const& _id);
virtual std::string codeAt(std::string const& _address);
virtual std::string coinbase();
virtual std::string compile(std::string const& _s);
virtual double countAt(std::string const& _address);
virtual int defaultBlock();
virtual std::string gasPrice();
virtual std::string get(std::string const& _name, std::string const& _key);
virtual Json::Value getMessages(int const& _id);
virtual std::string getString(std::string const& _name, std::string const& _key);
virtual bool haveIdentity(std::string const& _id);
virtual bool listening();
virtual bool mining();
virtual int newFilter(Json::Value const& _json);
virtual int newFilterString(std::string const& _filter);
virtual std::string newGroup(std::string const& _id, std::string const& _who);
virtual std::string newIdentity();
virtual int number();
virtual int peerCount();
virtual bool post(Json::Value const& _json);
virtual bool put(std::string const& _name, std::string const& _key, std::string const& _value);
virtual bool putString(std::string const& _name, std::string const& _key, std::string const& _value);
virtual bool setCoinbase(std::string const& _address);
virtual bool setDefaultBlock(int const& _block);
virtual bool setListening(bool const& _listening);
virtual bool setMining(bool const& _mining);
virtual Json::Value shhChanged(int const& _id);
virtual int shhNewFilter(Json::Value const& _json);
virtual bool shhUninstallFilter(int const& _id);
virtual std::string stateAt(std::string const& _address, std::string const& _storage);
virtual std::string transact(Json::Value const& _json);
virtual Json::Value transactionByHash(std::string const& _hash, int const& _i);
virtual Json::Value transactionByNumber(int const& _number, int const& _i);
virtual Json::Value uncleByHash(std::string const& _hash, int const& _i);
virtual Json::Value uncleByNumber(int const& _number, int const& _i);
virtual bool uninstallFilter(int const& _id);
virtual Json::Value eth_accounts();
virtual std::string eth_balanceAt(std::string const& _address);
virtual Json::Value eth_blockByHash(std::string const& _hash);
virtual Json::Value eth_blockByNumber(int const& _number);
virtual std::string eth_call(Json::Value const& _json);
virtual bool eth_changed(int const& _id);
virtual std::string eth_codeAt(std::string const& _address);
virtual std::string eth_coinbase();
virtual std::string eth_compile(std::string const& _s);
virtual double eth_countAt(std::string const& _address);
virtual int eth_defaultBlock();
virtual std::string eth_gasPrice();
virtual Json::Value eth_getMessages(int const& _id);
virtual bool eth_listening();
virtual bool eth_mining();
virtual int eth_newFilter(Json::Value const& _json);
virtual int eth_newFilterString(std::string const& _filter);
virtual int eth_number();
virtual int eth_peerCount();
virtual bool eth_setCoinbase(std::string const& _address);
virtual bool eth_setDefaultBlock(int const& _block);
virtual bool eth_setListening(bool const& _listening);
virtual std::string eth_lll(std::string const& _s);
virtual bool eth_setMining(bool const& _mining);
virtual std::string eth_stateAt(std::string const& _address, std::string const& _storage);
virtual std::string eth_transact(Json::Value const& _json);
virtual Json::Value eth_transactionByHash(std::string const& _hash, int const& _i);
virtual Json::Value eth_transactionByNumber(int const& _number, int const& _i);
virtual Json::Value eth_uncleByHash(std::string const& _hash, int const& _i);
virtual Json::Value eth_uncleByNumber(int const& _number, int const& _i);
virtual bool eth_uninstallFilter(int const& _id);
virtual std::string db_get(std::string const& _name, std::string const& _key);
virtual std::string db_getString(std::string const& _name, std::string const& _key);
virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value);
virtual bool db_putString(std::string const& _name, std::string const& _key, std::string const& _value);
virtual std::string shh_addToGroup(std::string const& _group, std::string const& _who);
virtual Json::Value shh_changed(int const& _id);
virtual bool shh_haveIdentity(std::string const& _id);
virtual int shh_newFilter(Json::Value const& _json);
virtual std::string shh_newGroup(std::string const& _id, std::string const& _who);
virtual std::string shh_newIdentity();
virtual bool shh_post(Json::Value const& _json);
virtual bool shh_uninstallFilter(int const& _id);
void setAccounts(std::vector<dev::KeyPair> const& _accounts);
void setIdentities(std::vector<dev::KeyPair> const& _ids);
std::map<dev::Public, dev::Secret> const& ids() const { return m_ids; }
private:
dev::eth::Interface* client() const;
std::shared_ptr<dev::shh::Interface> face() const;

344
libweb3jsonrpc/abstractwebthreestubserver.h

@ -13,311 +13,311 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
AbstractWebThreeStubServer(jsonrpc::AbstractServerConnector* conn) :
jsonrpc::AbstractServer<AbstractWebThreeStubServer>(conn)
{
this->bindAndAddMethod(new jsonrpc::Procedure("account", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::accountI);
this->bindAndAddMethod(new jsonrpc::Procedure("accounts", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::accountsI);
this->bindAndAddMethod(new jsonrpc::Procedure("addToGroup", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::addToGroupI);
this->bindAndAddMethod(new jsonrpc::Procedure("balanceAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::balanceAtI);
this->bindAndAddMethod(new jsonrpc::Procedure("blockByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::blockByHashI);
this->bindAndAddMethod(new jsonrpc::Procedure("blockByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::blockByNumberI);
this->bindAndAddMethod(new jsonrpc::Procedure("call", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::callI);
this->bindAndAddMethod(new jsonrpc::Procedure("changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::changedI);
this->bindAndAddMethod(new jsonrpc::Procedure("codeAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::codeAtI);
this->bindAndAddMethod(new jsonrpc::Procedure("coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::coinbaseI);
this->bindAndAddMethod(new jsonrpc::Procedure("compile", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::compileI);
this->bindAndAddMethod(new jsonrpc::Procedure("countAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_REAL, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::countAtI);
this->bindAndAddMethod(new jsonrpc::Procedure("defaultBlock", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::defaultBlockI);
this->bindAndAddMethod(new jsonrpc::Procedure("gasPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::gasPriceI);
this->bindAndAddMethod(new jsonrpc::Procedure("get", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::getI);
this->bindAndAddMethod(new jsonrpc::Procedure("getMessages", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::getMessagesI);
this->bindAndAddMethod(new jsonrpc::Procedure("getString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::getStringI);
this->bindAndAddMethod(new jsonrpc::Procedure("haveIdentity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::haveIdentityI);
this->bindAndAddMethod(new jsonrpc::Procedure("listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::listeningI);
this->bindAndAddMethod(new jsonrpc::Procedure("mining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::miningI);
this->bindAndAddMethod(new jsonrpc::Procedure("newFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::newFilterI);
this->bindAndAddMethod(new jsonrpc::Procedure("newFilterString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::newFilterStringI);
this->bindAndAddMethod(new jsonrpc::Procedure("newGroup", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::newGroupI);
this->bindAndAddMethod(new jsonrpc::Procedure("newIdentity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::newIdentityI);
this->bindAndAddMethod(new jsonrpc::Procedure("number", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::numberI);
this->bindAndAddMethod(new jsonrpc::Procedure("peerCount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::peerCountI);
this->bindAndAddMethod(new jsonrpc::Procedure("post", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::postI);
this->bindAndAddMethod(new jsonrpc::Procedure("put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::putI);
this->bindAndAddMethod(new jsonrpc::Procedure("putString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::putStringI);
this->bindAndAddMethod(new jsonrpc::Procedure("setCoinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::setCoinbaseI);
this->bindAndAddMethod(new jsonrpc::Procedure("setDefaultBlock", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::setDefaultBlockI);
this->bindAndAddMethod(new jsonrpc::Procedure("setListening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::setListeningI);
this->bindAndAddMethod(new jsonrpc::Procedure("setMining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::setMiningI);
this->bindAndAddMethod(new jsonrpc::Procedure("shhChanged", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::shhChangedI);
this->bindAndAddMethod(new jsonrpc::Procedure("shhNewFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::shhNewFilterI);
this->bindAndAddMethod(new jsonrpc::Procedure("shhUninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::shhUninstallFilterI);
this->bindAndAddMethod(new jsonrpc::Procedure("stateAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::stateAtI);
this->bindAndAddMethod(new jsonrpc::Procedure("transact", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::transactI);
this->bindAndAddMethod(new jsonrpc::Procedure("transactionByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::transactionByHashI);
this->bindAndAddMethod(new jsonrpc::Procedure("transactionByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::transactionByNumberI);
this->bindAndAddMethod(new jsonrpc::Procedure("uncleByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::uncleByHashI);
this->bindAndAddMethod(new jsonrpc::Procedure("uncleByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::uncleByNumberI);
this->bindAndAddMethod(new jsonrpc::Procedure("uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::uninstallFilterI);
this->bindAndAddMethod(new jsonrpc::Procedure("db_get", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getI);
this->bindAndAddMethod(new jsonrpc::Procedure("db_getString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getStringI);
this->bindAndAddMethod(new jsonrpc::Procedure("db_put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putI);
this->bindAndAddMethod(new jsonrpc::Procedure("db_putString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putStringI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_accounts", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_accountsI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_balanceAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_balanceAtI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_blockByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_blockByHashI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_blockByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_blockByNumberI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_call", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_callI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_changedI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_codeAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_codeAtI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_coinbaseI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_compile", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_compileI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_countAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_REAL, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_countAtI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_defaultBlock", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_defaultBlockI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_gasPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_gasPriceI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_getMessages", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_getMessagesI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_listeningI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_lll", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_lllI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_mining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_miningI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_newFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_newFilterI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_newFilterString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_newFilterStringI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_number", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_numberI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_peerCount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_peerCountI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_setCoinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_setCoinbaseI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_setDefaultBlock", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_setDefaultBlockI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_setListening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_setListeningI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_setMining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_setMiningI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_stateAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_stateAtI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_transact", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_transactI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_transactionByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_transactionByHashI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_transactionByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_transactionByNumberI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_uncleByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_uncleByHashI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_uncleByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_uncleByNumberI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_uninstallFilterI);
this->bindAndAddMethod(new jsonrpc::Procedure("shh_addToGroup", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_addToGroupI);
this->bindAndAddMethod(new jsonrpc::Procedure("shh_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::shh_changedI);
this->bindAndAddMethod(new jsonrpc::Procedure("shh_haveIdentity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_haveIdentityI);
this->bindAndAddMethod(new jsonrpc::Procedure("shh_newFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::shh_newFilterI);
this->bindAndAddMethod(new jsonrpc::Procedure("shh_newGroup", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_newGroupI);
this->bindAndAddMethod(new jsonrpc::Procedure("shh_newIdentity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_newIdentityI);
this->bindAndAddMethod(new jsonrpc::Procedure("shh_post", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::shh_postI);
this->bindAndAddMethod(new jsonrpc::Procedure("shh_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::shh_uninstallFilterI);
}
inline virtual void accountI(const Json::Value& request, Json::Value& response)
inline virtual void db_getI(const Json::Value& request, Json::Value& response)
{
response = this->account();
response = this->db_get(request[0u].asString(), request[1u].asString());
}
inline virtual void accountsI(const Json::Value& request, Json::Value& response)
inline virtual void db_getStringI(const Json::Value& request, Json::Value& response)
{
response = this->accounts();
response = this->db_getString(request[0u].asString(), request[1u].asString());
}
inline virtual void addToGroupI(const Json::Value& request, Json::Value& response)
inline virtual void db_putI(const Json::Value& request, Json::Value& response)
{
response = this->addToGroup(request[0u].asString(), request[1u].asString());
response = this->db_put(request[0u].asString(), request[1u].asString(), request[2u].asString());
}
inline virtual void balanceAtI(const Json::Value& request, Json::Value& response)
inline virtual void db_putStringI(const Json::Value& request, Json::Value& response)
{
response = this->balanceAt(request[0u].asString());
response = this->db_putString(request[0u].asString(), request[1u].asString(), request[2u].asString());
}
inline virtual void blockByHashI(const Json::Value& request, Json::Value& response)
inline virtual void eth_accountsI(const Json::Value& request, Json::Value& response)
{
response = this->blockByHash(request[0u].asString());
response = this->eth_accounts();
}
inline virtual void blockByNumberI(const Json::Value& request, Json::Value& response)
inline virtual void eth_balanceAtI(const Json::Value& request, Json::Value& response)
{
response = this->blockByNumber(request[0u].asInt());
response = this->eth_balanceAt(request[0u].asString());
}
inline virtual void callI(const Json::Value& request, Json::Value& response)
inline virtual void eth_blockByHashI(const Json::Value& request, Json::Value& response)
{
response = this->call(request[0u]);
response = this->eth_blockByHash(request[0u].asString());
}
inline virtual void changedI(const Json::Value& request, Json::Value& response)
inline virtual void eth_blockByNumberI(const Json::Value& request, Json::Value& response)
{
response = this->changed(request[0u].asInt());
response = this->eth_blockByNumber(request[0u].asInt());
}
inline virtual void codeAtI(const Json::Value& request, Json::Value& response)
inline virtual void eth_callI(const Json::Value& request, Json::Value& response)
{
response = this->codeAt(request[0u].asString());
response = this->eth_call(request[0u]);
}
inline virtual void coinbaseI(const Json::Value& request, Json::Value& response)
inline virtual void eth_changedI(const Json::Value& request, Json::Value& response)
{
response = this->coinbase();
response = this->eth_changed(request[0u].asInt());
}
inline virtual void compileI(const Json::Value& request, Json::Value& response)
inline virtual void eth_codeAtI(const Json::Value& request, Json::Value& response)
{
response = this->compile(request[0u].asString());
response = this->eth_codeAt(request[0u].asString());
}
inline virtual void countAtI(const Json::Value& request, Json::Value& response)
inline virtual void eth_coinbaseI(const Json::Value& request, Json::Value& response)
{
response = this->countAt(request[0u].asString());
response = this->eth_coinbase();
}
inline virtual void defaultBlockI(const Json::Value& request, Json::Value& response)
inline virtual void eth_compileI(const Json::Value& request, Json::Value& response)
{
response = this->defaultBlock();
response = this->eth_compile(request[0u].asString());
}
inline virtual void gasPriceI(const Json::Value& request, Json::Value& response)
inline virtual void eth_countAtI(const Json::Value& request, Json::Value& response)
{
response = this->gasPrice();
response = this->eth_countAt(request[0u].asString());
}
inline virtual void getI(const Json::Value& request, Json::Value& response)
inline virtual void eth_defaultBlockI(const Json::Value& request, Json::Value& response)
{
response = this->get(request[0u].asString(), request[1u].asString());
response = this->eth_defaultBlock();
}
inline virtual void getMessagesI(const Json::Value& request, Json::Value& response)
inline virtual void eth_gasPriceI(const Json::Value& request, Json::Value& response)
{
response = this->getMessages(request[0u].asInt());
response = this->eth_gasPrice();
}
inline virtual void getStringI(const Json::Value& request, Json::Value& response)
inline virtual void eth_getMessagesI(const Json::Value& request, Json::Value& response)
{
response = this->getString(request[0u].asString(), request[1u].asString());
response = this->eth_getMessages(request[0u].asInt());
}
inline virtual void haveIdentityI(const Json::Value& request, Json::Value& response)
inline virtual void eth_listeningI(const Json::Value& request, Json::Value& response)
{
response = this->haveIdentity(request[0u].asString());
response = this->eth_listening();
}
inline virtual void listeningI(const Json::Value& request, Json::Value& response)
inline virtual void eth_lllI(const Json::Value& request, Json::Value& response)
{
response = this->listening();
response = this->eth_lll(request[0u].asString());
}
inline virtual void miningI(const Json::Value& request, Json::Value& response)
inline virtual void eth_miningI(const Json::Value& request, Json::Value& response)
{
response = this->mining();
response = this->eth_mining();
}
inline virtual void newFilterI(const Json::Value& request, Json::Value& response)
inline virtual void eth_newFilterI(const Json::Value& request, Json::Value& response)
{
response = this->newFilter(request[0u]);
response = this->eth_newFilter(request[0u]);
}
inline virtual void newFilterStringI(const Json::Value& request, Json::Value& response)
inline virtual void eth_newFilterStringI(const Json::Value& request, Json::Value& response)
{
response = this->newFilterString(request[0u].asString());
response = this->eth_newFilterString(request[0u].asString());
}
inline virtual void newGroupI(const Json::Value& request, Json::Value& response)
inline virtual void eth_numberI(const Json::Value& request, Json::Value& response)
{
response = this->newGroup(request[0u].asString(), request[1u].asString());
response = this->eth_number();
}
inline virtual void newIdentityI(const Json::Value& request, Json::Value& response)
inline virtual void eth_peerCountI(const Json::Value& request, Json::Value& response)
{
response = this->newIdentity();
response = this->eth_peerCount();
}
inline virtual void numberI(const Json::Value& request, Json::Value& response)
inline virtual void eth_setCoinbaseI(const Json::Value& request, Json::Value& response)
{
response = this->number();
response = this->eth_setCoinbase(request[0u].asString());
}
inline virtual void peerCountI(const Json::Value& request, Json::Value& response)
inline virtual void eth_setDefaultBlockI(const Json::Value& request, Json::Value& response)
{
response = this->peerCount();
response = this->eth_setDefaultBlock(request[0u].asInt());
}
inline virtual void postI(const Json::Value& request, Json::Value& response)
inline virtual void eth_setListeningI(const Json::Value& request, Json::Value& response)
{
response = this->post(request[0u]);
response = this->eth_setListening(request[0u].asBool());
}
inline virtual void putI(const Json::Value& request, Json::Value& response)
inline virtual void eth_setMiningI(const Json::Value& request, Json::Value& response)
{
response = this->put(request[0u].asString(), request[1u].asString(), request[2u].asString());
response = this->eth_setMining(request[0u].asBool());
}
inline virtual void putStringI(const Json::Value& request, Json::Value& response)
inline virtual void eth_stateAtI(const Json::Value& request, Json::Value& response)
{
response = this->putString(request[0u].asString(), request[1u].asString(), request[2u].asString());
response = this->eth_stateAt(request[0u].asString(), request[1u].asString());
}
inline virtual void setCoinbaseI(const Json::Value& request, Json::Value& response)
inline virtual void eth_transactI(const Json::Value& request, Json::Value& response)
{
response = this->setCoinbase(request[0u].asString());
response = this->eth_transact(request[0u]);
}
inline virtual void setDefaultBlockI(const Json::Value& request, Json::Value& response)
inline virtual void eth_transactionByHashI(const Json::Value& request, Json::Value& response)
{
response = this->setDefaultBlock(request[0u].asInt());
response = this->eth_transactionByHash(request[0u].asString(), request[1u].asInt());
}
inline virtual void setListeningI(const Json::Value& request, Json::Value& response)
inline virtual void eth_transactionByNumberI(const Json::Value& request, Json::Value& response)
{
response = this->setListening(request[0u].asBool());
response = this->eth_transactionByNumber(request[0u].asInt(), request[1u].asInt());
}
inline virtual void setMiningI(const Json::Value& request, Json::Value& response)
inline virtual void eth_uncleByHashI(const Json::Value& request, Json::Value& response)
{
response = this->setMining(request[0u].asBool());
response = this->eth_uncleByHash(request[0u].asString(), request[1u].asInt());
}
inline virtual void shhChangedI(const Json::Value& request, Json::Value& response)
inline virtual void eth_uncleByNumberI(const Json::Value& request, Json::Value& response)
{
response = this->shhChanged(request[0u].asInt());
response = this->eth_uncleByNumber(request[0u].asInt(), request[1u].asInt());
}
inline virtual void shhNewFilterI(const Json::Value& request, Json::Value& response)
inline virtual void eth_uninstallFilterI(const Json::Value& request, Json::Value& response)
{
response = this->shhNewFilter(request[0u]);
response = this->eth_uninstallFilter(request[0u].asInt());
}
inline virtual void shhUninstallFilterI(const Json::Value& request, Json::Value& response)
inline virtual void shh_addToGroupI(const Json::Value& request, Json::Value& response)
{
response = this->shhUninstallFilter(request[0u].asInt());
response = this->shh_addToGroup(request[0u].asString(), request[1u].asString());
}
inline virtual void stateAtI(const Json::Value& request, Json::Value& response)
inline virtual void shh_changedI(const Json::Value& request, Json::Value& response)
{
response = this->stateAt(request[0u].asString(), request[1u].asString());
response = this->shh_changed(request[0u].asInt());
}
inline virtual void transactI(const Json::Value& request, Json::Value& response)
inline virtual void shh_haveIdentityI(const Json::Value& request, Json::Value& response)
{
response = this->transact(request[0u]);
response = this->shh_haveIdentity(request[0u].asString());
}
inline virtual void transactionByHashI(const Json::Value& request, Json::Value& response)
inline virtual void shh_newFilterI(const Json::Value& request, Json::Value& response)
{
response = this->transactionByHash(request[0u].asString(), request[1u].asInt());
response = this->shh_newFilter(request[0u]);
}
inline virtual void transactionByNumberI(const Json::Value& request, Json::Value& response)
inline virtual void shh_newGroupI(const Json::Value& request, Json::Value& response)
{
response = this->transactionByNumber(request[0u].asInt(), request[1u].asInt());
response = this->shh_newGroup(request[0u].asString(), request[1u].asString());
}
inline virtual void uncleByHashI(const Json::Value& request, Json::Value& response)
inline virtual void shh_newIdentityI(const Json::Value& request, Json::Value& response)
{
response = this->uncleByHash(request[0u].asString(), request[1u].asInt());
response = this->shh_newIdentity();
}
inline virtual void uncleByNumberI(const Json::Value& request, Json::Value& response)
inline virtual void shh_postI(const Json::Value& request, Json::Value& response)
{
response = this->uncleByNumber(request[0u].asInt(), request[1u].asInt());
response = this->shh_post(request[0u]);
}
inline virtual void uninstallFilterI(const Json::Value& request, Json::Value& response)
inline virtual void shh_uninstallFilterI(const Json::Value& request, Json::Value& response)
{
response = this->uninstallFilter(request[0u].asInt());
response = this->shh_uninstallFilter(request[0u].asInt());
}
virtual std::string account() = 0;
virtual Json::Value accounts() = 0;
virtual std::string addToGroup(const std::string& param1, const std::string& param2) = 0;
virtual std::string balanceAt(const std::string& param1) = 0;
virtual Json::Value blockByHash(const std::string& param1) = 0;
virtual Json::Value blockByNumber(const int& param1) = 0;
virtual std::string call(const Json::Value& param1) = 0;
virtual bool changed(const int& param1) = 0;
virtual std::string codeAt(const std::string& param1) = 0;
virtual std::string coinbase() = 0;
virtual std::string compile(const std::string& param1) = 0;
virtual double countAt(const std::string& param1) = 0;
virtual int defaultBlock() = 0;
virtual std::string gasPrice() = 0;
virtual std::string get(const std::string& param1, const std::string& param2) = 0;
virtual Json::Value getMessages(const int& param1) = 0;
virtual std::string getString(const std::string& param1, const std::string& param2) = 0;
virtual bool haveIdentity(const std::string& param1) = 0;
virtual bool listening() = 0;
virtual bool mining() = 0;
virtual int newFilter(const Json::Value& param1) = 0;
virtual int newFilterString(const std::string& param1) = 0;
virtual std::string newGroup(const std::string& param1, const std::string& param2) = 0;
virtual std::string newIdentity() = 0;
virtual int number() = 0;
virtual int peerCount() = 0;
virtual bool post(const Json::Value& param1) = 0;
virtual bool put(const std::string& param1, const std::string& param2, const std::string& param3) = 0;
virtual bool putString(const std::string& param1, const std::string& param2, const std::string& param3) = 0;
virtual bool setCoinbase(const std::string& param1) = 0;
virtual bool setDefaultBlock(const int& param1) = 0;
virtual bool setListening(const bool& param1) = 0;
virtual bool setMining(const bool& param1) = 0;
virtual Json::Value shhChanged(const int& param1) = 0;
virtual int shhNewFilter(const Json::Value& param1) = 0;
virtual bool shhUninstallFilter(const int& param1) = 0;
virtual std::string stateAt(const std::string& param1, const std::string& param2) = 0;
virtual std::string transact(const Json::Value& param1) = 0;
virtual Json::Value transactionByHash(const std::string& param1, const int& param2) = 0;
virtual Json::Value transactionByNumber(const int& param1, const int& param2) = 0;
virtual Json::Value uncleByHash(const std::string& param1, const int& param2) = 0;
virtual Json::Value uncleByNumber(const int& param1, const int& param2) = 0;
virtual bool uninstallFilter(const int& param1) = 0;
virtual std::string db_get(const std::string& param1, const std::string& param2) = 0;
virtual std::string db_getString(const std::string& param1, const std::string& param2) = 0;
virtual bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) = 0;
virtual bool db_putString(const std::string& param1, const std::string& param2, const std::string& param3) = 0;
virtual Json::Value eth_accounts() = 0;
virtual std::string eth_balanceAt(const std::string& param1) = 0;
virtual Json::Value eth_blockByHash(const std::string& param1) = 0;
virtual Json::Value eth_blockByNumber(const int& param1) = 0;
virtual std::string eth_call(const Json::Value& param1) = 0;
virtual bool eth_changed(const int& param1) = 0;
virtual std::string eth_codeAt(const std::string& param1) = 0;
virtual std::string eth_coinbase() = 0;
virtual std::string eth_compile(const std::string& param1) = 0;
virtual double eth_countAt(const std::string& param1) = 0;
virtual int eth_defaultBlock() = 0;
virtual std::string eth_gasPrice() = 0;
virtual Json::Value eth_getMessages(const int& param1) = 0;
virtual bool eth_listening() = 0;
virtual std::string eth_lll(const std::string& param1) = 0;
virtual bool eth_mining() = 0;
virtual int eth_newFilter(const Json::Value& param1) = 0;
virtual int eth_newFilterString(const std::string& param1) = 0;
virtual int eth_number() = 0;
virtual int eth_peerCount() = 0;
virtual bool eth_setCoinbase(const std::string& param1) = 0;
virtual bool eth_setDefaultBlock(const int& param1) = 0;
virtual bool eth_setListening(const bool& param1) = 0;
virtual bool eth_setMining(const bool& param1) = 0;
virtual std::string eth_stateAt(const std::string& param1, const std::string& param2) = 0;
virtual std::string eth_transact(const Json::Value& param1) = 0;
virtual Json::Value eth_transactionByHash(const std::string& param1, const int& param2) = 0;
virtual Json::Value eth_transactionByNumber(const int& param1, const int& param2) = 0;
virtual Json::Value eth_uncleByHash(const std::string& param1, const int& param2) = 0;
virtual Json::Value eth_uncleByNumber(const int& param1, const int& param2) = 0;
virtual bool eth_uninstallFilter(const int& param1) = 0;
virtual std::string shh_addToGroup(const std::string& param1, const std::string& param2) = 0;
virtual Json::Value shh_changed(const int& param1) = 0;
virtual bool shh_haveIdentity(const std::string& param1) = 0;
virtual int shh_newFilter(const Json::Value& param1) = 0;
virtual std::string shh_newGroup(const std::string& param1, const std::string& param2) = 0;
virtual std::string shh_newIdentity() = 0;
virtual bool shh_post(const Json::Value& param1) = 0;
virtual bool shh_uninstallFilter(const int& param1) = 0;
};
#endif //_ABSTRACTWEBTHREESTUBSERVER_H_

100
libweb3jsonrpc/spec.json

@ -1,55 +1,55 @@
[
{ "method": "coinbase", "params": [], "order": [], "returns" : "" },
{ "method": "setCoinbase", "params": [""], "order": [], "returns" : true },
{ "method": "listening", "params": [], "order": [], "returns" : false },
{ "method": "setListening", "params": [false], "order" : [], "returns" : true },
{ "method": "mining", "params": [], "order": [], "returns" : false },
{ "method": "setMining", "params": [false], "order" : [], "returns" : true },
{ "method": "gasPrice", "params": [], "order": [], "returns" : "" },
{ "method": "account", "params": [], "order": [], "returns" : "" },
{ "method": "accounts", "params": [], "order": [], "returns" : [] },
{ "method": "peerCount", "params": [], "order": [], "returns" : 0 },
{ "method": "defaultBlock", "params": [], "order": [], "returns" : 0},
{ "method": "setDefaultBlock", "params": [0], "order": [], "returns" : true},
{ "method": "number", "params": [], "order": [], "returns" : 0},
{ "method": "balanceAt", "params": [""], "order": [], "returns" : ""},
{ "method": "stateAt", "params": ["", ""], "order": [], "returns": ""},
{ "method": "countAt", "params": [""], "order": [], "returns" : 0.0},
{ "method": "codeAt", "params": [""], "order": [], "returns": ""},
{ "method": "transact", "params": [{}], "order": [], "returns": ""},
{ "method": "call", "params": [{}], "order": [], "returns": ""},
{ "method": "blockByHash", "params": [""],"order": [], "returns": {}},
{ "method": "blockByNumber", "params": [0],"order": [], "returns": {}},
{ "method": "transactionByHash", "params": ["", 0], "order": [], "returns": {}},
{ "method": "transactionByNumber", "params": [0, 0], "order": [], "returns": {}},
{ "method": "uncleByHash", "params": ["", 0], "order": [], "returns": {}},
{ "method": "uncleByNumber", "params": [0, 0], "order": [], "returns": {}},
{ "method": "compile", "params": [""], "order": [], "returns": ""},
{ "method": "newFilter", "params": [{}], "order": [], "returns": 0},
{ "method": "newFilterString", "params": [""], "order": [], "returns": 0},
{ "method": "uninstallFilter", "params": [0], "order": [], "returns": true},
{ "method": "changed", "params": [0], "order": [], "returns": false},
{ "method": "getMessages", "params": [0], "order": [], "returns": []},
{ "method": "put", "params": ["", "", ""], "order": [], "returns": true},
{ "method": "get", "params": ["", ""], "order": [], "returns": ""},
{ "method": "putString", "params": ["", "", ""], "order": [], "returns": true},
{ "method": "getString", "params": ["", ""], "order": [], "returns": ""},
{ "method": "post", "params": [{}], "order": [], "returns": true},
{ "method": "newIdentity", "params": [], "order": [], "returns": ""},
{ "method": "haveIdentity", "params": [""], "order": [], "returns": false},
{ "method": "newGroup", "params": ["", ""], "order": [], "returns": ""},
{ "method": "addToGroup", "params": ["", ""], "order": [], "returns": ""},
{ "method": "eth_coinbase", "params": [], "order": [], "returns" : "" },
{ "method": "eth_setCoinbase", "params": [""], "order": [], "returns" : true },
{ "method": "eth_listening", "params": [], "order": [], "returns" : false },
{ "method": "eth_setListening", "params": [false], "order" : [], "returns" : true },
{ "method": "eth_mining", "params": [], "order": [], "returns" : false },
{ "method": "eth_setMining", "params": [false], "order" : [], "returns" : true },
{ "method": "eth_gasPrice", "params": [], "order": [], "returns" : "" },
{ "method": "eth_accounts", "params": [], "order": [], "returns" : [] },
{ "method": "eth_peerCount", "params": [], "order": [], "returns" : 0 },
{ "method": "eth_defaultBlock", "params": [], "order": [], "returns" : 0},
{ "method": "eth_setDefaultBlock", "params": [0], "order": [], "returns" : true},
{ "method": "eth_number", "params": [], "order": [], "returns" : 0},
{ "method": "eth_balanceAt", "params": [""], "order": [], "returns" : ""},
{ "method": "eth_stateAt", "params": ["", ""], "order": [], "returns": ""},
{ "method": "eth_countAt", "params": [""], "order": [], "returns" : 0.0},
{ "method": "eth_codeAt", "params": [""], "order": [], "returns": ""},
{ "method": "eth_transact", "params": [{}], "order": [], "returns": ""},
{ "method": "eth_call", "params": [{}], "order": [], "returns": ""},
{ "method": "eth_blockByHash", "params": [""],"order": [], "returns": {}},
{ "method": "eth_blockByNumber", "params": [0],"order": [], "returns": {}},
{ "method": "eth_transactionByHash", "params": ["", 0], "order": [], "returns": {}},
{ "method": "eth_transactionByNumber", "params": [0, 0], "order": [], "returns": {}},
{ "method": "eth_uncleByHash", "params": ["", 0], "order": [], "returns": {}},
{ "method": "eth_uncleByNumber", "params": [0, 0], "order": [], "returns": {}},
{ "method": "eth_lll", "params": [""], "order": [], "returns": ""},
{ "method": "eth_compile", "params": [""], "order": [], "returns": ""},
{ "method": "eth_newFilter", "params": [{}], "order": [], "returns": 0},
{ "method": "eth_newFilterString", "params": [""], "order": [], "returns": 0},
{ "method": "eth_uninstallFilter", "params": [0], "order": [], "returns": true},
{ "method": "eth_changed", "params": [0], "order": [], "returns": false},
{ "method": "eth_getMessages", "params": [0], "order": [], "returns": []},
{ "method": "db_put", "params": ["", "", ""], "order": [], "returns": true},
{ "method": "db_get", "params": ["", ""], "order": [], "returns": ""},
{ "method": "db_putString", "params": ["", "", ""], "order": [], "returns": true},
{ "method": "db_getString", "params": ["", ""], "order": [], "returns": ""},
{ "method": "shh_post", "params": [{}], "order": [], "returns": true},
{ "method": "shh_newIdentity", "params": [], "order": [], "returns": ""},
{ "method": "shh_haveIdentity", "params": [""], "order": [], "returns": false},
{ "method": "shh_newGroup", "params": ["", ""], "order": [], "returns": ""},
{ "method": "shh_addToGroup", "params": ["", ""], "order": [], "returns": ""},
{ "method": "shhNewFilter", "params": [{}], "order": [], "returns": 0},
{ "method": "shhUninstallFilter", "params": [0], "order": [], "returns": true},
{ "method": "shhChanged", "params": [0], "order": [], "returns": []}
{ "method": "shh_newFilter", "params": [{}], "order": [], "returns": 0},
{ "method": "shh_uninstallFilter", "params": [0], "order": [], "returns": true},
{ "method": "shh_changed", "params": [0], "order": [], "returns": []}
]

35
libwhisper/Common.cpp

@ -22,11 +22,21 @@
#include "Common.h"
#include <libdevcrypto/SHA3.h>
#include "Message.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
Topic BuildTopic::toTopic() const
{
Topic ret;
ret.reserve(m_parts.size());
for (auto const& h: m_parts)
ret.push_back((TopicPart)u256(h));
return ret;
}
BuildTopic& BuildTopic::shiftBytes(bytes const& _b)
{
m_parts.push_back(dev::sha3(_b));
@ -40,19 +50,32 @@ h256 TopicFilter::sha3() const
return dev::sha3(s.out());
}
bool TopicFilter::matches(Envelope const& _e) const
{
for (TopicMask const& t: m_topicMasks)
{
if (_e.topics().size() == t.size())
for (unsigned i = 0; i < t.size(); ++i)
if (((t[i].first ^ _e.topics()[i]) & t[i].second) != 0)
goto NEXT_TOPICMASK;
return true;
NEXT_TOPICMASK:;
}
return false;
}
TopicMask BuildTopicMask::toTopicMask() const
{
TopicMask ret;
if (m_parts.size())
for (auto i = 0; i < 32; ++i)
{
ret.first[i] = m_parts[i * m_parts.size() / 32][i];
ret.second[i] = m_parts[i * m_parts.size() / 32] ? 255 : 0;
}
ret.reserve(m_parts.size());
for (auto const& h: m_parts)
ret.push_back(make_pair((TopicPart)u256(h), h ? ~(uint32_t)0 : 0));
return ret;
}
/*
web3.shh.watch({}).arrived(function(m) { env.note("New message:\n"+JSON.stringify(m)); })
k = web3.shh.newIdentity()
web3.shh.post({from: k, topic: web3.fromAscii("test"), payload: web3.fromAscii("Hello world!")})
*/

8
libwhisper/Common.h

@ -59,7 +59,9 @@ enum WhisperPacket
PacketCount
};
using Topic = h256;
using TopicPart = uint32_t;
using Topic = std::vector<TopicPart>;
class BuildTopic
{
@ -73,7 +75,7 @@ public:
BuildTopic& shiftRaw(h256 const& _part) { m_parts.push_back(_part); return *this; }
operator Topic() const { return toTopic(); }
Topic toTopic() const { Topic ret; for (auto i = 0; i < 32; ++i) ret[i] = m_parts[i * m_parts.size() / 32][i]; return ret; }
Topic toTopic() const;
protected:
BuildTopic& shiftBytes(bytes const& _b);
@ -81,7 +83,7 @@ protected:
h256s m_parts;
};
using TopicMask = std::pair<Topic, Topic>;
using TopicMask = std::vector<std::pair<TopicPart, TopicPart>>;
using TopicMasks = std::vector<TopicMask>;
class TopicFilter

7
libwhisper/Interface.cpp

@ -34,10 +34,7 @@ using namespace dev::shh;
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
bool TopicFilter::matches(Envelope const& _e) const
unsigned Interface::installWatch(TopicMask const& _mask)
{
for (TopicMask const& t: m_topicMasks)
if (((t.first ^ _e.topic()) & t.second) == 0)
return true;
return false;
return installWatch(TopicFilter(_mask));
}

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

Loading…
Cancel
Save