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. 9
      libp2p/RLPxHandshake.cpp
  25. 6
      libp2p/Session.cpp
  26. 30
      libsolidity/AST.cpp
  27. 17
      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. 55
      libsolidity/ExpressionCompiler.cpp
  36. 45
      libsolidity/NameAndTypeResolver.cpp
  37. 91
      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. 89
      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_Transact.h Transact.ui)
qt5_wrap_ui(ui_ExportState.h ExportState.ui) qt5_wrap_ui(ui_ExportState.h ExportState.ui)
qt5_wrap_ui(ui_GetPassword.h GetPassword.ui) qt5_wrap_ui(ui_GetPassword.h GetPassword.ui)
qt5_wrap_ui(ui_GasPricing.h GasPricing.ui)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
@ -36,7 +37,7 @@ endif ()
# eth_add_executable is defined in cmake/EthExecutableHelper.cmake # eth_add_executable is defined in cmake/EthExecutableHelper.cmake
eth_add_executable(${EXECUTABLE} eth_add_executable(${EXECUTABLE}
ICON alethzero 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 WIN_RESOURCES alethzero.rc
) )

18
alethzero/Context.cpp

@ -21,6 +21,7 @@
#include "Context.h" #include "Context.h"
#include <QComboBox> #include <QComboBox>
#include <QSpinBox>
#include <libethcore/Common.h> #include <libethcore/Common.h>
using namespace std; using namespace std;
using namespace dev; 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) void initUnits(QComboBox* _b)
{ {
for (auto n = (unsigned)units().size(); n-- != 0; ) for (auto n = (unsigned)units().size(); n-- != 0; )

4
alethzero/Context.h

@ -28,6 +28,7 @@
#include <libethcore/Common.h> #include <libethcore/Common.h>
class QComboBox; class QComboBox;
class QSpinBox;
namespace dev { namespace eth { struct StateDiff; class KeyManager; } } 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 "\">" #define Span(S) "<span style=\"" S "\">"
void initUnits(QComboBox* _b); 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); 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::Secret retrieveSecret(dev::Address const& _a) const = 0;
virtual dev::eth::KeyManager& keyManager() = 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="debugDumpState"/>
<addaction name="debugDumpStatePre"/> <addaction name="debugDumpStatePre"/>
</widget> </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_File"/>
<addaction name="menu_View"/> <addaction name="menu_View"/>
<addaction name="menu_Network"/> <addaction name="menu_Network"/>
<addaction name="menu_Tools"/> <addaction name="menu_Tools"/>
<addaction name="menuWhispe"/> <addaction name="menuWhispe"/>
<addaction name="menuDebug"/> <addaction name="menuDebug"/>
<addaction name="menu_Config"/>
<addaction name="menu_Help"/> <addaction name="menu_Help"/>
<addaction name="menu_Debug"/> <addaction name="menu_Debug"/>
</widget> </widget>
@ -1763,6 +1770,11 @@ font-size: 14pt</string>
<string>Re-Encrypt All Keys...</string> <string>Re-Encrypt All Keys...</string>
</property> </property>
</action> </action>
<action name="gasPrices">
<property name="text">
<string>&amp;Gas Prices...</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>

41
alethzero/MainWin.cpp

@ -76,6 +76,7 @@
#include "ExportState.h" #include "ExportState.h"
#include "ui_Main.h" #include "ui_Main.h"
#include "ui_GetPassword.h" #include "ui_GetPassword.h"
#include "ui_GasPricing.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
@ -128,7 +129,7 @@ static QString filterOutTerminal(QString _s)
Main::Main(QWidget *parent) : Main::Main(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::Main), ui(new Ui::Main),
m_transact(this, this), m_transact(nullptr),
m_dappLoader(nullptr), m_dappLoader(nullptr),
m_webPage(nullptr) m_webPage(nullptr)
{ {
@ -232,6 +233,11 @@ Main::Main(QWidget *parent) :
// inspector->setPage(page); // inspector->setPage(page);
setBeneficiary(*m_keyManager.accounts().begin()); setBeneficiary(*m_keyManager.accounts().begin());
readSettings(); readSettings();
m_transact = new Transact(this, this);
m_transact->setWindowFlags(Qt::Dialog);
m_transact->setWindowModality(Qt::WindowModal);
#if !ETH_FATDB #if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts); removeDockWidget(ui->dockWidget_accounts);
#endif #endif
@ -262,6 +268,23 @@ bool Main::confirm() const
return ui->natSpec->isChecked(); 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() void Main::on_newIdentity_triggered()
{ {
KeyPair kp = KeyPair::create(); KeyPair kp = KeyPair::create();
@ -467,10 +490,8 @@ void Main::load(QString _s)
void Main::on_newTransaction_triggered() void Main::on_newTransaction_triggered()
{ {
m_transact.setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB); m_transact->setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB);
m_transact.setWindowFlags(Qt::Dialog); m_transact->show();
m_transact.setWindowModality(Qt::WindowModal);
m_transact.show();
} }
void Main::on_loadJS_triggered() void Main::on_loadJS_triggered()
@ -653,6 +674,11 @@ void Main::on_paranoia_triggered()
ethereum()->setParanoia(ui->paranoia->isChecked()); ethereum()->setParanoia(ui->paranoia->isChecked());
} }
dev::u256 Main::gasPrice() const
{
return ethereum()->gasPricer()->bid();
}
void Main::writeSettings() void Main::writeSettings()
{ {
QSettings s("ethereum", "alethzero"); QSettings s("ethereum", "alethzero");
@ -669,6 +695,8 @@ void Main::writeSettings()
s.setValue("identities", b); 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("upnp", ui->upnp->isChecked());
s.setValue("forceAddress", ui->forcePublicIP->text()); s.setValue("forceAddress", ui->forcePublicIP->text());
s.setValue("forceMining", ui->forceMining->isChecked()); 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->upnp->setChecked(s.value("upnp", true).toBool());
ui->forcePublicIP->setText(s.value("forceAddress", "").toString()); ui->forcePublicIP->setText(s.value("forceAddress", "").toString());
ui->dropPeers->setChecked(false); ui->dropPeers->setChecked(false);

7
alethzero/MainWin.h

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

13
alethzero/Transact.cpp

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

2
alethzero/Transact.ui

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

45
eth/main.cpp

@ -124,9 +124,11 @@ void help()
<< " --password <password> Give a password for a private key." << endl << " --password <password> Give a password for a private key." << endl
<< endl << endl
<< "Client transacting:" << 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 << " -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 << endl
<< "Client mining:" << endl << "Client mining:" << endl
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << 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 /// Transaction params
TransactionPriority priority = TransactionPriority::Medium; TransactionPriority priority = TransactionPriority::Medium;
double etherPrice = 30.679; // double etherPrice = 30.679;
double blockFees = 15.0; // double blockFees = 15.0;
u256 askPrice("500000000000");
u256 bidPrice("500000000000");
// javascript console // javascript console
bool useConsole = false; 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) else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i]; dbPath = argv[++i];
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) /* else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{ {
try try
{ {
@ -487,6 +491,30 @@ int main(int argc, char** argv)
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1; 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) else if ((arg == "-P" || arg == "--priority") && i + 1 < argc)
{ {
@ -730,7 +758,8 @@ int main(int argc, char** argv)
cout << ethCredits(); cout << ethCredits();
web3.setIdealPeerCount(peers); 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; eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr;
StructuredLogger::starting(clientImplString, dev::Version); StructuredLogger::starting(clientImplString, dev::Version);
if (c) if (c)
@ -829,7 +858,7 @@ int main(int argc, char** argv)
iss >> enable; iss >> enable;
c->setForceMining(isTrue(enable)); c->setForceMining(isTrue(enable));
} }
else if (c && cmd == "setblockfees") /* else if (c && cmd == "setblockfees")
{ {
iss >> blockFees; iss >> blockFees;
try try
@ -884,7 +913,7 @@ int main(int argc, char** argv)
cerr << "Unknown priority: " << m << endl; cerr << "Unknown priority: " << m << endl;
} }
cout << "Priority: " << (int)priority << "/8" << endl; cout << "Priority: " << (int)priority << "/8" << endl;
} }*/
else if (cmd == "verbosity") else if (cmd == "verbosity")
{ {
if (iss.peek() != -1) if (iss.peek() != -1)

2
ethminer/farm.json

@ -1,4 +1,6 @@
[ [
{ "name": "eth_getWork", "params": [], "order": [], "returns": []}, { "name": "eth_getWork", "params": [], "order": [], "returns": []},
{ "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true} { "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. /// Resets the gas pricer to some other object.
void setGasPricer(std::shared_ptr<GasPricer> _gp) { m_gp = _gp; } 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. /// Blocks until all pending transactions have been processed.
virtual void flushTransactions() override; virtual void flushTransactions() override;

23
libethereum/EthereumHost.cpp

@ -54,7 +54,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
EthereumHost::~EthereumHost() EthereumHost::~EthereumHost()
{ {
forEachPeer([](EthereumPeer* _p) { _p->abortSync(); }); foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
} }
bool EthereumHost::ensureInitialised() bool EthereumHost::ensureInitialised()
@ -74,7 +74,7 @@ bool EthereumHost::ensureInitialised()
void EthereumHost::reset() void EthereumHost::reset()
{ {
forEachPeer([](EthereumPeer* _p) { _p->abortSync(); }); foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
m_man.resetToChain(h256s()); m_man.resetToChain(h256s());
m_hashMan.reset(m_chain.number() + 1); m_hashMan.reset(m_chain.number() + 1);
m_needSyncBlocks = true; m_needSyncBlocks = true;
@ -105,7 +105,7 @@ void EthereumHost::doWork()
} }
} }
forEachPeer([](EthereumPeer* _p) { _p->tick(); }); foreachPeer([](EthereumPeer* _p) { _p->tick(); });
// return netChange; // return netChange;
// TODO: Figure out what to do with netChange. // TODO: Figure out what to do with netChange.
@ -120,12 +120,13 @@ void EthereumHost::maintainTransactions()
for (auto const& i: ts) for (auto const& i: ts)
{ {
bool unsent = !m_transactionsSent.count(i.first); 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); peerTransactions[p].push_back(i.first);
} }
for (auto const& t: ts) for (auto const& t: ts)
m_transactionsSent.insert(t.first); m_transactionsSent.insert(t.first);
forEachPeerPtr([&](shared_ptr<EthereumPeer> _p) foreachPeerPtr([&](shared_ptr<EthereumPeer> _p)
{ {
bytes b; bytes b;
unsigned n = 0; 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) if (_p)
_f(_p.get()); _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()) for (auto s: peerSessions())
_f(s.first->cap<EthereumPeer>()); _f(s.first->cap<EthereumPeer>());
@ -551,7 +552,7 @@ void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
void EthereumHost::continueSync() void EthereumHost::continueSync()
{ {
clog(NetAllDetail) << "Getting help with downloading hashes and blocks"; clog(NetAllDetail) << "Getting help with downloading hashes and blocks";
forEachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p->m_asking == Asking::Nothing) if (_p->m_asking == Asking::Nothing)
continueSync(_p); continueSync(_p);
@ -564,7 +565,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
bool otherPeerSync = false; bool otherPeerSync = false;
if (m_needSyncHashes && peerShouldGrabChain(_peer)) if (m_needSyncHashes && peerShouldGrabChain(_peer))
{ {
forEachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p != _peer && _p->m_asking == Asking::Hashes && _p->m_protocolVersion != protocolVersion()) 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 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. /// 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. /// 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; bool syncing = false;
forEachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p->m_asking != Asking::Nothing) if (_p->m_asking != Asking::Nothing)
syncing = true; syncing = true;

4
libethereum/EthereumHost.h

@ -92,8 +92,8 @@ public:
private: 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; }); 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 foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void forEachPeer(std::function<void(EthereumPeer*)> const& _f) const; void foreachPeer(std::function<void(EthereumPeer*)> const& _f) const;
bool isSyncing_UNSAFE() 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. /// 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 class TrivialGasPricer: public GasPricer
{ {
protected: public:
u256 ask(State const&) const override { return 10 * szabo; } TrivialGasPricer() = default;
u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return 10 * szabo; } 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 enum class Permanence

14
libevmasm/Assembly.cpp

@ -22,9 +22,12 @@
#include "Assembly.h" #include "Assembly.h"
#include <fstream> #include <fstream>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libevmcore/Params.h>
#include <libevmasm/CommonSubexpressionEliminator.h> #include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/ControlFlowGraph.h> #include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/BlockDeduplicator.h> #include <libevmasm/BlockDeduplicator.h>
#include <libevmasm/ConstantOptimiser.h>
#include <libevmasm/GasMeter.h>
#include <json/json.h> #include <json/json.h>
using namespace std; using namespace std;
using namespace dev; 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; }; struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; };
#define copt dev::LogOutputStream<OptimiserChannel, true>() #define copt dev::LogOutputStream<OptimiserChannel, true>()
Assembly& Assembly::optimise(bool _enable) Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
{ {
if (!_enable) if (!_enable)
return *this; 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."; copt << total << " optimisations done.";
for (auto& sub: m_subs) for (auto& sub: m_subs)
sub.optimise(true); sub.optimise(true, false, _runs);
return *this; return *this;
} }

9
libevmasm/Assembly.h

@ -49,6 +49,7 @@ public:
AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); } AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); }
AssemblyItem newData(bytes const& _data) { h256 h = (u256)std::hash<std::string>()(asString(_data)); m_data[h] = _data; return AssemblyItem(PushData, h); } 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); } 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 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); } AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
@ -92,7 +93,13 @@ public:
void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; } void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; }
bytes assemble() const; 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( Json::Value stream(
std::ostream& _out, std::ostream& _out,
std::string const& _prefix = "", 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) if (_instruction == Instruction::JUMPDEST)
return GasConsumption(1); return 1;
int tier = instructionInfo(_instruction).gasPriceTier; 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; } u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }
static u256 runGas(Instruction _instruction);
private: private:
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise. /// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.
GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value); GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value);
@ -76,8 +78,6 @@ private:
/// given as values on the stack at the given relative positions. /// given as values on the stack at the given relative positions.
GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize); GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize);
static GasConsumption runGas(Instruction _instruction);
std::shared_ptr<KnownState> m_state; std::shared_ptr<KnownState> m_state;
/// Largest point where memory was accessed since the creation of this object. /// Largest point where memory was accessed since the creation of this object.
u256 m_largestMemoryAccess; u256 m_largestMemoryAccess;

4
libevmcore/Instruction.cpp

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

9
libp2p/RLPxHandshake.cpp

@ -265,9 +265,18 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
} }
clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session."; clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session.";
try
{
RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall); RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall);
m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); 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(); clog(NetWarn) << "Error reading: " << ec.message();
drop(TCPError); drop(TCPError);
} }
else if (ec && length == 0) else if (ec && length < tlen)
{
clog(NetWarn) << "Error reading - Abrupt peer disconnect: " << ec.message();
drop(TCPError);
return; return;
}
else else
{ {
if (!m_io->authAndDecryptFrame(bytesRef(m_data.data(), tlen))) 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.")); 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 bool VariableDeclaration::isExternalFunctionParameter() const
{ {
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope()); auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
@ -686,9 +697,14 @@ void Expression::expectType(Type const& _expectedType)
checkTypeRequirements(nullptr); checkTypeRequirements(nullptr);
Type const& type = *getType(); Type const& type = *getType();
if (!type.isImplicitlyConvertibleTo(_expectedType)) if (!type.isImplicitlyConvertibleTo(_expectedType))
BOOST_THROW_EXCEPTION(createTypeError("Type " + type.toString() + BOOST_THROW_EXCEPTION(createTypeError(
" not implicitly convertible to expected type " "Type " +
+ _expectedType.toString() + ".")); type.toString() +
" is not implicitly convertible to expected type " +
_expectedType.toString() +
"."
)
);
} }
void Expression::requireLValue() void Expression::requireLValue()
@ -874,7 +890,7 @@ void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
{ {
auto const& arrayType(dynamic_cast<ArrayType const&>(type)); auto const& arrayType(dynamic_cast<ArrayType const&>(type));
m_isLValue = (*m_memberName == "length" && m_isLValue = (*m_memberName == "length" &&
arrayType.getLocation() != ArrayType::Location::CallData && arrayType.isDynamicallySized()); arrayType.location() != ReferenceType::Location::CallData && arrayType.isDynamicallySized());
} }
else else
m_isLValue = false; m_isLValue = false;
@ -897,7 +913,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
m_type = make_shared<FixedBytesType>(1); m_type = make_shared<FixedBytesType>(1);
else else
m_type = type.getBaseType(); m_type = type.getBaseType();
m_isLValue = type.getLocation() != ArrayType::Location::CallData; m_isLValue = type.location() != ReferenceType::Location::CallData;
break; break;
} }
case Type::Category::Mapping: case Type::Category::Mapping:
@ -914,7 +930,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
{ {
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType()); TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
if (!m_index) 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 else
{ {
m_index->checkTypeRequirements(nullptr); m_index->checkTypeRequirements(nullptr);
@ -922,7 +938,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
if (!length) if (!length)
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected.")); BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
m_type = make_shared<TypeType>(make_shared<ArrayType>( 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; break;
} }

17
libsolidity/AST.h

@ -474,22 +474,26 @@ private:
class VariableDeclaration: public Declaration class VariableDeclaration: public Declaration
{ {
public: public:
enum Location { Default, Storage, Memory };
VariableDeclaration( VariableDeclaration(
SourceLocation const& _location, SourceLocation const& _sourceLocation,
ASTPointer<TypeName> const& _type, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<Expression> _value, ASTPointer<Expression> _value,
Visibility _visibility, Visibility _visibility,
bool _isStateVar = false, bool _isStateVar = false,
bool _isIndexed = 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_typeName(_type),
m_value(_value), m_value(_value),
m_isStateVariable(_isStateVar), m_isStateVariable(_isStateVar),
m_isIndexed(_isIndexed), m_isIndexed(_isIndexed),
m_isConstant(_isConstant){} m_isConstant(_isConstant),
m_location(_referenceLocation) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
@ -507,10 +511,14 @@ public:
void checkTypeRequirements(); void checkTypeRequirements();
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); } 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 isExternalFunctionParameter() const;
bool isStateVariable() const { return m_isStateVariable; } bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; } bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; } bool isConstant() const { return m_isConstant; }
Location referenceLocation() const { return m_location; }
protected: protected:
Visibility getDefaultVisibility() const override { return Visibility::Internal; } Visibility getDefaultVisibility() const override { return Visibility::Internal; }
@ -521,6 +529,7 @@ private:
bool m_isStateVariable; ///< Whether or not this is a contract state variable 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_isIndexed; ///< Whether this is an indexed variable (used by events).
bool m_isConstant; ///< Whether the variable is a compile-time constant. 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 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 // 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) // 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( solAssert(
_sourceType.getLocation() == ArrayType::Location::CallData || _sourceType.location() == ReferenceType::Location::CallData ||
_sourceType.getLocation() == ArrayType::Location::Storage, _sourceType.location() == ReferenceType::Location::Storage,
"Given array location not implemented." "Given array location not implemented."
); );
@ -51,7 +51,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// TODO unroll loop for small sizes // 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 directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16; bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16;
bool haveByteOffsetTarget = !directCopy && targetBaseType->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; m_context << eth::Instruction::POP;
// stack: target_ref source_ref [source_length] // stack: target_ref source_ref [source_length]
// retrieve 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 retrieveLength(_sourceType); // otherwise, length is already there
// stack: target_ref source_ref source_length // stack: target_ref source_ref source_length
m_context << eth::Instruction::DUP3; m_context << eth::Instruction::DUP3;
@ -82,7 +82,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
if (sourceBaseType->getCategory() == Type::Category::Mapping) if (sourceBaseType->getCategory() == Type::Category::Mapping)
{ {
solAssert(targetBaseType->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 // nothing to copy
m_context m_context
<< eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP
@ -106,7 +106,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag(); eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset); m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized()) if (_sourceType.location() == ReferenceType::Location::Storage && _sourceType.isDynamicallySized())
CompilerUtils(m_context).computeHashStatic(); CompilerUtils(m_context).computeHashStatic();
// stack: target_ref target_data_end source_length target_data_pos source_data_pos // stack: target_ref target_data_end source_length target_data_pos source_data_pos
m_context << eth::Instruction::SWAP2; m_context << eth::Instruction::SWAP2;
@ -155,7 +155,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// checking is easier. // checking is easier.
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] // 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); m_context << eth::dupInstruction(3 + byteOffsetSize);
if (_sourceType.getLocation() == ArrayType::Location::Storage) if (_sourceType.location() == ReferenceType::Location::Storage)
{ {
if (haveByteOffsetSource) if (haveByteOffsetSource)
m_context << eth::Instruction::DUP2; m_context << eth::Instruction::DUP2;
@ -228,7 +228,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
void ArrayUtils::clearArray(ArrayType const& _type) const void ArrayUtils::clearArray(ArrayType const& _type) const
{ {
unsigned stackHeightStart = m_context.getStackHeight(); unsigned stackHeightStart = m_context.getStackHeight();
solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.location() == ReferenceType::Location::Storage, "");
if (_type.getBaseType()->getStorageBytes() < 32) if (_type.getBaseType()->getStorageBytes() < 32)
{ {
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type."); 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 void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
{ {
solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.isDynamicallySized(), ""); solAssert(_type.isDynamicallySized(), "");
unsigned stackHeightStart = m_context.getStackHeight(); unsigned stackHeightStart = m_context.getStackHeight();
@ -311,7 +311,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
{ {
solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.isDynamicallySized(), ""); solAssert(_type.isDynamicallySized(), "");
if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32) if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32)
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type."); 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 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) if (_arrayType.getBaseType()->getStorageSize() <= 1)
{ {
@ -432,15 +432,15 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
else else
{ {
m_context << eth::Instruction::DUP1; 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 // length is stored on the stack
break; break;
case ArrayType::Location::Memory: case ReferenceType::Location::Memory:
m_context << eth::Instruction::MLOAD; m_context << eth::Instruction::MLOAD;
break; break;
case ArrayType::Location::Storage: case ReferenceType::Location::Storage:
m_context << eth::Instruction::SLOAD; m_context << eth::Instruction::SLOAD;
break; break;
} }
@ -449,16 +449,16 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
{ {
ArrayType::Location location = _arrayType.getLocation(); ReferenceType::Location location = _arrayType.location();
eth::Instruction load = eth::Instruction load =
location == ArrayType::Location::Storage ? eth::Instruction::SLOAD : location == ReferenceType::Location::Storage ? eth::Instruction::SLOAD :
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD : location == ReferenceType::Location::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD; eth::Instruction::CALLDATALOAD;
// retrieve length // retrieve length
if (!_arrayType.isDynamicallySized()) if (!_arrayType.isDynamicallySized())
m_context << _arrayType.getLength(); m_context << _arrayType.getLength();
else if (location == ArrayType::Location::CallData) else if (location == ReferenceType::Location::CallData)
// length is stored on the stack // length is stored on the stack
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
else else
@ -473,15 +473,15 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
if (_arrayType.isDynamicallySized()) if (_arrayType.isDynamicallySized())
{ {
if (location == ArrayType::Location::Storage) if (location == ReferenceType::Location::Storage)
CompilerUtils(m_context).computeHashStatic(); CompilerUtils(m_context).computeHashStatic();
else if (location == ArrayType::Location::Memory) else if (location == ReferenceType::Location::Memory)
m_context << u256(32) << eth::Instruction::ADD; m_context << u256(32) << eth::Instruction::ADD;
} }
// stack: <index> <data_ref> // stack: <index> <data_ref>
switch (location) switch (location)
{ {
case ArrayType::Location::CallData: case ReferenceType::Location::CallData:
if (!_arrayType.isByteArray()) if (!_arrayType.isByteArray())
m_context m_context
<< eth::Instruction::SWAP1 << eth::Instruction::SWAP1
@ -496,7 +496,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
false false
); );
break; break;
case ArrayType::Location::Storage: case ReferenceType::Location::Storage:
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
if (_arrayType.getBaseType()->getStorageBytes() <= 16) if (_arrayType.getBaseType()->getStorageBytes() <= 16)
{ {
@ -524,7 +524,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::ADD << u256(0); m_context << eth::Instruction::ADD << u256(0);
} }
break; break;
case ArrayType::Location::Memory: case ReferenceType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented."); 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); swap(m_context, m_runtimeContext);
initializeContext(_contract, _contracts); initializeContext(_contract, _contracts);
packIntoContractCreator(_contract, m_runtimeContext); packIntoContractCreator(_contract, m_runtimeContext);
if (m_optimize)
m_context.optimise(m_optimizeRuns);
} }
eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _function) const 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)) else if (auto c = m_context.getNextConstructor(_contract))
appendBaseConstructor(*c); 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 // 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; m_context << u256(0) << eth::Instruction::RETURN;
// note that we have to include the functions again because of absolute jump labels // 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>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints; 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 // retrieve the function signature hash from the calldata
if (!interfaceFunctions.empty()) if (!interfaceFunctions.empty())
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true); 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 << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); 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(); eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this); fallback->accept(*this);
@ -194,6 +211,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
} }
else else
m_context << eth::Instruction::STOP; // function not found m_context << eth::Instruction::STOP; // function not found
for (auto const& it: interfaceFunctions) for (auto const& it: interfaceFunctions)
{ {
FunctionTypePointer const& functionType = it.second; FunctionTypePointer const& functionType = it.second;

17
libsolidity/Compiler.h

@ -34,13 +34,18 @@ namespace solidity {
class Compiler: private ASTConstVisitor class Compiler: private ASTConstVisitor
{ {
public: public:
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(), explicit Compiler(bool _optimize = false, unsigned _runs = 200):
m_returnTag(m_context.newTag()) {} m_optimize(_optimize),
m_optimizeRuns(_runs),
m_context(),
m_returnTag(m_context.newTag())
{
}
void compileContract(ContractDefinition const& _contract, void compileContract(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts); std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } bytes getAssembledBytecode() { return m_context.getAssembledBytecode(); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);} bytes getRuntimeBytecode() { return m_context.getAssembledRuntimeBytecode(m_runtimeSub); }
/// @arg _sourceCodes is the map of input files to source code strings /// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFromat shows whether the out should be in Json format /// @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 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 /// @returns Assembly items of the normal compiler context
eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); } eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); }
/// @returns Assembly items of the runtime compiler context /// @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 /// @returns the entry label of the given function. Might return an AssemblyItem of type
/// UndefinedItem if it does not exist yet. /// UndefinedItem if it does not exist yet.
@ -93,7 +98,9 @@ private:
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
bool const m_optimize; bool const m_optimize;
unsigned const m_optimizeRuns;
CompilerContext m_context; CompilerContext m_context;
size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly
CompilerContext m_runtimeContext; CompilerContext m_runtimeContext;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement 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 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<<(u256 const& _value) { m_asm.append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); 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; } eth::Assembly const& getAssembly() const { return m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings /// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFormat shows whether the out should be in Json format /// @arg _inJsonFormat shows whether the out should be in Json format
@ -134,7 +136,8 @@ public:
return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat); 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 * 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) if (!m_parseSuccessful)
parse(); parse();
@ -157,9 +157,9 @@ void CompilerStack::compile(bool _optimize)
{ {
if (!contract->isFullyImplemented()) if (!contract->isFullyImplemented())
continue; continue;
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize); shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
compiler->compileContract(*contract, contractBytecode); compiler->compileContract(*contract, contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()]; Contract& compiledContract = m_contracts.at(contract->getName());
compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode(); compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler); compiledContract.compiler = move(compiler);

2
libsolidity/CompilerStack.h

@ -90,7 +90,7 @@ public:
std::string defaultContractName() const; std::string defaultContractName() const;
/// Compiles the source units that were previously added and parsed. /// 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. /// Parses and compiles the given source code.
/// @returns the compiled bytecode /// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false); 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); auto const& type = dynamic_cast<ArrayType const&>(_type);
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here."); 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 // stack: target source_offset source_len
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5 m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
@ -92,7 +92,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
} }
else 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::POP; // remove offset, arrays always start new slot
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes // stack here: memory_offset storage_offset length_bytes

55
libsolidity/ExpressionCompiler.cpp

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

45
libsolidity/NameAndTypeResolver.cpp

@ -424,10 +424,49 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
if (_variable.getTypeName()) if (_variable.getTypeName())
{ {
TypePointer type = _variable.getTypeName()->toType(); TypePointer type = _variable.getTypeName()->toType();
// All array parameter types should point to call data 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()) if (_variable.isExternalFunctionParameter())
if (auto const* arrayType = dynamic_cast<ArrayType const*>(type.get())) {
type = arrayType->copyForLocation(ArrayType::Location::CallData); // 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); _variable.setType(type);
if (!_variable.getType()) if (!_variable.getType())

91
libsolidity/Parser.cpp

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

9
libsolidity/Parser.h

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

3
libsolidity/Token.h

@ -161,12 +161,14 @@ namespace solidity
K(Import, "import", 0) \ K(Import, "import", 0) \
K(Is, "is", 0) \ K(Is, "is", 0) \
K(Mapping, "mapping", 0) \ K(Mapping, "mapping", 0) \
K(Memory, "memory", 0) \
K(Modifier, "modifier", 0) \ K(Modifier, "modifier", 0) \
K(New, "new", 0) \ K(New, "new", 0) \
K(Public, "public", 0) \ K(Public, "public", 0) \
K(Private, "private", 0) \ K(Private, "private", 0) \
K(Return, "return", 0) \ K(Return, "return", 0) \
K(Returns, "returns", 0) \ K(Returns, "returns", 0) \
K(Storage, "storage", 0) \
K(Struct, "struct", 0) \ K(Struct, "struct", 0) \
K(Var, "var", 0) \ K(Var, "var", 0) \
K(While, "while", 0) \ K(While, "while", 0) \
@ -370,6 +372,7 @@ public:
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } 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 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; } 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) else if (_typeToken == Token::Bool)
return make_shared<BoolType>(); return make_shared<BoolType>();
else if (_typeToken == Token::Bytes) else if (_typeToken == Token::Bytes)
return make_shared<ArrayType>(ArrayType::Location::Storage); return make_shared<ArrayType>(ReferenceType::Location::Storage);
else if (_typeToken == Token::String) else if (_typeToken == Token::String)
return make_shared<ArrayType>(ArrayType::Location::Storage, true); return make_shared<ArrayType>(ReferenceType::Location::Storage, true);
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type.")); 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()); auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
if (!length) if (!length)
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array 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 else
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType); return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType);
} }
TypePointer Type::forLiteral(Literal const& _literal) TypePointer Type::forLiteral(Literal const& _literal)
@ -317,9 +317,9 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
const MemberList IntegerType::AddressMemberList({ const MemberList IntegerType::AddressMemberList({
{"balance", make_shared<IntegerType >(256)}, {"balance", make_shared<IntegerType >(256)},
{"call", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Bare, true)}, {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)},
{"callcode", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::BareCallCode, true)}, {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)} {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
}); });
IntegerConstantType::IntegerConstantType(Literal const& _literal) IntegerConstantType::IntegerConstantType(Literal const& _literal)
@ -361,17 +361,27 @@ IntegerConstantType::IntegerConstantType(Literal const& _literal)
bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{ {
shared_ptr<IntegerType const> integerType = getIntegerType(); if (auto targetType = dynamic_cast<IntegerType const*>(&_convertTo))
if (!integerType) {
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; return false;
}
if (_convertTo.getCategory() == Category::FixedBytes) else if (_convertTo.getCategory() == Category::FixedBytes)
{ {
FixedBytesType const& convertTo = dynamic_cast<FixedBytesType const&>(_convertTo); FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
return convertTo.getNumBytes() * 8 >= integerType->getNumBits(); return fixedBytes.getNumBytes() * 8 >= getIntegerType()->getNumBits();
} }
else
return integerType->isImplicitlyConvertibleTo(_convertTo); return false;
} }
bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const
@ -514,9 +524,10 @@ shared_ptr<IntegerType const> IntegerConstantType::getIntegerType() const
if (value > u256(-1)) if (value > u256(-1))
return shared_ptr<IntegerType const>(); return shared_ptr<IntegerType const>();
else else
return make_shared<IntegerType>(max(bytesRequired(value), 1u) * 8, return make_shared<IntegerType>(
negative ? IntegerType::Modifier::Signed max(bytesRequired(value), 1u) * 8,
: IntegerType::Modifier::Unsigned); negative ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned
);
} }
shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal) shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal)
@ -663,7 +674,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
return false; return false;
auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo); auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
// let us not allow assignment to memory arrays for now // let us not allow assignment to memory arrays for now
if (convertTo.getLocation() != Location::Storage) if (convertTo.location() != Location::Storage)
return false; return false;
if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString()) if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
return false; return false;
@ -767,12 +778,12 @@ TypePointer ArrayType::externalType() const
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length); 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); auto copy = make_shared<ArrayType>(_location);
copy->m_arrayKind = m_arrayKind; copy->m_arrayKind = m_arrayKind;
if (m_baseType->getCategory() == Type::Category::Array) if (auto ref = dynamic_cast<ReferenceType const*>(m_baseType.get()))
copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location); copy->m_baseType = ref->copyForLocation(_location);
else else
copy->m_baseType = m_baseType; copy->m_baseType = m_baseType;
copy->m_hasDynamicLength = m_hasDynamicLength; copy->m_hasDynamicLength = m_hasDynamicLength;
@ -923,6 +934,13 @@ MemberList const& StructType::getMembers() const
return *m_members; 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 pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
{ {
auto const* offsets = getMembers().getMemberStorageOffset(_name); auto const* offsets = getMembers().getMemberStorageOffset(_name);
@ -1455,7 +1473,7 @@ MagicType::MagicType(MagicType::Kind _kind):
{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, {"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)}, {"gas", make_shared<IntegerType>(256)},
{"value", 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)} {"sig", make_shared<FixedBytesType>(4)}
})); }));
break; break;

41
libsolidity/Types.h

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

9
solc/CommandLineInterface.cpp

@ -299,7 +299,8 @@ bool CommandLineInterface::parseArguments(int argc, char** argv)
desc.add_options() desc.add_options()
("help", "Show help message and exit") ("help", "Show help message and exit")
("version", "Show version 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") ("add-std", po::value<bool>()->default_value(false), "Add standard contracts")
("input-file", po::value<vector<string>>(), "input file") ("input-file", po::value<vector<string>>(), "input file")
( (
@ -409,7 +410,11 @@ bool CommandLineInterface::processInput()
for (auto const& sourceCode: m_sourceCodes) for (auto const& sourceCode: m_sourceCodes)
m_compiler->addSource(sourceCode.first, sourceCode.second); m_compiler->addSource(sourceCode.first, sourceCode.second);
// TODO: Perhaps we should not compile unless requested // 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) 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.itemCount() == 4 || i.itemCount() == 11);
BOOST_REQUIRE(i[0].size() == 4 || i[0].size() == 16); BOOST_REQUIRE(i[0].size() == 4 || i[0].size() == 16);
} }
for (auto host: hosts)
delete host;
} }
BOOST_AUTO_TEST_SUITE_END() 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_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() 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_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() 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_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() 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_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() 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_AUTO_TEST_CASE(storage_layout_arrays)
{ {
BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared<FixedBytesType>(1), 32).getStorageSize() == 1); BOOST_CHECK(ArrayType(ReferenceType::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(ReferenceType::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(ReferenceType::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(ReferenceType::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(ReferenceType::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(ReferenceType::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>(32), 9).getStorageSize() == 9);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

3
test/libsolidity/solidityExecutionFramework.h

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

89
test/libwhisper/whisperTopic.cpp

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

Loading…
Cancel
Save