Browse Source

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

Conflicts:
	libethereum/Executive.cpp
	libethereum/Executive.h
	libethereum/State.cpp
cl-refactor
Paweł Bylica 10 years ago
parent
commit
6e06b09ddb
  1. 13
      alethzero/MainWin.cpp
  2. 16
      eth/main.cpp
  3. 2
      libethcore/CommonEth.cpp
  4. 5
      libethereum/BlockChain.cpp
  5. 3
      libethereum/BlockDetails.cpp
  6. 3
      libethereum/BlockDetails.h
  7. 77
      libethereum/Executive.cpp
  8. 3
      libethereum/Executive.h
  9. 23
      libethereum/ExtVM.h
  10. 46
      libethereum/Manifest.cpp
  11. 73
      libethereum/Manifest.h
  12. 125
      libethereum/MessageFilter.cpp
  13. 40
      libethereum/MessageFilter.h
  14. 28
      libethereum/PastMessage.cpp
  15. 56
      libethereum/PastMessage.h
  16. 98
      libethereum/State.cpp
  17. 18
      libethereum/State.h
  18. 6
      libethereum/TransactionReceipt.h
  19. 7
      libevm/ExtVMFace.h
  20. 4
      libevm/FeeStructure.cpp
  21. 2
      libevm/FeeStructure.h
  22. 2
      libevm/VM.h
  23. 5
      liblll/CompilerState.cpp
  24. 9
      libsolidity/AST.h
  25. 112
      libsolidity/InterfaceHandler.cpp
  26. 21
      libsolidity/InterfaceHandler.h
  27. 5
      libsolidity/Parser.cpp
  28. 1
      libsolidity/Types.h
  29. 15
      solc/CommandLineInterface.cpp
  30. 121
      test/solidityEndToEndTest.cpp
  31. 161
      test/solidityExecutionFramework.h
  32. 88
      test/solidityNatspecJSON.cpp
  33. 131
      test/solidityOptimizerTest.cpp
  34. 7
      test/vm.cpp
  35. 3
      test/vm.h

13
alethzero/MainWin.cpp

@ -1263,7 +1263,10 @@ void Main::on_blocks_currentItemChanged()
s << "<br/>Nonce: <b>" << info.nonce << "</b>";
s << "<br/>Hash w/o nonce: <b>" << info.headerHashWithoutNonce() << "</b>";
s << "<br/>Difficulty: <b>" << info.difficulty << "</b>";
s << "<br/>Proof-of-Work: <b>" << ProofOfWork::eval(info.headerHashWithoutNonce(), info.nonce) << " &lt;= " << (h256)u256((bigint(1) << 256) / info.difficulty) << "</b>";
if (info.number)
s << "<br/>Proof-of-Work: <b>" << ProofOfWork::eval(info.headerHashWithoutNonce(), info.nonce) << " &lt;= " << (h256)u256((bigint(1) << 256) / info.difficulty) << "</b>";
else
s << "<br/>Proof-of-Work: <i>Phil has nothing to prove</i>";
s << "<br/>Parent: <b>" << info.parentHash << "</b>";
// s << "<br/>Bloom: <b>" << details.bloom << "</b>";
s << "<br/>Log Bloom: <b>" << info.logBloom << "</b>";
@ -1280,7 +1283,7 @@ void Main::on_blocks_currentItemChanged()
if (info.parentHash)
s << "<br/>Pre: <b>" << BlockInfo(ethereum()->blockChain().block(info.parentHash)).stateRoot << "</b>";
else
s << "<br/>Pre: <i>Nothing is before the Gensesis</i>";
s << "<br/>Pre: <i>Nothing is before Phil</i>";
for (auto const& i: block[1])
s << "<br/>" << sha3(i.data()).abridged();// << ": <b>" << i[1].toHash<h256>() << "</b> [<b>" << i[2].toInt<u256>() << "</b> used]";
s << "<br/>Post: <b>" << info.stateRoot << "</b>";
@ -1395,10 +1398,10 @@ void Main::populateDebugger(dev::bytesConstRef _r)
bytesConstRef lastData;
h256 lastHash;
h256 lastDataHash;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt)
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, dev::eth::VM* voidVM, dev::eth::ExtVMFace const* voidExt)
{
dev::eth::VM& vm = *(dev::eth::VM*)voidVM;
dev::eth::ExtVM const& ext = *(dev::eth::ExtVM const*)voidExt;
dev::eth::VM& vm = *voidVM;
dev::eth::ExtVM const& ext = *static_cast<dev::eth::ExtVM const*>(voidExt);
if (ext.code != lastExtCode)
{
lastExtCode = ext.code;

16
eth/main.cpp

@ -630,10 +630,10 @@ int main(int argc, char** argv)
OnOpFunc oof;
if (format == "pretty")
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, void* vvm, void const* vextVM)
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = (VM*)vvm;
dev::eth::ExtVM const* ext = (ExtVM const*)vextVM;
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
f << endl << " STACK" << endl;
for (auto i: vm->stack())
f << (h256)i << endl;
@ -644,17 +644,17 @@ int main(int argc, char** argv)
f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << vm->gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32";
};
else if (format == "standard")
oof = [&](uint64_t, Instruction instr, bigint, bigint, void* vvm, void const* vextVM)
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = (VM*)vvm;
dev::eth::ExtVM const* ext = (ExtVM const*)vextVM;
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl;
};
else if (format == "standard+")
oof = [&](uint64_t, Instruction instr, bigint, bigint, void* vvm, void const* vextVM)
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = (VM*)vvm;
dev::eth::ExtVM const* ext = (ExtVM const*)vextVM;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE)
for (auto const& i: ext->state().storage(ext->myAddress))
f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl;

2
libethcore/CommonEth.cpp

@ -33,7 +33,7 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 48;
const unsigned c_protocolVersion = 49;
const unsigned c_databaseVersion = 5;
static const vector<pair<u256, string>> g_units =

5
libethereum/BlockChain.cpp

@ -145,7 +145,7 @@ void BlockChain::open(std::string _path, bool _killExisting)
if (!details(m_genesisHash))
{
// Insert details of genesis block.
m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {}, h256());
m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {});
auto r = m_details[m_genesisHash].rlp();
m_extrasDB->Put(m_writeOptions, ldb::Slice((char const*)&m_genesisHash, 32), (ldb::Slice)dev::ref(r));
}
@ -303,7 +303,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
// Get total difficulty increase and update state, checking it.
State s(bi.coinbaseAddress, _db);
auto tdIncrease = s.enactOn(&_block, bi, *this);
auto b = s.oldBloom();
BlockLogBlooms blb;
BlockReceipts br;
for (unsigned i = 0; i < s.pending().size(); ++i)
@ -320,7 +319,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
// All ok - insert into DB
{
WriteGuard l(x_details);
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}, b);
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {});
m_details[bi.parentHash].children.push_back(newHash);
}
{

3
libethereum/BlockDetails.cpp

@ -32,10 +32,9 @@ BlockDetails::BlockDetails(RLP const& _r)
totalDifficulty = _r[1].toInt<u256>();
parent = _r[2].toHash<h256>();
children = _r[3].toVector<h256>();
bloom = _r[4].toHash<h256>();
}
bytes BlockDetails::rlp() const
{
return rlpList(number, totalDifficulty, parent, children, bloom);
return rlpList(number, totalDifficulty, parent, children);
}

3
libethereum/BlockDetails.h

@ -40,7 +40,7 @@ namespace eth
struct BlockDetails
{
BlockDetails(): number(0), totalDifficulty(0) {}
BlockDetails(unsigned _n, u256 _tD, h256 _p, h256s _c, h256 _bloom): number(_n), totalDifficulty(_tD), parent(_p), children(_c), bloom(_bloom) {}
BlockDetails(unsigned _n, u256 _tD, h256 _p, h256s _c): number(_n), totalDifficulty(_tD), parent(_p), children(_c) {}
BlockDetails(RLP const& _r);
bytes rlp() const;
@ -51,7 +51,6 @@ struct BlockDetails
u256 totalDifficulty;
h256 parent;
h256s children;
h256 bloom;
};
struct BlockLogBlooms

77
libethereum/Executive.cpp

@ -85,14 +85,6 @@ bool Executive::setup(bytesConstRef _rlp)
clog(StateDetail) << "Paying" << formatBalance(cost) << "from sender (includes" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")";
m_s.subBalance(m_sender, cost);
if (m_ms)
{
m_ms->from = m_sender;
m_ms->to = m_t.receiveAddress();
m_ms->value = m_t.value();
m_ms->input = m_t.data();
}
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
@ -104,11 +96,24 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
m_s.addBalance(_receiveAddress, _value);
if (m_s.addressHasCode(_receiveAddress))
auto it = !(_receiveAddress & ~h160(0xffffffff)) ? State::precompiled().find((unsigned)(u160)_receiveAddress) : State::precompiled().end();
if (it != State::precompiled().end())
{
bigint g = it->second.gas(_data);
if (_gas < g)
{
m_endGas = 0;
return false;
}
m_endGas = (u256)(_gas - g);
it->second.exec(_data, bytesRef());
return true;
}
else if (m_s.addressHasCode(_receiveAddress))
{
m_vm = VMFactory::create(_gas);
bytes const& c = m_s.code(_receiveAddress);
m_ext.reset(new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms));
m_ext.reset(new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c));
}
else
m_endGas = _gas;
@ -126,16 +131,16 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
// Execute _init.
m_vm = VMFactory::create(_gas);
m_ext.reset(new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_ms));
m_ext.reset(new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init));
return _init.empty();
}
OnOpFunc Executive::simpleTrace()
{
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, void* voidVM, void const* voidExt)
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *(ExtVM const*)voidExt;
VM& vm = *(VM*)voidVM;
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
ostringstream o;
o << endl << " STACK" << endl;
@ -156,16 +161,16 @@ bool Executive::go(OnOpFunc const& _onOp)
{
boost::timer t;
auto sgas = m_vm->gas();
bool revert = false;
try
{
m_out = m_vm->go(*m_ext, _onOp);
if (m_ext)
{
m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);
m_logs = m_ext->sub.logs;
}
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();
}
catch (StepsDone const&)
{
@ -175,7 +180,16 @@ bool Executive::go(OnOpFunc const& _onOp)
{
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
m_endGas = 0;//m_vm->gas();
revert = 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)
{
@ -188,18 +202,6 @@ bool Executive::go(OnOpFunc const& _onOp)
cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
}
cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
// Write state out only in the case of a non-excepted transaction.
if (revert)
{
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();
}
}
}
return true;
}
@ -211,9 +213,11 @@ u256 Executive::gas() const
void Executive::finalize(OnOpFunc const&)
{
if (m_t.isCreation() && m_newAddress && m_out.size())
// non-reverted creation - put code in place.
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);
}
// 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());
@ -222,9 +226,6 @@ void Executive::finalize(OnOpFunc const&)
// cnote << "Transferring" << formatBalance(gasSpent) << "to miner.";
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);
if (m_ms)
m_ms->output = m_out.toBytes();
// Suicides...
if (m_ext)
for (auto a: m_ext->sub.suicides)

3
libethereum/Executive.h

@ -42,7 +42,7 @@ struct VMTraceChannel: public LogChannel { static const char* name() { return "E
class Executive
{
public:
Executive(State& _s, Manifest* o_ms = nullptr): m_s(_s), m_ms(o_ms) {}
Executive(State& _s): m_s(_s) {}
~Executive() = default;
Executive(Executive const&) = delete;
void operator=(Executive) = delete;
@ -72,7 +72,6 @@ private:
State& m_s;
std::unique_ptr<ExtVM> m_ext;
std::unique_ptr<VMFace> m_vm;
Manifest* m_ms = nullptr;
bytesConstRef m_out;
Address m_newAddress;

23
libethereum/ExtVM.h

@ -39,8 +39,8 @@ class ExtVM: public ExtVMFace
{
public:
/// Full constructor.
ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, Manifest* o_ms, unsigned _depth = 0):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _s.m_previousBlock, _s.m_currentBlock, _depth), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms)
ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, unsigned _depth = 0):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _s.m_previousBlock, _s.m_currentBlock, _depth), m_s(_s), m_origCache(_s.m_cache)
{
m_s.ensureCached(_myAddress, true, true);
}
@ -49,7 +49,7 @@ public:
virtual u256 store(u256 _n) override final { return m_s.storage(myAddress, _n); }
/// Write a value in storage.
virtual void setStore(u256 _n, u256 _v) override final { m_s.setStorage(myAddress, _n, _v); if (m_ms) m_ms->altered.push_back(_n); }
virtual void setStore(u256 _n, u256 _v) override final { m_s.setStorage(myAddress, _n, _v); }
/// Read address's code.
virtual bytes const& codeAt(Address _a) override final { return m_s.code(_a); }
@ -59,23 +59,13 @@ public:
{
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1);
auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back();
return ret;
return m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, _onOp, depth + 1);
}
/// 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
{
if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1);
auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back();
return ret;
return m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, _onOp, depth + 1);
}
/// Read address's balance.
@ -96,14 +86,13 @@ 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 { if (m_ms) *m_ms = Manifest(); m_s.m_cache = m_origCache; }
virtual void revert() override final { m_s.m_cache = m_origCache; }
State& state() const { return m_s; }
private:
State& m_s; ///< A reference to the base state.
std::map<Address, Account> m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution.
Manifest* m_ms;
};
}

46
libethereum/Manifest.cpp

@ -1,46 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Manifest.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Manifest.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
Manifest::Manifest(bytesConstRef _r)
{
RLP r(_r);
from = r[0].toHash<Address>();
to = r[1].toHash<Address>();
value = r[2].toInt<u256>();
altered = r[3].toVector<u256>();
input = r[4].toBytes();
output = r[5].toBytes();
for (auto const& i: r[6])
internal.emplace_back(i.data());
}
void Manifest::streamRLP(RLPStream& _s) const
{
_s.appendList(7) << from << to << value << altered << input << output;
_s.appendList(internal.size());
for (auto const& i: internal)
i.streamRLP(_s);
}

73
libethereum/Manifest.h

@ -1,73 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Manifest.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <iostream>
#include <sstream>
#include <libdevcore/RLP.h>
#include <libethcore/CommonEth.h>
namespace dev
{
namespace eth
{
struct Manifest;
using Manifests = std::vector<Manifest>;
/**
* @brief A record of the state-interaction of a transaction/call/create.
*/
struct Manifest
{
Manifest() {}
Manifest(bytesConstRef _r);
void streamRLP(RLPStream& _s) const;
h256 bloom() const { h256 ret = from.bloom() | to.bloom(); for (auto const& i: internal) ret |= i.bloom(); for (auto const& i: altered) ret |= h256(i).bloom(); return ret; }
std::string toString(unsigned _indent = 0) const
{
std::ostringstream oss;
oss << std::string(_indent * 3, ' ') << from << " -> " << to << " [" << value << "]: {";
if (internal.size())
{
oss << std::endl;
for (auto const& m: internal)
oss << m.toString(_indent + 1) << std::endl;
oss << std::string(_indent * 3, ' ');
}
oss << "} I:" << toHex(input) << "; O:" << toHex(output);
return oss.str();
}
Address from;
Address to;
u256 value;
u256s altered;
bytes input;
bytes output;
Manifests internal;
};
}
}

125
libethereum/MessageFilter.cpp

@ -27,131 +27,6 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
void MessageFilter::streamRLP(RLPStream& _s) const
{
_s.appendList(8) << m_from << m_to << m_stateAltered << m_altered << m_earliest << m_latest << m_max << m_skip;
}
h256 MessageFilter::sha3() const
{
RLPStream s;
streamRLP(s);
return dev::sha3(s.out());
}
bool MessageFilter::matches(h256 _bloom) const
{
auto have = [=](Address const& a) { return _bloom.contains(a.bloom()); };
if (m_from.size())
{
for (auto i: m_from)
if (have(i))
goto OK1;
return false;
}
OK1:
if (m_to.size())
{
for (auto i: m_to)
if (have(i))
goto OK2;
return false;
}
OK2:
if (m_stateAltered.size() || m_altered.size())
{
for (auto i: m_altered)
if (have(i))
goto OK3;
for (auto i: m_stateAltered)
if (have(i.first) && _bloom.contains(h256(i.second).bloom()))
goto OK3;
return false;
}
OK3:
return true;
}
bool MessageFilter::matches(State const& _s, unsigned _i) const
{
h256 b = _s.changesFromPending(_i).bloom();
if (!matches(b))
return false;
Transaction t = _s.pending()[_i];
if (!m_to.empty() && !m_to.count(t.receiveAddress()))
return false;
if (!m_from.empty() && !m_from.count(t.sender()))
return false;
if (m_stateAltered.empty() && m_altered.empty())
return true;
StateDiff d = _s.pendingDiff(_i);
if (!m_altered.empty())
{
for (auto const& s: m_altered)
if (d.accounts.count(s))
return true;
return false;
}
if (!m_stateAltered.empty())
{
for (auto const& s: m_stateAltered)
if (d.accounts.count(s.first) && d.accounts.at(s.first).storage.count(s.second))
return true;
return false;
}
return true;
}
PastMessages MessageFilter::matches(Manifest const& _m, unsigned _i) const
{
PastMessages ret;
matches(_m, vector<unsigned>(1, _i), _m.from, PastMessages(), ret);
return ret;
}
bool MessageFilter::matches(Manifest const& _m, vector<unsigned> _p, Address _o, PastMessages _limbo, PastMessages& o_ret) const
{
bool ret;
if ((m_from.empty() || m_from.count(_m.from)) && (m_to.empty() || m_to.count(_m.to)))
_limbo.push_back(PastMessage(_m, _p, _o));
// Handle limbos, by checking against all addresses in alteration.
bool alters = m_altered.empty() && m_stateAltered.empty();
alters = alters || m_altered.count(_m.from) || m_altered.count(_m.to);
if (!alters)
for (auto const& i: _m.altered)
if (m_altered.count(_m.to) || m_stateAltered.count(make_pair(_m.to, i)))
{
alters = true;
break;
}
// If we do alter stuff,
if (alters)
{
o_ret += _limbo;
_limbo.clear();
ret = true;
}
_p.push_back(0);
for (auto const& m: _m.internal)
{
if (matches(m, _p, _o, _limbo, o_ret))
{
_limbo.clear();
ret = true;
}
_p.back()++;
}
return ret;
}
void LogFilter::streamRLP(RLPStream& _s) const
{
_s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip;

40
libethereum/MessageFilter.h

@ -24,7 +24,6 @@
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libethcore/CommonEth.h>
#include "PastMessage.h"
#include "TransactionReceipt.h"
namespace dev
@ -32,47 +31,8 @@ namespace dev
namespace eth
{
struct Manifest;
class State;
class MessageFilter
{
public:
MessageFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {}
void streamRLP(RLPStream& _s) const;
h256 sha3() const;
int earliest() const { return m_earliest; }
int latest() const { return m_latest; }
unsigned max() const { return m_max; }
unsigned skip() const { return m_skip; }
bool matches(h256 _bloom) const;
bool matches(State const& _s, unsigned _i) const;
PastMessages matches(Manifest const& _m, unsigned _i) const;
MessageFilter from(Address _a) { m_from.insert(_a); return *this; }
MessageFilter to(Address _a) { m_to.insert(_a); return *this; }
MessageFilter altered(Address _a, u256 _l) { m_stateAltered.insert(std::make_pair(_a, _l)); return *this; }
MessageFilter altered(Address _a) { m_altered.insert(_a); return *this; }
MessageFilter withMax(unsigned _m) { m_max = _m; return *this; }
MessageFilter withSkip(unsigned _m) { m_skip = _m; return *this; }
MessageFilter withEarliest(int _e) { m_earliest = _e; return *this; }
MessageFilter withLatest(int _e) { m_latest = _e; return *this; }
private:
bool matches(Manifest const& _m, std::vector<unsigned> _p, Address _o, PastMessages _limbo, PastMessages& o_ret) const;
std::set<Address> m_from;
std::set<Address> m_to;
std::set<std::pair<Address, u256>> m_stateAltered;
std::set<Address> m_altered;
int m_earliest = 0;
int m_latest = -1;
unsigned m_max;
unsigned m_skip;
};
class LogFilter
{
public:

28
libethereum/PastMessage.cpp

@ -1,28 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file PastMessage.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "PastMessage.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
#pragma GCC diagnostic ignored "-Wunused-variable"
namespace { char dummy; };

56
libethereum/PastMessage.h

@ -1,56 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file PastMessage.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Common.h>
#include <libethcore/CommonEth.h>
#include "Manifest.h"
namespace dev
{
namespace eth
{
struct PastMessage
{
PastMessage(Manifest const& _m, std::vector<unsigned> _path, Address _o): to(_m.to), from(_m.from), value(_m.value), input(_m.input), output(_m.output), path(_path), origin(_o) {}
PastMessage& polish(h256 _b, u256 _ts, unsigned _n, Address _coinbase) { block = _b; timestamp = _ts; number = _n; coinbase = _coinbase; return *this; }
Address to; ///< The receiving address of the transaction. Address() in the case of a creation.
Address from; ///< The receiving address of the transaction. Address() in the case of a creation.
u256 value; ///< The value associated with the call.
bytes input; ///< The data associated with the message, or the initialiser if it's a creation transaction.
bytes output; ///< The data returned by the message, or the body code if it's a creation transaction.
std::vector<unsigned> path; ///< Call path into the block transaction. size() is always > 0. First item is the transaction index in the block.
Address origin; ///< Originating sender of the transaction.
Address coinbase; ///< Block coinbase.
h256 block; ///< Block hash.
u256 timestamp; ///< Block timestamp.
unsigned number; ///< Block number.
};
typedef std::vector<PastMessage> PastMessages;
}
}

98
libethereum/State.cpp

@ -90,9 +90,9 @@ void ripemd160Code(bytesConstRef _in, bytesRef _out)
const std::map<unsigned, PrecompiledAddress> State::c_precompiled =
{
{ 1, { 500, ecrecoverCode }},
{ 2, { 100, sha256Code }},
{ 3, { 100, ripemd160Code }}
{ 1, { [](bytesConstRef) -> bigint { return (bigint)500; }, ecrecoverCode }},
{ 2, { [](bytesConstRef i) -> bigint { return (bigint)50 + (i.size() + 31) / 32 * 50; }, sha256Code }},
{ 3, { [](bytesConstRef i) -> bigint { return (bigint)50 + (i.size() + 31) / 32 * 50; }, ripemd160Code }}
};
OverlayDB State::openDB(std::string _path, bool _killExisting)
@ -792,14 +792,6 @@ bool State::amIJustParanoid(BlockChain const& _bc)
return false;
}
h256 State::oldBloom() const
{
h256 ret = m_currentBlock.coinbaseAddress.bloom();
for (auto const& i: m_receipts)
ret |= i.changes().bloom();
return ret;
}
LogBloom State::logBloom() const
{
LogBloom ret;
@ -1127,9 +1119,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
auto h = rootHash();
#endif
Manifest ms;
Executive e(*this, &ms);
Executive e(*this);
e.setup(_rlp);
u256 startGasUsed = gasUsed();
@ -1179,12 +1169,12 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(e.t());
m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs(), ms));
m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs()));
m_transactionSet.insert(e.t().sha3());
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, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
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;
@ -1192,81 +1182,63 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
addBalance(_receiveAddress, _value);
if (o_ms)
{
o_ms->from = _senderAddress;
o_ms->to = _receiveAddress;
o_ms->value = _value;
o_ms->input = _data.toBytes();
}
auto it = !(_codeAddress & ~h160(0xffffffff)) ? c_precompiled.find((unsigned)(u160)_codeAddress) : c_precompiled.end();
if (it != c_precompiled.end())
{
if (*_gas < it->second.gas)
bigint g = it->second.gas(_data);
if (*_gas < g)
{
*_gas = 0;
return false;
}
*_gas -= it->second.gas;
*_gas -= (u256)g;
it->second.exec(_data, _out);
}
else if (addressHasCode(_codeAddress))
{
auto vm = VMFactory::create(*_gas);
ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), o_ms, _level);
bool revert = false;
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;
if (o_ms)
o_ms->output = out.toBytes();
*_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);
revert = true;
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;
}
// Write state out only in the case of a non-excepted transaction.
if (revert)
evm.revert();
return !revert;
}
return true;
}
h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
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;
if (o_ms)
{
o_ms->from = _sender;
o_ms->to = Address();
o_ms->value = _endowment;
o_ms->input = _code.toBytes();
}
Address newAddress = right160(sha3(rlpList(_sender, transactionsFrom(_sender) - 1)));
// Set up new account...
@ -1274,50 +1246,44 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
// Execute init code.
auto vm = VMFactory::create(*_gas);
ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms, _level);
bool revert = false;
ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, _level);
bytesConstRef out;
try
{
out = vm->go(evm, _onOp);
if (o_ms)
o_ms->output = out.toBytes();
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);
revert = true;
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;
}
// TODO: CHECK: AUDIT: IS THIS CORRECT?! (esp. given account created prior to revertion init.)
// Write state out only in the case of a non-out-of-gas transaction.
if (revert)
{
evm.revert();
m_cache.erase(newAddress);
newAddress = Address();
}
else
// Set code.
if (addressInUse(newAddress))
m_cache[newAddress].setCode(out);
return newAddress;
}

18
libethereum/State.h

@ -54,7 +54,7 @@ struct StateDetail: public LogChannel { static const char* name() { return "/S/"
struct PrecompiledAddress
{
unsigned gas;
std::function<bigint(bytesConstRef)> gas;
std::function<void(bytesConstRef, bytesRef)> exec;
};
@ -210,15 +210,6 @@ public:
/// Get the list of pending transactions.
Transactions const& pending() const { return m_transactions; }
/// Get the list of pending transactions. TODO: PoC-7: KILL
Manifest changesFromPending(unsigned _i) const { return m_receipts[_i].changes(); }
/// Get the bloom filter of all changes happened in the block. TODO: PoC-7: KILL
h256 oldBloom() const;
/// Get the bloom filter of a particular transaction that happened in the block. TODO: PoC-7: KILL
h256 oldBloom(unsigned _i) const { return m_receipts[_i].changes().bloom(); }
/// Get the transaction receipt for the transaction of the given index.
TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; }
@ -258,6 +249,9 @@ public:
/// the block since all state changes are ultimately reversed.
void cleanup(bool _fullCommit);
/// Info on precompiled contract accounts baked into the protocol.
static std::map<unsigned, PrecompiledAddress> const& precompiled() { return c_precompiled; }
private:
/// Undo the changes to the state for committing to mine.
void uncommitToMine();
@ -282,12 +276,12 @@ private:
// 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, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
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, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
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();

6
libethereum/TransactionReceipt.h

@ -38,9 +38,7 @@ class TransactionReceipt
{
public:
TransactionReceipt(bytesConstRef _rlp) { RLP r(_rlp); m_stateRoot = (h256)r[0]; m_gasUsed = (u256)r[1]; m_bloom = (LogBloom)r[2]; for (auto const& i: r[3]) m_log.emplace_back(i); }
TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log, Manifest const& _ms): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log), m_changes(_ms) {}
Manifest const& changes() const { return m_changes; }
TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log) {}
h256 const& stateRoot() const { return m_stateRoot; }
u256 const& gasUsed() const { return m_gasUsed; }
@ -62,8 +60,6 @@ private:
u256 m_gasUsed;
LogBloom m_bloom;
LogEntries m_log;
Manifest m_changes; ///< TODO: PoC-7: KILL
};
using TransactionReceipts = std::vector<TransactionReceipt>;

7
libevm/ExtVMFace.h

@ -85,7 +85,10 @@ struct SubState
}
};
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, void/*VM*/*, void/*ExtVM*/ const*)>;
class ExtVMFace;
class VM;
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, VM*, ExtVMFace const*)>;
/**
* @brief Interface and null implementation of the class for specifying VM externalities.
@ -146,7 +149,7 @@ public:
u256 value; ///< Value (in Wei) that was passed to this address.
u256 gasPrice; ///< Price of gas (that we already paid).
bytesConstRef data; ///< Current input data.
bytes code; ///< Current code that is executing.
bytes code; ///< Current code that is executing.
BlockInfo previousBlock; ///< The previous block's information.
BlockInfo currentBlock; ///< The current block's information.
SubState sub; ///< Sub-band VM state (suicides, refund counter, logs).

4
libevm/FeeStructure.cpp

@ -27,12 +27,14 @@ using namespace dev::eth;
u256 const dev::eth::c_stepGas = 1;
u256 const dev::eth::c_balanceGas = 20;
u256 const dev::eth::c_sha3Gas = 20;
u256 const dev::eth::c_sha3Gas = 10;
u256 const dev::eth::c_sha3WordGas = 10;
u256 const dev::eth::c_sloadGas = 20;
u256 const dev::eth::c_sstoreSetGas = 300;
u256 const dev::eth::c_sstoreResetGas = 100;
u256 const dev::eth::c_sstoreRefundGas = 100;
u256 const dev::eth::c_createGas = 100;
u256 const dev::eth::c_createDataGas = 5;
u256 const dev::eth::c_callGas = 20;
u256 const dev::eth::c_expGas = 1;
u256 const dev::eth::c_expByteGas = 1;

2
libevm/FeeStructure.h

@ -31,11 +31,13 @@ namespace eth
extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL.
extern u256 const c_balanceGas; ///< Once per BALANCE operation.
extern u256 const c_sha3Gas; ///< Once per SHA3 operation.
extern u256 const c_sha3WordGas;
extern u256 const c_sloadGas; ///< Once per SLOAD operation.
extern u256 const c_sstoreSetGas; ///< Once per SSTORE operation if the zeroness changes from zero.
extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness doesn't change.
extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero.
extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction.
extern u256 const c_createDataGas;
extern u256 const c_callGas; ///< Once per CALL operation & message call transaction.
extern u256 const c_expGas; ///< Once per EXP instuction.
extern u256 const c_expByteGas; ///< Times ceil(log256(exponent)) for the EXP instruction.

2
libevm/VM.h

@ -175,7 +175,7 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st
break;
case Instruction::SHA3:
require(2);
runGas = c_sha3Gas;
runGas = c_sha3Gas + (m_stack[m_stack.size() - 2] + 31) / 32 * c_sha3WordGas;
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]);
break;
case Instruction::CALLDATACOPY:

5
liblll/CompilerState.cpp

@ -72,6 +72,11 @@ void CompilerState::populateStandard()
"(def 'regname (name) { [32]'register [64]name (call allgas namereg 0 32 64 0 0) })"
"(def 'regcoin (name) { [32]name (call allgas coinreg 0 32 32 0 0) })"
"(def 'regcoin (name denom) { [32]name [64]denom (call allgas coinreg 0 32 64 0 0) })"
"(def 'ecrecover (r s v hash) { [0] r [32] s [64] v [96] hash (msg allgas 1 0 0 128) })"
"(def 'sha256 (data datasize) (msg allgas 2 0 data datasize))"
"(def 'ripemd160 (data datasize) (msg allgas 3 0 data datasize))"
"(def 'sha256 (val) { [0]:val (sha256 0 32) })"
"(def 'ripemd160 (val) { [0]:val (ripemd160 0 32) })"
"}";
CodeFragment::compile(s, *this);
}

9
libsolidity/AST.h

@ -156,13 +156,15 @@ class ContractDefinition: public Declaration
public:
ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
Declaration(_location, _name),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions)
m_definedFunctions(_definedFunctions),
m_documentation(_documentation)
{}
virtual void accept(ASTVisitor& _visitor) override;
@ -172,6 +174,10 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
/// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
@ -179,6 +185,7 @@ private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
ASTPointer<ASTString> m_documentation;
};
class StructDefinition: public Declaration

112
libsolidity/InterfaceHandler.cpp

@ -75,7 +75,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (strPtr)
{
resetUser();
parseDocString(*strPtr);
parseDocString(*strPtr, CommentOwner::FUNCTION);
if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice);
@ -95,6 +95,20 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
Json::Value doc;
Json::Value methods(Json::objectValue);
auto contractDoc = _contractDef.getDocumentation();
if (contractDoc)
{
m_contractAuthor.clear();
m_title.clear();
parseDocString(*contractDoc, CommentOwner::CONTRACT);
if (!m_contractAuthor.empty())
doc["author"] = m_contractAuthor;
if (!m_title.empty())
doc["title"] = m_title;
}
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{
Json::Value method;
@ -102,16 +116,21 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
if (strPtr)
{
resetDev();
parseDocString(*strPtr);
parseDocString(*strPtr, CommentOwner::FUNCTION);
if (!m_dev.empty())
method["details"] = Json::Value(m_dev);
if (!m_author.empty())
method["author"] = m_author;
Json::Value params(Json::objectValue);
for (auto const& pair: m_params)
params[pair.first] = pair.second;
if (!m_params.empty())
method["params"] = params;
if (!m_return.empty())
method["return"] = m_return;
@ -133,6 +152,7 @@ void InterfaceHandler::resetUser()
void InterfaceHandler::resetDev()
{
m_dev.clear();
m_author.clear();
m_return.clear();
m_params.clear();
}
@ -193,10 +213,12 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con
std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag)
std::string const& _tag,
CommentOwner _owner)
{
// LTODO: need to check for @(start of a tag) between here and the end of line
// for all cases
// for all cases. Also somehow automate list of acceptable tags for each
// language construct since current way does not scale well.
if (m_lastTag == DocTagType::NONE || _tag != "")
{
if (_tag == "dev")
@ -205,37 +227,77 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
else if (_tag == "return")
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
else if (_tag == "author")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR);
else if (_owner == CommentOwner::FUNCTION)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR);
else
// LTODO: for now this else makes no sense but later comments will go to more language constructs
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts"));
}
else if (_tag == "title")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts"));
}
else if (_tag == "param")
return parseDocTagParam(_pos, _end);
else
{
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered"));
}
}
else
return appendDocTag(_pos, _end);
return appendDocTag(_pos, _end, _owner);
}
std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end)
std::string::const_iterator _end,
CommentOwner _owner)
{
switch (m_lastTag)
{
case DocTagType::DEV:
m_dev += " ";
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV);
case DocTagType::NOTICE:
m_notice += " ";
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
case DocTagType::RETURN:
m_return += " ";
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
case DocTagType::PARAM:
return appendDocTagParam(_pos, _end);
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
break;
case DocTagType::DEV:
m_dev += " ";
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV);
case DocTagType::NOTICE:
m_notice += " ";
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
case DocTagType::RETURN:
m_return += " ";
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
case DocTagType::AUTHOR:
if (_owner == CommentOwner::CONTRACT)
{
m_contractAuthor += " ";
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR);
}
else if (_owner == CommentOwner::FUNCTION)
{
m_author += " ";
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR);
}
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment"));
case DocTagType::TITLE:
if (_owner == CommentOwner::CONTRACT)
{
m_title += " ";
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE);
}
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment"));
case DocTagType::PARAM:
return appendDocTagParam(_pos, _end);
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
break;
}
}
@ -247,7 +309,7 @@ static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_i
return (spacePos < nlPos) ? spacePos : nlPos;
}
void InterfaceHandler::parseDocString(std::string const& _string)
void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _owner)
{
auto currPos = _string.begin();
auto end = _string.end();
@ -265,10 +327,10 @@ void InterfaceHandler::parseDocString(std::string const& _string)
BOOST_THROW_EXCEPTION(DocstringParsingError() <<
errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found"));
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos));
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner);
}
else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag
currPos = appendDocTag(currPos + 1, end);
currPos = appendDocTag(currPos + 1, end, _owner);
else if (currPos != end) // skip the line if a newline was found
currPos = nlPos + 1;
}

21
libsolidity/InterfaceHandler.h

@ -45,7 +45,15 @@ enum class DocTagType: uint8_t
DEV,
NOTICE,
PARAM,
RETURN
RETURN,
AUTHOR,
TITLE
};
enum class CommentOwner
{
CONTRACT,
FUNCTION
};
class InterfaceHandler
@ -89,12 +97,14 @@ private:
std::string::const_iterator _end);
std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end);
void parseDocString(std::string const& _string);
void parseDocString(std::string const& _string, CommentOwner _owner);
std::string::const_iterator appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end);
std::string::const_iterator _end,
CommentOwner _owner);
std::string::const_iterator parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag);
std::string const& _tag,
CommentOwner _owner);
Json::StyledWriter m_writer;
@ -103,6 +113,9 @@ private:
std::string m_notice;
std::string m_dev;
std::string m_return;
std::string m_contractAuthor;
std::string m_author;
std::string m_title;
std::vector<std::pair<std::string, std::string>> m_params;
};

5
libsolidity/Parser.cpp

@ -112,6 +112,9 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::CONTRACT);
ASTPointer<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE);
@ -146,7 +149,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions);
return nodeFactory.createNode<ContractDefinition>(name, docstring, structs, stateVariables, functions);
}
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)

1
libsolidity/Types.h

@ -184,6 +184,7 @@ private:
class BoolType: public Type
{
public:
BoolType() {}
virtual Category getCategory() const { return Category::BOOL; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool acceptsBinaryOperator(Token::Value _operator) const override

15
solc/CommandLineInterface.cpp

@ -255,7 +255,22 @@ bool CommandLineInterface::processInput()
}
else
for (string const& infile: m_args["input-file"].as<vector<string>>())
{
auto path = boost::filesystem::path(infile);
if (!boost::filesystem::exists(path))
{
cout << "Skipping non existant input file \"" << infile << "\"" << endl;
continue;
}
if (!boost::filesystem::is_regular_file(path))
{
cout << "\"" << infile << "\" is not a valid file. Skipping" << endl;
continue;
}
m_sourceCodes[infile] = asString(dev::contents(infile));
}
try
{

121
test/solidityEndToEndTest.cpp

@ -24,137 +24,18 @@
#include <string>
#include <tuple>
#include <boost/test/unit_test.hpp>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h>
#include <libdevcrypto/SHA3.h>
#include <test/solidityExecutionFramework.h>
using namespace std;
namespace dev
{
/// Provides additional overloads for toBigEndian to encode arguments and return values.
inline bytes toBigEndian(byte _value) { return bytes({_value}); }
inline bytes toBigEndian(bool _value) { return bytes({byte(_value)}); }
namespace solidity
{
namespace test
{
class ExecutionFramework
{
public:
ExecutionFramework() { g_logVerbosity = 0; }
bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0, string const& _contractName = "")
{
dev::solidity::CompilerStack compiler;
compiler.compile(_sourceCode);
bytes code = compiler.getBytecode(_contractName);
sendMessage(code, true, _value);
BOOST_REQUIRE(!m_output.empty());
return m_output;
}
bytes const& callContractFunction(byte _index, bytes const& _data = bytes(), u256 const& _value = 0)
{
sendMessage(bytes(1, _index) + _data, false, _value);
return m_output;
}
template <class... Args>
bytes const& callContractFunction(byte _index, Args const&... _arguments)
{
return callContractFunction(_index, argsToBigEndian(_arguments...));
}
template <class CppFunction, class... Args>
void testSolidityAgainstCpp(byte _index, CppFunction const& _cppFunction, Args const&... _arguments)
{
bytes solidityResult = callContractFunction(_index, _arguments...);
bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match."
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult));
}
template <class CppFunction, class... Args>
void testSolidityAgainstCppOnRange(byte _index, CppFunction const& _cppFunction,
u256 const& _rangeStart, u256 const& _rangeEnd)
{
for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument)
{
bytes solidityResult = callContractFunction(_index, argument);
bytes cppResult = callCppAndEncodeResult(_cppFunction, argument);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match."
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult) +
"\nArgument: " + toHex(toBigEndian(argument)));
}
}
private:
template <class FirstArg, class... Args>
bytes argsToBigEndian(FirstArg const& _firstArg, Args const&... _followingArgs) const
{
return toBigEndian(_firstArg) + argsToBigEndian(_followingArgs...);
}
bytes argsToBigEndian() const { return bytes(); }
template <class CppFunction, class... Args>
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)
-> typename enable_if<is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type
{
_cppFunction(_arguments...);
return bytes();
}
template <class CppFunction, class... Args>
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)
-> typename enable_if<!is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type
{
return toBigEndian(_cppFunction(_arguments...));
}
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::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();
try
{
// this will throw since the transaction is invalid, but it should nevertheless store the transaction
executive.setup(&transactionRLP);
}
catch (...) {}
if (_isCreation)
{
BOOST_REQUIRE(!executive.create(m_sender, _value, m_gasPrice, m_gas, &_data, m_sender));
m_contractAddress = executive.newAddress();
BOOST_REQUIRE(m_contractAddress);
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
}
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.go());
m_state.noteSending(m_sender);
executive.finalize();
m_output = executive.out().toVector();
}
protected:
Address m_sender;
Address m_contractAddress;
eth::State m_state;
u256 const m_gasPrice = 100 * eth::szabo;
u256 const m_gas = 1000000;
bytes m_output;
};
BOOST_FIXTURE_TEST_SUITE(SolidityCompilerEndToEndTest, ExecutionFramework)
BOOST_AUTO_TEST_CASE(smoke_test)

161
test/solidityExecutionFramework.h

@ -0,0 +1,161 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Framework for executing Solidity contracts and testing them against C++ implementation.
*/
#pragma once
#include <string>
#include <tuple>
#include <boost/test/unit_test.hpp>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h>
namespace dev
{
/// Provides additional overloads for toBigEndian to encode arguments and return values.
inline bytes toBigEndian(byte _value) { return bytes({_value}); }
inline bytes toBigEndian(bool _value) { return bytes({byte(_value)}); }
namespace solidity
{
namespace test
{
class ExecutionFramework
{
public:
ExecutionFramework() { g_logVerbosity = 0; }
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "")
{
dev::solidity::CompilerStack compiler;
compiler.compile(_sourceCode, m_optimize);
bytes code = compiler.getBytecode(_contractName);
sendMessage(code, true, _value);
BOOST_REQUIRE(!m_output.empty());
return m_output;
}
bytes const& callContractFunction(byte _index, bytes const& _data = bytes(), u256 const& _value = 0)
{
sendMessage(bytes(1, _index) + _data, false, _value);
return m_output;
}
template <class... Args>
bytes const& callContractFunction(byte _index, Args const&... _arguments)
{
return callContractFunction(_index, argsToBigEndian(_arguments...));
}
template <class CppFunction, class... Args>
void testSolidityAgainstCpp(byte _index, CppFunction const& _cppFunction, Args const&... _arguments)
{
bytes solidityResult = callContractFunction(_index, _arguments...);
bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match."
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult));
}
template <class CppFunction, class... Args>
void testSolidityAgainstCppOnRange(byte _index, CppFunction const& _cppFunction,
u256 const& _rangeStart, u256 const& _rangeEnd)
{
for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument)
{
bytes solidityResult = callContractFunction(_index, argument);
bytes cppResult = callCppAndEncodeResult(_cppFunction, argument);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match."
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult) +
"\nArgument: " + toHex(toBigEndian(argument)));
}
}
private:
template <class FirstArg, class... Args>
bytes argsToBigEndian(FirstArg const& _firstArg, Args const&... _followingArgs) const
{
return toBigEndian(_firstArg) + argsToBigEndian(_followingArgs...);
}
bytes argsToBigEndian() const { return bytes(); }
template <class CppFunction, class... Args>
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)
-> typename std::enable_if<std::is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type
{
_cppFunction(_arguments...);
return bytes();
}
template <class CppFunction, class... Args>
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)
-> typename std::enable_if<!std::is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type
{
return toBigEndian(_cppFunction(_arguments...));
}
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::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();
try
{
// this will throw since the transaction is invalid, but it should nevertheless store the transaction
executive.setup(&transactionRLP);
}
catch (...) {}
if (_isCreation)
{
BOOST_REQUIRE(!executive.create(m_sender, _value, m_gasPrice, m_gas, &_data, m_sender));
m_contractAddress = executive.newAddress();
BOOST_REQUIRE(m_contractAddress);
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
}
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.go());
m_state.noteSending(m_sender);
executive.finalize();
m_output = executive.out().toVector();
}
protected:
bool m_optimize = false;
Address m_sender;
Address m_contractAddress;
eth::State m_state;
u256 const m_gasPrice = 100 * eth::szabo;
u256 const m_gas = 1000000;
bytes m_output;
};
}
}
} // end namespaces

88
test/solidityNatspecJSON.cpp

@ -22,6 +22,7 @@
#include <boost/test/unit_test.hpp>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Exceptions.h>
#include <jsonrpc/json/json.h>
#include <libdevcore/Exceptions.h>
@ -393,6 +394,93 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return)
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_contract_no_doc)
{
char const* sourceCode = "contract test {\n"
" /// @dev Mul function\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
" \"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Mul function\"\n"
" }\n"
" }\n"
"}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_contract_doc)
{
char const* sourceCode = " /// @author Lefteris\n"
" /// @title Just a test contract\n"
"contract test {\n"
" /// @dev Mul function\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Mul function\"\n"
" }\n"
" }\n"
"}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_author_at_function)
{
char const* sourceCode = " /// @author Lefteris\n"
" /// @title Just a test contract\n"
"contract test {\n"
" /// @dev Mul function\n"
" /// @author John Doe\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Mul function\",\n"
" \"author\": \"John Doe\",\n"
" }\n"
" }\n"
"}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
{
char const* sourceCode = " /// @author Lefteris\n"
" /// @title Just a test contract\n"
"contract test {\n"
" /// @dev Mul function\n"
" /// @title I really should not be here\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Mul function\"\n"
" }\n"
" }\n"
"}";
BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError);
}
BOOST_AUTO_TEST_SUITE_END()
}

131
test/solidityOptimizerTest.cpp

@ -0,0 +1,131 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Tests for the Solidity optimizer.
*/
#include <string>
#include <tuple>
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
#include <test/solidityExecutionFramework.h>
using namespace std;
namespace dev
{
namespace solidity
{
namespace test
{
class OptimizerTestFramework: public ExecutionFramework
{
public:
OptimizerTestFramework() { }
/// Compiles the source code with and without optimizing.
void compileBothVersions(unsigned _expectedSizeDecrease, std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") {
m_optimize = false;
bytes nonOptimizedBytecode = compileAndRun(_sourceCode, _value, _contractName);
m_nonOptimizedContract = m_contractAddress;
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::lexical_cast<string>(sizeDiff) + " bytes, expected: "
+ boost::lexical_cast<string>(_expectedSizeDecrease));
m_optimizedContract = m_contractAddress;
}
template <class... Args>
void compareVersions(byte _index, Args const&... _arguments)
{
m_contractAddress = m_nonOptimizedContract;
bytes nonOptimizedOutput = callContractFunction(_index, _arguments...);
m_contractAddress = m_optimizedContract;
bytes optimizedOutput = callContractFunction(_index, _arguments...);
BOOST_CHECK_MESSAGE(nonOptimizedOutput == optimizedOutput, "Computed values do not match."
"\nNon-Optimized: " + toHex(nonOptimizedOutput) +
"\nOptimized: " + toHex(optimizedOutput));
}
protected:
Address m_optimizedContract;
Address m_nonOptimizedContract;
};
BOOST_FIXTURE_TEST_SUITE(SolidityOptimizerTest, OptimizerTestFramework)
BOOST_AUTO_TEST_CASE(smoke_test)
{
char const* sourceCode = R"(
contract test {
function f(uint a) returns (uint b) {
return a;
}
})";
compileBothVersions(4, sourceCode);
compareVersions(0, u256(7));
}
BOOST_AUTO_TEST_CASE(large_integers)
{
char const* sourceCode = R"(
contract test {
function f() returns (uint a, uint b) {
a = 0x234234872642837426347000000;
b = 0x110000000000000000000000002;
}
})";
compileBothVersions(28, sourceCode);
compareVersions(0);
}
BOOST_AUTO_TEST_CASE(invariants)
{
char const* sourceCode = R"(
contract test {
function f(uint a) returns (uint b) {
return (((a + (1 - 1)) ^ 0) | 0) & (uint(0) - 1);
}
})";
compileBothVersions(19, sourceCode);
compareVersions(0, u256(0x12334664));
}
BOOST_AUTO_TEST_CASE(unused_expressions)
{
char const* sourceCode = R"(
contract test {
uint data;
function f() returns (uint a, uint b) {
10 + 20;
data;
}
})";
compileBothVersions(11, sourceCode);
compareVersions(0);
}
BOOST_AUTO_TEST_SUITE_END()
}
}
} // end namespaces

7
test/vm.cpp

@ -243,10 +243,11 @@ void FakeExtVM::importCallCreates(mArray& _callcreates)
eth::OnOpFunc FakeExtVM::simpleTrace()
{
return [](uint64_t steps, eth::Instruction inst, bigint newMemSize, bigint gasCost, void* voidVM, void const* voidExt)
return [](uint64_t steps, eth::Instruction inst, bigint newMemSize, bigint gasCost, dev::eth::VM* voidVM, dev::eth::ExtVMFace const* voidExt)
{
FakeExtVM const& ext = *(FakeExtVM const*)voidExt;
eth::VM& vm = *(eth::VM*)voidVM;
FakeExtVM const& ext = *static_cast<FakeExtVM const*>(voidExt);
eth::VM& vm = *voidVM;
std::ostringstream o;
o << std::endl << " STACK" << std::endl;

3
test/vm.h

@ -80,9 +80,6 @@ public:
bytes thisTxData;
bytes thisTxCode;
u256 gas;
private:
eth::Manifest m_ms;
};

Loading…
Cancel
Save