Browse Source

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

Conflicts:
	windows/LibEthereum.vcxproj
	windows/LibEthereum.vcxproj.filters

Solved by removing files (not necessary anymore)
cl-refactor
sveneh 10 years ago
parent
commit
8cbd3b67bc
  1. 61
      alethzero/MainWin.cpp
  2. 2
      alethzero/MainWin.h
  3. 2
      eth/main.cpp
  4. 1
      libdevcore/vector_ref.h
  5. 1
      libethereum/BlockDetails.h
  6. 5
      libethereum/Client.h
  7. 83
      libethereum/Executive.cpp
  8. 31
      libethereum/Executive.h
  9. 36
      libethereum/ExtVM.cpp
  10. 14
      libethereum/ExtVM.h
  11. 7
      libethereum/Interface.h
  12. 4
      libethereum/LogFilter.cpp
  13. 2
      libethereum/LogFilter.h
  14. 0
      libethereum/Manifest.cpp
  15. 0
      libethereum/Manifest.h
  16. 0
      libethereum/PastMessage.cpp
  17. 0
      libethereum/PastMessage.h
  18. 154
      libethereum/State.cpp
  19. 14
      libethereum/State.h
  20. 51
      libethereum/TransactionReceipt.cpp
  21. 13
      libethereum/TransactionReceipt.h
  22. 13
      libevm/ExtVMFace.h
  23. 6
      libevm/VM.cpp
  24. 49
      libevm/VM.h
  25. 55
      libevm/VMFace.h
  26. 42
      libevm/VMFactory.cpp
  27. 42
      libevm/VMFactory.h
  28. 36
      libsolidity/Compiler.cpp
  29. 6
      libsolidity/Compiler.h
  30. 4
      libsolidity/CompilerStack.cpp
  31. 40
      libsolidity/CompilerUtils.cpp
  32. 12
      libsolidity/CompilerUtils.h
  33. 66
      libsolidity/ExpressionCompiler.cpp
  34. 10
      libsolidity/ExpressionCompiler.h
  35. 35
      libsolidity/Token.h
  36. 43
      libsolidity/Types.cpp
  37. 31
      libsolidity/Types.h
  38. 10
      test/TestHelper.cpp
  39. 8
      test/createRandomTest.cpp
  40. 4
      test/jsonrpc.cpp
  41. 14
      test/solidityCompiler.cpp
  42. 70
      test/solidityEndToEndTest.cpp
  43. 4
      test/solidityExecutionFramework.h
  44. 8
      test/solidityNameAndTypeResolution.cpp
  45. 19
      test/solidityOptimizerTest.cpp
  46. 2
      test/state.cpp
  47. 10
      test/trie.cpp
  48. 16
      test/vm.cpp
  49. 4
      test/vm.h
  50. 6
      test/whisperTopic.cpp

61
alethzero/MainWin.cpp

@ -43,6 +43,7 @@
#include <libethereum/BlockChain.h>
#include <libethereum/ExtVM.h>
#include <libethereum/Client.h>
#include <libethereum/Utility.h>
#include <libethereum/EthereumHost.h>
#include <libethereum/DownloadMan.h>
#include <libweb3jsonrpc/WebThreeStubServer.h>
@ -1349,7 +1350,7 @@ void Main::on_debugCurrent_triggered()
{
unsigned txi = item->data(Qt::UserRole + 1).toInt();
m_executiveState = ethereum()->state(txi + 1, h);
m_currentExecution = unique_ptr<Executive>(new Executive(m_executiveState));
m_currentExecution = unique_ptr<Executive>(new Executive(m_executiveState, 0));
Transaction t = m_executiveState.pending()[txi];
m_executiveState = m_executiveState.fromPending(txi);
auto r = t.rlp();
@ -1499,58 +1500,6 @@ void Main::on_destination_currentTextChanged()
// updateFee();
}
static bytes dataFromText(QString _s)
{
bytes ret;
while (_s.size())
{
QRegExp r("(@|\\$)?\"([^\"]*)\"(\\s.*)?");
QRegExp d("(@|\\$)?([0-9]+)(\\s*(ether)|(finney)|(szabo))?(\\s.*)?");
QRegExp h("(@|\\$)?(0x)?(([a-fA-F0-9])+)(\\s.*)?");
if (r.exactMatch(_s))
{
for (auto i: r.cap(2))
ret.push_back((byte)i.toLatin1());
if (r.cap(1) != "$")
for (int i = r.cap(2).size(); i < 32; ++i)
ret.push_back(0);
else
ret.push_back(0);
_s = r.cap(3);
}
else if (d.exactMatch(_s))
{
u256 v(d.cap(2).toStdString());
if (d.cap(6) == "szabo")
v *= dev::eth::szabo;
else if (d.cap(5) == "finney")
v *= dev::eth::finney;
else if (d.cap(4) == "ether")
v *= dev::eth::ether;
bytes bs = dev::toCompactBigEndian(v);
if (d.cap(1) != "$")
for (auto i = bs.size(); i < 32; ++i)
ret.push_back(0);
for (auto b: bs)
ret.push_back(b);
_s = d.cap(7);
}
else if (h.exactMatch(_s))
{
bytes bs = fromHex((((h.cap(3).size() & 1) ? "0" : "") + h.cap(3)).toStdString());
if (h.cap(1) != "$")
for (auto i = bs.size(); i < 32; ++i)
ret.push_back(0);
for (auto b: bs)
ret.push_back(b);
_s = h.cap(5);
}
else
_s = _s.mid(1);
}
return ret;
}
static shh::Topic topicFromText(QString _s)
{
shh::BuildTopic ret;
@ -1678,7 +1627,7 @@ void Main::on_data_textChanged()
}
else
{
m_data = dataFromText(ui->data->toPlainText());
m_data = parseData(ui->data->toPlainText().toStdString());
ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true)));
if (ethereum()->codeAt(fromString(ui->destination->currentText()), 0).size())
{
@ -1854,7 +1803,7 @@ void Main::on_debug_clicked()
{
Secret s = i.secret();
m_executiveState = ethereum()->postState();
m_currentExecution = unique_ptr<Executive>(new Executive(m_executiveState));
m_currentExecution = unique_ptr<Executive>(new Executive(m_executiveState, 0));
Transaction t = isCreation() ?
Transaction(value(), gasPrice(), ui->gas->value(), m_data, m_executiveState.transactionsFrom(dev::toAddress(s)), s) :
Transaction(value(), gasPrice(), ui->gas->value(), fromString(ui->destination->currentText()), m_data, m_executiveState.transactionsFrom(dev::toAddress(s)), s);
@ -2206,7 +2155,7 @@ void Main::on_post_clicked()
{
shh::Message m;
m.setTo(stringToPublic(ui->shhTo->currentText()));
m.setPayload(dataFromText(ui->shhData->toPlainText()));
m.setPayload(parseData(ui->shhData->toPlainText().toStdString()));
Public f = stringToPublic(ui->shhFrom->currentText());
Secret from;
if (m_server->ids().count(f))

2
alethzero/MainWin.h

@ -33,6 +33,7 @@
#include <libdevcore/RLP.h>
#include <libethcore/CommonEth.h>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libqethereum/QEthereum.h>
#include <libwebthree/WebThree.h>
@ -43,7 +44,6 @@ class Main;
namespace dev { namespace eth {
class Client;
class State;
class MessageFilter;
}}
class QQuickView;

2
eth/main.cpp

@ -620,7 +620,7 @@ int main(int argc, char** argv)
dev::eth::State state =c->state(index + 1,c->blockChain().numberHash(block));
if (index < state.pending().size())
{
Executive e(state);
Executive e(state, 0);
Transaction t = state.pending()[index];
state = state.fromPending(index);
bytes r = t.rlp();

1
libdevcore/vector_ref.h

@ -40,6 +40,7 @@ public:
vector_ref<_T> cropped(size_t _begin, size_t _count = ~size_t(0)) const { if (m_data && _begin + std::max(size_t(0), _count) <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); }
void retarget(_T const* _d, size_t _s) { m_data = _d; m_count = _s; }
void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); }
void copyTo(vector_ref<typename std::remove_const<_T>::type> _t) const { memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); }
_T* begin() { return m_data; }
_T* end() { return m_data + m_count; }

1
libethereum/BlockDetails.h

@ -28,7 +28,6 @@
#include <libdevcore/Log.h>
#include <libdevcore/RLP.h>
#include "Manifest.h"
#include "TransactionReceipt.h"
namespace ldb = leveldb;

5
libethereum/Client.h

@ -36,8 +36,7 @@
#include "TransactionQueue.h"
#include "State.h"
#include "CommonNet.h"
#include "PastMessage.h"
#include "MessageFilter.h"
#include "LogFilter.h"
#include "Miner.h"
#include "Interface.h"
@ -79,8 +78,6 @@ static const int GenesisBlock = INT_MIN;
struct InstalledFilter
{
// InstalledFilter(MessageFilter const& _f): filter(_f) {}
// MessageFilter filter;
InstalledFilter(LogFilter const& _f): filter(_f) {}
LogFilter filter;

83
libethereum/Executive.cpp

@ -19,11 +19,13 @@
* @date 2014
*/
#include "Executive.h"
#include <boost/timer.hpp>
#include <libdevcore/CommonIO.h>
#include <libevm/VMFactory.h>
#include <libevm/VM.h>
#include "Interface.h"
#include "Executive.h"
#include "State.h"
#include "ExtVM.h"
using namespace std;
@ -32,10 +34,6 @@ using namespace dev::eth;
#define ETH_VMTRACE 1
Executive::~Executive()
{
}
u256 Executive::gasUsed() const
{
return m_t.gas() - m_endGas;
@ -91,32 +89,36 @@ bool Executive::setup(bytesConstRef _rlp)
if (m_t.isCreation())
return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasCost, &m_t.data(), m_sender);
else
return call(m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasCost, m_sender);
return call(m_t.receiveAddress(), m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasCost, m_sender);
}
bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
{
m_isCreation = false;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
m_s.addBalance(_receiveAddress, _value);
auto it = !(_receiveAddress & ~h160(0xffffffff)) ? State::precompiled().find((unsigned)(u160)_receiveAddress) : State::precompiled().end();
auto it = !(_codeAddress & ~h160(0xffffffff)) ? State::precompiled().find((unsigned)(u160)_codeAddress) : State::precompiled().end();
if (it != State::precompiled().end())
{
bigint g = it->second.gas(_data);
if (_gas < g)
{
m_endGas = 0;
return false;
m_excepted = true;
}
else
{
m_endGas = (u256)(_gas - g);
m_precompiledOut = it->second.exec(_data);
m_out = &m_precompiledOut;
}
m_endGas = (u256)(_gas - g);
it->second.exec(_data, bytesRef());
return true;
}
else if (m_s.addressHasCode(_receiveAddress))
else if (m_s.addressHasCode(_codeAddress))
{
m_vm = make_shared<VM>(_gas);
bytes const& c = m_s.code(_receiveAddress);
m_ext = make_shared<ExtVM>(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c);
m_vm = VMFactory::create(_gas);
bytes const& c = m_s.code(_codeAddress);
m_ext = make_shared<ExtVM>(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth);
}
else
m_endGas = _gas;
@ -125,6 +127,8 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu
bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin)
{
m_isCreation = true;
// We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since
// we delete it explicitly if we decide we need to revert.
m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));
@ -133,8 +137,8 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress) + _endowment, Account::ContractConception);
// Execute _init.
m_vm = make_shared<VM>(_gas);
m_ext = make_shared<ExtVM>(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init);
m_vm = VMFactory::create(_gas);
m_ext = make_shared<ExtVM>(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth);
return _init.empty();
}
@ -163,17 +167,20 @@ bool Executive::go(OnOpFunc const& _onOp)
if (m_vm)
{
boost::timer t;
auto sgas = m_vm->gas();
// auto sgas = m_vm->gas();
try
{
m_out = m_vm->go(*m_ext, _onOp);
m_endGas = m_vm->gas();
m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);
m_logs = m_ext->sub.logs;
if (m_out.size() * c_createDataGas <= m_endGas)
m_endGas -= m_out.size() * c_createDataGas;
else
m_out.reset();
if (m_isCreation)
{
if (m_out.size() * c_createDataGas <= m_endGas)
m_endGas -= m_out.size() * c_createDataGas;
else
m_out.reset();
m_s.m_cache[m_newAddress].setCode(m_out);
}
}
catch (StepsDone const&)
{
@ -183,16 +190,10 @@ bool Executive::go(OnOpFunc const& _onOp)
{
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
m_endGas = 0;//m_vm->gas();
m_excepted = true;
// Write state out only in the case of a non-excepted transaction.
m_ext->revert();
// Explicitly delete a newly created address - this will still be in the reverted state.
/* if (m_newAddress)
{
m_s.m_cache.erase(m_newAddress);
m_newAddress = Address();
}*/
}
catch (Exception const& _e)
{
@ -204,25 +205,22 @@ bool Executive::go(OnOpFunc const& _onOp)
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
}
cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
// cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
}
return true;
}
u256 Executive::gas() const
/*u256 Executive::gas() const
{
return m_vm ? m_vm->gas() : m_endGas;
}
}*/
void Executive::finalize(OnOpFunc const&)
{
if (m_t.isCreation() && !m_ext->sub.suicides.count(m_newAddress))
{
// creation - put code in place.
m_s.m_cache[m_newAddress].setCode(m_out);
}
// SSTORE refunds.
m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_sender, m_endGas * m_t.gasPrice());
u256 feesEarned = (m_t.gas() - m_endGas) * m_t.gasPrice();
@ -233,4 +231,7 @@ void Executive::finalize(OnOpFunc const&)
if (m_ext)
for (auto a: m_ext->sub.suicides)
m_s.m_cache[a].kill();
// Logs
m_logs = m_ext->sub.logs;
}

31
libethereum/Executive.h

@ -25,30 +25,32 @@
#include <libdevcore/Log.h>
#include <libevmcore/Instruction.h>
#include <libethcore/CommonEth.h>
#include <libevm/ExtVMFace.h>
#include <libevm/VMFace.h>
#include "Transaction.h"
#include "Manifest.h"
namespace dev
{
namespace eth
{
class VM;
class ExtVM;
class State;
class ExtVM;
struct Manifest;
struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; };
class Executive
{
public:
Executive(State& _s): m_s(_s){}
~Executive();
Executive(State& _s, unsigned _level): m_s(_s), m_depth(_level) {}
~Executive() = default;
Executive(Executive const&) = delete;
void operator=(Executive) = delete;
bool setup(bytesConstRef _transaction);
bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool go(OnOpFunc const& _onOp = OnOpFunc());
void finalize(OnOpFunc const& _onOp = OnOpFunc());
u256 gasUsed() const;
@ -57,24 +59,29 @@ public:
Transaction const& t() const { return m_t; }
u256 gas() const;
u256 endGas() const { return m_endGas; }
bytesConstRef out() const { return m_out; }
h160 newAddress() const { return m_newAddress; }
LogEntries const& logs() const { return m_logs; }
bool excepted() const { return m_excepted; }
VM const& vm() const { return *m_vm; }
State const& state() const { return m_s; }
VMFace const& vm() const { return *m_vm; }
ExtVM const& ext() const { return *m_ext; }
State const& state() const { return m_s; }
private:
State& m_s;
std::shared_ptr<ExtVM> m_ext;
std::shared_ptr<VM> m_vm;
bytesConstRef m_out;
std::unique_ptr<VMFace> m_vm;
bytes m_precompiledOut; ///< Used for the output when there is no VM for a contract (i.e. precompiled).
bytesConstRef m_out; ///< Holds the copyable output.
Address m_newAddress;
Transaction m_t;
bool m_isCreation;
bool m_excepted = false;
unsigned m_depth = 0;
Address m_sender;
u256 m_endGas;

36
libethereum/ExtVM.cpp

@ -21,5 +21,37 @@
#include "ExtVM.h"
#pragma GCC diagnostic ignored "-Wunused-variable"
namespace { char dummy; };
#include "Executive.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
bool ExtVM::call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256& io_gas, bytesRef _out, OnOpFunc const& _onOp, Address _myAddressOverride, Address _codeAddressOverride)
{
Executive e(m_s, depth + 1);
if (!e.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, io_gas, origin))
{
e.go(_onOp);
sub += e.ext().sub;
}
io_gas = e.endGas();
e.out().copyTo(_out);
return !e.excepted();
}
h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp)
{
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
Executive e(m_s, depth + 1);
if (!e.create(myAddress, _endowment, gasPrice, io_gas, _code, origin))
{
e.go(_onOp);
sub += e.ext().sub;
}
io_gas = e.endGas();
return e.newAddress();
}

14
libethereum/ExtVM.h

@ -55,18 +55,10 @@ public:
virtual bytes const& codeAt(Address _a) override final { return m_s.code(_a); }
/// Create a new contract.
virtual h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, OnOpFunc const& _onOp = OnOpFunc()) override final
{
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
return m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, _onOp, depth + 1);
}
virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp = {}) override final;
/// Create a new message call. Leave _myAddressOverride as the default to use the present address as caller.
virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final
{
return m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, _onOp, depth + 1);
}
virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256& io_gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final;
/// Read address's balance.
virtual u256 balance(Address _a) override final { return m_s.balance(_a); }
@ -86,7 +78,7 @@ public:
/// Revert any changes made (by any of the other calls).
/// @TODO check call site for the parent manifest being discarded.
virtual void revert() override final { m_s.m_cache = m_origCache; }
virtual void revert() override final { m_s.m_cache = m_origCache; sub.clear(); }
State& state() const { return m_s; }

7
libethereum/Interface.h

@ -26,7 +26,7 @@
#include <libdevcore/Guards.h>
#include <libdevcrypto/Common.h>
#include <libevm/FeeStructure.h>
#include "MessageFilter.h"
#include "LogFilter.h"
#include "Transaction.h"
#include "AccountDiff.h"
#include "BlockDetails.h"
@ -84,11 +84,6 @@ public:
virtual bytes codeAt(Address _a, int _block) const = 0;
virtual std::map<u256, u256> storageAt(Address _a, int _block) const = 0;
// // [MESSAGE API]
//
// virtual PastMessages messages(unsigned _watchId) const = 0;
// virtual PastMessages messages(MessageFilter const& _filter) const = 0;
// [LOGS API]
virtual LogEntries logs(unsigned _watchId) const = 0;

4
libethereum/MessageFilter.cpp → libethereum/LogFilter.cpp

@ -14,12 +14,12 @@
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 MessageFilter.cpp
/** @file LogFilter.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "MessageFilter.h"
#include "LogFilter.h"
#include <libdevcrypto/SHA3.h>
#include "State.h"

2
libethereum/MessageFilter.h → libethereum/LogFilter.h

@ -14,7 +14,7 @@
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file MessageFilter.h
/** @file LogFilter.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/

0
libethereum/Manifest.cpp

0
libethereum/Manifest.h

0
libethereum/PastMessage.cpp

0
libethereum/PastMessage.h

154
libethereum/State.cpp

@ -29,10 +29,11 @@
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libethcore/Exceptions.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include "BlockChain.h"
#include "Defaults.h"
#include "ExtVM.h"
#include "Executive.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -41,7 +42,7 @@ using namespace dev::eth;
static const u256 c_blockReward = 1500 * finney;
void ecrecoverCode(bytesConstRef _in, bytesRef _out)
bytes ecrecoverCode(bytesConstRef _in)
{
struct inType
{
@ -53,38 +54,38 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out)
memcpy(&in, _in.data(), min(_in.size(), sizeof(in)));
memset(_out.data(), 0, _out.size());
h256 ret;
if ((u256)in.v > 28)
return;
return ret.asBytes();
SignatureStruct sig{in.r, in.s, (byte)((int)(u256)in.v - 27)};
if (!sig.isValid())
return;
return ret.asBytes();
h256 ret;
byte pubkey[65];
int pubkeylen = 65;
secp256k1_start();
if (secp256k1_ecdsa_recover_compact(in.hash.data(), 32, in.r.data(), pubkey, &pubkeylen, 0, (int)(u256)in.v - 27))
ret = dev::sha3(bytesConstRef(&(pubkey[1]), 64));
memset(ret.data(), 0, 12);
memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret)));
return ret.asBytes();
}
void sha256Code(bytesConstRef _in, bytesRef _out)
bytes sha256Code(bytesConstRef _in)
{
h256 ret;
sha256(_in, bytesRef(ret.data(), 32));
memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret)));
bytes ret(32);
sha256(_in, &ret);
return ret;
}
void ripemd160Code(bytesConstRef _in, bytesRef _out)
bytes ripemd160Code(bytesConstRef _in)
{
h256 ret;
ripemd160(_in, bytesRef(ret.data(), 32));
memset(_out.data(), 0, std::min<int>(12, _out.size()));
if (_out.size() > 12)
memcpy(_out.data() + 12, &ret, min(_out.size() - 12, sizeof(ret)));
bytes ret(32);
ripemd160(_in, &ret);
// leaves the 20-byte hash left-aligned. we want it right-aligned:
memmove(ret.data() + 12, ret.data(), 20);
memset(ret.data(), 0, 12);
return ret;
}
const std::map<unsigned, PrecompiledAddress> State::c_precompiled =
@ -1093,7 +1094,7 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const
return false;
}
}
catch (InvalidTrie)
catch (InvalidTrie const&)
{
cwarn << "BAD TRIE" << (e ? "[enforced" : "[unenforced") << "refs]";
cnote << m_db.keys();
@ -1118,7 +1119,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
auto h = rootHash();
#endif
Executive e(*this);
Executive e(*this, 0);
e.setup(_rlp);
u256 startGasUsed = gasUsed();
@ -1173,119 +1174,6 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
return e.gasUsed();
}
bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, SubState* o_sub, OnOpFunc const& _onOp, unsigned _level)
{
if (!_originAddress)
_originAddress = _senderAddress;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
addBalance(_receiveAddress, _value);
auto it = !(_codeAddress & ~h160(0xffffffff)) ? c_precompiled.find((unsigned)(u160)_codeAddress) : c_precompiled.end();
if (it != c_precompiled.end())
{
bigint g = it->second.gas(_data);
if (*_gas < g)
{
*_gas = 0;
return false;
}
*_gas -= (u256)g;
it->second.exec(_data, _out);
}
else if (addressHasCode(_codeAddress))
{
VM vm(*_gas);
ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), _level);
try
{
auto out = vm.go(evm, _onOp);
memcpy(_out.data(), out.data(), std::min(out.size(), _out.size()));
if (o_sub)
*o_sub += evm.sub;
*_gas = vm.gas();
// Write state out only in the case of a non-excepted transaction.
return true;
}
catch (VMException const& _e)
{
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
evm.revert();
*_gas = 0;
return false;
}
catch (Exception const& _e)
{
cwarn << "Unexpected exception in VM: " << diagnostic_information(_e) << ". This is exceptionally bad.";
// TODO: use fallback known-safe VM.
// AUDIT: THIS SHOULD NEVER HAPPEN! PROVE IT!
throw;
}
catch (std::exception const& _e)
{
cwarn << "Unexpected exception in VM: " << _e.what() << ". This is exceptionally bad.";
// TODO: use fallback known-safe VM.
// AUDIT: THIS SHOULD NEVER HAPPEN! PROVE IT!
throw;
}
}
return true;
}
h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, OnOpFunc const& _onOp, unsigned _level)
{
if (!_origin)
_origin = _sender;
Address newAddress = right160(sha3(rlpList(_sender, transactionsFrom(_sender) - 1)));
// Set up new account...
m_cache[newAddress] = Account(balance(newAddress) + _endowment, Account::ContractConception);
// Execute init code.
VM vm(*_gas);
ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, _level);
bytesConstRef out;
try
{
out = vm.go(evm, _onOp);
if (o_sub)
*o_sub += evm.sub;
*_gas = vm.gas();
if (out.size() * c_createDataGas <= *_gas)
*_gas -= out.size() * c_createDataGas;
else
out.reset();
// Set code.
if (!evm.sub.suicides.count(newAddress))
m_cache[newAddress].setCode(out);
}
catch (VMException const& _e)
{
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
evm.revert();
*_gas = 0;
}
catch (Exception const& _e)
{
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e);
throw;
}
catch (std::exception const& _e)
{
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
throw;
}
return newAddress;
}
State State::fromPending(unsigned _i) const
{
State ret = *this;

14
libethereum/State.h

@ -36,7 +36,6 @@
#include "Account.h"
#include "Transaction.h"
#include "TransactionReceipt.h"
#include "Executive.h"
#include "AccountDiff.h"
namespace dev
@ -56,7 +55,7 @@ struct StateDetail: public LogChannel { static const char* name() { return "/S/"
struct PrecompiledAddress
{
std::function<bigint(bytesConstRef)> gas;
std::function<void(bytesConstRef, bytesRef)> exec;
std::function<bytes(bytesConstRef)> exec;
};
/**
@ -273,17 +272,6 @@ private:
/// Throws on failure.
u256 enact(bytesConstRef _block, BlockChain const* _bc = nullptr, bool _checkNonce = true);
// Two priviledged entry points for the VM (these don't get added to the Transaction lists):
// We assume all instrinsic fees are paid up before this point.
/// Execute a contract-creation transaction.
h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), SubState* o_sub = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Execute a call.
/// @a _gas points to the amount of gas to use for the call, and will lower it accordingly.
/// @returns false if the call ran out of gas before completion. true otherwise.
bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), SubState* o_sub = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();

51
libethereum/TransactionReceipt.cpp

@ -0,0 +1,51 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TransactionReceipt.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "TransactionReceipt.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
TransactionReceipt::TransactionReceipt(bytesConstRef _rlp)
{
RLP r(_rlp);
m_stateRoot = (h256)r[0];
m_gasUsed = (u256)r[1];
m_bloom = (LogBloom)r[2];
for (auto const& i: r[3])
m_log.emplace_back(i);
}
TransactionReceipt::TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log):
m_stateRoot(_root),
m_gasUsed(_gasUsed),
m_bloom(eth::bloom(_log)),
m_log(_log)
{}
void TransactionReceipt::streamRLP(RLPStream& _s) const
{
_s.appendList(4) << m_stateRoot << m_gasUsed << m_bloom;
_s.appendList(m_log.size());
for (LogEntry const& l: m_log)
l.streamRLP(_s);
}

13
libethereum/TransactionReceipt.h

@ -26,7 +26,6 @@
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libevm/ExtVMFace.h>
#include "Manifest.h"
namespace dev
{
@ -37,21 +36,15 @@ namespace eth
class TransactionReceipt
{
public:
TransactionReceipt(bytesConstRef _rlp) { RLP r(_rlp); m_stateRoot = (h256)r[0]; m_gasUsed = (u256)r[1]; m_bloom = (LogBloom)r[2]; for (auto const& i: r[3]) m_log.emplace_back(i); }
TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log) {}
TransactionReceipt(bytesConstRef _rlp);
TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log);
h256 const& stateRoot() const { return m_stateRoot; }
u256 const& gasUsed() const { return m_gasUsed; }
LogBloom const& bloom() const { return m_bloom; }
LogEntries const& log() const { return m_log; }
void streamRLP(RLPStream& _s) const
{
_s.appendList(4) << m_stateRoot << m_gasUsed << m_bloom;
_s.appendList(m_log.size());
for (LogEntry const& l: m_log)
l.streamRLP(_s);
}
void streamRLP(RLPStream& _s) const;
bytes rlp() const { RLPStream s; streamRLP(s); return s.out(); }

13
libevm/ExtVMFace.h

@ -83,6 +83,13 @@ struct SubState
logs += _s.logs;
return *this;
}
void clear()
{
suicides.clear();
logs.clear();
refunds = 0;
}
};
class ExtVMFace;
@ -129,10 +136,10 @@ public:
virtual void suicide(Address) { sub.suicides.insert(myAddress); }
/// Create a new (contract) account.
virtual h160 create(u256, u256*, bytesConstRef, OnOpFunc const&) { return h160(); }
virtual h160 create(u256, u256&, bytesConstRef, OnOpFunc const&) { return h160(); }
/// Make a new message call.
virtual bool call(Address, u256, bytesConstRef, u256*, bytesRef, OnOpFunc const&, Address, Address) { return false; }
virtual bool call(Address, u256, bytesConstRef, u256&, bytesRef, OnOpFunc const&, Address, Address) { return false; }
/// Revert any changes made (by any of the other calls).
virtual void log(h256s&& _topics, bytesConstRef _data) { sub.logs.push_back(LogEntry(myAddress, std::move(_topics), _data.toBytes())); }
@ -153,7 +160,7 @@ public:
BlockInfo previousBlock; ///< The previous block's information.
BlockInfo currentBlock; ///< The current block's information.
SubState sub; ///< Sub-band VM state (suicides, refund counter, logs).
unsigned depth; ///< Depth of the present call.
unsigned depth = 0; ///< Depth of the present call.
};
}

6
libevm/VM.cpp

@ -20,14 +20,14 @@
*/
#include "VM.h"
#include <libethereum/ExtVM.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
void VM::reset(u256 _gas)
void VM::reset(u256 _gas) noexcept
{
m_gas = _gas;
VMFace::reset(_gas);
m_curPC = 0;
m_jumpDests.clear();
}

49
libevm/VM.h

@ -28,21 +28,13 @@
#include <libdevcrypto/SHA3.h>
#include <libethcore/BlockInfo.h>
#include "FeeStructure.h"
#include "ExtVMFace.h"
#include "VMFace.h"
namespace dev
{
namespace eth
{
struct VMException: virtual Exception {};
struct StepsDone: virtual VMException {};
struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {};
struct StackTooSmall: virtual public VMException {};
// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash.
// Currently we just pull out the right (low-order in BE) 160-bits.
inline Address asAddress(u256 _item)
@ -57,28 +49,27 @@ inline u256 fromAddress(Address _a)
/**
*/
class VM
class VM: public VMFace
{
public:
/// Construct VM object.
explicit VM(u256 _gas = 0) { reset(_gas); }
void reset(u256 _gas = 0);
virtual void reset(u256 _gas = 0) noexcept override final;
template <class Ext>
bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1);
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void require(u256 _n) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackTooSmall() << RequirementError((bigint)_n, (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 gas() const { return m_gas; }
u256 curPC() const { return m_curPC; }
bytes const& memory() const { return m_temp; }
u256s const& stack() const { return m_stack; }
private:
u256 m_gas = 0;
friend class VMFactory;
/// Construct VM object.
explicit VM(u256 _gas): VMFace(_gas) {}
u256 m_curPC = 0;
bytes m_temp;
u256s m_stack;
@ -86,10 +77,8 @@ private:
std::function<void()> m_onFail;
};
}
// INLINE:
template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps)
// TODO: Move it to cpp file. Not done to make review easier.
inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{
auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; };
@ -164,7 +153,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::SLOAD:
require(1);
runGas = c_sloadGas;
runGas = c_sloadGas;
break;
// These all operate on memory and therefore potentially expand it:
@ -412,7 +401,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.pop_back();
break;
case Instruction::SDIV:
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0;
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0;
m_stack.pop_back();
break;
case Instruction::MOD:
@ -420,7 +409,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.pop_back();
break;
case Instruction::SMOD:
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0;
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0;
m_stack.pop_back();
break;
case Instruction::EXP:
@ -443,11 +432,11 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.pop_back();
break;
case Instruction::SLT:
m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0;
m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::SGT:
m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0;
m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::EQ:
@ -809,7 +798,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
if (_ext.depth == 1024)
BOOST_THROW_EXCEPTION(OutOfGas());
_ext.subBalance(endowment);
m_stack.push_back((u160)_ext.create(endowment, &m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
m_stack.push_back((u160)_ext.create(endowment, m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
}
else
m_stack.push_back(0);
@ -839,7 +828,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
if (_ext.depth == 1024)
BOOST_THROW_EXCEPTION(OutOfGas());
_ext.subBalance(value);
m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, Address(), receiveAddress));
m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, {}, receiveAddress));
}
else
m_stack.push_back(0);
@ -870,4 +859,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
BOOST_THROW_EXCEPTION(StepsDone());
return bytesConstRef();
}
}
}

55
libevm/VMFace.h

@ -0,0 +1,55 @@
/*
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/>.
*/
#pragma once
#include <memory>
#include <libdevcore/Exceptions.h>
#include "ExtVMFace.h"
namespace dev
{
namespace eth
{
struct VMException: virtual Exception {};
struct StepsDone: virtual VMException {};
struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {};
struct StackTooSmall: virtual VMException {};
/// EVM Virtual Machine interface
class VMFace
{
public:
explicit VMFace(u256 _gas): m_gas(_gas) {}
virtual ~VMFace() = default;
VMFace(VMFace const&) = delete;
void operator=(VMFace const&) = delete;
virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; }
u256 gas() const noexcept { return m_gas; }
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;
protected:
u256 m_gas = 0;
};
}
}

42
libevm/VMFactory.cpp

@ -0,0 +1,42 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
#include "VMFactory.h"
#include "VM.h"
namespace dev
{
namespace eth
{
namespace
{
VMKind g_kind = VMKind::Interpreter;
}
void VMFactory::setKind(VMKind _kind)
{
g_kind = _kind;
}
std::unique_ptr<VMFace> VMFactory::create(u256 _gas)
{
asserts(g_kind == VMKind::Interpreter && "Only interpreter supported for now");
return std::unique_ptr<VMFace>(new VM(_gas));
}
}
}

42
libevm/VMFactory.h

@ -0,0 +1,42 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "VMFace.h"
namespace dev
{
namespace eth
{
enum class VMKind: bool
{
Interpreter,
JIT
};
class VMFactory
{
public:
VMFactory() = delete;
static std::unique_ptr<VMFace> create(u256 _gas);
static void setKind(VMKind _kind);
};
}
}

36
libsolidity/Compiler.cpp

@ -109,8 +109,8 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
callDataUnpackerEntryPoints.push_back(m_context.newTag());
m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back());
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
//@todo avoid the last ADD (or remove it in the optimizer)
if (funid < interfaceFunctions.size() - 1)
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
}
m_context << eth::Instruction::STOP; // function not found
@ -130,21 +130,17 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
{
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1;
eth::Instruction load = _fromMemory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD;
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{
unsigned const numBytes = var->getType()->getCalldataEncodedSize();
if (numBytes == 0 || numBytes > 32)
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(var->getLocation())
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
if (numBytes == 32)
m_context << u256(dataOffset) << load;
else
m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset)
<< load << eth::Instruction::DIV;
bool leftAligned = var->getType()->getCategory() == Type::Category::STRING;
CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory);
dataOffset += numBytes;
}
return dataOffset;
@ -160,14 +156,13 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
{
Type const& paramType = *parameters[i]->getType();
unsigned numBytes = paramType.getCalldataEncodedSize();
if (numBytes == 0 || numBytes > 32)
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(parameters[i]->getLocation())
<< errinfo_comment("Type " + paramType.toString() + " not yet supported."));
CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
if (numBytes != 32)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(dataOffset) << eth::Instruction::MSTORE;
bool const leftAligned = paramType.getCategory() == Type::Category::STRING;
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
stackDepth -= paramType.getSizeOnStack();
dataOffset += numBytes;
}
@ -246,7 +241,7 @@ bool Compiler::visit(FunctionDefinition const& _function)
bool Compiler::visit(IfStatement const& _ifStatement)
{
ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition());
compileExpression(_ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
if (_ifStatement.getFalseStatement())
_ifStatement.getFalseStatement()->accept(*this);
@ -265,7 +260,7 @@ bool Compiler::visit(WhileStatement const& _whileStatement)
m_breakTags.push_back(loopEnd);
m_context << loopStart;
ExpressionCompiler::compileExpression(m_context, _whileStatement.getCondition());
compileExpression(_whileStatement.getCondition());
m_context << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(loopEnd);
@ -298,7 +293,7 @@ bool Compiler::visit(Return const& _return)
//@todo modifications are needed to make this work with functions returning multiple values
if (Expression const* expression = _return.getExpression())
{
ExpressionCompiler::compileExpression(m_context, *expression);
compileExpression(*expression);
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());
@ -312,7 +307,7 @@ bool Compiler::visit(VariableDefinition const& _variableDefinition)
{
if (Expression const* expression = _variableDefinition.getExpression())
{
ExpressionCompiler::compileExpression(m_context, *expression);
compileExpression(*expression);
ExpressionCompiler::appendTypeConversion(m_context,
*expression->getType(),
*_variableDefinition.getDeclaration().getType());
@ -324,10 +319,15 @@ bool Compiler::visit(VariableDefinition const& _variableDefinition)
bool Compiler::visit(ExpressionStatement const& _expressionStatement)
{
Expression const& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression);
compileExpression(expression);
CompilerUtils(m_context).popStackElement(*expression.getType());
return false;
}
void Compiler::compileExpression(Expression const& _expression)
{
ExpressionCompiler::compileExpression(m_context, _expression, m_optimize);
}
}
}

6
libsolidity/Compiler.h

@ -30,10 +30,10 @@ namespace solidity {
class Compiler: private ASTConstVisitor
{
public:
Compiler(): m_returnTag(m_context.newTag()) {}
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {}
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); }
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private:
@ -57,7 +57,9 @@ private:
virtual bool visit(VariableDefinition const& _variableDefinition) override;
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
void compileExpression(Expression const& _expression);
bool const m_optimize;
CompilerContext m_context;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement

4
libsolidity/CompilerStack.cpp

@ -101,10 +101,10 @@ void CompilerStack::compile(bool _optimize)
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
m_globalContext->setCurrentContract(*contract);
shared_ptr<Compiler> compiler = make_shared<Compiler>();
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, m_globalContext->getMagicVariables());
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode(_optimize);
compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.compiler = move(compiler);
}
}

40
libsolidity/CompilerUtils.cpp

@ -31,6 +31,46 @@ namespace dev
namespace solidity
{
void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata)
{
if (_bytes == 0)
{
m_context << u256(0);
return;
}
eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD;
if (asserts(_bytes <= 32))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory load of more than 32 bytes requested."));
if (_bytes == 32)
m_context << u256(_offset) << load;
else
{
// load data and add leading or trailing zeros by dividing/multiplying depending on alignment
u256 shiftFactor = u256(1) << ((32 - _bytes) * 8);
m_context << shiftFactor;
if (_leftAligned)
m_context << eth::Instruction::DUP1;
m_context << u256(_offset) << load << eth::Instruction::DIV;
if (_leftAligned)
m_context << eth::Instruction::MUL;
}
}
void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned)
{
if (_bytes == 0)
{
m_context << eth::Instruction::POP;
return;
}
if (asserts(_bytes <= 32))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory store of more than 32 bytes requested."));
if (_bytes != 32 && !_leftAligned)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
m_context << u256(_offset) << eth::Instruction::MSTORE;
}
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
{
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));

12
libsolidity/CompilerUtils.h

@ -35,6 +35,18 @@ class CompilerUtils
public:
CompilerUtils(CompilerContext& _context): m_context(_context) {}
/// Loads data from memory to the stack.
/// @param _offset offset in memory (or calldata)
/// @param _bytes number of bytes to load
/// @param _leftAligned if true, store left aligned on stack (otherwise right aligned)
/// @param _fromCalldata if true, load from calldata, not from memory
void loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, bool _fromCalldata = false);
/// Stores data from stack in memory.
/// @param _offset offset in memory
/// @param _bytes number of bytes to store
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false);
/// Moves the value that is at the top of the stack to a stack variable.
void moveToStackVariable(VariableDeclaration const& _variable);
/// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack.

66
libsolidity/ExpressionCompiler.cpp

@ -33,9 +33,9 @@ using namespace std;
namespace dev {
namespace solidity {
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression)
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize)
{
ExpressionCompiler compiler(_context);
ExpressionCompiler compiler(_context, _optimize);
_expression.accept(compiler);
}
@ -145,10 +145,24 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD)
cleanupNeeded = true;
rightExpression.accept(*this);
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
leftExpression.accept(*this);
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
// for commutative operators, push the literal as late as possible to allow improved optimization
//@todo this has to be extended for literal expressions
bool swap = (m_optimize && Token::isCommutativeOp(op) && dynamic_cast<Literal const*>(&rightExpression)
&& !dynamic_cast<Literal const*>(&leftExpression));
if (swap)
{
leftExpression.accept(*this);
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
rightExpression.accept(*this);
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
}
else
{
rightExpression.accept(*this);
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
leftExpression.accept(*this);
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
}
if (Token::isCompareOp(op))
appendCompareOperatorCode(op, commonType);
else
@ -225,14 +239,14 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(arguments[i]->getLocation())
<< errinfo_comment("Type " + type.toString() + " not yet supported."));
if (numBytes != 32)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(dataOffset) << eth::Instruction::MSTORE;
bool const leftAligned = type.getCategory() == Type::Category::STRING;
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
dataOffset += numBytes;
}
//@todo only return the first return value for now
unsigned retSize = function.getReturnParameterTypes().empty() ? 0
: function.getReturnParameterTypes().front()->getCalldataEncodedSize();
Type const* firstType = function.getReturnParameterTypes().empty() ? nullptr :
function.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0);
_functionCall.getExpression().accept(*this); // pushes addr and function index
@ -240,11 +254,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
<< u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL
<< eth::Instruction::POP; // @todo do not ignore failure indicator
if (retSize == 32)
m_context << u256(0) << eth::Instruction::MLOAD;
else if (retSize > 0)
m_context << (u256(1) << ((32 - retSize) * 8))
<< u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV;
if (retSize > 0)
{
bool const leftAligned = firstType->getCategory() == Type::Category::STRING;
CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned);
}
break;
}
case Location::SEND:
@ -267,7 +281,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
break;
case Location::ECRECOVER:
case Location::SHA256:
@ -283,13 +298,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
// @todo move this once we actually use memory
m_context << u256(i * 32) << eth::Instruction::MSTORE;
CompilerUtils(m_context).storeInMemory(i * 32);
}
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
<< contractAddress << u256(500) //@todo determine actual gas requirement
<< eth::Instruction::CALL
<< eth::Instruction::POP
<< u256(0) << eth::Instruction::MLOAD;
<< eth::Instruction::POP;
CompilerUtils(m_context).loadFromMemory(0);
break;
}
default:
@ -373,7 +388,8 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
true);
// @todo move this once we actually use memory
m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE;
CompilerUtils(m_context).storeInMemory(0);
CompilerUtils(m_context).storeInMemory(32);
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
@ -411,10 +427,11 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
{
case Type::Category::INTEGER:
case Type::Category::BOOL:
case Type::Category::STRING:
m_context << _literal.getType()->literalValue(_literal);
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer and boolean literals implemented for now."));
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now."));
}
}
@ -550,6 +567,11 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
return;
if (_typeOnStack.getCategory() == Type::Category::INTEGER)
appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack));
else if (_typeOnStack.getCategory() == Type::Category::STRING)
{
// nothing to do, strings are high-order-bit-aligned
//@todo clear lower-order bytes if we allow explicit conversion to shorter strings
}
else if (_typeOnStack != _targetType)
// All other types should not be convertible to non-equal types.
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested."));

10
libsolidity/ExpressionCompiler.h

@ -35,6 +35,7 @@ namespace solidity {
class CompilerContext;
class Type;
class IntegerType;
class StaticStringType;
/**
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
@ -45,14 +46,14 @@ class ExpressionCompiler: private ASTConstVisitor
{
public:
/// Compile the given @a _expression into the @a _context.
static void compileExpression(CompilerContext& _context, Expression const& _expression);
static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false);
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType);
private:
ExpressionCompiler(CompilerContext& _compilerContext):
m_context(_compilerContext), m_currentLValue(m_context) {}
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {}
virtual bool visit(Assignment const& _assignment) override;
virtual void endVisit(UnaryOperation const& _unaryOperation) override;
@ -75,7 +76,7 @@ private:
/// @}
/// Appends an implicit or explicit type conversion. For now this comprises only erasing
/// higher-order bits (@see appendHighBitCleanup) when widening integer types.
/// higher-order bits (@see appendHighBitCleanup) when widening integer.
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
/// necessary.
void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false);
@ -132,6 +133,7 @@ private:
unsigned m_stackSize;
};
bool m_optimize;
CompilerContext& m_context;
LValue m_currentLValue;
};

35
libsolidity/Token.h

@ -269,6 +269,39 @@ namespace solidity
K(ADDRESS, "address", 0) \
K(BOOL, "bool", 0) \
K(STRING_TYPE, "string", 0) \
K(STRING0, "string0", 0) \
K(STRING1, "string1", 0) \
K(STRING2, "string2", 0) \
K(STRING3, "string3", 0) \
K(STRING4, "string4", 0) \
K(STRING5, "string5", 0) \
K(STRING6, "string6", 0) \
K(STRING7, "string7", 0) \
K(STRING8, "string8", 0) \
K(STRING9, "string9", 0) \
K(STRING10, "string10", 0) \
K(STRING11, "string11", 0) \
K(STRING12, "string12", 0) \
K(STRING13, "string13", 0) \
K(STRING14, "string14", 0) \
K(STRING15, "string15", 0) \
K(STRING16, "string16", 0) \
K(STRING17, "string17", 0) \
K(STRING18, "string18", 0) \
K(STRING19, "string19", 0) \
K(STRING20, "string20", 0) \
K(STRING21, "string21", 0) \
K(STRING22, "string22", 0) \
K(STRING23, "string23", 0) \
K(STRING24, "string24", 0) \
K(STRING25, "string25", 0) \
K(STRING26, "string26", 0) \
K(STRING27, "string27", 0) \
K(STRING28, "string28", 0) \
K(STRING29, "string29", 0) \
K(STRING30, "string30", 0) \
K(STRING31, "string31", 0) \
K(STRING32, "string32", 0) \
K(TEXT, "text", 0) \
K(REAL, "real", 0) \
K(UREAL, "ureal", 0) \
@ -320,6 +353,8 @@ public:
static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; }
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
static bool isCommutativeOp(Value op) { return op == BIT_OR || op == BIT_XOR || op == BIT_AND ||
op == ADD || op == MUL || op == EQ || op == NE; }
static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }

43
libsolidity/Types.cpp

@ -53,6 +53,8 @@ shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
return make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS);
else if (_typeToken == Token::BOOL)
return make_shared<BoolType const>();
else if (Token::STRING0 <= _typeToken && _typeToken <= Token::STRING32)
return make_shared<StaticStringType const>(int(_typeToken) - int(Token::STRING0));
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
@ -91,7 +93,8 @@ shared_ptr<Type const> Type::forLiteral(Literal const& _literal)
case Token::NUMBER:
return IntegerType::smallestTypeForLiteral(_literal.getValue());
case Token::STRING_LITERAL:
return shared_ptr<Type const>(); // @todo add string literals
//@todo put larger strings into dynamic strings
return StaticStringType::smallestTypeForLiteral(_literal.getValue());
default:
return shared_ptr<Type const>();
}
@ -194,6 +197,44 @@ const MemberList IntegerType::AddressMemberList =
{"send", make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}),
TypePointers(), FunctionType::Location::SEND)}});
shared_ptr<StaticStringType> StaticStringType::smallestTypeForLiteral(string const& _literal)
{
if (_literal.length() <= 32)
return make_shared<StaticStringType>(_literal.length());
return shared_ptr<StaticStringType>();
}
StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes)
{
if (asserts(m_bytes >= 0 && m_bytes <= 32))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid byte number for static string type: " +
dev::toString(m_bytes)));
}
bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.getCategory() != getCategory())
return false;
StaticStringType const& convertTo = dynamic_cast<StaticStringType const&>(_convertTo);
return convertTo.m_bytes >= m_bytes;
}
bool StaticStringType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
StaticStringType const& other = dynamic_cast<StaticStringType const&>(_other);
return other.m_bytes == m_bytes;
}
u256 StaticStringType::literalValue(const Literal& _literal) const
{
u256 value = 0;
for (char c: _literal.getValue())
value = (value << 8) | byte(c);
return value << ((32 - _literal.getValue().length()) * 8);
}
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
// conversion to integer is fine, but not to address

31
libsolidity/Types.h

@ -36,7 +36,7 @@ namespace dev
namespace solidity
{
// @todo realMxN, string<N>
// @todo realMxN, dynamic strings, text, arrays
class Type; // forward
using TypePointer = std::shared_ptr<Type const>;
@ -178,6 +178,35 @@ private:
static const MemberList AddressMemberList;
};
/**
* String type with fixed length, up to 32 bytes.
*/
class StaticStringType: public Type
{
public:
virtual Category getCategory() const override { return Category::STRING; }
/// @returns the smallest string type for the given literal or an empty pointer
/// if no type fits.
static std::shared_ptr<StaticStringType> smallestTypeForLiteral(std::string const& _literal);
StaticStringType(int _bytes);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize() const override { return m_bytes; }
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "string" + dev::toString(m_bytes); }
virtual u256 literalValue(Literal const& _literal) const override;
int getNumBytes() const { return m_bytes; }
private:
int m_bytes;
};
/**
* The boolean type.
*/

10
test/TestHelper.cpp

@ -109,9 +109,6 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state)
Address address = Address(i.first);
for (auto const& j: o["storage"].get_obj())
_state.setStorage(address, toInt(j.first), toInt(j.second));
bytes code = importCode(o);
if (code.size())
@ -122,6 +119,9 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state)
else
_state.m_cache[address] = Account(toInt(o["balance"]), Account::NormalCreation);
for (auto const& j: o["storage"].get_obj())
_state.setStorage(address, toInt(j.first), toInt(j.second));
for(int i=0; i<toInt(o["nonce"]); ++i)
_state.noteSending(address);
@ -329,6 +329,10 @@ void checkStorage(map<u256, u256> _expectedStore, map<u256, u256> _resultStore,
BOOST_CHECK_MESSAGE(expectedStoreValue == resultStoreValue, _expectedAddr << ": store[" << expectedStoreKey << "] = " << resultStoreValue << ", expected " << expectedStoreValue);
}
}
BOOST_CHECK_EQUAL(_resultStore.size(), _expectedStore.size());
for (auto&& resultStorePair : _resultStore)
if (!_expectedStore.count(resultStorePair.first))
BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first);
}
void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs)

8
test/createRandomTest.cpp

@ -32,7 +32,7 @@
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libevmcore/Instruction.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include "vm.h"
using namespace std;
@ -142,14 +142,14 @@ void doMyTests(json_spirit::mValue& v)
}
bytes output;
eth::VM vm(fev.gas);
auto vm = eth::VMFactory::create(fev.gas);
u256 gas;
bool vmExceptionOccured = false;
try
{
output = vm.go(fev, fev.simpleTrace()).toBytes();
gas = vm.gas();
output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas();
}
catch (eth::VMException const& _e)
{

4
test/jsonrpc.cpp

@ -19,7 +19,7 @@
* @date 2014
*/
#if ETH_JSONRPC
#if ETH_JSONRPC && 0
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
@ -44,7 +44,7 @@ using namespace dev;
using namespace dev::eth;
namespace js = json_spirit;
WebThreeDirect *web3;
WebThreeDirect* web3;
unique_ptr<WebThreeStubServer> jsonrpcServer;
unique_ptr<WebThreeStubClient> jsonrpcClient;

14
test/solidityCompiler.cpp

@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
"}\n";
bytes code = compileContract(sourceCode);
unsigned boilerplateSize = 42;
unsigned boilerplateSize = 40;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize local variable x
byte(Instruction::PUSH1), 0x2,
@ -107,8 +107,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers)
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 70;
unsigned boilerplateSize = 83;
unsigned shift = 68;
unsigned boilerplateSize = 81;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize return variable d
byte(Instruction::DUP3),
@ -158,8 +158,8 @@ BOOST_AUTO_TEST_CASE(ifStatement)
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 29;
unsigned boilerplateSize = 42;
unsigned shift = 27;
unsigned boilerplateSize = 40;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1),
@ -200,8 +200,8 @@ BOOST_AUTO_TEST_CASE(loops)
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 29;
unsigned boilerplateSize = 42;
unsigned shift = 27;
unsigned boilerplateSize = 40;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x1,

70
test/solidityEndToEndTest.cpp

@ -363,6 +363,48 @@ BOOST_AUTO_TEST_CASE(small_signed_types)
testSolidityAgainstCpp(0, small_signed_types_cpp);
}
BOOST_AUTO_TEST_CASE(strings)
{
char const* sourceCode = "contract test {\n"
" function fixed() returns(string32 ret) {\n"
" return \"abc\\x00\\xff__\";\n"
" }\n"
" function pipeThrough(string2 small, bool one) returns(string16 large, bool oneRet) {\n"
" oneRet = one;\n"
" large = small;\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
bytes expectation(32, 0);
expectation[0] = byte('a');
expectation[1] = byte('b');
expectation[2] = byte('c');
expectation[3] = byte(0);
expectation[4] = byte(0xff);
expectation[5] = byte('_');
expectation[6] = byte('_');
BOOST_CHECK(callContractFunction(0, bytes()) == expectation);
expectation = bytes(17, 0);
expectation[0] = 0;
expectation[1] = 2;
expectation[16] = 1;
BOOST_CHECK(callContractFunction(1, bytes({0x00, 0x02, 0x01})) == expectation);
}
BOOST_AUTO_TEST_CASE(empty_string_on_stack)
{
char const* sourceCode = "contract test {\n"
" function run(string0 empty, uint8 inp) returns(uint16 a, string0 b, string4 c) {\n"
" var x = \"abc\";\n"
" var y = \"\";\n"
" var z = inp;\n"
" a = z; b = y; c = x;"
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, bytes({0x02})) == bytes({0x00, 0x02, 'a', 'b', 'c', 0x00}));
}
BOOST_AUTO_TEST_CASE(state_smoke_test)
{
char const* sourceCode = "contract test {\n"
@ -942,6 +984,34 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars)
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 9));
}
BOOST_AUTO_TEST_CASE(strings_in_calls)
{
char const* sourceCode = R"(
contract Helper {
function invoke(string3 x, bool stop) returns (string4 ret) {
return x;
}
}
contract Main {
Helper h;
function callHelper(string2 x, bool stop) returns (string5 ret) {
return h.invoke(x, stop);
}
function getHelper() returns (address addr) {
return address(h);
}
function setHelper(address addr) {
h = Helper(addr);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0}));
}
BOOST_AUTO_TEST_SUITE_END()
}

4
test/solidityExecutionFramework.h

@ -117,7 +117,7 @@ private:
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0)
{
m_state.addBalance(m_sender, _value); // just in case
eth::Executive executive(m_state);
eth::Executive executive(m_state, 0);
eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec())
: eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
bytes transactionRLP = t.rlp();
@ -137,7 +137,7 @@ private:
else
{
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
BOOST_REQUIRE(!executive.call(m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender));
BOOST_REQUIRE(!executive.call(m_contractAddress, m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender));
}
BOOST_REQUIRE(executive.go());
m_state.noteSending(m_sender);

8
test/solidityNameAndTypeResolution.cpp

@ -226,6 +226,14 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion)
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(large_string_literal)
{
char const* text = "contract test {\n"
" function f() { var x = \"123456789012345678901234567890123\"; }"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(balance)
{
char const* text = "contract test {\n"

19
test/solidityOptimizerTest.cpp

@ -48,7 +48,7 @@ public:
m_optimize = true;
bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName);
int sizeDiff = nonOptimizedBytecode.size() - optimizedBytecode.size();
BOOST_CHECK_MESSAGE(sizeDiff >= _expectedSizeDecrease, "Bytecode did only shrink by "
BOOST_CHECK_MESSAGE(sizeDiff == int(_expectedSizeDecrease), "Bytecode did only shrink by "
+ boost::lexical_cast<string>(sizeDiff) + " bytes, expected: "
+ boost::lexical_cast<string>(_expectedSizeDecrease));
m_optimizedContract = m_contractAddress;
@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(invariants)
return (((a + (1 - 1)) ^ 0) | 0) & (uint(0) - 1);
}
})";
compileBothVersions(19, sourceCode);
compileBothVersions(28, sourceCode);
compareVersions(0, u256(0x12334664));
}
@ -124,6 +124,21 @@ BOOST_AUTO_TEST_CASE(unused_expressions)
compareVersions(0);
}
BOOST_AUTO_TEST_CASE(constant_folding_both_sides)
{
// if constants involving the same associative and commutative operator are applied from both
// sides, the operator should be applied only once, because the expression compiler
// (even in non-optimized mode) pushes literals as late as possible
char const* sourceCode = R"(
contract test {
function f(uint x) returns (uint y) {
return 98 ^ (7 * ((1 | (x | 1000)) * 40) ^ 102);
}
})";
compileBothVersions(31, sourceCode);
compareVersions(0);
}
BOOST_AUTO_TEST_SUITE_END()
}

2
test/state.cpp

@ -45,7 +45,7 @@ void doStateTests(json_spirit::mValue& v, bool _fillin)
{
for (auto& i: v.get_obj())
{
cnote << i.first;
cerr << i.first << endl;
mObject& o = i.second.get_obj();
BOOST_REQUIRE(o.count("env") > 0);

10
test/trie.cpp

@ -54,13 +54,12 @@ BOOST_AUTO_TEST_CASE(trie_tests)
{
string testPath = test::getTestPath();
testPath += "/TrieTests";
cnote << "Testing Trie...";
js::mValue v;
string s = asString(contents(testPath + "/trietest.json"));
BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'trietest.json' is empty. Have you cloned the 'tests' repo branch develop?");
string s = asString(contents(testPath + "/trieanyorder.json"));
BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'trieanyorder.json' is empty. Have you cloned the 'tests' repo branch develop?");
js::read_string(s, v);
for (auto& i: v.get_obj())
{
@ -88,12 +87,11 @@ BOOST_AUTO_TEST_CASE(trie_tests)
BOOST_REQUIRE(t.check(true));
}
BOOST_REQUIRE(!o["root"].is_null());
BOOST_CHECK_EQUAL(o["root"].get_str(), toHex(t.root().asArray()));
BOOST_CHECK_EQUAL(o["root"].get_str(), "0x" + toHex(t.root().asArray()));
}
}
}
inline h256 stringMapHash256(StringMap const& _s)
{
return hash256(_s);
@ -246,6 +244,7 @@ BOOST_AUTO_TEST_CASE(moreTrieTests)
BOOST_AUTO_TEST_CASE(trieLowerBound)
{
cnote << "Stress-testing Trie.lower_bound...";
if (0)
{
MemoryDB dm;
EnforceRefs e(dm, true);
@ -291,6 +290,7 @@ BOOST_AUTO_TEST_CASE(trieLowerBound)
BOOST_AUTO_TEST_CASE(trieStess)
{
cnote << "Stress-testing Trie...";
if (0)
{
MemoryDB m;
MemoryDB dm;

16
test/vm.cpp

@ -21,6 +21,8 @@
*/
#include <boost/filesystem.hpp>
#include <libethereum/Executive.h>
#include <libevm/VMFactory.h>
#include "vm.h"
using namespace std;
@ -32,18 +34,18 @@ using namespace dev::test;
FakeExtVM::FakeExtVM(eth::BlockInfo const& _previousBlock, eth::BlockInfo const& _currentBlock, unsigned _depth): /// TODO: XXX: remove the default argument & fix.
ExtVMFace(Address(), Address(), Address(), 0, 1, bytesConstRef(), bytes(), _previousBlock, _currentBlock, _depth) {}
h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFunc const&)
h160 FakeExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _init, OnOpFunc const&)
{
Address na = right160(sha3(rlpList(myAddress, get<1>(addresses[myAddress]))));
Transaction t(_endowment, gasPrice, *_gas, _init.toBytes());
Transaction t(_endowment, gasPrice, io_gas, _init.toBytes());
callcreates.push_back(t);
return na;
}
bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, OnOpFunc const&, Address _myAddressOverride, Address _codeAddressOverride)
bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256& io_gas, bytesRef _out, OnOpFunc const&, Address _myAddressOverride, Address _codeAddressOverride)
{
Transaction t(_value, gasPrice, *_gas, _receiveAddress, _data.toVector());
Transaction t(_value, gasPrice, io_gas, _receiveAddress, _data.toVector());
callcreates.push_back(t);
(void)_out;
(void)_myAddressOverride;
@ -298,14 +300,14 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
}
bytes output;
VM vm(fev.gas);
auto vm = eth::VMFactory::create(fev.gas);
u256 gas;
bool vmExceptionOccured = false;
try
{
output = vm.go(fev, fev.simpleTrace()).toBytes();
gas = vm.gas();
output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas();
}
catch (VMException const& _e)
{

4
test/vm.h

@ -55,8 +55,8 @@ public:
virtual u256 txCount(Address _a) override { return std::get<1>(addresses[_a]); }
virtual void suicide(Address _a) override { std::get<0>(addresses[_a]) += std::get<0>(addresses[myAddress]); addresses.erase(myAddress); }
virtual bytes const& codeAt(Address _a) override { return std::get<3>(addresses[_a]); }
virtual h160 create(u256 _endowment, u256* _gas, bytesConstRef _init, eth::OnOpFunc const&) override;
virtual bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, eth::OnOpFunc const&, Address, Address) override;
virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _init, eth::OnOpFunc const&) override;
virtual bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256& io_gas, bytesRef _out, eth::OnOpFunc const&, Address, Address) override;
void setTransaction(Address _caller, u256 _value, u256 _gasPrice, bytes const& _data);
void setContract(Address _myAddress, u256 _myBalance, u256 _myNonce, std::map<u256, u256> const& _storage, bytes const& _code);
void set(Address _a, u256 _myBalance, u256 _myNonce, std::map<u256, u256> const& _storage, bytes const& _code);

6
test/whisperTopic.cpp

@ -32,7 +32,9 @@ BOOST_AUTO_TEST_SUITE(whisper)
BOOST_AUTO_TEST_CASE(topic)
{
g_logVerbosity = 20;
cnote << "Testing Whisper...";
auto oldLogVerbosity = g_logVerbosity;
g_logVerbosity = 0;
bool started = false;
unsigned result = 0;
@ -80,7 +82,7 @@ BOOST_AUTO_TEST_CASE(topic)
}
listener.join();
g_logVerbosity = 0;
g_logVerbosity = oldLogVerbosity;
BOOST_REQUIRE_EQUAL(result, 1 + 9 + 25 + 49 + 81);
}

Loading…
Cancel
Save