Browse Source

Merge branch 'develop' into mk_jsonrpc_upgrade

Conflicts:
	alethzero/MainWin.cpp
	libweb3jsonrpc/WebThreeStubServer.cpp
	libweb3jsonrpc/abstractwebthreestubserver.h
	libweb3jsonrpc/spec.json
	test/jsonrpc.cpp
	test/webthreestubclient.h
cl-refactor
Marek Kotewicz 10 years ago
parent
commit
c2ea3dc2d5
  1. 2
      CMakeLists.txt
  2. 2
      alethzero/CMakeLists.txt
  3. 192
      alethzero/MainWin.cpp
  4. 9
      alethzero/MainWin.h
  5. 36
      alethzero/OurWebThreeStubServer.cpp
  6. 38
      alethzero/OurWebThreeStubServer.h
  7. 2
      cmake/EthDependenciesDeprecated.cmake
  8. 2
      eth/main.cpp
  9. 96
      exp/main.cpp
  10. 2
      iethxi/MainWin.cpp
  11. 2
      libdevcore/Common.cpp
  12. 1
      libdevcore/Common.h
  13. 8
      libdevcore/CommonData.cpp
  14. 2
      libdevcore/CommonIO.cpp
  15. 34
      libdevcore/CommonJS.cpp
  16. 11
      libdevcore/CommonJS.h
  17. 4
      libdevcore/Exceptions.h
  18. 21
      libdevcore/FixedHash.h
  19. 1
      libdevcore/Log.h
  20. 9
      libdevcore/RLP.h
  21. 47
      libdevcrypto/AES.cpp
  22. 89
      libdevcrypto/AES.h
  23. 8
      libdevcrypto/All.h
  24. 222
      libdevcrypto/Common.cpp
  25. 49
      libdevcrypto/Common.h
  26. 210
      libdevcrypto/CryptoPP.cpp
  27. 90
      libdevcrypto/CryptoPP.h
  28. 77
      libdevcrypto/EC.cpp
  29. 41
      libdevcrypto/EC.h
  30. 100
      libdevcrypto/ECDHE.cpp
  31. 109
      libdevcrypto/ECDHE.h
  32. 1
      libdevcrypto/FileSystem.h
  33. 11
      libdevcrypto/SHA3.cpp
  34. 8
      libdevcrypto/SHA3.h
  35. 38
      libdevcrypto/SHA3MAC.h
  36. 2
      libethcore/BlockInfo.cpp
  37. 31
      libethcore/CommonEth.cpp
  38. 18
      libethereum/BlockChain.cpp
  39. 12
      libethereum/BlockChain.h
  40. 22
      libethereum/BlockDetails.h
  41. 42
      libethereum/Client.cpp
  42. 64
      libethereum/Executive.cpp
  43. 2
      libethereum/Executive.h
  44. 4
      libethereum/ExtVM.h
  45. 57
      libethereum/MessageFilter.cpp
  46. 34
      libethereum/MessageFilter.h
  47. 78
      libethereum/State.cpp
  48. 37
      libethereum/State.h
  49. 38
      libethereum/Transaction.cpp
  50. 132
      libethereum/Transaction.h
  51. 72
      libethereum/TransactionReceipt.h
  52. 2
      libevm/CMakeLists.txt
  53. 2
      libevm/ExtVMFace.cpp
  54. 27
      libevm/ExtVMFace.h
  55. 3
      libevm/FeeStructure.cpp
  56. 3
      libevm/FeeStructure.h
  57. 38
      libevm/VM.h
  58. 81
      libevmcore/Assembly.cpp
  59. 14
      libevmcore/Assembly.h
  60. 2
      libevmcore/CMakeLists.txt
  61. 32
      libevmcore/Exceptions.h
  62. 337
      libevmcore/Instruction.cpp
  63. 30
      libevmcore/Instruction.h
  64. 337
      libevmface/Instruction.cpp
  65. 117
      libjsqrc/main.js
  66. 8
      libjsqrc/qt.js
  67. 2
      libjsqrc/setup.js
  68. 1
      liblll/All.h
  69. 2
      liblll/CMakeLists.txt
  70. 2
      liblll/CodeFragment.cpp
  71. 4
      liblll/CodeFragment.h
  72. 3
      liblll/Exceptions.h
  73. 50
      libp2p/Host.cpp
  74. 4
      libp2p/Host.h
  75. 2
      libpyserpent/CMakeLists.txt
  76. 30
      libqethereum/QEthereum.cpp
  77. 1
      libqethereum/QEthereum.h
  78. 2
      libqethereum/QmlEthereum.cpp
  79. 2
      libserpent/CMakeLists.txt
  80. 14
      libserpent/bignum.cpp
  81. 12
      libserpent/bignum.h
  82. 211
      libserpent/compiler.cpp
  83. 224
      libserpent/opcodes.h
  84. 53
      libserpent/parser.cpp
  85. 870
      libserpent/rewriter.cpp
  86. 2
      libserpent/tokenize.cpp
  87. 22
      libserpent/util.cpp
  88. 6
      libserpent/util.h
  89. 165
      libsolidity/AST.cpp
  90. 150
      libsolidity/AST.h
  91. 1
      libsolidity/ASTForward.h
  92. 20
      libsolidity/ASTPrinter.cpp
  93. 6
      libsolidity/ASTPrinter.h
  94. 2
      libsolidity/ASTVisitor.h
  95. 10
      libsolidity/CMakeLists.txt
  96. 519
      libsolidity/Compiler.cpp
  97. 138
      libsolidity/Compiler.h
  98. 87
      libsolidity/CompilerContext.cpp
  99. 104
      libsolidity/CompilerContext.h
  100. 134
      libsolidity/CompilerStack.cpp

2
CMakeLists.txt

@ -106,7 +106,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 ${CRYPTOPP_LS} serpent lll evmface devcore web3jsonrpc jsqrc)
target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore devcrypto secp256k1 gmp ${CRYPTOPP_LS} serpent lll solidity evmcore devcore web3jsonrpc jsqrc)
if (APPLE)
# First have qt5 install plugins and frameworks

192
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
@ -330,8 +353,16 @@ void Main::on_enableOptimizer_triggered()
on_data_textChanged();
}
QString Main::contents(QString _s)
{
return QString::fromStdString(dev::asString(dev::contents(_s.toStdString())));
}
void Main::load(QString _s)
{
QString contents = QString::fromStdString(dev::asString(dev::contents(_s.toStdString())));
ui->webView->page()->currentFrame()->evaluateJavaScript(contents);
/*
QFile fin(_s);
if (!fin.open(QFile::ReadOnly))
return;
@ -352,7 +383,7 @@ void Main::load(QString _s)
//eval(line);
line.clear();
}
}
}*/
}
void Main::on_loadJS_triggered()
@ -587,6 +618,7 @@ void Main::readSettings(bool _skipGeometry)
}
}
ethereum()->setAddress(m_myKeys.back().address());
m_server->setAccounts(keysAsVector(m_myKeys));
}
{
@ -655,7 +687,7 @@ void Main::on_importKeyFile_triggered()
try
{
js::mValue val;
json_spirit::read_string(asString(contents(s.toStdString())), val);
json_spirit::read_string(asString(dev::contents(s.toStdString())), val);
auto obj = val.get_obj();
if (obj["encseed"].type() == js::str_type)
{
@ -868,18 +900,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);
}
}
@ -924,7 +956,7 @@ void Main::refreshBlockCount()
cwatch << "refreshBlockCount()";
auto d = ethereum()->blockChain().details();
auto diff = BlockInfo(ethereum()->blockChain().block()).difficulty;
ui->blockCount->setText(QString("%6 #%1 @%3 T%2 N%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(dev::eth::c_protocolVersion).arg(dev::eth::c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet"));
ui->blockCount->setText(QString("%6 #%1 @%3 T%2 PV%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(dev::eth::c_protocolVersion).arg(dev::eth::c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet"));
}
static bool blockMatch(string const& _f, dev::eth::BlockDetails const& _b, h256 _h, BlockChain const& _bc)
@ -946,7 +978,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 +1018,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 +1176,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/>";
@ -1234,10 +1266,18 @@ void Main::on_blocks_currentItemChanged()
s << "<br/>Coinbase: <b>" << pretty(info.coinbaseAddress).toHtmlEscaped().toStdString() << "</b> " << info.coinbaseAddress;
s << "<br/>Nonce: <b>" << info.nonce << "</b>";
s << "<br/>Parent: <b>" << info.parentHash << "</b>";
s << "<br/>Bloom: <b>" << details.bloom << "</b>";
// s << "<br/>Bloom: <b>" << details.bloom << "</b>";
s << "<br/>Log Bloom: <b>" << info.logBloom << "</b>";
s << "<br/>Transactions: <b>" << block[1].itemCount() << "</b> @<b>" << info.transactionsRoot << "</b>";
s << "<br/>Receipts: @<b>" << info.receiptsRoot << "</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 +1291,36 @@ 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()));
auto receipt = ethereum()->blockChain().receipts(h).receipts[txi];
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>";
s << "<div>Hex: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(block[1][txi].data()) << "</span></div>";
auto r = receipt.rlp();
s << "<div>Receipt: " << toString(RLP(r)) << "</div>";
s << "<div>Receipt-Hex: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(receipt.rlp()) << "</span></div>";
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 +1389,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 +1402,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 +1609,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
{
dev::solidity::CompilerStack compiler;
try
{
m_data = compiler.compile(src, m_enableOptimizer);
}
catch (dev::Exception const& exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler.getScanner());
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 +1666,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 +1836,7 @@ void Main::on_send_clicked()
void Main::keysChanged()
{
onBalancesChange();
m_server->setAccounts(keysAsVector(m_myKeys));
}
void Main::on_debug_clicked()
@ -1786,14 +1851,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 +2210,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 +2234,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);
}
}

9
alethzero/MainWin.h

@ -47,7 +47,7 @@ class MessageFilter;
}}
class QQuickView;
class WebThreeStubServer;
class OurWebThreeStubServer;
struct WorldState
{
@ -85,6 +85,7 @@ public slots:
void note(QString _entry);
void debug(QString _entry);
void warn(QString _entry);
QString contents(QString _file);
void onKeysChanged();
@ -154,6 +155,7 @@ private slots:
void on_newIdentity_triggered();
void refreshWhisper();
void refreshBlockChain();
void addNewId(QString _ids);
signals:
@ -214,7 +216,6 @@ private:
void refreshPending();
void refreshAccounts();
void refreshDestination();
void refreshBlockChain();
void refreshBlockCount();
void refreshBalances();
@ -255,7 +256,7 @@ private:
QString m_logHistory;
bool m_logChanged = true;
std::unique_ptr<WebThreeStubServer> m_server;
QWebThreeConnector m_qwebConnector;
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
cmake/EthDependenciesDeprecated.cmake

@ -128,6 +128,7 @@ else()
find_path( JSONRPC_ID jsonrpccpp/server.h
/usr/include
/usr/local/include
../libjson-rpc-cpp/src
)
if ( JSONRPC_ID )
@ -146,6 +147,7 @@ else()
/usr/local/lib
/opt/local/lib
/usr/lib/*/
../libjson-rpc-cpp/build/out
)
find_library( JSONRPC_COMMON_LS NAMES jsonrpccpp-common

2
eth/main.cpp

@ -31,7 +31,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.10";
}

1
libdevcore/Common.h

@ -59,6 +59,7 @@ using bytesConstRef = vector_ref<byte const>;
// Numeric types.
using bigint = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<>>;
using u128 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<128, 128, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;
using u256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;
using s256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>>;
using u160 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<160, 160, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;

8
libdevcore/CommonData.cpp

@ -29,14 +29,20 @@ using namespace dev;
std::string dev::escaped(std::string const& _s, bool _all)
{
static const map<char, char> prettyEscapes{{'\r', 'r'}, {'\n', 'n'}, {'\t', 't'}, {'\v', 'v'}};
std::string ret;
ret.reserve(_s.size());
ret.reserve(_s.size() + 2);
ret.push_back('"');
for (auto i: _s)
if (i == '"' && !_all)
ret += "\\\"";
else if (i == '\\' && !_all)
ret += "\\\\";
else if (prettyEscapes.count(i))
{
ret += '\\';
ret += prettyEscapes.find(i)->second;
}
else if (i < ' ' || _all)
{
ret += "\\x";

2
libdevcore/CommonIO.cpp

@ -30,7 +30,7 @@ string dev::memDump(bytes const& _b, unsigned _w, bool _html)
{
stringstream ret;
if (_html)
ret << "<pre style=\"font-family: Monospace, sans-serif; font-size: small\">";
ret << "<pre style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">";
for (unsigned i = 0; i < _b.size(); i += _w)
{
ret << hex << setw(4) << setfill('0') << i << " ";

34
libdevcore/CommonJS.cpp

@ -35,35 +35,23 @@ 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)
bytes padded(bytes _b, unsigned _l)
{
bytes b = jsToBytes(_s);
while (b.size() < _l)
b.insert(b.begin(), 0);
while (b.size() < _r)
b.push_back(0);
return asString(b).substr(b.size() - std::max(_l, _r));
while (_b.size() < _l)
_b.insert(_b.begin(), 0);
while (_b.size() < _l)
_b.push_back(0);
return asBytes(asString(_b).substr(_b.size() - std::max(_l, _l)));
}
std::string jsPadded(std::string const& _s, unsigned _l)
bytes unpadded(bytes _b)
{
if (_s.substr(0, 2) == "0x" || _s.find_first_not_of("0123456789") == std::string::npos)
// Numeric: pad to right
return jsPadded(_s, _l, _l);
else
// Text: pad to the left
return jsPadded(_s, 0, _l);
}
std::string jsUnpadded(std::string _s)
{
auto p = _s.find_last_not_of((char)0);
_s.resize(p == std::string::npos ? 0 : (p + 1));
return _s;
auto p = asString(_b).find_last_not_of((char)0);
_b.resize(p == std::string::npos ? 0 : (p + 1));
return _b;
}
}

11
libdevcore/CommonJS.h

@ -47,9 +47,8 @@ inline std::string toJS(dev::bytes const& _n)
}
bytes jsToBytes(std::string const& _s);
std::string jsPadded(std::string const& _s, unsigned _l, unsigned _r);
std::string jsPadded(std::string const& _s, unsigned _l);
std::string jsUnpadded(std::string _s);
bytes padded(bytes _b, unsigned _l);
bytes unpadded(bytes _s);
template <unsigned N> FixedHash<N> jsToFixed(std::string const& _s)
{
@ -61,7 +60,7 @@ template <unsigned N> FixedHash<N> jsToFixed(std::string const& _s)
return (typename FixedHash<N>::Arith)(_s);
else
// Binary
return FixedHash<N>(asBytes(jsPadded(_s, N)));
return FixedHash<N>(); // FAIL
}
inline std::string jsToFixed(double _s)
@ -79,7 +78,7 @@ template <unsigned N> boost::multiprecision::number<boost::multiprecision::cpp_i
return boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>(_s);
else
// Binary
return fromBigEndian<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>>(asBytes(jsPadded(_s, N)));
return 0; // FAIL
}
inline Address jsToAddress(std::string const& _s) { return jsToFixed<sizeof(dev::Address)>(_s); }
@ -89,7 +88,7 @@ inline u256 jsToU256(std::string const& _s) { return jsToInt<32>(_s); }
inline std::string jsToBinary(std::string const& _s)
{
return jsUnpadded(dev::toString(jsToBytes(_s)));
return dev::toString(unpadded(jsToBytes(_s)));
}
inline std::string jsToDecimal(std::string const& _s)

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, bigint> errinfo_required;
typedef boost::error_info<struct tag_got, bigint> errinfo_got;
typedef boost::tuple<errinfo_required, errinfo_got> RequirementError;
}

21
libdevcore/FixedHash.h

@ -158,21 +158,30 @@ public:
return ret;
}
template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> const& _h) { return (*this |= _h.template nbloom<P, N>()); }
template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> const& _h)
{
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_bloomBytes = (M + 7) / 8;
unsigned mask = (1 << c_bloomBytes) - 1;
static const unsigned c_bloomBits = M * 8;
unsigned mask = c_bloomBits - 1;
unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8;
FixedHash<M> ret;
byte const* p = data();
for (unsigned i = 0; i < P; ++i)
{
unsigned index = 0;
for (unsigned j = 0; j < c_bloomBytes; ++j, ++p)
for (unsigned j = 0; j < bloomBytes; ++j, ++p)
index = (index << 8) | *p;
index &= mask;
ret[N - 1 - index / 8] |= (1 << (index % 8));
ret[M - 1 - index / 8] |= (1 << (index % 8));
}
return ret;
}
@ -231,6 +240,8 @@ using h520 = FixedHash<65>;
using h512 = FixedHash<64>;
using h256 = FixedHash<32>;
using h160 = FixedHash<20>;
using h128 = FixedHash<16>;
using h512s = std::vector<h512>;
using h256s = std::vector<h256>;
using h160s = std::vector<h160>;
using h256Set = std::set<h256>;

1
libdevcore/Log.h

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

9
libdevcore/RLP.h

@ -158,8 +158,13 @@ public:
/// Best-effort conversion operators.
explicit operator std::string() const { return toString(); }
explicit operator bytes() const { return toBytes(); }
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>>(); }
@ -337,7 +342,7 @@ public:
RLPStream& append(char const* _s) { return append(std::string(_s)); }
template <unsigned N> RLPStream& append(FixedHash<N> _s, bool _compact = false, bool _allOrNothing = false) { return _allOrNothing && !_s ? append(bytesConstRef()) : append(_s.ref(), _compact); }
/// Appends an arbitrary RLP fragment - this *must* be a single item.
/// Appends an arbitrary RLP fragment - this *must* be a single item unless @a _itemCount is given.
RLPStream& append(RLP const& _rlp, unsigned _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); }
/// Appends a sequence of data to the stream as a list.

47
libdevcrypto/SHA3MAC.cpp → libdevcrypto/AES.cpp

@ -1,39 +1,60 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file SHA3MAC.cpp
/** @file AES.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* SHA3 MAC
*/
#include "CryptoPP.h"
#include "SHA3MAC.h"
#include "AES.h"
using namespace std;
using namespace dev;
using namespace dev::crypto;
using namespace dev::crypto::aes;
using namespace CryptoPP;
void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output)
struct aes::Aes128Ctr
{
Aes128Ctr(h128 _k)
{
mode.SetKeyWithIV(_k.data(), sizeof(h128), Nonce::get().data());
}
CTR_Mode<AES>::Encryption mode;
};
Stream::Stream(StreamType, h128 _ckey):
m_cSecret(_ckey)
{
cryptor = new Aes128Ctr(_ckey);
}
Stream::~Stream()
{
delete cryptor;
}
void Stream::update(bytesRef)
{
}
size_t Stream::streamOut(bytes&)
{
CryptoPP::SHA3_256 ctx;
ctx.Update((byte*)_secret.data(), _secret.size());
ctx.Update((byte*)_plain.data(), _plain.size());
assert(_output.size() >= 32);
ctx.Final(_output.data());
return 0;
}

89
libdevcrypto/AES.h

@ -0,0 +1,89 @@
/*
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 AES.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* AES
* todo: use openssl
*/
#pragma once
#include <atomic>
#include "Common.h"
namespace dev
{
namespace crypto
{
namespace aes
{
struct Aes128Ctr;
enum StreamType { Encrypt, Decrypt };
/**
* @brief Encrypted stream
*/
class Stream
{
public:
// streamtype maybe irrelevant w/ctr
Stream(StreamType _t, h128 _ckey);
~Stream();
virtual void update(bytesRef io_bytes);
/// Move ciphertext to _bytes.
virtual size_t streamOut(bytes& o_bytes);
private:
Stream(Stream const&) = delete;
Stream& operator=(Stream const&) = delete;
h128 m_cSecret;
bytes m_text;
Aes128Ctr* cryptor;
};
/**
* @brief Encrypted stream with inband SHA3 mac at specific interval.
*/
class AuthenticatedStream: public Stream
{
public:
AuthenticatedStream(StreamType _t, h128 _ckey, h128 _mackey, unsigned _interval): Stream(_t, _ckey), m_macSecret(_mackey) { m_macInterval = _interval; }
AuthenticatedStream(StreamType _t, Secret const& _s, unsigned _interval): Stream(_t, h128(_s)), m_macSecret(FixedHash<16>((byte const*)_s.data()+16,h128::ConstructFromPointer)) { m_macInterval = _interval; }
/// Adjust mac interval. Next mac will be xored with value.
void adjustInterval(unsigned _interval) { m_macInterval = _interval; }
private:
AuthenticatedStream(AuthenticatedStream const&) = delete;
AuthenticatedStream& operator=(AuthenticatedStream const&) = delete;
std::atomic<unsigned> m_macInterval;
h128 m_macSecret;
};
}
}
}

8
libdevcrypto/All.h

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

222
libdevcrypto/Common.cpp

@ -14,165 +14,151 @@
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 "EC.h"
#include <chrono>
#include <mutex>
#include "SHA3.h"
#include "FileSystem.h"
#include "CryptoPP.h"
#include "Common.h"
using namespace std;
using namespace dev;
using namespace dev::crypto;
//#define ETH_ADDRESS_DEBUG 1
static Secp256k1 s_secp256k1;
Address dev::toAddress(Secret _private)
Public dev::toPublic(Secret const& _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;
Public p;
s_secp256k1.toPublic(_secret, p);
return std::move(p);
}
KeyPair KeyPair::create()
Address dev::toAddress(Public const& _public)
{
secp256k1_start();
static std::mt19937_64 s_eng(time(0));
std::uniform_int_distribution<uint16_t> d(0, 255);
for (int i = 0; i < 100; ++i)
{
h256 sec;
for (unsigned i = 0; i < 32; ++i)
sec[i] = (byte)d(s_eng);
KeyPair ret(sec);
if (ret.address())
return ret;
}
return KeyPair();
return s_secp256k1.toAddress(_public);
}
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)));
#if ETH_ADDRESS_DEBUG
cout << "---- ADDRESS -------------------------------" << endl;
cout << "SEC: " << m_secret << endl;
cout << "PUB: " << m_public << endl;
cout << "ADR: " << m_address << endl;
#endif
}
KeyPair KeyPair::fromEncryptedSeed(bytesConstRef _seed, std::string const& _password)
Address dev::toAddress(Secret const& _secret)
{
return KeyPair(sha3(aesDecrypt(_seed, _password)));
Public p;
s_secp256k1.toPublic(_secret, p);
return s_secp256k1.toAddress(p);
}
void dev::encrypt(Public _k, bytesConstRef _plain, bytes& o_cipher)
void dev::encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher)
{
bytes io = _plain.toBytes();
crypto::encrypt(_k, io);
s_secp256k1.encrypt(_k, io);
o_cipher = std::move(io);
}
bool dev::decrypt(Secret _k, bytesConstRef _cipher, bytes& o_plaintext)
bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext)
{
bytes io = _cipher.toBytes();
crypto::decrypt(_k, io);
s_secp256k1.decrypt(_k, io);
if (io.empty())
return false;
o_plaintext = std::move(io);
return true;
}
Public dev::recover(Signature _sig, h256 _message)
Public dev::recover(Signature const& _sig, h256 const& _message)
{
return s_secp256k1.recover(_sig, _message.ref());
}
Signature dev::sign(Secret const& _k, h256 const& _hash)
{
return s_secp256k1.sign(_k, _hash);
}
bool dev::verify(Public const& _p, Signature const& _s, h256 const& _hash)
{
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 s_secp256k1.verify(_p, _s, _hash.ref(), true);
}
inline h256 kFromMessage(h256 _msg, h256 _priv)
KeyPair KeyPair::create()
{
return _msg ^ _priv;
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)
{
KeyPair ret(FixedHash<32>::random(s_eng));
if (ret.address())
return ret;
}
return KeyPair();
}
Signature dev::sign(Secret _k, h256 _message)
KeyPair::KeyPair(h256 _sec):
m_secret(_sec)
{
int v = 0;
if (s_secp256k1.verifySecret(m_secret, m_public))
m_address = s_secp256k1.toAddress(m_public);
}
secp256k1_start();
KeyPair KeyPair::fromEncryptedSeed(bytesConstRef _seed, std::string const& _password)
{
return KeyPair(sha3(aesDecrypt(_seed, _password)));
}
SignatureStruct ret;
h256 nonce = kFromMessage(_message, _k);
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);
}
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) + chrono::high_resolution_clock::now().time_since_epoch().count());
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);
}

49
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,24 +52,36 @@ using Address = h160;
/// A vector of Ethereum addresses.
using Addresses = h160s;
/// A set of Ethereum addresses.
using AddressSet = std::set<h160>;
/// A vector of secrets.
using Secrets = h256s;
/// Convert a secret key into the public key equivalent.
Public toPublic(Secret const& _secret);
/// Convert a public key to address.
Address toAddress(Public const& _public);
/// Convert a secret key into address of public key equivalent.
/// @returns 0 if it's not a valid secret key.
Address toAddress(Secret _secret);
Address toAddress(Secret const& _secret);
/// Encrypts plain text using Public key.
void encrypt(Public _k, bytesConstRef _plain, bytes& o_cipher);
void encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Decrypts cipher using Secret key.
bool decrypt(Secret _k, bytesConstRef _cipher, bytes& o_plaintext);
bool decrypt(Secret const& _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 const& _sig, h256 const& _hash);
/// Returns siganture of message hash.
Signature sign(Secret _k, h256 _message);
Signature sign(Secret const& _k, h256 const& _hash);
/// Verify signature.
bool verify(Public const& _k, Signature const& _s, h256 const& _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 +121,23 @@ private:
Address m_address;
};
namespace crypto
{
struct InvalidState: public dev::Exception {};
/// Key derivation
h256 kdf(Secret const& _priv, h256 const& _hash);
/**
* @brief Generator for nonce material
*/
struct Nonce
{
static h256 get(bool _commit = false);
private:
Nonce() {}
~Nonce();
};
}
}

210
libdevcrypto/CryptoPP.cpp

@ -21,54 +21,206 @@
#include "CryptoPP.h"
using namespace std;
using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
ECP::Point pp::PointFromPublic(Public const& _p)
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.");
void Secp256k1::encrypt(Public const& _k, bytes& io_cipher)
{
ECP::Point p;
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> pub;
pub.AccessGroupParameters().Initialize(pp::secp256k1());
ECIES<ECP>::Encryptor e;
initializeDLScheme(_k, e);
size_t plen = io_cipher.size();
bytes ciphertext;
ciphertext.resize(e.CiphertextLength(plen));
{
lock_guard<mutex> l(x_rng);
e.Encrypt(m_rng, io_cipher.data(), plen, ciphertext.data());
}
memset(io_cipher.data(), 0, io_cipher.size());
io_cipher = std::move(ciphertext);
}
void Secp256k1::decrypt(Secret const& _k, bytes& io_text)
{
CryptoPP::ECIES<CryptoPP::ECP>::Decryptor d;
initializeDLScheme(_k, d);
size_t clen = io_text.size();
bytes plain;
plain.resize(d.MaxPlaintextLength(io_text.size()));
DecodingResult r;
{
lock_guard<mutex> l(x_rng);
r = d.Decrypt(m_rng, io_text.data(), clen, plain.data());
}
if (!r.isValidCoding)
{
io_text.clear();
return;
}
io_text.resize(r.messageLength);
io_text = std::move(plain);
}
Signature Secp256k1::sign(Secret const& _k, bytesConstRef _message)
{
return sign(_k, sha3(_message));
}
bytes prefixedKey(pub.GetGroupParameters().GetEncodedElementSize(true));
prefixedKey[0] = 0x04;
assert(Public::size == prefixedKey.size() - 1);
memcpy(&prefixedKey[1], _p.data(), prefixedKey.size() - 1);
Signature Secp256k1::sign(Secret const& _key, h256 const& _hash)
{
// assumption made by signing alogrithm
asserts(m_q == m_qs);
Signature sig;
Integer k(kdf(_key, _hash).data(), 32);
if (k == 0)
BOOST_THROW_EXCEPTION(InvalidState());
k = 1 + (k % (m_qs - 1));
ECP::Point rp;
Integer r;
{
lock_guard<mutex> l(x_params);
rp = m_params.ExponentiateBase(k);
r = m_params.ConvertElementToInteger(rp);
}
sig[64] = 0;
// sig[64] = (r >= m_q) ? 2 : 0;
Integer kInv = k.InverseMod(m_q);
Integer z(_hash.asBytes().data(), 32);
Integer s = (kInv * (Integer(_key.asBytes().data(), 32)*r + z)) % m_q;
if (r == 0 || s == 0)
BOOST_THROW_EXCEPTION(InvalidState());
// if (s > m_qs)
// {
// s = m_q - s;
// if (sig[64])
// sig[64] ^= 1;
// }
pub.GetGroupParameters().GetCurve().DecodePoint(p, prefixedKey.data(), prefixedKey.size());
return std::move(p);
sig[64] |= rp.y.IsOdd() ? 1 : 0;
r.Encode(sig.data(), 32);
s.Encode(sig.data() + 32, 32);
return sig;
}
Integer pp::ExponentFromSecret(Secret const& _s)
bool Secp256k1::verify(Signature const& _signature, bytesConstRef _message)
{
static_assert(Secret::size == 32, "Secret key must be 32 bytes.");
return std::move(Integer(_s.data(), Secret::size));
return !!recover(_signature, _message);
}
void pp::PublicFromExponent(Integer const& _e, Public& _p)
bool Secp256k1::verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed)
{
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);
// todo: verify w/o recovery (if faster)
return _p == _hashed ? recover(_sig, _message) : recover(_sig, sha3(_message).ref());
}
void pp::PublicFromDL_PublicKey_EC(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p)
Public Secp256k1::recover(Signature _signature, bytesConstRef _message)
{
Public recovered;
Integer r(_signature.data(), 32);
Integer s(_signature.data()+32, 32);
// cryptopp encodes sign of y as 0x02/0x03 instead of 0/1 or 27/28
byte encodedpoint[33];
encodedpoint[0] = _signature[64]|2;
memcpy(&encodedpoint[1], _signature.data(), 32);
ECP::Element x;
{
lock_guard<mutex> l(x_curve);
m_curve.DecodePoint(x, encodedpoint, 33);
if (!m_curve.VerifyPoint(x))
return recovered;
}
// if (_signature[64] & 2)
// {
// r += m_q;
// lock_guard<mutex> l(x_params);
// if (r >= m_params.GetMaxExponent())
// return recovered;
// }
Integer z(_message.data(), 32);
Integer rn = r.InverseMod(m_q);
Integer u1 = m_q - (rn.Times(z)).Modulo(m_q);
Integer u2 = (rn.Times(s)).Modulo(m_q);
ECP::Point p;
byte recoveredbytes[65];
{
lock_guard<mutex> l(x_curve);
// todo: make generator member
p = m_curve.CascadeMultiply(u2, x, u1, m_params.GetSubgroupGenerator());
m_curve.EncodePoint(recoveredbytes, p, false);
}
memcpy(recovered.data(), &recoveredbytes[1], 64);
return recovered;
}
bool Secp256k1::verifySecret(Secret const& _s, Public& _p)
{
DL_PrivateKey_EC<ECP> k;
k.Initialize(m_params, secretToExponent(_s));
if (!k.Validate(m_rng, 3))
return false;
DL_PublicKey_EC<CryptoPP::ECP> pub;
k.MakePublicKey(pub);
if (!k.Validate(m_rng, 3))
return false;
exportPublicKey(pub, _p);
return true;
}
void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s)
{
ECDH<ECP>::Domain d(m_oid);
assert(d.AgreedValueLength() == sizeof(o_s));
byte remote[65] = {0x04};
memcpy(&remote[1], _r.data(), 64);
assert(d.Agree(o_s.data(), _s.data(), remote));
}
void Secp256k1::exportPublicKey(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& o_p)
{
bytes prefixedKey(_k.GetGroupParameters().GetEncodedElementSize(true));
_k.GetGroupParameters().GetCurve().EncodePoint(prefixedKey.data(), _k.GetPublicElement(), false);
static_assert(Public::size == 64, "Public key must be 64 bytes.");
assert(Public::size + 1 == _k.GetGroupParameters().GetEncodedElementSize(true));
memcpy(_p.data(), &prefixedKey[1], Public::size);
{
lock_guard<mutex> l(x_params);
m_params.GetCurve().EncodePoint(prefixedKey.data(), _k.GetPublicElement(), false);
assert(Public::size + 1 == _k.GetGroupParameters().GetEncodedElementSize(true));
}
memcpy(o_p.data(), &prefixedKey[1], Public::size);
}
void pp::SecretFromDL_PrivateKey_EC(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s)
void Secp256k1::exponentToPublic(Integer const& _e, Public& o_p)
{
_k.GetPrivateExponent().Encode(_s.data(), Secret::size);
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> pk;
{
lock_guard<mutex> l(x_params);
pk.Initialize(m_params, m_params.ExponentiateBase(_e));
}
exportPublicKey(pk, o_p);
}

90
libdevcrypto/CryptoPP.h

@ -18,11 +18,12 @@
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* CryptoPP headers and helper methods
* CryptoPP headers and primitive helper methods
*/
#pragma once
#include <mutex>
// need to leave this one disabled for link-time. blame cryptopp.
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma warning(push)
@ -45,9 +46,10 @@
#include <files.h>
#include <osrng.h>
#include <oids.h>
#include <secp256k1/secp256k1.h>
#include <dsa.h>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include "SHA3.h"
#include "Common.h"
namespace dev
@ -55,31 +57,77 @@ namespace dev
namespace crypto
{
namespace pp
{
/// RNG used by CryptoPP
inline CryptoPP::AutoSeededRandomPool& PRNG() { static CryptoPP::AutoSeededRandomPool prng; return prng; }
/// EC curve used by CryptoPP
inline CryptoPP::OID const& secp256k1() { static CryptoPP::OID curve = CryptoPP::ASN1::secp256k1(); return curve; }
using namespace CryptoPP;
/// Conversion from bytes to cryptopp point
CryptoPP::ECP::Point PointFromPublic(Public const& _p);
inline ECP::Point publicToPoint(Public const& _p) { Integer x(_p.data(), 32); Integer y(_p.data() + 32, 32); return std::move(ECP::Point(x,y)); }
/// Conversion from bytes to cryptopp exponent
CryptoPP::Integer ExponentFromSecret(Secret const& _s);
inline Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s.data(), Secret::size)); }
/**
* CryptoPP secp256k1 algorithms.
*/
class Secp256k1
{
public:
Secp256k1(): m_oid(ASN1::secp256k1()), m_params(m_oid), m_curve(m_params.GetCurve()), m_q(m_params.GetGroupOrder()), m_qs(m_params.GetSubgroupOrder()) {}
Address toAddress(Public const& _p) { return right160(sha3(_p.ref())); }
void toPublic(Secret const& _s, Public& o_public) { exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public); }
/// Encrypts text (replace input).
void encrypt(Public const& _k, bytes& io_cipher);
/// Decrypts text (replace input).
void decrypt(Secret const& _k, bytes& io_text);
/// @returns siganture of message.
Signature sign(Secret const& _k, bytesConstRef _message);
/// @returns compact siganture of provided hash.
Signature sign(Secret const& _k, h256 const& _hash);
/// Verify compact signature (public key is extracted from signature).
bool verify(Signature const& _signature, bytesConstRef _message);
/// Conversion from cryptopp exponent Integer to bytes
void PublicFromExponent(CryptoPP::Integer const& _k, Public& _s);
/// Verify signature.
bool verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed = false);
/// Conversion from cryptopp public key to bytes
void PublicFromDL_PublicKey_EC(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p);
/// Recovers public key from compact signature. Uses libsecp256k1.
Public recover(Signature _signature, bytesConstRef _message);
/// Conversion from cryptopp private key to bytes
void SecretFromDL_PrivateKey_EC(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s);
/// Verifies _s is a valid secret key and returns corresponding public key in o_p.
bool verifySecret(Secret const& _s, Public& o_p);
void agree(Secret const& _s, Public const& _r, h256& o_s);
protected:
void exportPrivateKey(DL_PrivateKey_EC<ECP> const& _k, Secret& o_s) { _k.GetPrivateExponent().Encode(o_s.data(), Secret::size); }
void exportPublicKey(DL_PublicKey_EC<ECP> const& _k, Public& o_p);
void exponentToPublic(Integer const& _e, Public& o_p);
template <class T> void initializeDLScheme(Secret const& _s, T& io_operator) { std::lock_guard<std::mutex> l(x_params); io_operator.AccessKey().Initialize(m_params, secretToExponent(_s)); }
template <class T> void initializeDLScheme(Public const& _p, T& io_operator) { std::lock_guard<std::mutex> l(x_params); io_operator.AccessKey().Initialize(m_params, publicToPoint(_p)); }
private:
OID m_oid;
std::mutex x_rng;
AutoSeededRandomPool m_rng;
std::mutex x_params;
DL_GroupParameters_EC<ECP> m_params;
std::mutex x_curve;
DL_GroupParameters_EC<ECP>::EllipticCurve m_curve;
Integer m_q;
Integer m_qs;
};
}
}
}

77
libdevcrypto/EC.cpp

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

41
libdevcrypto/EC.h

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

100
libdevcrypto/ECDHE.cpp

@ -0,0 +1,100 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ECDHE.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
#include "SHA3.h"
#include "CryptoPP.h"
#include "ECDHE.h"
using namespace std;
using namespace dev;
using namespace dev::crypto;
static Secp256k1 s_secp256k1;
void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret)
{
if (m_remoteEphemeral)
// agreement can only occur once
BOOST_THROW_EXCEPTION(InvalidState());
m_remoteEphemeral = _remote;
s_secp256k1.agree(m_ephemeral.sec(), m_remoteEphemeral, o_sharedSecret);
}
void ECDHEKeyExchange::agree(Public const& _remoteEphemeral)
{
s_secp256k1.agree(m_ephemeral.sec(), _remoteEphemeral, m_ephemeralSecret);
}
void ECDHEKeyExchange::exchange(bytes& o_exchange)
{
if (!m_ephemeralSecret)
// didn't agree on public remote
BOOST_THROW_EXCEPTION(InvalidState());
// The key exchange payload is in two parts and is encrypted
// using ephemeral keypair.
//
// The first part is the 'prefix' which is a zero-knowledge proof
// allowing the remote to resume or emplace a previous session.
// If a session previously exists:
// prefix is sha3(token) // todo: ephemeral entropy from both sides
// If a session doesn't exist:
// prefix is sha3(m_ephemeralSecret)
//
// The second part is encrypted using the public key which relates to the prefix.
Public encpk = m_known.first ? m_known.first : m_remoteEphemeral;
bytes exchange(encpk.asBytes());
// This is the public key which we would like the remote to use,
// which maybe different than the previously-known public key.
//
// Here we should pick an appropriate alias or generate a new one,
// but for now, we use static alias passed to constructor.
//
Public p = toPublic(m_alias.m_secret);
exchange.resize(exchange.size() + sizeof(p));
memcpy(&exchange[exchange.size() - sizeof(p)], p.data(), sizeof(p));
// protocol parameters; should be fixed size
bytes v(1, 0x80);
exchange.resize(exchange.size() + v.size());
memcpy(&exchange[exchange.size() - v.size()], v.data(), v.size());
h256 auth;
sha3mac(m_alias.m_secret.ref(), m_ephemeralSecret.ref(), auth.ref());
Signature sig = s_secp256k1.sign(m_alias.m_secret, auth);
exchange.resize(exchange.size() + sizeof(sig));
memcpy(&exchange[exchange.size() - sizeof(sig)], sig.data(), sizeof(sig));
aes::AuthenticatedStream aes(aes::Encrypt, m_ephemeralSecret, 0);
h256 prefix(sha3(m_known.second ? m_known.second : (h256)m_remoteEphemeral));
aes.update(prefix.ref());
s_secp256k1.encrypt(encpk, exchange);
aes.update(&exchange);
aes.streamOut(o_exchange);
}

109
libdevcrypto/ECDHE.h

@ -0,0 +1,109 @@
/*
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 ECDHE.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* Elliptic curve Diffie-Hellman ephemeral key exchange
*/
#pragma once
#include "AES.h"
namespace dev
{
namespace crypto
{
/// Public key of remote and corresponding shared secret.
typedef std::pair<Public,h256> AliasSession;
/**
* @brief An addressable EC key pair.
*/
class Alias
{
friend class ECDHEKeyExchange; // todo: remove
public:
Alias(Secret _s): m_secret(_s) {};
AliasSession session(Address _a) { return m_sessions.count(_a) ? m_sessions.find(_a)->second : AliasSession(); }
private:
std::map<Address,AliasSession> m_sessions;
Secret m_secret;
};
/**
* @brief Derive DH shared secret from EC keypairs.
* As ephemeral keys are single-use, agreement is limited to a single occurence.
*/
class ECDHE
{
public:
/// Constructor (pass public key for ingress exchange).
ECDHE(): m_ephemeral(KeyPair::create()) {};
/// Public key sent to remote.
Public pubkey() { return m_ephemeral.pub(); }
/// Input public key for dh agreement, output generated shared secret.
void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret);
protected:
KeyPair m_ephemeral; ///< Ephemeral keypair; generated.
Public m_remoteEphemeral; ///< Public key of remote; parameter.
};
/**
* @brief Secure exchange of static keys.
* Key exchange is encrypted with public key of remote and then encrypted by block cipher. For a blind remote the ecdhe public key is used to encrypt exchange, and for a known remote the known public key is used. The block cipher key is derived from ecdhe shared secret.
*
* Usage: Agree -> Exchange -> Authenticate
*/
class ECDHEKeyExchange: private ECDHE
{
public:
/// Exchange with unknown remote (pass public key for ingress exchange)
ECDHEKeyExchange(Alias& _k): m_alias(_k) {};
/// Exchange with known remote
ECDHEKeyExchange(Alias& _k, AliasSession _known): m_alias(_k), m_known(_known) {};
/// Provide public key for dh agreement to generate shared secret.
void agree(Public const& _remoteEphemeral);
/// @returns encrypted payload of key exchange
void exchange(bytes& o_exchange);
/// Decrypt payload, check mac, check trust, decrypt exchange, authenticate exchange, verify version, verify signature, and if no failure occurs, update or creats trust and derive session-shared-secret.
bool authenticate(bytes _exchangeIn);
private:
Secret m_ephemeralSecret;
Alias m_alias;
AliasSession m_known;
Secret m_sharedAliasSecret;
FixedHash<16> m_sharedC;
FixedHash<16> m_sharedM;
};
}
}

1
libdevcrypto/FileSystem.h

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

11
libdevcrypto/SHA3.cpp

@ -30,6 +30,7 @@ namespace dev
{
h256 EmptySHA3 = sha3(bytesConstRef());
h256 EmptyListSHA3 = sha3(RLPEmptyList);
std::string sha3(std::string const& _input, bool _hex)
{
@ -85,6 +86,16 @@ h256 sha3(bytesConstRef _input)
sha3(_input, bytesRef(&ret[0], 32));
return ret;
}
void 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);
ctx.Final(_output.data());
}
bytes aesDecrypt(bytesConstRef _ivCipher, std::string const& _password, unsigned _rounds, bytesConstRef _salt)
{

8
libdevcrypto/SHA3.h

@ -55,9 +55,17 @@ 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 MAC
void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output);
/// 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;
// Other crypto convenience routines
bytes aesDecrypt(bytesConstRef _cipher, std::string const& _password, unsigned _rounds = 2000, bytesConstRef _salt = bytesConstRef());

38
libdevcrypto/SHA3MAC.h

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

2
libethcore/BlockInfo.cpp

@ -56,8 +56,6 @@ h256 BlockInfo::headerHashWithoutNonce() const
return sha3(s.out());
}
auto static const c_sha3EmptyList = sha3(RLPEmptyList);
void BlockInfo::streamRLP(RLPStream& _s, bool _nonce) const
{
_s.appendList(_nonce ? 15 : 14)

31
libethcore/CommonEth.cpp

@ -28,14 +28,13 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
//#define ETH_ADDRESS_DEBUG 1
namespace dev
{
namespace eth
{
const unsigned c_protocolVersion = 38;
const unsigned c_databaseVersion = 3;
const unsigned c_protocolVersion = 42;
const unsigned c_databaseVersion = 4;
static const vector<pair<u256, string>> g_units =
{
@ -84,30 +83,4 @@ std::string formatBalance(u256 _b)
return ret.str();
}
Address toAddress(Secret _private)
{
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);
assert(pubkeylen == 65);
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;
}
}}

18
libethereum/BlockChain.cpp

@ -103,7 +103,7 @@ bytes BlockChain::createGenesisBlock()
block.appendList(15)
// TODO: maybe make logbloom correct?
<< h256() << EmptySHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
<< h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
@ -309,10 +309,14 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
auto b = s.oldBloom();
BlockBlooms bb;
BlockTraces bt;
BlockLogBlooms blb;
BlockReceipts br;
for (unsigned i = 0; i < s.pending().size(); ++i)
{
bt.traces.push_back(s.changesFromPending(i));
bb.blooms.push_back(s.changesFromPending(i).bloom());
bt.traces.push_back(s.changesFromPending(i));
blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i));
}
s.cleanup(true);
td = pd.totalDifficulty + tdIncrease;
@ -334,11 +338,21 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
WriteGuard l(x_traces);
m_traces[newHash] = bt;
}
{
WriteGuard l(x_logBlooms);
m_logBlooms[newHash] = blb;
}
{
WriteGuard l(x_receipts);
m_receipts[newHash] = br;
}
m_extrasDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)dev::ref(m_details[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 1), (ldb::Slice)dev::ref(m_blooms[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 2), (ldb::Slice)dev::ref(m_traces[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 3), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 4), (ldb::Slice)dev::ref(m_receipts[newHash].rlp()));
m_db->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block));
#if ETH_PARANOIA

12
libethereum/BlockChain.h

@ -105,6 +105,14 @@ public:
BlockTraces traces(h256 _hash) const { return queryExtras<BlockTraces, 2>(_hash, m_traces, x_traces, NullBlockTraces); }
BlockTraces traces() const { return traces(currentHash()); }
/// Get the transactions' log blooms of a block (or the most recent mined if none given). Thread-safe.
BlockLogBlooms logBlooms(h256 _hash) const { return queryExtras<BlockLogBlooms, 3>(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); }
BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); }
/// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe.
BlockReceipts receipts(h256 _hash) const { return queryExtras<BlockReceipts, 4>(_hash, m_receipts, x_receipts, NullBlockReceipts); }
BlockReceipts receipts() const { return receipts(currentHash()); }
/// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe.
bytes block(h256 _hash) const;
bytes block() const { return block(currentHash()); }
@ -185,6 +193,10 @@ private:
mutable BlockBloomsHash m_blooms;
mutable boost::shared_mutex x_traces;
mutable BlockTracesHash m_traces;
mutable boost::shared_mutex x_logBlooms;
mutable BlockLogBloomsHash m_logBlooms;
mutable boost::shared_mutex x_receipts;
mutable BlockReceiptsHash m_receipts;
mutable boost::shared_mutex x_cache;
mutable std::map<h256, bytes> m_cache;

22
libethereum/BlockDetails.h

@ -29,6 +29,7 @@
#include <libdevcore/Log.h>
#include <libdevcore/RLP.h>
#include "Manifest.h"
#include "TransactionReceipt.h"
namespace ldb = leveldb;
namespace dev
@ -71,14 +72,35 @@ struct BlockTraces
Manifests traces;
};
struct BlockLogBlooms
{
BlockLogBlooms() {}
BlockLogBlooms(RLP const& _r) { blooms = _r.toVector<h512>(); }
bytes rlp() const { RLPStream s; s << blooms; return s.out(); }
h512s blooms;
};
struct BlockReceipts
{
BlockReceipts() {}
BlockReceipts(RLP const& _r) { for (auto const& i: _r) receipts.emplace_back(i.data()); }
bytes rlp() const { RLPStream s(receipts.size()); for (TransactionReceipt const& i: receipts) i.streamRLP(s); return s.out(); }
TransactionReceipts receipts;
};
typedef std::map<h256, BlockDetails> BlockDetailsHash;
typedef std::map<h256, BlockBlooms> BlockBloomsHash;
typedef std::map<h256, BlockTraces> BlockTracesHash;
typedef std::map<h256, BlockLogBlooms> BlockLogBloomsHash;
typedef std::map<h256, BlockReceipts> BlockReceiptsHash;
static const BlockDetails NullBlockDetails;
static const BlockBlooms NullBlockBlooms;
static const BlockTraces NullBlockTraces;
static const BlockLogBlooms NullBlockLogBlooms;
static const BlockReceipts NullBlockReceipts;
}
}

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)

64
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;
BOOST_THROW_EXCEPTION(OutOfGas());
clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << gasCost << " Got" << m_t.gas();
BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)gasCost, (bigint)m_t.gas()));
}
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((bigint)cost, (bigint)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((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)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)
@ -123,11 +123,7 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu
m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms);
}
else
{
m_endGas = _gas;
if (m_ext)
m_ext->sub.logs.push_back(LogEntry(_receiveAddress, {u256((u160)_senderAddress) + 1}, bytes()));
}
return !m_ext;
}
@ -177,22 +173,20 @@ 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_logs = m_ext->sub.logs;
}
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 +228,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"

4
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);
}
@ -102,7 +102,7 @@ public:
private:
State& m_s; ///< A reference to the base state.
std::map<Address, Account> m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution.
std::map<Address, Account> m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution.
Manifest* m_ms;
};

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;
};
}
}

78
libethereum/State.cpp

@ -21,12 +21,13 @@
#include "State.h"
#include <boost/filesystem.hpp>
#include <time.h>
#include <ctime>
#include <random>
#include <boost/filesystem.hpp>
#include <boost/timer.hpp>
#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 +61,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)));
}
@ -112,8 +114,6 @@ State::State(Address _coinbaseAddress, OverlayDB const& _db):
m_ourAddress(_coinbaseAddress),
m_blockReward(c_blockReward)
{
secp256k1_start();
// Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly.
m_state.init();
@ -137,8 +137,6 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h):
m_state(&m_db),
m_blockReward(c_blockReward)
{
secp256k1_start();
// TODO THINK: is this necessary?
m_state.init();
@ -324,8 +322,8 @@ StateDiff State::diff(State const& _c) const
for (auto i: _c.m_cache)
ads.insert(i.first);
cnote << *this;
cnote << _c;
// cnote << *this;
// cnote << _c;
for (auto i: ads)
{
@ -520,7 +518,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;
@ -552,10 +550,12 @@ h256s State::sync(TransactionQueue& _tq, bool* o_transactionQueueChanged)
try
{
uncommitToMine();
// boost::timer t;
execute(i.second);
ret.push_back(m_receipts.back().changes().bloom());
_tq.noteGood(i);
++goodTxs;
// cnote << "TX took:" << t.elapsed() * 1000;
}
catch (InvalidNonce const& in)
{
@ -723,9 +723,7 @@ void State::cleanup(bool _fullCommit)
m_previousBlock = m_currentBlock;
}
else
{
m_db.rollback();
}
resetCurrent();
}
@ -793,7 +791,6 @@ h256 State::oldBloom() const
LogBloom State::logBloom() const
{
LogBloom ret;
ret.shiftBloom<3>(sha3(m_currentBlock.coinbaseAddress.ref()));
for (TransactionReceipt const& i: m_receipts)
ret |= i.bloom();
return ret;
@ -1127,10 +1124,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
@ -1155,10 +1152,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());
@ -1194,11 +1191,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))
{
@ -1214,40 +1214,31 @@ 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
{
// non-contract call
if (o_sub)
o_sub->logs.push_back(LogEntry(_receiveAddress, {u256((u160)_senderAddress) + 1}, bytes()));
}
return true;
}
@ -1282,16 +1273,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)
{
@ -1318,8 +1306,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;
}
@ -1381,7 +1367,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;
@ -1410,7 +1396,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>());

37
libethereum/State.h

@ -35,13 +35,14 @@
#include "TransactionQueue.h"
#include "Account.h"
#include "Transaction.h"
#include "TransactionReceipt.h"
#include "Executive.h"
#include "AccountDiff.h"
namespace dev
{
namespace test { class FakeExtVM; class FakeState; }
namespace test { class ImportTest; }
namespace eth
{
@ -52,37 +53,6 @@ struct StateChat: public LogChannel { static const char* name() { return "-S-";
struct StateTrace: public LogChannel { static const char* name() { return "=S="; } static const int verbosity = 7; };
struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; };
class TransactionReceipt
{
public:
TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log, Manifest const& _ms): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log), m_changes(_ms) {}
Manifest const& changes() const { return m_changes; }
h256 const& stateRoot() const { return m_stateRoot; }
u256 const& gasUsed() const { return m_gasUsed; }
LogBloom const& bloom() const { return m_bloom; }
LogEntries const& log() const { return m_log; }
void streamRLP(RLPStream& _s) const
{
_s.appendList(4) << m_stateRoot << m_gasUsed << m_bloom;
_s.appendList(m_log.size());
for (LogEntry const& l: m_log)
l.streamRLP(_s);
}
private:
h256 m_stateRoot;
u256 m_gasUsed;
LogBloom m_bloom;
LogEntries m_log;
Manifest m_changes; ///< TODO: PoC-7: KILL
};
using TransactionReceipts = std::vector<TransactionReceipt>;
struct PrecompiledAddress
{
unsigned gas;
@ -97,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;
}

72
libethereum/TransactionReceipt.h

@ -0,0 +1,72 @@
/*
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 TransactionReceipt.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <array>
#include <map>
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libevm/ExtVMFace.h>
#include "Manifest.h"
namespace dev
{
namespace eth
{
class TransactionReceipt
{
public:
TransactionReceipt(bytesConstRef _rlp) { RLP r(_rlp); m_stateRoot = (h256)r[0]; m_gasUsed = (u256)r[1]; m_bloom = (LogBloom)r[2]; for (auto const& i: r[3]) m_log.emplace_back(i); }
TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log, Manifest const& _ms): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log), m_changes(_ms) {}
Manifest const& changes() const { return m_changes; }
h256 const& stateRoot() const { return m_stateRoot; }
u256 const& gasUsed() const { return m_gasUsed; }
LogBloom const& bloom() const { return m_bloom; }
LogEntries const& log() const { return m_log; }
void streamRLP(RLPStream& _s) const
{
_s.appendList(4) << m_stateRoot << m_gasUsed << m_bloom;
_s.appendList(m_log.size());
for (LogEntry const& l: m_log)
l.streamRLP(_s);
}
bytes rlp() const { RLPStream s; streamRLP(s); return s.out(); }
private:
h256 m_stateRoot;
u256 m_gasUsed;
LogBloom m_bloom;
LogEntries m_log;
Manifest m_changes; ///< TODO: PoC-7: KILL
};
using TransactionReceipts = std::vector<TransactionReceipt>;
}
}

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 = _r[2].toBytes(); }
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).

3
libevm/FeeStructure.cpp

@ -37,3 +37,6 @@ u256 const dev::eth::c_callGas = 20;
u256 const dev::eth::c_memoryGas = 1;
u256 const dev::eth::c_txDataGas = 5;
u256 const dev::eth::c_txGas = 500;
u256 const dev::eth::c_logGas = 32;
u256 const dev::eth::c_logDataGas = 1;
u256 const dev::eth::c_logTopicGas = 32;

3
libevm/FeeStructure.h

@ -40,6 +40,9 @@ extern u256 const c_callGas; ///< Once per CALL operation & message call trans
extern u256 const c_memoryGas; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
extern u256 const c_txDataGas; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions.
extern u256 const c_txGas; ///< Per transaction. NOTE: Not payable on data of calls between transactions.
extern u256 const c_logGas; ///< Per LOG* operation.
extern u256 const c_logDataGas; ///< Per byte in a LOG* operation's data.
extern u256 const c_logTopicGas; ///< Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
}
}

38
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((bigint)_n, (bigint)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; }
@ -104,7 +104,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
{
int in = _ext.code[i] - (unsigned)Instruction::PUSH1 + 1;
u256 p = 0;
for (; in--; i++)
for (i++; in--; i++)
p = (p << 8) | _ext.getCode(i);
if ((_ext.getCode(i) == (byte)Instruction::JUMP || _ext.getCode(i) == (byte)Instruction::JUMPI) && !(_ext.getCode(p) == (byte)Instruction::JUMP || _ext.getCode(p) == (byte)Instruction::JUMPI))
if (p >= _ext.code.size())
@ -112,6 +112,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
else
implicit.insert(p);
else {}
i--;
}
for (unsigned i = 0; i < _ext.code.size(); i += instructionInfo((Instruction)_ext.getCode(i)).additional + 1)
if (implicit.count(i))
@ -205,14 +206,15 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
{
unsigned n = (unsigned)inst - (unsigned)Instruction::LOG0;
require(n + 2);
newTempSize = memNeed(m_stack[m_stack.size() - 1 - n], m_stack[m_stack.size() - 2 - n]);
runGas = c_logGas + c_logTopicGas * n + (bigint)c_logDataGas * m_stack[m_stack.size() - 2];
newTempSize = memNeed(m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]);
break;
}
case Instruction::CALL:
case Instruction::CALLCODE:
require(7);
runGas = c_callGas + m_stack[m_stack.size() - 1];
runGas = (bigint)c_callGas + m_stack[m_stack.size() - 1];
newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]));
break;
@ -700,7 +702,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;
@ -708,7 +710,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();
@ -742,18 +744,38 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;*/
case Instruction::LOG0:
_ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::LOG1:
_ext.log({m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
m_stack.pop_back();
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::LOG2:
_ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
m_stack.pop_back();
m_stack.pop_back();
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::LOG3:
_ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
m_stack.pop_back();
m_stack.pop_back();
m_stack.pop_back();
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::LOG4:
_ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5], m_stack[m_stack.size() - 6]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
m_stack.pop_back();
m_stack.pop_back();
m_stack.pop_back();
m_stack.pop_back();
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::CREATE:
{

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)

32
libethcore/CryptoHeaders.h → libevmcore/Exceptions.h

@ -14,23 +14,23 @@
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CryptoHeaders.h
* @author Tim Hughes <tim@twistedfury.com>
/** @file Exceptions.h
* @author Christian <c@ethdev.com>
* @date 2014
*/
#pragma once
// need to leave this one disabled
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma warning(push)
#pragma warning(disable:4100 4244)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <sha.h>
#include <sha3.h>
#include <ripemd.h>
#include <secp256k1/secp256k1.h>
#pragma warning(pop)
#pragma GCC diagnostic pop
#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, 2, 0, true } },
{ Instruction::LOG1, { "LOG1", 0, 3, 0, true } },
{ Instruction::LOG2, { "LOG2", 0, 4, 0, true } },
{ Instruction::LOG3, { "LOG3", 0, 5, 0, true } },
{ Instruction::LOG4, { "LOG4", 0, 6, 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 {};
}
}

50
libp2p/Host.cpp

@ -62,8 +62,8 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool
m_clientVersion(_clientVersion),
m_netPrefs(_n),
m_ioService(new ba::io_service),
m_acceptor(*m_ioService),
m_socket(*m_ioService),
m_acceptor(new bi::tcp::acceptor(*m_ioService)),
m_socket(new bi::tcp::socket(*m_ioService)),
m_key(KeyPair::create())
{
populateAddresses();
@ -91,11 +91,11 @@ void Host::start()
bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : m_netPrefs.listenPort);
try
{
m_acceptor.open(endpoint.protocol());
m_acceptor.set_option(ba::socket_base::reuse_address(true));
m_acceptor.bind(endpoint);
m_acceptor.listen();
m_listenPort = i ? m_acceptor.local_endpoint().port() : m_netPrefs.listenPort;
m_acceptor->open(endpoint.protocol());
m_acceptor->set_option(ba::socket_base::reuse_address(true));
m_acceptor->bind(endpoint);
m_acceptor->listen();
m_listenPort = i ? m_acceptor->local_endpoint().port() : m_netPrefs.listenPort;
break;
}
catch (...)
@ -105,7 +105,7 @@ void Host::start()
cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information();
return;
}
m_acceptor.close();
m_acceptor->close();
continue;
}
}
@ -118,20 +118,24 @@ void Host::start()
void Host::stop()
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
return;
for (auto const& h: m_capabilities)
h.second->onStopping();
stopWorking();
if (m_acceptor.is_open())
if (m_acceptor->is_open())
{
if (m_accepting)
m_acceptor.cancel();
m_acceptor.close();
m_acceptor->cancel();
m_acceptor->close();
m_accepting = false;
}
if (m_socket.is_open())
m_socket.close();
if (m_socket->is_open())
m_socket->close();
disconnectPeers();
if (!!m_ioService)
@ -147,6 +151,8 @@ void Host::quit()
// such tasks may involve socket reads from Capabilities that maintain references
// to resources we're about to free.
stop();
m_acceptor.reset();
m_socket.reset();
m_ioService.reset();
// m_acceptor & m_socket are DANGEROUS now.
}
@ -379,8 +385,8 @@ void Host::populateAddresses()
shared_ptr<Node> Host::noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId)
{
RecursiveGuard l(x_peers);
if (_a.port() < 30300 || _a.port() > 30303)
cwarn << "Wierd port being recorded!";
if (_a.port() < 30300 || _a.port() > 30305)
cwarn << "Weird port being recorded: " << _a.port();
if (_a.port() >= /*49152*/32768)
{
@ -463,18 +469,18 @@ void Host::ensureAccepting()
{
clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")";
m_accepting = true;
m_acceptor.async_accept(m_socket, [=](boost::system::error_code ec)
m_acceptor->async_accept(*m_socket, [=](boost::system::error_code ec)
{
if (!ec)
{
try
{
try {
clog(NetConnect) << "Accepted connection from " << m_socket.remote_endpoint();
clog(NetConnect) << "Accepted connection from " << m_socket->remote_endpoint();
} catch (...){}
bi::address remoteAddress = m_socket.remote_endpoint().address();
bi::address remoteAddress = m_socket->remote_endpoint().address();
// Port defaults to 0 - we let the hello tell us which port the peer listens to
auto p = std::make_shared<Session>(this, std::move(m_socket), bi::tcp::endpoint(remoteAddress, 0));
auto p = std::make_shared<Session>(this, std::move(*m_socket), bi::tcp::endpoint(remoteAddress, 0));
p->start();
}
catch (Exception const& _e)
@ -772,7 +778,7 @@ bytes Host::saveNodes() const
{
Node const& n = *(i.second);
// TODO: PoC-7: Figure out why it ever shares these ports.//n.address.port() >= 30300 && n.address.port() <= 30305 &&
if (!n.dead && n.address.port() > 0 && n.address.port() < /*49152*/32768 && n.id != id() && !isPrivateAddress(n.address.address()))
if (!n.dead && chrono::system_clock::now() - n.lastConnected < chrono::seconds(3600 * 48) && n.address.port() > 0 && n.address.port() < /*49152*/32768 && n.id != id() && !isPrivateAddress(n.address.address()))
{
nodes.appendList(10);
if (n.address.address().is_v4())
@ -780,8 +786,8 @@ bytes Host::saveNodes() const
else
nodes << n.address.address().to_v6().to_bytes();
nodes << n.address.port() << n.id << (int)n.idOrigin
<< std::chrono::duration_cast<std::chrono::seconds>(n.lastConnected.time_since_epoch()).count()
<< std::chrono::duration_cast<std::chrono::seconds>(n.lastAttempted.time_since_epoch()).count()
<< chrono::duration_cast<chrono::seconds>(n.lastConnected.time_since_epoch()).count()
<< chrono::duration_cast<chrono::seconds>(n.lastAttempted.time_since_epoch()).count()
<< n.failedAttempts << (unsigned)n.lastDisconnect << n.score << n.rating;
count++;
}

4
libp2p/Host.h

@ -214,8 +214,8 @@ private:
int m_listenPort = NetworkStopped; ///< What port are we listening on?
std::unique_ptr<ba::io_service> m_ioService; ///< IOService for network stuff.
bi::tcp::acceptor m_acceptor; ///< Listening acceptor.
bi::tcp::socket m_socket; ///< Listening socket.
std::unique_ptr<bi::tcp::acceptor> m_acceptor; ///< Listening acceptor.
std::unique_ptr<bi::tcp::socket> m_socket; ///< Listening socket.
UPnP* m_upnp = nullptr; ///< UPnP helper.
bi::tcp::endpoint m_public; ///< Our public listening endpoint.

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

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

1
libqethereum/QEthereum.h

@ -83,6 +83,7 @@ private:
{ \
_frame->disconnect(); \
_frame->addToJavaScriptWindowObject("_web3", qweb, QWebFrame::ScriptOwnership); \
_frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/es6-promise-2.0.0.js")); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/main.js")); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/qt.js")); \

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

211
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,156 @@ 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 {
return pd(aux, multiToken(nullptr, 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 +289,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 +301,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 +316,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 +329,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 +341,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 +353,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 +389,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 +416,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 +444,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);

165
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,12 +263,28 @@ TypeError ASTNode::createTypeError(string const& _description)
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
void Statement::expectType(Expression& _expression, Type const& _expectedType)
vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
{
vector<FunctionDefinition const*> exportedFunctions;
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName())
exportedFunctions.push_back(f.get());
auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b)
{
return _a->getName().compare(_b->getName()) < 0;
};
sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames);
return exportedFunctions;
}
void FunctionDefinition::checkTypeRequirements()
{
_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
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
m_body->checkTypeRequirements();
}
void Block::checkTypeRequirements()
@ -271,7 +295,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,39 +303,34 @@ 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()
{
// Variables can be declared without type (with "var"), in which case the first assignment
// setsthe type.
// sets the type.
// Note that assignments before the first declaration are legal because of the special scoping
// rules inherited from JavaScript.
if (m_value)
{
if (m_variable->getType())
expectType(*m_value, *m_variable->getType());
m_value->expectType(*m_variable->getType());
else
{
// no type declared and no previous assignment, infer the type
@ -319,27 +338,50 @@ void VariableDefinition::checkTypeRequirements()
m_variable->setType(m_value->getType());
}
}
if (m_variable->getType() && !m_variable->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(m_variable->createTypeError("Type is required to live outside storage."));
}
void Assignment::checkTypeRequirements()
{
//@todo lefthandside actually has to be assignable
// add a feature to the type system to check that
m_leftHandSide->checkTypeRequirements();
expectType(*m_rightHandSide, *m_leftHandSide->getType());
m_leftHandSide->requireLValue();
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 Expression::requireLValue()
{
if (!isLvalue())
BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue."));
m_lvalueRequested = true;
}
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)
m_subExpression->requireLValue();
m_type = m_subExpression->getType();
if (!m_type->acceptsUnaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
@ -347,22 +389,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 +420,24 @@ 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
{
m_expression->requireLValue();
//@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 +446,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 +460,44 @@ bool FunctionCall::isTypeConversion() const
void MemberAccess::checkTypeRequirements()
{
assert(false); // not yet implemented
// m_type = ;
m_expression->checkTypeRequirements();
m_expression->requireLValue();
if (m_expression->getType()->getCategory() != Type::Category::STRUCT)
BOOST_THROW_EXCEPTION(createTypeError("Member access to a non-struct (is " +
m_expression->getType()->toString() + ")"));
StructType const& type = dynamic_cast<StructType const&>(*m_expression->getType());
unsigned memberIndex = type.memberNameToIndex(*m_memberName);
if (memberIndex >= type.getMemberCount())
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString()));
m_type = type.getMemberByIndex(memberIndex).getType();
m_isLvalue = true;
}
void IndexAccess::checkTypeRequirements()
{
assert(false); // not yet implemented
// m_type = ;
m_base->checkTypeRequirements();
m_base->requireLValue();
if (m_base->getType()->getCategory() != Type::Category::MAPPING)
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
m_base->getType()->toString() + ")"));
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
m_isLvalue = true;
}
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()?
@ -461,6 +515,7 @@ void Identifier::checkTypeRequirements()
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion.
m_type = make_shared<FunctionType>(*functionDef);
m_isLvalue = true;
return;
}
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
@ -469,7 +524,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 +535,8 @@ void ElementaryTypeNameExpression::checkTypeRequirements()
void Literal::checkTypeRequirements()
{
m_type = Type::forLiteral(*this);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Literal value too large."));
}
}

150
libsolidity/AST.h

@ -116,10 +116,12 @@ 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; }
/// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
@ -135,6 +137,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;
};
@ -142,7 +146,7 @@ private:
/**
* Parameter list, used as function parameter list and return list.
* None of the parameters is allowed to contain mappings (not even recursively
* inside structs), but (@todo) this is not yet enforced.
* inside structs).
*/
class ParameterList: public ASTNode
{
@ -152,7 +156,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 +179,23 @@ 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; }
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
void checkTypeRequirements();
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;
};
/**
@ -226,7 +238,7 @@ public:
/// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared
/// pointer until the types have been resolved using the @ref NameAndTypeResolver.
virtual std::shared_ptr<Type> toType() = 0;
virtual std::shared_ptr<Type> toType() const = 0;
};
/**
@ -237,9 +249,12 @@ 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); }
virtual std::shared_ptr<Type> toType() const override { return Type::fromElementaryTypeName(m_type); }
Token::Value getTypeName() const { return m_type; }
@ -257,7 +272,7 @@ public:
UserDefinedTypeName(Location const& _location, ASTPointer<ASTString> const& _name):
TypeName(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() override { return Type::fromUserDefinedTypeName(*this); }
virtual std::shared_ptr<Type> toType() const override { return Type::fromUserDefinedTypeName(*this); }
ASTString const& getName() const { return *m_name; }
void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; }
@ -279,7 +294,10 @@ public:
ASTPointer<TypeName> const& _valueType):
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() override { return Type::fromMapping(*this); }
virtual std::shared_ptr<Type> toType() const override { return Type::fromMapping(*this); }
ElementaryTypeName const& getKeyType() const { return *m_keyType; }
TypeName const& getValueType() const { return *m_valueType; }
private:
ASTPointer<ElementaryTypeName> m_keyType;
@ -305,11 +323,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 +355,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;
@ -350,7 +368,6 @@ private:
/**
* Statement in which a break statement is legal.
* @todo actually check this requirement.
*/
class BreakableStatement: public Statement
{
@ -368,6 +385,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 +398,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 +406,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 +418,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 +447,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 +478,51 @@ 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), m_lvalueRequested(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);
/// Checks that this expression is an lvalue and also registers that an address and
/// not a value is generated during compilation. Can be called after checkTypeRequirements()
/// by an enclosing expression.
void requireLValue();
/// Returns true if @a requireLValue was previously called on this expression.
bool lvalueRequested() const { return m_lvalueRequested; }
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;
//! Whether the outer expression requested the address (true) or the value (false) of this expression.
bool m_lvalueRequested;
};
/// 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,12 +546,16 @@ 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;
Token::Value getOperator() const { return m_operator; }
bool isPrefixOperation() const { return m_isPrefix; }
Expression& getSubExpression() const { return *m_subExpression; }
private:
Token::Value m_operator;
@ -502,19 +572,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 +606,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;
@ -549,6 +628,7 @@ public:
ASTPointer<ASTString> const& _memberName):
Expression(_location), m_expression(_expression), m_memberName(_memberName) {}
virtual void accept(ASTVisitor& _visitor) override;
Expression& getExpression() const { return *m_expression; }
ASTString const& getMemberName() const { return *m_memberName; }
virtual void checkTypeRequirements() override;
@ -569,6 +649,9 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
Expression& getBaseExpression() const { return *m_base; }
Expression& getIndexExpression() const { return *m_index; }
private:
ASTPointer<Expression> m_base;
ASTPointer<Expression> m_index;
@ -616,7 +699,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;

20
libsolidity/ASTPrinter.cpp

@ -30,8 +30,8 @@ namespace dev
namespace solidity
{
ASTPrinter::ASTPrinter(ASTPointer<ASTNode> const& _ast, string const& _source):
m_indentation(0), m_source(_source), m_ast(_ast)
ASTPrinter::ASTPrinter(ASTNode& _ast, string const& _source):
m_indentation(0), m_source(_source), m_ast(&_ast)
{
}
@ -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--;
@ -418,8 +430,8 @@ void ASTPrinter::printSourcePart(ASTNode const& _node)
if (!m_source.empty())
{
Location const& location(_node.getLocation());
*m_ostream << getIndentation() << " Source: |"
<< m_source.substr(location.start, location.end - location.start) << "|" << endl;
*m_ostream << getIndentation() << " Source: "
<< escaped(m_source.substr(location.start, location.end - location.start), false) << endl;
}
}

6
libsolidity/ASTPrinter.h

@ -38,7 +38,7 @@ class ASTPrinter: public ASTVisitor
public:
/// Create a printer for the given abstract syntax tree. If the source is specified,
/// the corresponding parts of the source are printed with each node.
ASTPrinter(ASTPointer<ASTNode> const& _ast, std::string const& _source = std::string());
ASTPrinter(ASTNode& _ast, std::string const& _source = std::string());
/// Output the string representation of the AST to _stream.
void print(std::ostream& _stream);
@ -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;
@ -112,7 +114,7 @@ private:
int m_indentation;
std::string m_source;
ASTPointer<ASTNode> m_ast;
ASTNode* m_ast;
std::ostream* m_ostream;
};

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

519
libsolidity/Compiler.cpp

@ -17,385 +17,296 @@
/**
* @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;
}
void ExpressionCompiler::compile(Expression& _expression)
{
m_assemblyItems.clear();
_expression.accept(*this);
}
m_context = CompilerContext(); // clear it just in case
bytes ExpressionCompiler::getAssembledBytecode() const
{
bytes assembled;
assembled.reserve(m_assemblyItems.size());
//@todo constructor
// resolve label references
for (uint32_t pos = 0; pos < m_assemblyItems.size(); ++pos)
{
AssemblyItem const& item = m_assemblyItems[pos];
if (item.getType() == AssemblyItem::Type::LABEL)
m_context.setLabelPosition(item.getLabel(), pos + 1);
}
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);
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());
appendFunctionSelector(_contract.getDefinedFunctions());
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
function->accept(*this);
return assembled;
packIntoContractCreator();
}
AssemblyItems ExpressionCompiler::compileExpression(CompilerContext& _context,
Expression& _expression)
void Compiler::packIntoContractCreator()
{
ExpressionCompiler compiler(_context);
compiler.compile(_expression);
return compiler.getAssemblyItems();
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);
}
void ExpressionCompiler::endVisit(Assignment& _assignment)
void Compiler::appendFunctionSelector(vector<ASTPointer<FunctionDefinition>> const& _functions)
{
Expression& rightHandSide = _assignment.getRightHandSide();
Token::Value op = _assignment.getAssignmentOperator();
if (op != Token::ASSIGN)
// 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."));
// retrieve the first byte of the call data, which determines the called function
// @todo This code had a jump table in a previous version which was more efficient but also
// error prone (due to the optimizer and variable length tag addresses)
m_context << u256(1) << u256(0) // some constants
<< eth::dupInstruction(1) << eth::Instruction::CALLDATALOAD
<< eth::dupInstruction(2) << eth::Instruction::BYTE
<< eth::dupInstruction(2);
// stack here: 1 0 <funid> 0, stack top will be counted up until it matches funid
for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
{
// compound assignment
// @todo retrieve lvalue value
rightHandSide.accept(*this);
Type const& resultType = *_assignment.getType();
cleanHigherOrderBitsIfNeeded(*rightHandSide.getType(), resultType);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), resultType);
eth::AssemblyItem const& callDataUnpackerEntry = f.second.second;
m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntry);
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
//@todo avoid the last ADD (or remove it in the optimizer)
}
else
rightHandSide.accept(*this);
// @todo store value
}
m_context << eth::Instruction::STOP; // function not found
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())
for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
{
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
FunctionDefinition const& function = *f.second.first;
eth::AssemblyItem const& callDataUnpackerEntry = f.second.second;
m_context << callDataUnpackerEntry;
eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(function);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
m_context << returnTag;
appendReturnValuePacker(function);
}
}
bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function)
{
Expression& leftExpression = _binaryOperation.getLeftExpression();
Expression& rightExpression = _binaryOperation.getRightExpression();
Type const& resultType = *_binaryOperation.getType();
Token::Value const op = _binaryOperation.getOperator();
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1;
if (op == Token::AND || op == Token::OR)
{
// special case: short-circuiting
appendAndOrOperatorCode(_binaryOperation);
}
else if (Token::isCompareOp(op))
{
leftExpression.accept(*this);
rightExpression.accept(*this);
// 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
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{
leftExpression.accept(*this);
cleanHigherOrderBitsIfNeeded(*leftExpression.getType(), resultType);
rightExpression.accept(*this);
cleanHigherOrderBitsIfNeeded(*rightExpression.getType(), resultType);
appendOrdinaryBinaryOperatorCode(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;
}
// do not visit the child nodes, we already did that explicitly
return false;
}
void ExpressionCompiler::endVisit(FunctionCall& _functionCall)
void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
{
if (_functionCall.isTypeConversion())
{
//@todo binary representation for all supported types (bool and int) is the same, so no-op
// here for now.
}
else
//@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)
{
//@todo
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;
}
void ExpressionCompiler::endVisit(MemberAccess&)
bool Compiler::visit(FunctionDefinition& _function)
{
//@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
}
m_context.startNewFunction();
m_returnTag = m_context.newTag();
m_breakTags.clear();
m_continueTags.clear();
void ExpressionCompiler::endVisit(IndexAccess&)
{
m_context << m_context.getFunctionEntryLabel(_function);
}
// stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
void ExpressionCompiler::endVisit(Identifier&)
{
unsigned const numArguments = _function.getParameters().size();
unsigned const numReturnValues = _function.getReturnParameters().size();
unsigned const numLocalVariables = _function.getLocalVariables().size();
}
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);
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
}
}
_function.getBody().accept(*this);
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.
}
}
m_context << m_returnTag;
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperation)
{
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);
}
// 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::appendCompareOperatorCode(Token::Value _operator, Type const& _type)
{
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();
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);
// note that EVM opcodes compare like "stack[0] < stack[1]",
// but our left value is at stack[1], so everyhing is reversed.
switch (_operator)
while (stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0)
{
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 << 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
m_context << eth::Instruction::JUMP;
return false;
}
void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type)
bool Compiler::visit(IfStatement& _ifStatement)
{
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
ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
if (_ifStatement.getFalseStatement())
_ifStatement.getFalseStatement()->accept(*this);
eth::AssemblyItem endTag = m_context.appendJumpToNew();
m_context << trueTag;
_ifStatement.getTrueStatement().accept(*this);
m_context << endTag;
return false;
}
void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
bool Compiler::visit(WhileStatement& _whileStatement)
{
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);
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(isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
break;
case Token::MOD:
append(isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
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::appendBitOperatorCode(Token::Value _operator)
bool Compiler::visit(Continue&)
{
switch (_operator)
{
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);
}
if (!m_continueTags.empty())
m_context.appendJumpTo(m_continueTags.back());
return false;
}
void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
bool Compiler::visit(Break&)
{
switch (_operator)
if (!m_breakTags.empty())
m_context.appendJumpTo(m_breakTags.back());
return false;
}
bool Compiler::visit(Return& _return)
{
//@todo modifications are needed to make this work with functions returning multiple values
if (Expression* expression = _return.getExpression())
{
case Token::SHL:
assert(false); //@todo
break;
case Token::SAR:
assert(false); //@todo
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;
}
uint32_t ExpressionCompiler::appendConditionalJump()
bool Compiler::visit(VariableDefinition& _variableDefinition)
{
uint32_t label = m_context.dispenseNewLabel();
append(eth::Instruction::PUSH1);
appendLabelref(label);
append(eth::Instruction::JUMPI);
return label;
if (Expression* expression = _variableDefinition.getExpression())
{
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;
}
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;
}
}
}

104
libsolidity/CompilerContext.h

@ -0,0 +1,104 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* 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 appendJumpToNew() { return m_asm.appendJump().tag(); }
/// Appends a JUMP to a tag already on the stack
CompilerContext& appendJump() { return *this << eth::Instruction::JUMP; }
/// 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;
};
}
}

134
libsolidity/CompilerStack.cpp

@ -0,0 +1,134 @@
/*
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
{
void CompilerStack::setSource(string const& _sourceCode)
{
reset();
m_scanner = make_shared<Scanner>(CharStream(_sourceCode));
}
void CompilerStack::parse()
{
if (!m_scanner)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available."));
m_contractASTNode = Parser().parse(m_scanner);
NameAndTypeResolver().resolveNamesAndTypes(*m_contractASTNode);
m_parseSuccessful = true;
}
void CompilerStack::parse(string const& _sourceCode)
{
setSource(_sourceCode);
parse();
}
bytes const& CompilerStack::compile(bool _optimize)
{
if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
m_bytecode.clear();
m_compiler = make_shared<Compiler>();
m_compiler->compileContract(*m_contractASTNode);
return m_bytecode = m_compiler->getAssembledBytecode(_optimize);
}
bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
{
parse(_sourceCode);
return compile(_optimize);
}
void CompilerStack::streamAssembly(ostream& _outStream)
{
if (!m_compiler || m_bytecode.empty())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
m_compiler->streamAssembly(_outStream);
}
string const& CompilerStack::getInterface()
{
if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
if (m_interface.empty())
{
stringstream interface;
interface << '[';
vector<FunctionDefinition const*> exportedFunctions = m_contractASTNode->getInterfaceFunctions();
unsigned functionsCount = exportedFunctions.size();
for (FunctionDefinition const* f: exportedFunctions)
{
auto streamVariables = [&](vector<ASTPointer<VariableDeclaration>> const& _vars)
{
unsigned varCount = _vars.size();
for (ASTPointer<VariableDeclaration> const& var: _vars)
{
interface << "{"
<< "\"name\":" << escaped(var->getName(), false) << ","
<< "\"type\":" << escaped(var->getType()->toString(), false)
<< "}";
if (--varCount > 0)
interface << ",";
}
};
interface << '{'
<< "\"name\":" << escaped(f->getName(), false) << ","
<< "\"inputs\":[";
streamVariables(f->getParameters());
interface << "],"
<< "\"outputs\":[";
streamVariables(f->getReturnParameters());
interface << "]"
<< "}";
if (--functionsCount > 0)
interface << ",";
}
interface << ']';
m_interface = interface.str();
}
return m_interface;
}
bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
{
CompilerStack stack;
return stack.compile(_sourceCode, _optimize);
}
}
}

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

Loading…
Cancel
Save