Browse Source

Merge remote-tracking branch 'upstream/develop' into addTests

cl-refactor
CJentzsch 10 years ago
parent
commit
47c3e3aebd
  1. 3
      alethzero/CMakeLists.txt
  2. 18
      alethzero/Context.cpp
  3. 4
      alethzero/Context.h
  4. 181
      alethzero/GasPricing.ui
  5. 12
      alethzero/Main.ui
  6. 41
      alethzero/MainWin.cpp
  7. 7
      alethzero/MainWin.h
  8. 13
      alethzero/Transact.cpp
  9. 1
      alethzero/Transact.h
  10. 2
      alethzero/Transact.ui
  11. 45
      eth/main.cpp
  12. 2
      ethminer/farm.json
  13. 1
      libethereum/Client.h
  14. 23
      libethereum/EthereumHost.cpp
  15. 4
      libethereum/EthereumHost.h
  16. 17
      libethereum/State.h
  17. 14
      libevmasm/Assembly.cpp
  18. 9
      libevmasm/Assembly.h
  19. 225
      libevmasm/ConstantOptimiser.cpp
  20. 147
      libevmasm/ConstantOptimiser.h
  21. 7
      libevmasm/GasMeter.cpp
  22. 4
      libevmasm/GasMeter.h
  23. 4
      libevmcore/Instruction.cpp
  24. 13
      libp2p/RLPxHandshake.cpp
  25. 6
      libp2p/Session.cpp
  26. 30
      libsolidity/AST.cpp
  27. 27
      libsolidity/AST.h
  28. 50
      libsolidity/ArrayUtils.cpp
  29. 24
      libsolidity/Compiler.cpp
  30. 17
      libsolidity/Compiler.h
  31. 5
      libsolidity/CompilerContext.h
  32. 6
      libsolidity/CompilerStack.cpp
  33. 2
      libsolidity/CompilerStack.h
  34. 4
      libsolidity/CompilerUtils.cpp
  35. 61
      libsolidity/ExpressionCompiler.cpp
  36. 47
      libsolidity/NameAndTypeResolver.cpp
  37. 105
      libsolidity/Parser.cpp
  38. 9
      libsolidity/Parser.h
  39. 3
      libsolidity/Token.h
  40. 64
      libsolidity/Types.cpp
  41. 41
      libsolidity/Types.h
  42. 9
      solc/CommandLineInterface.cpp
  43. 3
      test/libp2p/peer.cpp
  44. 37
      test/libsolidity/SolidityEndToEndTest.cpp
  45. 94
      test/libsolidity/SolidityNameAndTypeResolution.cpp
  46. 45
      test/libsolidity/SolidityOptimizer.cpp
  47. 41
      test/libsolidity/SolidityParser.cpp
  48. 14
      test/libsolidity/SolidityTypes.cpp
  49. 3
      test/libsolidity/solidityExecutionFramework.h
  50. 99
      test/libwhisper/whisperTopic.cpp

3
alethzero/CMakeLists.txt

@ -24,6 +24,7 @@ qt5_wrap_ui(ui_Debugger.h Debugger.ui)
qt5_wrap_ui(ui_Transact.h Transact.ui)
qt5_wrap_ui(ui_ExportState.h ExportState.ui)
qt5_wrap_ui(ui_GetPassword.h GetPassword.ui)
qt5_wrap_ui(ui_GasPricing.h GasPricing.ui)
file(GLOB HEADERS "*.h")
@ -36,7 +37,7 @@ endif ()
# eth_add_executable is defined in cmake/EthExecutableHelper.cmake
eth_add_executable(${EXECUTABLE}
ICON alethzero
UI_RESOURCES alethzero.icns Main.ui Connect.ui Debugger.ui Transact.ui ExportState.ui GetPassword.ui
UI_RESOURCES alethzero.icns Main.ui Connect.ui Debugger.ui Transact.ui ExportState.ui GetPassword.ui GasPricing.ui
WIN_RESOURCES alethzero.rc
)

18
alethzero/Context.cpp

@ -21,6 +21,7 @@
#include "Context.h"
#include <QComboBox>
#include <QSpinBox>
#include <libethcore/Common.h>
using namespace std;
using namespace dev;
@ -34,6 +35,23 @@ Context::~Context()
{
}
void setValueUnits(QComboBox* _units, QSpinBox* _value, u256 _v)
{
initUnits(_units);
_units->setCurrentIndex(0);
while (_v > 50000 && _units->currentIndex() < (int)(units().size() - 2))
{
_v /= 1000;
_units->setCurrentIndex(_units->currentIndex() + 1);
}
_value->setValue((unsigned)_v);
}
u256 fromValueUnits(QComboBox* _units, QSpinBox* _value)
{
return _value->value() * units()[units().size() - 1 - _units->currentIndex()].first;
}
void initUnits(QComboBox* _b)
{
for (auto n = (unsigned)units().size(); n-- != 0; )

4
alethzero/Context.h

@ -28,6 +28,7 @@
#include <libethcore/Common.h>
class QComboBox;
class QSpinBox;
namespace dev { namespace eth { struct StateDiff; class KeyManager; } }
@ -37,6 +38,8 @@ namespace dev { namespace eth { struct StateDiff; class KeyManager; } }
#define Span(S) "<span style=\"" S "\">"
void initUnits(QComboBox* _b);
void setValueUnits(QComboBox* _units, QSpinBox* _value, dev::u256 _v);
dev::u256 fromValueUnits(QComboBox* _units, QSpinBox* _value);
std::vector<dev::KeyPair> keysAsVector(QList<dev::KeyPair> const& _keys);
@ -67,5 +70,6 @@ public:
virtual dev::Secret retrieveSecret(dev::Address const& _a) const = 0;
virtual dev::eth::KeyManager& keyManager() = 0;
virtual dev::u256 gasPrice() const = 0;
};

181
alethzero/GasPricing.ui

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GasPricing</class>
<widget class="QDialog" name="GasPricing">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>416</width>
<height>286</height>
</rect>
</property>
<property name="windowTitle">
<string>Gas Pricing</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>398</width>
<height>45</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="label5_3">
<property name="text">
<string>&amp;Bid: The minimum grice of gas that is accepting into a block which we mine.</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>bidValue</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="askValue">
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="10" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="close">
<property name="text">
<string>Close</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label5_2">
<property name="text">
<string>&amp;Ask: The minimum grice of gas that is accepting into a block which we mine.</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>askValue</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="askUnits"/>
</item>
<item row="5" column="0">
<widget class="QSpinBox" name="bidValue">
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="bidUnits"/>
</item>
<item row="7" column="0" colspan="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="9" column="0" colspan="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="8" column="0" colspan="2">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>close</sender>
<signal>clicked()</signal>
<receiver>GasPricing</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>387</x>
<y>264</y>
</hint>
<hint type="destinationlabel">
<x>240</x>
<y>262</y>
</hint>
</hints>
</connection>
</connections>
</ui>

12
alethzero/Main.ui

@ -212,12 +212,19 @@
<addaction name="debugDumpState"/>
<addaction name="debugDumpStatePre"/>
</widget>
<widget class="QMenu" name="menu_Config">
<property name="title">
<string>&amp;Config</string>
</property>
<addaction name="gasPrices"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_View"/>
<addaction name="menu_Network"/>
<addaction name="menu_Tools"/>
<addaction name="menuWhispe"/>
<addaction name="menuDebug"/>
<addaction name="menu_Config"/>
<addaction name="menu_Help"/>
<addaction name="menu_Debug"/>
</widget>
@ -1763,6 +1770,11 @@ font-size: 14pt</string>
<string>Re-Encrypt All Keys...</string>
</property>
</action>
<action name="gasPrices">
<property name="text">
<string>&amp;Gas Prices...</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

41
alethzero/MainWin.cpp

@ -76,6 +76,7 @@
#include "ExportState.h"
#include "ui_Main.h"
#include "ui_GetPassword.h"
#include "ui_GasPricing.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
@ -128,7 +129,7 @@ static QString filterOutTerminal(QString _s)
Main::Main(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Main),
m_transact(this, this),
m_transact(nullptr),
m_dappLoader(nullptr),
m_webPage(nullptr)
{
@ -232,6 +233,11 @@ Main::Main(QWidget *parent) :
// inspector->setPage(page);
setBeneficiary(*m_keyManager.accounts().begin());
readSettings();
m_transact = new Transact(this, this);
m_transact->setWindowFlags(Qt::Dialog);
m_transact->setWindowModality(Qt::WindowModal);
#if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts);
#endif
@ -262,6 +268,23 @@ bool Main::confirm() const
return ui->natSpec->isChecked();
}
void Main::on_gasPrices_triggered()
{
QDialog d;
Ui_GasPricing gp;
gp.setupUi(&d);
d.setWindowTitle("Gas Pricing");
setValueUnits(gp.bidUnits, gp.bidValue, static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->bid());
setValueUnits(gp.askUnits, gp.askValue, static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->ask());
if (d.exec() == QDialog::Accepted)
{
static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->setBid(fromValueUnits(gp.bidUnits, gp.bidValue));
static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->setAsk(fromValueUnits(gp.askUnits, gp.askValue));
m_transact->resetGasPrice();
}
}
void Main::on_newIdentity_triggered()
{
KeyPair kp = KeyPair::create();
@ -467,10 +490,8 @@ void Main::load(QString _s)
void Main::on_newTransaction_triggered()
{
m_transact.setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB);
m_transact.setWindowFlags(Qt::Dialog);
m_transact.setWindowModality(Qt::WindowModal);
m_transact.show();
m_transact->setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB);
m_transact->show();
}
void Main::on_loadJS_triggered()
@ -653,6 +674,11 @@ void Main::on_paranoia_triggered()
ethereum()->setParanoia(ui->paranoia->isChecked());
}
dev::u256 Main::gasPrice() const
{
return ethereum()->gasPricer()->bid();
}
void Main::writeSettings()
{
QSettings s("ethereum", "alethzero");
@ -669,6 +695,8 @@ void Main::writeSettings()
s.setValue("identities", b);
}
s.setValue("askPrice", QString::fromStdString(toString(static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->ask())));
s.setValue("bidPrice", QString::fromStdString(toString(static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->bid())));
s.setValue("upnp", ui->upnp->isChecked());
s.setValue("forceAddress", ui->forcePublicIP->text());
s.setValue("forceMining", ui->forceMining->isChecked());
@ -752,6 +780,9 @@ void Main::readSettings(bool _skipGeometry)
}
}
static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->setAsk(u256(s.value("askPrice", "500000000000").toString().toStdString()));
static_cast<TrivialGasPricer*>(ethereum()->gasPricer().get())->setBid(u256(s.value("bidPrice", "500000000000").toString().toStdString()));
ui->upnp->setChecked(s.value("upnp", true).toBool());
ui->forcePublicIP->setText(s.value("forceAddress", "").toString());
ui->dropPeers->setChecked(false);

7
alethzero/MainWin.h

@ -91,7 +91,7 @@ public:
QList<dev::KeyPair> owned() const { return m_myIdentities; }
dev::u256 gasPrice() const { return 10 * dev::eth::szabo; }
dev::u256 gasPrice() const override;
dev::eth::KeyManager& keyManager() override { return m_keyManager; }
bool doConfirm();
@ -194,6 +194,9 @@ private slots:
void on_newIdentity_triggered();
void on_post_clicked();
// Config
void on_gasPrices_triggered();
void refreshWhisper();
void refreshBlockChain();
void addNewId(QString _ids);
@ -279,7 +282,7 @@ private:
static std::string fromRaw(dev::h256 _n, unsigned* _inc = nullptr);
NatspecHandler m_natSpecDB;
Transact m_transact;
Transact* m_transact;
std::unique_ptr<DappHost> m_dappHost;
DappLoader* m_dappLoader;
QWebEnginePage* m_webPage;

13
alethzero/Transact.cpp

@ -58,11 +58,9 @@ Transact::Transact(Context* _c, QWidget* _parent):
{
ui->setupUi(this);
initUnits(ui->gasPriceUnits);
initUnits(ui->valueUnits);
ui->valueUnits->setCurrentIndex(6);
ui->gasPriceUnits->setCurrentIndex(4);
ui->gasPrice->setValue(10);
resetGasPrice();
setValueUnits(ui->valueUnits, ui->value, 0);
on_destination_currentTextChanged(QString());
}
@ -92,6 +90,11 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e
ui->from->setCurrentIndex(0);
}
void Transact::resetGasPrice()
{
setValueUnits(ui->gasPriceUnits, ui->gasPrice, m_context->gasPrice());
}
bool Transact::isCreation() const
{
return ui->destination->currentText().isEmpty() || ui->destination->currentText() == "(Create Contract)";

1
alethzero/Transact.h

@ -41,6 +41,7 @@ public:
explicit Transact(Context* _context, QWidget* _parent = 0);
~Transact();
void resetGasPrice();
void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB);
private slots:

2
alethzero/Transact.ui

@ -159,7 +159,7 @@
<string>@ </string>
</property>
<property name="minimum">
<number>1</number>
<number>0</number>
</property>
<property name="maximum">
<number>430000000</number>

45
eth/main.cpp

@ -124,9 +124,11 @@ void help()
<< " --password <password> Give a password for a private key." << endl
<< endl
<< "Client transacting:" << endl
<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl
/*<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (default: 30.679)." << endl
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl*/
<< " --ask <wei> Set the minimum ask gas price under which no transactions will be mined (default 500000000000)." << endl
<< " --bid <wei> Set the bid gas price for to pay for transactions (default 500000000000)." << endl
<< endl
<< "Client mining:" << endl
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl
@ -299,8 +301,10 @@ int main(int argc, char** argv)
/// Transaction params
TransactionPriority priority = TransactionPriority::Medium;
double etherPrice = 30.679;
double blockFees = 15.0;
// double etherPrice = 30.679;
// double blockFees = 15.0;
u256 askPrice("500000000000");
u256 bidPrice("500000000000");
// javascript console
bool useConsole = false;
@ -464,7 +468,7 @@ int main(int argc, char** argv)
}
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i];
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
/* else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try
{
@ -487,6 +491,30 @@ int main(int argc, char** argv)
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}*/
else if (arg == "--ask" && i + 1 < argc)
{
try
{
askPrice = u256(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}
else if (arg == "--bid" && i + 1 < argc)
{
try
{
bidPrice = u256(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}
else if ((arg == "-P" || arg == "--priority") && i + 1 < argc)
{
@ -730,7 +758,8 @@ int main(int argc, char** argv)
cout << ethCredits();
web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
// std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
std::shared_ptr<eth::TrivialGasPricer> gasPricer = make_shared<eth::TrivialGasPricer>(askPrice, bidPrice);
eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr;
StructuredLogger::starting(clientImplString, dev::Version);
if (c)
@ -829,7 +858,7 @@ int main(int argc, char** argv)
iss >> enable;
c->setForceMining(isTrue(enable));
}
else if (c && cmd == "setblockfees")
/* else if (c && cmd == "setblockfees")
{
iss >> blockFees;
try
@ -884,7 +913,7 @@ int main(int argc, char** argv)
cerr << "Unknown priority: " << m << endl;
}
cout << "Priority: " << (int)priority << "/8" << endl;
}
}*/
else if (cmd == "verbosity")
{
if (iss.peek() != -1)

2
ethminer/farm.json

@ -1,4 +1,6 @@
[
{ "name": "eth_getWork", "params": [], "order": [], "returns": []},
{ "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true}
{ "name": "eth_awaitNewWork", "params": [], "order": [], "returns": []},
{ "name": "eth_progress", "params": [], "order": [], "returns": true}
]

1
libethereum/Client.h

@ -133,6 +133,7 @@ public:
/// Resets the gas pricer to some other object.
void setGasPricer(std::shared_ptr<GasPricer> _gp) { m_gp = _gp; }
std::shared_ptr<GasPricer> gasPricer() const { return m_gp; }
/// Blocks until all pending transactions have been processed.
virtual void flushTransactions() override;

23
libethereum/EthereumHost.cpp

@ -54,7 +54,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
EthereumHost::~EthereumHost()
{
forEachPeer([](EthereumPeer* _p) { _p->abortSync(); });
foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
}
bool EthereumHost::ensureInitialised()
@ -74,7 +74,7 @@ bool EthereumHost::ensureInitialised()
void EthereumHost::reset()
{
forEachPeer([](EthereumPeer* _p) { _p->abortSync(); });
foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
m_man.resetToChain(h256s());
m_hashMan.reset(m_chain.number() + 1);
m_needSyncBlocks = true;
@ -105,7 +105,7 @@ void EthereumHost::doWork()
}
}
forEachPeer([](EthereumPeer* _p) { _p->tick(); });
foreachPeer([](EthereumPeer* _p) { _p->tick(); });
// return netChange;
// TODO: Figure out what to do with netChange.
@ -120,12 +120,13 @@ void EthereumHost::maintainTransactions()
for (auto const& i: ts)
{
bool unsent = !m_transactionsSent.count(i.first);
for (auto const& p: get<1>(randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); })))
auto peers = get<1>(randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }));
for (auto const& p: peers)
peerTransactions[p].push_back(i.first);
}
for (auto const& t: ts)
m_transactionsSent.insert(t.first);
forEachPeerPtr([&](shared_ptr<EthereumPeer> _p)
foreachPeerPtr([&](shared_ptr<EthereumPeer> _p)
{
bytes b;
unsigned n = 0;
@ -148,16 +149,16 @@ void EthereumHost::maintainTransactions()
});
}
void EthereumHost::forEachPeer(std::function<void(EthereumPeer*)> const& _f) const
void EthereumHost::foreachPeer(std::function<void(EthereumPeer*)> const& _f) const
{
forEachPeerPtr([&](std::shared_ptr<EthereumPeer> _p)
foreachPeerPtr([&](std::shared_ptr<EthereumPeer> _p)
{
if (_p)
_f(_p.get());
});
}
void EthereumHost::forEachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const
void EthereumHost::foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const
{
for (auto s: peerSessions())
_f(s.first->cap<EthereumPeer>());
@ -551,7 +552,7 @@ void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
void EthereumHost::continueSync()
{
clog(NetAllDetail) << "Getting help with downloading hashes and blocks";
forEachPeer([&](EthereumPeer* _p)
foreachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking == Asking::Nothing)
continueSync(_p);
@ -564,7 +565,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
bool otherPeerSync = false;
if (m_needSyncHashes && peerShouldGrabChain(_peer))
{
forEachPeer([&](EthereumPeer* _p)
foreachPeer([&](EthereumPeer* _p)
{
if (_p != _peer && _p->m_asking == Asking::Hashes && _p->m_protocolVersion != protocolVersion())
otherPeerSync = true; // Already have a peer downloading hash chain with old protocol, do nothing
@ -630,7 +631,7 @@ bool EthereumHost::isSyncing_UNSAFE() const
/// We need actual peer information here to handle the case when we are the first ever peer on the network to mine.
/// I.e. on a new private network the first node mining has noone to sync with and should start block propogation immediately.
bool syncing = false;
forEachPeer([&](EthereumPeer* _p)
foreachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking != Asking::Nothing)
syncing = true;

4
libethereum/EthereumHost.h

@ -92,8 +92,8 @@ public:
private:
std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
void forEachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void forEachPeer(std::function<void(EthereumPeer*)> const& _f) const;
void foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void foreachPeer(std::function<void(EthereumPeer*)> const& _f) const;
bool isSyncing_UNSAFE() const;
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.

17
libethereum/State.h

@ -84,9 +84,20 @@ public:
class TrivialGasPricer: public GasPricer
{
protected:
u256 ask(State const&) const override { return 10 * szabo; }
u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return 10 * szabo; }
public:
TrivialGasPricer() = default;
TrivialGasPricer(u256 const& _ask, u256 const& _bid): m_ask(_ask), m_bid(_bid) {}
void setAsk(u256 const& _ask) { m_ask = _ask; }
void setBid(u256 const& _bid) { m_bid = _bid; }
u256 ask() const { return m_ask; }
u256 ask(State const&) const override { return m_ask; }
u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return m_bid; }
private:
u256 m_ask = 10 * szabo;
u256 m_bid = 10 * szabo;
};
enum class Permanence

14
libevmasm/Assembly.cpp

@ -22,9 +22,12 @@
#include "Assembly.h"
#include <fstream>
#include <libdevcore/Log.h>
#include <libevmcore/Params.h>
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/BlockDeduplicator.h>
#include <libevmasm/ConstantOptimiser.h>
#include <libevmasm/GasMeter.h>
#include <json/json.h>
using namespace std;
using namespace dev;
@ -302,7 +305,7 @@ inline bool matches(AssemblyItemsConstRef _a, AssemblyItemsConstRef _b)
struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; };
#define copt dev::LogOutputStream<OptimiserChannel, true>()
Assembly& Assembly::optimise(bool _enable)
Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
{
if (!_enable)
return *this;
@ -364,10 +367,17 @@ Assembly& Assembly::optimise(bool _enable)
}
}
total += ConstantOptimisationMethod::optimiseConstants(
_isCreation,
_isCreation ? 1 : _runs,
*this,
m_items
);
copt << total << " optimisations done.";
for (auto& sub: m_subs)
sub.optimise(true);
sub.optimise(true, false, _runs);
return *this;
}

9
libevmasm/Assembly.h

@ -49,6 +49,7 @@ public:
AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); }
AssemblyItem newData(bytes const& _data) { h256 h = (u256)std::hash<std::string>()(asString(_data)); m_data[h] = _data; return AssemblyItem(PushData, h); }
AssemblyItem newSub(Assembly const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
Assembly const& getSub(size_t _sub) const { return m_subs.at(_sub); }
AssemblyItem newPushString(std::string const& _data) { h256 h = (u256)std::hash<std::string>()(_data); m_strings[h] = _data; return AssemblyItem(PushString, h); }
AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
@ -92,7 +93,13 @@ public:
void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; }
bytes assemble() const;
Assembly& optimise(bool _enable);
bytes const& data(h256 const& _i) const { return m_data[_i]; }
/// Modify (if @a _enable is set) and return the current assembly such that creation and
/// execution gas usage is optimised. @a _isCreation should be true for the top-level assembly.
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200);
Json::Value stream(
std::ostream& _out,
std::string const& _prefix = "",

225
libevmasm/ConstantOptimiser.cpp

@ -0,0 +1,225 @@
/*
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 ConstantOptimiser.cpp
* @author Christian <c@ethdev.com>
* @date 2015
*/
#include "libevmasm/ConstantOptimiser.h"
#include <libevmasm/Assembly.h>
#include <libevmasm/GasMeter.h>
#include <libevmcore/Params.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
unsigned ConstantOptimisationMethod::optimiseConstants(
bool _isCreation,
size_t _runs,
Assembly& _assembly,
AssemblyItems& _items
)
{
unsigned optimisations = 0;
map<AssemblyItem, size_t> pushes;
for (AssemblyItem const& item: _items)
if (item.type() == Push)
pushes[item]++;
for (auto it: pushes)
{
AssemblyItem const& item = it.first;
if (item.data() < 0x100)
continue;
Params params;
params.multiplicity = it.second;
params.isCreation = _isCreation;
params.runs = _runs;
LiteralMethod lit(params, item.data());
bigint literalGas = lit.gasNeeded();
CodeCopyMethod copy(params, item.data());
bigint copyGas = copy.gasNeeded();
ComputeMethod compute(params, item.data());
bigint computeGas = compute.gasNeeded();
if (copyGas < literalGas && copyGas < computeGas)
{
copy.execute(_assembly, _items);
optimisations++;
}
else if (computeGas < literalGas && computeGas < copyGas)
{
compute.execute(_assembly, _items);
optimisations++;
}
}
return optimisations;
}
bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
{
bigint gas = 0;
for (AssemblyItem const& item: _items)
if (item.type() == Push)
gas += GasMeter::runGas(Instruction::PUSH1);
else if (item.type() == Operation)
gas += GasMeter::runGas(item.instruction());
return gas;
}
bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
{
if (m_params.isCreation)
{
bigint gas;
for (auto b: _data)
gas += b ? c_txDataNonZeroGas : c_txDataZeroGas;
return gas;
}
else
return c_createDataGas * dataSize();
}
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
{
size_t size = 0;
for (AssemblyItem const& item: _items)
size += item.bytesRequired(3); // assume 3 byte addresses
return size;
}
void ConstantOptimisationMethod::replaceConstants(
AssemblyItems& _items,
AssemblyItems const& _replacement
) const
{
assertThrow(_items.size() > 0, OptimizerException, "");
for (size_t i = 0; i < _items.size(); ++i)
{
if (_items.at(i) != AssemblyItem(m_value))
continue;
_items[i] = _replacement[0];
_items.insert(_items.begin() + i + 1, _replacement.begin() + 1, _replacement.end());
i += _replacement.size() - 1;
}
}
bigint LiteralMethod::gasNeeded()
{
return combineGas(
simpleRunGas({Instruction::PUSH1}),
// PUSHX plus data
(m_params.isCreation ? c_txDataNonZeroGas : c_createDataGas) + dataGas(),
0
);
}
CodeCopyMethod::CodeCopyMethod(Params const& _params, u256 const& _value):
ConstantOptimisationMethod(_params, _value)
{
m_copyRoutine = AssemblyItems{
u256(0),
Instruction::DUP1,
Instruction::MLOAD, // back up memory
u256(32),
AssemblyItem(PushData, u256(1) << 16), // has to be replaced
Instruction::DUP4,
Instruction::CODECOPY,
Instruction::DUP2,
Instruction::MLOAD,
Instruction::SWAP2,
Instruction::MSTORE
};
}
bigint CodeCopyMethod::gasNeeded()
{
return combineGas(
// Run gas: we ignore memory increase costs
simpleRunGas(m_copyRoutine) + c_copyGas,
// Data gas for copy routines: Some bytes are zero, but we ignore them.
bytesRequired(m_copyRoutine) * (m_params.isCreation ? c_txDataNonZeroGas : c_createDataGas),
// Data gas for data itself
dataGas(toBigEndian(m_value))
);
}
void CodeCopyMethod::execute(Assembly& _assembly, AssemblyItems& _items)
{
bytes data = toBigEndian(m_value);
m_copyRoutine[4] = _assembly.newData(data);
replaceConstants(_items, m_copyRoutine);
}
AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
{
if (_value < 0x10000)
// Very small value, not worth computing
return AssemblyItems{_value};
else if (dev::bytesRequired(~_value) < dev::bytesRequired(_value))
// Negated is shorter to represent
return findRepresentation(~_value) + AssemblyItems{Instruction::NOT};
else
{
// Decompose value into a * 2**k + b where abs(b) << 2**k
// Is not always better, try literal and decomposition method.
AssemblyItems routine{u256(_value)};
bigint bestGas = gasNeeded(routine);
for (unsigned bits = 255; bits > 8; --bits)
{
unsigned gapDetector = unsigned(_value >> (bits - 8)) & 0x1ff;
if (gapDetector != 0xff && gapDetector != 0x100)
continue;
u256 powerOfTwo = u256(1) << bits;
u256 upperPart = _value >> bits;
bigint lowerPart = _value & (powerOfTwo - 1);
if (abs(powerOfTwo - lowerPart) < lowerPart)
lowerPart = lowerPart - powerOfTwo; // make it negative
if (abs(lowerPart) >= (powerOfTwo >> 8))
continue;
AssemblyItems newRoutine;
if (lowerPart != 0)
newRoutine += findRepresentation(u256(abs(lowerPart)));
newRoutine += AssemblyItems{u256(bits), u256(2), Instruction::EXP};
if (upperPart != 1 && upperPart != 0)
newRoutine += findRepresentation(upperPart) + AssemblyItems{Instruction::MUL};
if (lowerPart > 0)
newRoutine += AssemblyItems{Instruction::ADD};
else if (lowerPart < 0)
newRoutine.push_back(Instruction::SUB);
bigint newGas = gasNeeded(newRoutine);
if (newGas < bestGas)
{
bestGas = move(newGas);
routine = move(newRoutine);
}
}
return routine;
}
}
bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine)
{
size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP);
return combineGas(
simpleRunGas(_routine) + numExps * (c_expGas + c_expByteGas),
// Data gas for routine: Some bytes are zero, but we ignore them.
bytesRequired(_routine) * (m_params.isCreation ? c_txDataNonZeroGas : c_createDataGas),
0
);
}

147
libevmasm/ConstantOptimiser.h

@ -0,0 +1,147 @@
/*
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 ConstantOptimiser.cpp
* @author Christian <c@ethdev.com>
* @date 2015
*/
#pragma once
#include <vector>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
namespace dev
{
namespace eth
{
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
class Assembly;
/**
* Abstract base class for one way to change how constants are represented in the code.
*/
class ConstantOptimisationMethod
{
public:
/// Tries to optimised how constants are represented in the source code and modifies
/// @a _assembly and its @a _items.
/// @returns zero if no optimisations could be performed.
static unsigned optimiseConstants(
bool _isCreation,
size_t _runs,
Assembly& _assembly,
AssemblyItems& _items
);
struct Params
{
bool isCreation; ///< Whether this is called during contract creation or runtime.
size_t runs; ///< Estimated number of calls per opcode oven the lifetime of the contract.
size_t multiplicity; ///< Number of times the constant appears in the code.
};
explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):
m_params(_params), m_value(_value) {}
virtual bigint gasNeeded() = 0;
virtual void execute(Assembly& _assembly, AssemblyItems& _items) = 0;
protected:
size_t dataSize() const { return std::max<size_t>(1, dev::bytesRequired(m_value)); }
/// @returns the run gas for the given items ignoring special gas costs
static bigint simpleRunGas(AssemblyItems const& _items);
/// @returns the gas needed to store the given data literally
bigint dataGas(bytes const& _data) const;
/// @returns the gas needed to store the value literally
bigint dataGas() const { return dataGas(toCompactBigEndian(m_value, 1)); }
static size_t bytesRequired(AssemblyItems const& _items);
/// @returns the combined estimated gas usage taking @a m_params into account.
bigint combineGas(
bigint const& _runGas,
bigint const& _repeatedDataGas,
bigint const& _uniqueDataGas
)
{
// _runGas is not multiplied by _multiplicity because the runs are "per opcode"
return m_params.runs * _runGas + m_params.multiplicity * _repeatedDataGas + _uniqueDataGas;
}
/// Replaces the constant by the code given in @a _replacement.
void replaceConstants(AssemblyItems& _items, AssemblyItems const& _replacement) const;
Params m_params;
u256 const& m_value;
};
/**
* Optimisation method that pushes the constant to the stack literally. This is the default method,
* i.e. executing it does not alter the Assembly.
*/
class LiteralMethod: public ConstantOptimisationMethod
{
public:
explicit LiteralMethod(Params const& _params, u256 const& _value):
ConstantOptimisationMethod(_params, _value) {}
virtual bigint gasNeeded() override;
virtual void execute(Assembly&, AssemblyItems&) override {}
};
/**
* Method that stores the data in the .data section of the code and copies it to the stack.
*/
class CodeCopyMethod: public ConstantOptimisationMethod
{
public:
explicit CodeCopyMethod(Params const& _params, u256 const& _value);
virtual bigint gasNeeded() override;
virtual void execute(Assembly& _assembly, AssemblyItems& _items) override;
protected:
AssemblyItems m_copyRoutine;
};
/**
* Method that tries to compute the constant.
*/
class ComputeMethod: public ConstantOptimisationMethod
{
public:
explicit ComputeMethod(Params const& _params, u256 const& _value):
ConstantOptimisationMethod(_params, _value)
{
m_routine = findRepresentation(m_value);
}
virtual bigint gasNeeded() override { return gasNeeded(m_routine); }
virtual void execute(Assembly&, AssemblyItems& _items) override
{
replaceConstants(_items, m_routine);
}
protected:
/// Tries to recursively find a way to compute @a _value.
AssemblyItems findRepresentation(u256 const& _value);
bigint gasNeeded(AssemblyItems const& _routine);
AssemblyItems m_routine;
};
}
}

7
libevmasm/GasMeter.cpp

@ -201,13 +201,14 @@ GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosS
}));
}
GasMeter::GasConsumption GasMeter::runGas(Instruction _instruction)
u256 GasMeter::runGas(Instruction _instruction)
{
if (_instruction == Instruction::JUMPDEST)
return GasConsumption(1);
return 1;
int tier = instructionInfo(_instruction).gasPriceTier;
return tier == InvalidTier ? GasConsumption::infinite() : c_tierStepGas[tier];
assertThrow(tier != InvalidTier, OptimizerException, "Invalid gas tier.");
return c_tierStepGas[tier];
}

4
libevmasm/GasMeter.h

@ -66,6 +66,8 @@ public:
u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }
static u256 runGas(Instruction _instruction);
private:
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.
GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value);
@ -76,8 +78,6 @@ private:
/// given as values on the stack at the given relative positions.
GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize);
static GasConsumption runGas(Instruction _instruction);
std::shared_ptr<KnownState> m_state;
/// Largest point where memory was accessed since the creation of this object.
u256 m_largestMemoryAccess;

4
libevmcore/Instruction.cpp

@ -300,7 +300,7 @@ void dev::eth::eachInstruction(
function<void(Instruction,u256 const&)> const& _onInstruction
)
{
for (auto it = _mem.begin(); it != _mem.end(); ++it)
for (auto it = _mem.begin(); it < _mem.end(); ++it)
{
Instruction instr = Instruction(*it);
size_t additional = 0;
@ -310,7 +310,7 @@ void dev::eth::eachInstruction(
for (size_t i = 0; i < additional; ++i)
{
data <<= 8;
if (it != _mem.end() && ++it != _mem.end())
if (++it < _mem.end())
data |= *it;
}
_onInstruction(instr, data);

13
libp2p/RLPxHandshake.cpp

@ -265,8 +265,17 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
}
clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session.";
RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall);
m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint());
try
{
RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall);
m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint());
}
catch (std::exception const& _e)
{
clog(NetWarn) << "Handshake causing an exception:" << _e.what();
m_nextState = Error;
transition();
}
}
});
}

6
libp2p/Session.cpp

@ -447,8 +447,12 @@ void Session::doRead()
clog(NetWarn) << "Error reading: " << ec.message();
drop(TCPError);
}
else if (ec && length == 0)
else if (ec && length < tlen)
{
clog(NetWarn) << "Error reading - Abrupt peer disconnect: " << ec.message();
drop(TCPError);
return;
}
else
{
if (!m_io->authAndDecryptFrame(bytesRef(m_data.data(), tlen)))

30
libsolidity/AST.cpp

@ -528,6 +528,17 @@ void VariableDeclaration::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables."));
}
bool VariableDeclaration::isFunctionParameter() const
{
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
if (!function)
return false;
for (auto const& variable: function->getParameters() + function->getReturnParameters())
if (variable.get() == this)
return true;
return false;
}
bool VariableDeclaration::isExternalFunctionParameter() const
{
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
@ -686,9 +697,14 @@ void Expression::expectType(Type const& _expectedType)
checkTypeRequirements(nullptr);
Type const& type = *getType();
if (!type.isImplicitlyConvertibleTo(_expectedType))
BOOST_THROW_EXCEPTION(createTypeError("Type " + type.toString() +
" not implicitly convertible to expected type "
+ _expectedType.toString() + "."));
BOOST_THROW_EXCEPTION(createTypeError(
"Type " +
type.toString() +
" is not implicitly convertible to expected type " +
_expectedType.toString() +
"."
)
);
}
void Expression::requireLValue()
@ -874,7 +890,7 @@ void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
{
auto const& arrayType(dynamic_cast<ArrayType const&>(type));
m_isLValue = (*m_memberName == "length" &&
arrayType.getLocation() != ArrayType::Location::CallData && arrayType.isDynamicallySized());
arrayType.location() != ReferenceType::Location::CallData && arrayType.isDynamicallySized());
}
else
m_isLValue = false;
@ -897,7 +913,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
m_type = make_shared<FixedBytesType>(1);
else
m_type = type.getBaseType();
m_isLValue = type.getLocation() != ArrayType::Location::CallData;
m_isLValue = type.location() != ReferenceType::Location::CallData;
break;
}
case Type::Category::Mapping:
@ -914,7 +930,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
{
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
if (!m_index)
m_type = make_shared<TypeType>(make_shared<ArrayType>(ArrayType::Location::Memory, type.getActualType()));
m_type = make_shared<TypeType>(make_shared<ArrayType>(ReferenceType::Location::Memory, type.getActualType()));
else
{
m_index->checkTypeRequirements(nullptr);
@ -922,7 +938,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
if (!length)
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
m_type = make_shared<TypeType>(make_shared<ArrayType>(
ArrayType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
ReferenceType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
}
break;
}

27
libsolidity/AST.h

@ -474,22 +474,26 @@ private:
class VariableDeclaration: public Declaration
{
public:
enum Location { Default, Storage, Memory };
VariableDeclaration(
SourceLocation const& _location,
SourceLocation const& _sourceLocation,
ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name,
ASTPointer<Expression> _value,
Visibility _visibility,
bool _isStateVar = false,
bool _isIndexed = false,
bool _isConstant = false
bool _isConstant = false,
Location _referenceLocation = Location::Default
):
Declaration(_location, _name, _visibility),
Declaration(_sourceLocation, _name, _visibility),
m_typeName(_type),
m_value(_value),
m_isStateVariable(_isStateVar),
m_isIndexed(_isIndexed),
m_isConstant(_isConstant){}
m_isConstant(_isConstant),
m_location(_referenceLocation) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@ -507,20 +511,25 @@ public:
void checkTypeRequirements();
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
/// @returns true if this variable is a parameter or return parameter of a function.
bool isFunctionParameter() const;
/// @returns true if this variable is a parameter (not return parameter) of an external function.
bool isExternalFunctionParameter() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; }
Location referenceLocation() const { return m_location; }
protected:
Visibility getDefaultVisibility() const override { return Visibility::Internal; }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
ASTPointer<Expression> m_value; ///< the assigned value, can be missing
bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
bool m_isConstant; ///< Whether the variable is a compile-time constant.
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
ASTPointer<Expression> m_value; ///< the assigned value, can be missing
bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
bool m_isConstant; ///< Whether the variable is a compile-time constant.
Location m_location; ///< Location of the variable if it is of reference type.
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};

50
libsolidity/ArrayUtils.cpp

@ -38,10 +38,10 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// need to leave "target_ref target_byte_off" on the stack at the end
// stack layout: [source_ref] [source_byte_off] [source length] target_ref target_byte_off (top)
solAssert(_targetType.getLocation() == ArrayType::Location::Storage, "");
solAssert(_targetType.location() == ReferenceType::Location::Storage, "");
solAssert(
_sourceType.getLocation() == ArrayType::Location::CallData ||
_sourceType.getLocation() == ArrayType::Location::Storage,
_sourceType.location() == ReferenceType::Location::CallData ||
_sourceType.location() == ReferenceType::Location::Storage,
"Given array location not implemented."
);
@ -51,7 +51,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// TODO unroll loop for small sizes
bool sourceIsStorage = _sourceType.getLocation() == ArrayType::Location::Storage;
bool sourceIsStorage = _sourceType.location() == ReferenceType::Location::Storage;
bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16;
bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16;
@ -69,7 +69,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
m_context << eth::Instruction::POP;
// stack: target_ref source_ref [source_length]
// retrieve source length
if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized())
if (_sourceType.location() != ReferenceType::Location::CallData || !_sourceType.isDynamicallySized())
retrieveLength(_sourceType); // otherwise, length is already there
// stack: target_ref source_ref source_length
m_context << eth::Instruction::DUP3;
@ -82,7 +82,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
if (sourceBaseType->getCategory() == Type::Category::Mapping)
{
solAssert(targetBaseType->getCategory() == Type::Category::Mapping, "");
solAssert(_sourceType.getLocation() == ArrayType::Location::Storage, "");
solAssert(_sourceType.location() == ReferenceType::Location::Storage, "");
// nothing to copy
m_context
<< eth::Instruction::POP << eth::Instruction::POP
@ -106,7 +106,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized())
if (_sourceType.location() == ReferenceType::Location::Storage && _sourceType.isDynamicallySized())
CompilerUtils(m_context).computeHashStatic();
// stack: target_ref target_data_end source_length target_data_pos source_data_pos
m_context << eth::Instruction::SWAP2;
@ -155,7 +155,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// checking is easier.
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
m_context << eth::dupInstruction(3 + byteOffsetSize);
if (_sourceType.getLocation() == ArrayType::Location::Storage)
if (_sourceType.location() == ReferenceType::Location::Storage)
{
if (haveByteOffsetSource)
m_context << eth::Instruction::DUP2;
@ -228,7 +228,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
void ArrayUtils::clearArray(ArrayType const& _type) const
{
unsigned stackHeightStart = m_context.getStackHeight();
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
solAssert(_type.location() == ReferenceType::Location::Storage, "");
if (_type.getBaseType()->getStorageBytes() < 32)
{
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
@ -283,7 +283,7 @@ void ArrayUtils::clearArray(ArrayType const& _type) const
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
{
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.isDynamicallySized(), "");
unsigned stackHeightStart = m_context.getStackHeight();
@ -311,7 +311,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
{
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.isDynamicallySized(), "");
if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32)
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
@ -396,7 +396,7 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const
{
if (_arrayType.getLocation() == ArrayType::Location::Storage)
if (_arrayType.location() == ReferenceType::Location::Storage)
{
if (_arrayType.getBaseType()->getStorageSize() <= 1)
{
@ -432,15 +432,15 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
else
{
m_context << eth::Instruction::DUP1;
switch (_arrayType.getLocation())
switch (_arrayType.location())
{
case ArrayType::Location::CallData:
case ReferenceType::Location::CallData:
// length is stored on the stack
break;
case ArrayType::Location::Memory:
case ReferenceType::Location::Memory:
m_context << eth::Instruction::MLOAD;
break;
case ArrayType::Location::Storage:
case ReferenceType::Location::Storage:
m_context << eth::Instruction::SLOAD;
break;
}
@ -449,16 +449,16 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
{
ArrayType::Location location = _arrayType.getLocation();
ReferenceType::Location location = _arrayType.location();
eth::Instruction load =
location == ArrayType::Location::Storage ? eth::Instruction::SLOAD :
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
location == ReferenceType::Location::Storage ? eth::Instruction::SLOAD :
location == ReferenceType::Location::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD;
// retrieve length
if (!_arrayType.isDynamicallySized())
m_context << _arrayType.getLength();
else if (location == ArrayType::Location::CallData)
else if (location == ReferenceType::Location::CallData)
// length is stored on the stack
m_context << eth::Instruction::SWAP1;
else
@ -473,15 +473,15 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::SWAP1;
if (_arrayType.isDynamicallySized())
{
if (location == ArrayType::Location::Storage)
if (location == ReferenceType::Location::Storage)
CompilerUtils(m_context).computeHashStatic();
else if (location == ArrayType::Location::Memory)
else if (location == ReferenceType::Location::Memory)
m_context << u256(32) << eth::Instruction::ADD;
}
// stack: <index> <data_ref>
switch (location)
{
case ArrayType::Location::CallData:
case ReferenceType::Location::CallData:
if (!_arrayType.isByteArray())
m_context
<< eth::Instruction::SWAP1
@ -496,7 +496,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
false
);
break;
case ArrayType::Location::Storage:
case ReferenceType::Location::Storage:
m_context << eth::Instruction::SWAP1;
if (_arrayType.getBaseType()->getStorageBytes() <= 16)
{
@ -524,7 +524,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::ADD << u256(0);
}
break;
case ArrayType::Location::Memory:
case ReferenceType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
}

24
libsolidity/Compiler.cpp

@ -69,6 +69,8 @@ void Compiler::compileContract(ContractDefinition const& _contract,
swap(m_context, m_runtimeContext);
initializeContext(_contract, _contracts);
packIntoContractCreator(_contract, m_runtimeContext);
if (m_optimize)
m_context.optimise(m_optimizeRuns);
}
eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _function) const
@ -120,9 +122,11 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
else if (auto c = m_context.getNextConstructor(_contract))
appendBaseConstructor(*c);
eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.getAssembly());
solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
m_runtimeSub = size_t(runtimeSub.data());
// stack contains sub size
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN;
// note that we have to include the functions again because of absolute jump labels
@ -174,6 +178,16 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
FunctionDefinition const* fallback = _contract.getFallbackFunction();
eth::AssemblyItem notFound = m_context.newTag();
// shortcut messages without data if we have many functions in order to be able to receive
// ether with constant gas
if (interfaceFunctions.size() > 5 || fallback)
{
m_context << eth::Instruction::CALLDATASIZE << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(notFound);
}
// retrieve the function signature hash from the calldata
if (!interfaceFunctions.empty())
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
@ -185,7 +199,10 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
}
if (FunctionDefinition const* fallback = _contract.getFallbackFunction())
m_context.appendJumpTo(notFound);
m_context << notFound;
if (fallback)
{
eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this);
@ -194,6 +211,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
}
else
m_context << eth::Instruction::STOP; // function not found
for (auto const& it: interfaceFunctions)
{
FunctionTypePointer const& functionType = it.second;

17
libsolidity/Compiler.h

@ -34,13 +34,18 @@ namespace solidity {
class Compiler: private ASTConstVisitor
{
public:
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(),
m_returnTag(m_context.newTag()) {}
explicit Compiler(bool _optimize = false, unsigned _runs = 200):
m_optimize(_optimize),
m_optimizeRuns(_runs),
m_context(),
m_returnTag(m_context.newTag())
{
}
void compileContract(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(); }
bytes getRuntimeBytecode() { return m_context.getAssembledRuntimeBytecode(m_runtimeSub); }
/// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFromat shows whether the out should be in Json format
Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
@ -50,7 +55,7 @@ public:
/// @returns Assembly items of the normal compiler context
eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); }
/// @returns Assembly items of the runtime compiler context
eth::AssemblyItems const& getRuntimeAssemblyItems() const { return m_runtimeContext.getAssembly().getItems(); }
eth::AssemblyItems const& getRuntimeAssemblyItems() const { return m_context.getAssembly().getSub(m_runtimeSub).getItems(); }
/// @returns the entry label of the given function. Might return an AssemblyItem of type
/// UndefinedItem if it does not exist yet.
@ -93,7 +98,9 @@ private:
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
bool const m_optimize;
unsigned const m_optimizeRuns;
CompilerContext m_context;
size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly
CompilerContext m_runtimeContext;
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

5
libsolidity/CompilerContext.h

@ -126,6 +126,8 @@ public:
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
void optimise(unsigned _runs = 200) { m_asm.optimise(true, true, _runs); }
eth::Assembly const& getAssembly() const { return m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFormat shows whether the out should be in Json format
@ -134,7 +136,8 @@ public:
return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
}
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }
bytes getAssembledBytecode() { return m_asm.assemble(); }
bytes getAssembledRuntimeBytecode(size_t _subIndex) { m_asm.assemble(); return m_asm.data(u256(_subIndex)); }
/**
* Helper class to pop the visited nodes stack when a scope closes

6
libsolidity/CompilerStack.cpp

@ -145,7 +145,7 @@ vector<string> CompilerStack::getContractNames() const
}
void CompilerStack::compile(bool _optimize)
void CompilerStack::compile(bool _optimize, unsigned _runs)
{
if (!m_parseSuccessful)
parse();
@ -157,9 +157,9 @@ void CompilerStack::compile(bool _optimize)
{
if (!contract->isFullyImplemented())
continue;
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
compiler->compileContract(*contract, contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()];
Contract& compiledContract = m_contracts.at(contract->getName());
compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler);

2
libsolidity/CompilerStack.h

@ -90,7 +90,7 @@ public:
std::string defaultContractName() const;
/// Compiles the source units that were previously added and parsed.
void compile(bool _optimize = false);
void compile(bool _optimize = false, unsigned _runs = 200);
/// Parses and compiles the given source code.
/// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false);

4
libsolidity/CompilerUtils.cpp

@ -81,7 +81,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
auto const& type = dynamic_cast<ArrayType const&>(_type);
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
if (type.getLocation() == ArrayType::Location::CallData)
if (type.location() == ReferenceType::Location::CallData)
{
// stack: target source_offset source_len
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
@ -92,7 +92,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
}
else
{
solAssert(type.getLocation() == ArrayType::Location::Storage, "Memory arrays not yet implemented.");
solAssert(type.location() == ReferenceType::Location::Storage, "Memory arrays not yet implemented.");
m_context << eth::Instruction::POP; // remove offset, arrays always start new slot
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes

61
libsolidity/ExpressionCompiler.cpp

@ -23,6 +23,7 @@
#include <utility>
#include <numeric>
#include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Params.h>
#include <libdevcore/Common.h>
#include <libdevcore/SHA3.h>
#include <libsolidity/AST.h>
@ -497,6 +498,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
// stack layout: contract_address function_id [gas] [value]
_functionCall.getExpression().accept(*this);
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), IntegerType(256), true);
// Note that function is not the original function, but the ".gas" function.
@ -519,7 +521,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break;
case Location::Send:
_functionCall.getExpression().accept(*this);
m_context << u256(0); // 0 gas, we do not want to execute code
m_context << u256(0); // do not send gas (there still is the stipend)
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(),
*function.getParameterTypes().front(), true);
@ -769,12 +771,12 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << type.getLength();
}
else
switch (type.getLocation())
switch (type.location())
{
case ArrayType::Location::CallData:
case ReferenceType::Location::CallData:
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
break;
case ArrayType::Location::Storage:
case ReferenceType::Location::Storage:
setLValue<StorageArrayLength>(_memberAccess, type);
break;
default:
@ -815,13 +817,13 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
// remove storage byte offset
if (arrayType.getLocation() == ArrayType::Location::Storage)
if (arrayType.location() == ReferenceType::Location::Storage)
m_context << eth::Instruction::POP;
_indexAccess.getIndexExpression()->accept(*this);
// stack layout: <base_ref> [<length>] <index>
ArrayUtils(m_context).accessIndex(arrayType);
if (arrayType.getLocation() == ArrayType::Location::Storage)
if (arrayType.location() == ReferenceType::Location::Storage)
{
if (arrayType.isByteArray())
{
@ -1056,10 +1058,15 @@ void ExpressionCompiler::appendExternalFunctionCall(
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
bool returnSuccessCondition =
_functionType.getLocation() == FunctionType::Location::Bare ||
_functionType.getLocation() == FunctionType::Location::BareCallCode;
//@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
if (returnSuccessCondition)
retSize = 0; // return value actually is success condition
m_context << u256(retSize) << u256(0);
if (_functionType.isBareCall())
@ -1098,7 +1105,10 @@ void ExpressionCompiler::appendExternalFunctionCall(
else
// send all gas except the amount needed to execute "SUB" and "CALL"
// @todo this retains too much gas for now, needs to be fine-tuned.
m_context << u256(50 + (_functionType.valueSet() ? 9000 : 0) + 25000) << eth::Instruction::GAS << eth::Instruction::SUB;
m_context <<
u256(eth::c_callGas + 10 + (_functionType.valueSet() ? eth::c_callValueTransferGas : 0) + eth::c_callNewAccountGas) <<
eth::Instruction::GAS <<
eth::Instruction::SUB;
if (
_functionType.getLocation() == FunctionType::Location::CallCode ||
_functionType.getLocation() == FunctionType::Location::BareCallCode
@ -1107,19 +1117,28 @@ void ExpressionCompiler::appendExternalFunctionCall(
else
m_context << eth::Instruction::CALL;
//Propagate error condition (if CALL pushes 0 on stack).
m_context << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(m_context.errorTag());
unsigned remainsSize =
1 + // contract address
_functionType.valueSet() +
_functionType.gasSet() +
!_functionType.isBareCall();
if (_functionType.valueSet())
m_context << eth::Instruction::POP;
if (_functionType.gasSet())
m_context << eth::Instruction::POP;
if (!_functionType.isBareCall())
m_context << eth::Instruction::POP;
m_context << eth::Instruction::POP; // pop contract address
if (returnSuccessCondition)
m_context << eth::swapInstruction(remainsSize);
else
{
//Propagate error condition (if CALL pushes 0 on stack).
m_context << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(m_context.errorTag());
}
if (_functionType.getLocation() == FunctionType::Location::RIPEMD160)
CompilerUtils(m_context).popStackSlots(remainsSize);
if (returnSuccessCondition)
{
// already there
}
else if (_functionType.getLocation() == FunctionType::Location::RIPEMD160)
{
// fix: built-in contract returns right-aligned data
CompilerUtils(m_context).loadFromMemory(0, IntegerType(160), false, true);
@ -1165,13 +1184,13 @@ void ExpressionCompiler::appendArgumentsCopyToMemory(
auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType());
// move memory reference to top of stack
CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack());
if (arrayType.getLocation() == ArrayType::Location::CallData)
if (arrayType.location() == ReferenceType::Location::CallData)
m_context << eth::Instruction::DUP2; // length is on stack
else if (arrayType.getLocation() == ArrayType::Location::Storage)
else if (arrayType.location() == ReferenceType::Location::Storage)
m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
else
{
solAssert(arrayType.getLocation() == ArrayType::Location::Memory, "");
solAssert(arrayType.location() == ReferenceType::Location::Memory, "");
m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
}
appendTypeMoveToMemory(IntegerType(256), true);

47
libsolidity/NameAndTypeResolver.cpp

@ -424,10 +424,49 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
if (_variable.getTypeName())
{
TypePointer type = _variable.getTypeName()->toType();
// All array parameter types should point to call data
if (_variable.isExternalFunctionParameter())
if (auto const* arrayType = dynamic_cast<ArrayType const*>(type.get()))
type = arrayType->copyForLocation(ArrayType::Location::CallData);
using Location = VariableDeclaration::Location;
Location loc = _variable.referenceLocation();
// References are forced to calldata for external function parameters (not return)
// and memory for parameters (also return) of publicly visible functions.
// They default to memory for function parameters and storage for local variables.
if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
{
if (_variable.isExternalFunctionParameter())
{
// force location of external function parameters (not return) to calldata
if (loc != Location::Default)
BOOST_THROW_EXCEPTION(_variable.createTypeError(
"Location has to be calldata for external functions "
"(remove the \"memory\" or \"storage\" keyword)."
));
type = ref->copyForLocation(ReferenceType::Location::CallData);
}
else if (_variable.isFunctionParameter() && _variable.getScope()->isPublic())
{
// force locations of public or external function (return) parameters to memory
if (loc == VariableDeclaration::Location::Storage)
BOOST_THROW_EXCEPTION(_variable.createTypeError(
"Location has to be memory for publicly visible functions "
"(remove the \"storage\" keyword)."
));
type = ref->copyForLocation(ReferenceType::Location::Memory);
}
else
{
if (loc == Location::Default)
loc = _variable.isFunctionParameter() ? Location::Memory : Location::Storage;
type = ref->copyForLocation(
loc == Location::Memory ?
ReferenceType::Location::Memory :
ReferenceType::Location::Storage
);
}
}
else if (loc != Location::Default && !ref)
BOOST_THROW_EXCEPTION(_variable.createTypeError(
"Storage location can only be given for array or struct types."
));
_variable.setType(type);
if (!_variable.getType())

105
libsolidity/Parser.cpp

@ -224,7 +224,9 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
name = make_shared<ASTString>(); // anonymous function
else
name = expectIdentifierToken();
ASTPointer<ParameterList> parameters(parseParameterList());
VarDeclParserOptions options;
options.allowLocationSpecifier = true;
ASTPointer<ParameterList> parameters(parseParameterList(options));
bool isDeclaredConst = false;
Declaration::Visibility visibility(Declaration::Visibility::Default);
vector<ASTPointer<ModifierInvocation>> modifiers;
@ -252,7 +254,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
{
bool const permitEmptyParameterList = false;
m_scanner->next();
returnParameters = parseParameterList(permitEmptyParameterList);
returnParameters = parseParameterList(options, permitEmptyParameterList);
}
else
returnParameters = createEmptyParameterList();
@ -319,7 +321,9 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
}
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
VarDeclParserOptions const& _options, ASTPointer<TypeName> const& _lookAheadArrayType)
VarDeclParserOptions const& _options,
ASTPointer<TypeName> const& _lookAheadArrayType
)
{
ASTNodeFactory nodeFactory = _lookAheadArrayType ?
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
@ -334,20 +338,41 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
}
bool isIndexed = false;
bool isDeclaredConst = false;
ASTPointer<ASTString> identifier;
Token::Value token = m_scanner->getCurrentToken();
Declaration::Visibility visibility(Declaration::Visibility::Default);
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
visibility = parseVisibilitySpecifier(token);
if (_options.allowIndexed && token == Token::Indexed)
{
isIndexed = true;
m_scanner->next();
}
if (token == Token::Const)
VariableDeclaration::Location location = VariableDeclaration::Location::Default;
ASTPointer<ASTString> identifier;
while (true)
{
isDeclaredConst = true;
m_scanner->next();
Token::Value token = m_scanner->getCurrentToken();
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
{
if (visibility != Declaration::Visibility::Default)
BOOST_THROW_EXCEPTION(createParserError("Visibility already specified."));
visibility = parseVisibilitySpecifier(token);
}
else
{
if (_options.allowIndexed && token == Token::Indexed)
isIndexed = true;
else if (token == Token::Const)
isDeclaredConst = true;
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
{
if (location != VariableDeclaration::Location::Default)
BOOST_THROW_EXCEPTION(createParserError("Location already specified."));
if (!type)
BOOST_THROW_EXCEPTION(createParserError("Location specifier needs explicit type name."));
location = (
token == Token::Memory ?
VariableDeclaration::Location::Memory :
VariableDeclaration::Location::Storage
);
}
else
break;
m_scanner->next();
}
}
nodeFactory.markEndPosition();
@ -369,9 +394,16 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
nodeFactory.setEndPositionFromNode(value);
}
}
return nodeFactory.createNode<VariableDeclaration>(type, identifier, value,
visibility, _options.isStateVariable,
isIndexed, isDeclaredConst);
return nodeFactory.createNode<VariableDeclaration>(
type,
identifier,
value,
visibility,
_options.isStateVariable,
isIndexed,
isDeclaredConst,
location
);
}
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
@ -388,7 +420,12 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LParen)
parameters = parseParameterList();
{
VarDeclParserOptions options;
options.allowIndexed = true;
options.allowLocationSpecifier = true;
parameters = parseParameterList(options);
}
else
parameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock();
@ -407,7 +444,11 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LParen)
parameters = parseParameterList(true, true);
{
VarDeclParserOptions options;
options.allowIndexed = true;
parameters = parseParameterList(options);
}
else
parameters = createEmptyParameterList();
bool anonymous = false;
@ -505,12 +546,14 @@ ASTPointer<Mapping> Parser::parseMapping()
return nodeFactory.createNode<Mapping>(keyType, valueType);
}
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed)
ASTPointer<ParameterList> Parser::parseParameterList(
VarDeclParserOptions const& _options,
bool _allowEmpty
)
{
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<VariableDeclaration>> parameters;
VarDeclParserOptions options;
options.allowIndexed = _allowIndexed;
VarDeclParserOptions options(_options);
options.allowEmptyName = true;
expectToken(Token::LParen);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen)
@ -691,7 +734,7 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
}
while (m_scanner->getCurrentToken() == Token::LBrack);
if (m_scanner->getCurrentToken() == Token::Identifier)
if (m_scanner->getCurrentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->getCurrentToken()))
return parseVariableDeclarationStatement(typeNameIndexAccessStructure(primary, indices));
else
return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices));
@ -703,6 +746,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
VarDeclParserOptions options;
options.allowVar = true;
options.allowInitialValue = true;
options.allowLocationSpecifier = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType);
ASTNodeFactory nodeFactory(*this, variable);
return nodeFactory.createNode<VariableDeclarationStatement>(variable);
@ -944,11 +988,16 @@ Parser::LookAheadInfo Parser::peekStatementType() const
Token::Value token(m_scanner->getCurrentToken());
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
if (token == Token::Mapping || token == Token::Var ||
(mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
if (token == Token::Mapping || token == Token::Var)
return LookAheadInfo::VariableDeclarationStatement;
if (mightBeTypeName && m_scanner->peekNextToken() == Token::LBrack)
return LookAheadInfo::IndexAccessStructure;
if (mightBeTypeName)
{
Token::Value next = m_scanner->peekNextToken();
if (next == Token::Identifier || Token::isLocationSpecifier(next))
return LookAheadInfo::VariableDeclarationStatement;
if (m_scanner->peekNextToken() == Token::LBrack)
return LookAheadInfo::IndexAccessStructure;
}
return LookAheadInfo::ExpressionStatement;
}

9
libsolidity/Parser.h

@ -47,13 +47,15 @@ private:
/// End position of the current token
int getEndPosition() const;
struct VarDeclParserOptions {
struct VarDeclParserOptions
{
VarDeclParserOptions() {}
bool allowVar = false;
bool isStateVariable = false;
bool allowIndexed = false;
bool allowEmptyName = false;
bool allowInitialValue = false;
bool allowLocationSpecifier = false;
};
///@{
@ -74,7 +76,10 @@ private:
ASTPointer<Identifier> parseIdentifier();
ASTPointer<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
ASTPointer<ParameterList> parseParameterList(
VarDeclParserOptions const& _options,
bool _allowEmpty = true
);
ASTPointer<Block> parseBlock();
ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement();

3
libsolidity/Token.h

@ -161,12 +161,14 @@ namespace solidity
K(Import, "import", 0) \
K(Is, "is", 0) \
K(Mapping, "mapping", 0) \
K(Memory, "memory", 0) \
K(Modifier, "modifier", 0) \
K(New, "new", 0) \
K(Public, "public", 0) \
K(Private, "private", 0) \
K(Return, "return", 0) \
K(Returns, "returns", 0) \
K(Storage, "storage", 0) \
K(Struct, "struct", 0) \
K(Var, "var", 0) \
K(While, "while", 0) \
@ -370,6 +372,7 @@ public:
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; }
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; }

64
libsolidity/Types.cpp

@ -144,9 +144,9 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
else if (_typeToken == Token::Bool)
return make_shared<BoolType>();
else if (_typeToken == Token::Bytes)
return make_shared<ArrayType>(ArrayType::Location::Storage);
return make_shared<ArrayType>(ReferenceType::Location::Storage);
else if (_typeToken == Token::String)
return make_shared<ArrayType>(ArrayType::Location::Storage, true);
return make_shared<ArrayType>(ReferenceType::Location::Storage, true);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
@ -196,10 +196,10 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
if (!length)
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType, length->literalValue(nullptr));
return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType, length->literalValue(nullptr));
}
else
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType);
return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType);
}
TypePointer Type::forLiteral(Literal const& _literal)
@ -317,9 +317,9 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
const MemberList IntegerType::AddressMemberList({
{"balance", make_shared<IntegerType >(256)},
{"call", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Bare, true)},
{"callcode", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::BareCallCode, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)}
{"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)},
{"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
});
IntegerConstantType::IntegerConstantType(Literal const& _literal)
@ -361,17 +361,27 @@ IntegerConstantType::IntegerConstantType(Literal const& _literal)
bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
shared_ptr<IntegerType const> integerType = getIntegerType();
if (!integerType)
if (auto targetType = dynamic_cast<IntegerType const*>(&_convertTo))
{
if (m_value == 0)
return true;
int forSignBit = (targetType->isSigned() ? 1 : 0);
if (m_value > 0)
{
if (m_value <= (u256(-1) >> (256 - targetType->getNumBits() + forSignBit)))
return true;
}
else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->getNumBits() - forSignBit)))
return true;
return false;
if (_convertTo.getCategory() == Category::FixedBytes)
}
else if (_convertTo.getCategory() == Category::FixedBytes)
{
FixedBytesType const& convertTo = dynamic_cast<FixedBytesType const&>(_convertTo);
return convertTo.getNumBytes() * 8 >= integerType->getNumBits();
FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
return fixedBytes.getNumBytes() * 8 >= getIntegerType()->getNumBits();
}
return integerType->isImplicitlyConvertibleTo(_convertTo);
else
return false;
}
bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const
@ -514,9 +524,10 @@ shared_ptr<IntegerType const> IntegerConstantType::getIntegerType() const
if (value > u256(-1))
return shared_ptr<IntegerType const>();
else
return make_shared<IntegerType>(max(bytesRequired(value), 1u) * 8,
negative ? IntegerType::Modifier::Signed
: IntegerType::Modifier::Unsigned);
return make_shared<IntegerType>(
max(bytesRequired(value), 1u) * 8,
negative ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned
);
}
shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal)
@ -663,7 +674,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
return false;
auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
// let us not allow assignment to memory arrays for now
if (convertTo.getLocation() != Location::Storage)
if (convertTo.location() != Location::Storage)
return false;
if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
return false;
@ -767,12 +778,12 @@ TypePointer ArrayType::externalType() const
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length);
}
shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const
TypePointer ArrayType::copyForLocation(ReferenceType::Location _location) const
{
auto copy = make_shared<ArrayType>(_location);
copy->m_arrayKind = m_arrayKind;
if (m_baseType->getCategory() == Type::Category::Array)
copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location);
if (auto ref = dynamic_cast<ReferenceType const*>(m_baseType.get()))
copy->m_baseType = ref->copyForLocation(_location);
else
copy->m_baseType = m_baseType;
copy->m_hasDynamicLength = m_hasDynamicLength;
@ -923,6 +934,13 @@ MemberList const& StructType::getMembers() const
return *m_members;
}
TypePointer StructType::copyForLocation(ReferenceType::Location _location) const
{
auto copy = make_shared<StructType>(m_struct);
copy->m_location = _location;
return copy;
}
pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
{
auto const* offsets = getMembers().getMemberStorageOffset(_name);
@ -1455,7 +1473,7 @@ MagicType::MagicType(MagicType::Kind _kind):
{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)},
{"data", make_shared<ArrayType>(ArrayType::Location::CallData)},
{"data", make_shared<ArrayType>(ReferenceType::Location::CallData)},
{"sig", make_shared<FixedBytesType>(4)}
}));
break;

41
libsolidity/Types.h

@ -353,6 +353,24 @@ public:
virtual TypePointer externalType() const override { return shared_from_this(); }
};
/**
* Trait used by types which are not value types and can be stored either in storage, memory
* or calldata. This is currently used by arrays and structs.
*/
class ReferenceType
{
public:
enum class Location { Storage, CallData, Memory };
explicit ReferenceType(Location _location): m_location(_location) {}
Location location() const { return m_location; }
/// @returns a copy of this type with location (recursively) changed to @a _location.
virtual TypePointer copyForLocation(Location _location) const = 0;
protected:
Location m_location = Location::Storage;
};
/**
* The type of an array. The flavours are byte array (bytes), statically- (<type>[<length>])
* and dynamically-sized array (<type>[]).
@ -360,27 +378,26 @@ public:
* one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and
* thus start on their own slot.
*/
class ArrayType: public Type
class ArrayType: public Type, public ReferenceType
{
public:
enum class Location { Storage, CallData, Memory };
virtual Category getCategory() const override { return Category::Array; }
/// Constructor for a byte array ("bytes") and string.
explicit ArrayType(Location _location, bool _isString = false):
m_location(_location),
ReferenceType(_location),
m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
m_baseType(std::make_shared<FixedBytesType>(1))
{}
/// Constructor for a dynamically sized array type ("type[]")
ArrayType(Location _location, const TypePointer &_baseType):
m_location(_location),
ReferenceType(_location),
m_baseType(_baseType)
{}
/// Constructor for a fixed-size array type ("type[20]")
ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length):
m_location(_location),
ReferenceType(_location),
m_baseType(_baseType),
m_hasDynamicLength(false),
m_length(_length)
@ -400,7 +417,6 @@ public:
}
virtual TypePointer externalType() const override;
Location getLocation() const { return m_location; }
/// @returns true if this is a byte array or a string
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
/// @returns true if this is a string
@ -408,15 +424,12 @@ public:
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
u256 const& getLength() const { return m_length; }
/// @returns a copy of this type with location changed to @a _location
/// @todo this might move as far up as Type later
std::shared_ptr<ArrayType> copyForLocation(Location _location) const;
TypePointer copyForLocation(Location _location) const override;
private:
/// String is interpreted as a subtype of Bytes.
enum class ArrayKind { Ordinary, Bytes, String };
Location m_location;
///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
ArrayKind m_arrayKind = ArrayKind::Ordinary;
TypePointer m_baseType;
@ -484,11 +497,13 @@ private:
/**
* The type of a struct instance, there is one distinct type per struct definition.
*/
class StructType: public Type
class StructType: public Type, public ReferenceType
{
public:
virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct): m_struct(_struct) {}
explicit StructType(StructDefinition const& _struct):
//@todo only storage until we have non-storage structs
ReferenceType(Location::Storage), m_struct(_struct) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
@ -498,6 +513,8 @@ public:
virtual MemberList const& getMembers() const override;
TypePointer copyForLocation(Location _location) const override;
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
private:

9
solc/CommandLineInterface.cpp

@ -299,7 +299,8 @@ bool CommandLineInterface::parseArguments(int argc, char** argv)
desc.add_options()
("help", "Show help message and exit")
("version", "Show version and exit")
("optimize", po::value<bool>()->default_value(false), "Optimize bytecode for size")
("optimize", po::value<bool>()->default_value(false), "Optimize bytecode")
("optimize-runs", po::value<unsigned>()->default_value(200), "Estimated number of contract runs for optimizer.")
("add-std", po::value<bool>()->default_value(false), "Add standard contracts")
("input-file", po::value<vector<string>>(), "input file")
(
@ -409,7 +410,11 @@ bool CommandLineInterface::processInput()
for (auto const& sourceCode: m_sourceCodes)
m_compiler->addSource(sourceCode.first, sourceCode.second);
// TODO: Perhaps we should not compile unless requested
m_compiler->compile(m_args["optimize"].as<bool>());
bool optimize = m_args["optimize"].as<bool>();
unsigned runs = m_args["optimize-runs"].as<unsigned>();
if (m_args.count("optimize-runs"))
optimize = true;
m_compiler->compile(optimize, runs);
}
catch (ParserError const& _exception)
{

3
test/libp2p/peer.cpp

@ -116,6 +116,9 @@ BOOST_AUTO_TEST_CASE(saveNodes)
BOOST_REQUIRE(i.itemCount() == 4 || i.itemCount() == 11);
BOOST_REQUIRE(i[0].size() == 4 || i[0].size() == 16);
}
for (auto host: hosts)
delete host;
}
BOOST_AUTO_TEST_SUITE_END()

37
test/libsolidity/SolidityEndToEndTest.cpp

@ -4172,6 +4172,43 @@ BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund)
BOOST_CHECK(compileAndRunWthoutCheck(sourceCode, 0, "A").empty());
}
BOOST_AUTO_TEST_CASE(positive_integers_to_signed)
{
char const* sourceCode = R"(
contract test {
int8 public x = 2;
int8 public y = 127;
int16 public q = 250;
}
)";
compileAndRun(sourceCode, 0, "test");
BOOST_CHECK(callContractFunction("x()") == encodeArgs(2));
BOOST_CHECK(callContractFunction("y()") == encodeArgs(127));
BOOST_CHECK(callContractFunction("q()") == encodeArgs(250));
}
BOOST_AUTO_TEST_CASE(failing_send)
{
char const* sourceCode = R"(
contract Helper {
uint[] data;
function () {
data[9]; // trigger exception
}
}
contract Main {
function callHelper(address _a) returns (bool r, uint bal) {
r = !_a.send(5);
bal = this.balance;
}
}
)";
compileAndRun(sourceCode, 0, "Helper");
u160 const c_helperAddress = m_contractAddress;
compileAndRun(sourceCode, 20, "Main");
BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20));
}
BOOST_AUTO_TEST_SUITE_END()
}

94
test/libsolidity/SolidityNameAndTypeResolution.cpp

@ -1816,6 +1816,100 @@ BOOST_AUTO_TEST_CASE(string_length)
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(negative_integers_to_signed_out_of_bound)
{
char const* sourceCode = R"(
contract test {
int8 public i = -129;
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(negative_integers_to_signed_min)
{
char const* sourceCode = R"(
contract test {
int8 public i = -128;
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode));
}
BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound)
{
char const* sourceCode = R"(
contract test {
int8 public j = 128;
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound_max)
{
char const* sourceCode = R"(
contract test {
int8 public j = 127;
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode));
}
BOOST_AUTO_TEST_CASE(negative_integers_to_unsigned)
{
char const* sourceCode = R"(
contract test {
uint8 public x = -1;
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound)
{
char const* sourceCode = R"(
contract test {
uint8 public x = 700;
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(overwrite_memory_location_external)
{
char const* sourceCode = R"(
contract C {
function f(uint[] memory a) external {}
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(overwrite_storage_location_external)
{
char const* sourceCode = R"(
contract C {
function f(uint[] storage a) external {}
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(storage_location_local_variables)
{
char const* sourceCode = R"(
contract C {
function f() {
uint[] storage x;
uint[] memory y;
uint[] memory z;
}
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode));
}
BOOST_AUTO_TEST_SUITE_END()
}

45
test/libsolidity/SolidityOptimizer.cpp

@ -1036,6 +1036,51 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_loops)
BOOST_CHECK_EQUAL(pushTags.size(), 1);
}
BOOST_AUTO_TEST_CASE(computing_constants)
{
char const* sourceCode = R"(
contract c {
uint a;
uint b;
uint c;
function set() returns (uint a, uint b, uint c) {
a = 0x77abc0000000000000000000000000000000000000000000000000000000001;
b = 0x817416927846239487123469187231298734162934871263941234127518276;
g();
}
function g() {
b = 0x817416927846239487123469187231298734162934871263941234127518276;
c = 0x817416927846239487123469187231298734162934871263941234127518276;
}
function get() returns (uint ra, uint rb, uint rc) {
ra = a;
rb = b;
rc = c ;
}
}
)";
compileBothVersions(sourceCode);
compareVersions("set()");
compareVersions("get()");
m_optimize = true;
m_optimizeRuns = 1;
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c");
bytes complicatedConstant = toBigEndian(u256("0x817416927846239487123469187231298734162934871263941234127518276"));
unsigned occurrences = 0;
for (auto iter = optimizedBytecode.cbegin(); iter < optimizedBytecode.cend(); ++occurrences)
iter = search(iter, optimizedBytecode.cend(), complicatedConstant.cbegin(), complicatedConstant.cend()) + 1;
BOOST_CHECK_EQUAL(2, occurrences);
bytes constantWithZeros = toBigEndian(u256("0x77abc0000000000000000000000000000000000000000000000000000000001"));
BOOST_CHECK(search(
optimizedBytecode.cbegin(),
optimizedBytecode.cend(),
constantWithZeros.cbegin(),
constantWithZeros.cend()
) == optimizedBytecode.cend());
}
BOOST_AUTO_TEST_SUITE_END()
}

41
test/libsolidity/SolidityParser.cpp

@ -873,6 +873,47 @@ BOOST_AUTO_TEST_CASE(var_array)
BOOST_CHECK_THROW(parseText(text), ParserError);
}
BOOST_AUTO_TEST_CASE(location_specifiers_for_params)
{
char const* text = R"(
contract Foo {
function f(uint[] storage constant x, uint[] memory y) { }
}
)";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(location_specifiers_for_locals)
{
char const* text = R"(
contract Foo {
function f() {
uint[] storage x;
uint[] memory y;
}
}
)";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(location_specifiers_for_state)
{
char const* text = R"(
contract Foo {
uint[] memory x;
})";
BOOST_CHECK_THROW(parseText(text), ParserError);
}
BOOST_AUTO_TEST_CASE(location_specifiers_with_var)
{
char const* text = R"(
contract Foo {
function f() { var memory x; }
})";
BOOST_CHECK_THROW(parseText(text), ParserError);
}
BOOST_AUTO_TEST_SUITE_END()
}

14
test/libsolidity/SolidityTypes.cpp

@ -77,13 +77,13 @@ BOOST_AUTO_TEST_CASE(storage_layout_mapping)
BOOST_AUTO_TEST_CASE(storage_layout_arrays)
{
BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared<FixedBytesType>(1), 32).getStorageSize() == 1);
BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared<FixedBytesType>(1), 33).getStorageSize() == 2);
BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared<FixedBytesType>(2), 31).getStorageSize() == 2);
BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared<FixedBytesType>(7), 8).getStorageSize() == 2);
BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared<FixedBytesType>(7), 9).getStorageSize() == 3);
BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared<FixedBytesType>(31), 9).getStorageSize() == 9);
BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared<FixedBytesType>(32), 9).getStorageSize() == 9);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(1), 32).getStorageSize() == 1);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(1), 33).getStorageSize() == 2);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(2), 31).getStorageSize() == 2);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(7), 8).getStorageSize() == 2);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(7), 9).getStorageSize() == 3);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(31), 9).getStorageSize() == 9);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(32), 9).getStorageSize() == 9);
}
BOOST_AUTO_TEST_SUITE_END()

3
test/libsolidity/solidityExecutionFramework.h

@ -46,7 +46,7 @@ public:
{
m_compiler.reset(false, m_addStandardSources);
m_compiler.addSource("", _sourceCode);
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize), "Compiling contract failed");
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
bytes code = m_compiler.getBytecode(_contractName);
sendMessage(code, true, _value);
return m_output;
@ -180,6 +180,7 @@ protected:
m_logs = executive.logs();
}
size_t m_optimizeRuns = 200;
bool m_optimize = false;
bool m_addStandardSources = false;
dev::solidity::CompilerStack m_compiler;

99
test/libwhisper/whisperTopic.cpp

@ -30,40 +30,40 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
BOOST_AUTO_TEST_SUITE(whisper)
struct P2PFixture
{
P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = true; }
~P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = false; }
};
BOOST_FIXTURE_TEST_SUITE(whisper, P2PFixture)
#if ALEX_HASH_FIXED_NETWORKING
BOOST_AUTO_TEST_CASE(topic)
{
cnote << "Testing Whisper...";
auto oldLogVerbosity = g_logVerbosity;
g_logVerbosity = 0;
Host host1("Test", NetworkPreferences(30303, "127.0.0.1", false, true));
Host host1("Test", NetworkPreferences("127.0.0.1", 30303, false));
host1.setIdealPeerCount(1);
auto whost1 = host1.registerCapability(new WhisperHost());
host1.start();
while (!host1.isStarted())
this_thread::sleep_for(chrono::milliseconds(2));
bool started = false;
bool host1Ready = false;
unsigned result = 0;
std::thread listener([&]()
{
setThreadName("other");
started = true;
/// Only interested in odd packets
auto w = whost1->installWatch(BuildTopicMask("odd"));
started = true;
host1Ready = true;
set<unsigned> received;
for (int iterout = 0, last = 0; iterout < 200 && last < 81; ++iterout)
{
for (auto i: whost1->checkWatch(w))
{
Message msg = whost1->envelope(i).open(whost1->fullTopic(w));
Message msg = whost1->envelope(i).open(whost1->fullTopics(w));
last = RLP(msg.payload()).toInt<unsigned>();
if (received.count(last))
continue;
@ -76,21 +76,21 @@ BOOST_AUTO_TEST_CASE(topic)
});
Host host2("Test", NetworkPreferences(30300, "127.0.0.1", false, true));
Host host2("Test", NetworkPreferences("127.0.0.1", 30300, false));
host1.setIdealPeerCount(1);
auto whost2 = host2.registerCapability(new WhisperHost());
host2.start();
while (!host2.isStarted())
this_thread::sleep_for(chrono::milliseconds(2));
this_thread::sleep_for(chrono::milliseconds(100));
host2.addNode(host1.id(), "127.0.0.1", 30303, 30303);
this_thread::sleep_for(chrono::milliseconds(500));
while (!started)
this_thread::sleep_for(chrono::milliseconds(2));
while (!host1.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(5));
host2.addNode(host1.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30303, 30303));
// wait for nodes to connect
this_thread::sleep_for(chrono::milliseconds(1000));
while (!host1Ready)
this_thread::sleep_for(chrono::milliseconds(10));
KeyPair us = KeyPair::create();
for (int i = 0; i < 10; ++i)
{
@ -111,11 +111,11 @@ BOOST_AUTO_TEST_CASE(forwarding)
g_logVerbosity = 0;
// Host must be configured not to share peers.
Host host1("Listner", NetworkPreferences(30303, "", false, true));
host1.setIdealPeerCount(0);
Host host1("Listner", NetworkPreferences("127.0.0.1", 30303, false));
host1.setIdealPeerCount(1);
auto whost1 = host1.registerCapability(new WhisperHost());
host1.start();
while (!host1.isStarted())
while (!host1.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(2));
unsigned result = 0;
@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(forwarding)
{
for (auto i: whost1->checkWatch(w))
{
Message msg = whost1->envelope(i).open(whost1->fullTopic(w));
Message msg = whost1->envelope(i).open(whost1->fullTopics(w));
unsigned last = RLP(msg.payload()).toInt<unsigned>();
cnote << "New message from:" << msg.from() << RLP(msg.payload()).toInt<unsigned>();
result = last;
@ -146,11 +146,11 @@ BOOST_AUTO_TEST_CASE(forwarding)
// Host must be configured not to share peers.
Host host2("Forwarder", NetworkPreferences(30305, "", false, true));
Host host2("Forwarder", NetworkPreferences("127.0.0.1", 30305, false));
host2.setIdealPeerCount(1);
auto whost2 = host2.registerCapability(new WhisperHost());
host2.start();
while (!host2.isStarted())
while (!host2.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(2));
Public fwderid;
@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(forwarding)
this_thread::sleep_for(chrono::milliseconds(50));
this_thread::sleep_for(chrono::milliseconds(500));
host2.addNode(host1.id(), "127.0.0.1", 30303, 30303);
host2.addNode(host1.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30303, 30303));
startedForwarder = true;
@ -174,7 +174,7 @@ BOOST_AUTO_TEST_CASE(forwarding)
{
for (auto i: whost2->checkWatch(w))
{
Message msg = whost2->envelope(i).open(whost2->fullTopic(w));
Message msg = whost2->envelope(i).open(whost2->fullTopics(w));
cnote << "New message from:" << msg.from() << RLP(msg.payload()).toInt<unsigned>();
}
this_thread::sleep_for(chrono::milliseconds(50));
@ -184,12 +184,15 @@ BOOST_AUTO_TEST_CASE(forwarding)
while (!startedForwarder)
this_thread::sleep_for(chrono::milliseconds(50));
Host ph("Sender", NetworkPreferences(30300, "", false, true));
Host ph("Sender", NetworkPreferences("127.0.0.1", 30300, false));
ph.setIdealPeerCount(1);
shared_ptr<WhisperHost> wh = ph.registerCapability(new WhisperHost());
ph.start();
ph.addNode(host2.id(), "127.0.0.1", 30305, 30305);
while (!ph.isStarted())
ph.addNode(host2.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30305, 30305));
while (!ph.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(10));
while (!ph.peerCount())
this_thread::sleep_for(chrono::milliseconds(10));
KeyPair us = KeyPair::create();
@ -214,11 +217,11 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
bool done = false;
// Host must be configured not to share peers.
Host host1("Forwarder", NetworkPreferences(30305, "", false, true));
Host host1("Forwarder", NetworkPreferences("127.0.0.1", 30305, false));
host1.setIdealPeerCount(1);
auto whost1 = host1.registerCapability(new WhisperHost());
host1.start();
while (!host1.isStarted())
while (!host1.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(2));
bool startedForwarder = false;
@ -227,7 +230,6 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
setThreadName("forwarder");
this_thread::sleep_for(chrono::milliseconds(500));
// ph.addNode("127.0.0.1", 30303, 30303);
startedForwarder = true;
@ -238,7 +240,7 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
{
for (auto i: whost1->checkWatch(w))
{
Message msg = whost1->envelope(i).open(whost1->fullTopic(w));
Message msg = whost1->envelope(i).open(whost1->fullTopics(w));
cnote << "New message from:" << msg.from() << RLP(msg.payload()).toInt<unsigned>();
}
this_thread::sleep_for(chrono::milliseconds(50));
@ -249,13 +251,13 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
this_thread::sleep_for(chrono::milliseconds(2));
{
Host host2("Sender", NetworkPreferences(30300, "", false, true));
Host host2("Sender", NetworkPreferences("127.0.0.1", 30300, false));
host2.setIdealPeerCount(1);
shared_ptr<WhisperHost> whost2 = host2.registerCapability(new WhisperHost());
host2.start();
while (!host2.isStarted())
while (!host2.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(2));
host2.addNode(host1.id(), "127.0.0.1", 30305, 30305);
host2.addNode(host1.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30305, 30305));
while (!host2.peerCount())
this_thread::sleep_for(chrono::milliseconds(5));
@ -266,13 +268,13 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
}
{
Host ph("Listener", NetworkPreferences(30300, "", false, true));
Host ph("Listener", NetworkPreferences("127.0.0.1", 30300, false));
ph.setIdealPeerCount(1);
shared_ptr<WhisperHost> wh = ph.registerCapability(new WhisperHost());
ph.start();
while (!ph.isStarted())
while (!ph.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(2));
ph.addNode(host1.id(), "127.0.0.1", 30305, 30305);
ph.addNode(host1.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30305, 30305));
/// Only interested in odd packets
auto w = wh->installWatch(BuildTopicMask("test"));
@ -281,7 +283,7 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
{
for (auto i: wh->checkWatch(w))
{
Message msg = wh->envelope(i).open(wh->fullTopic(w));
Message msg = wh->envelope(i).open(wh->fullTopics(w));
unsigned last = RLP(msg.payload()).toInt<unsigned>();
cnote << "New message from:" << msg.from() << RLP(msg.payload()).toInt<unsigned>();
result = last;
@ -296,6 +298,5 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
BOOST_REQUIRE_EQUAL(result, 1);
}
#endif
BOOST_AUTO_TEST_SUITE_END()

Loading…
Cancel
Save