diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index db1b5dd10..122085225 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -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(new Executive(m_executiveState)); + m_currentExecution = unique_ptr(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(new Executive(m_executiveState)); + m_currentExecution = unique_ptr(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)) diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 2582aa23d..b39b65106 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,6 @@ class Main; namespace dev { namespace eth { class Client; class State; -class MessageFilter; }} class QQuickView; diff --git a/eth/main.cpp b/eth/main.cpp index ccbbdb979..49eac8876 100644 --- a/eth/main.cpp +++ b/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(); diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index be2fea13c..db46d62f6 100644 --- a/libdevcore/vector_ref.h +++ b/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::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; } diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h index 2fd0d3048..bbfd48e56 100644 --- a/libethereum/BlockDetails.h +++ b/libethereum/BlockDetails.h @@ -28,7 +28,6 @@ #include #include -#include "Manifest.h" #include "TransactionReceipt.h" namespace ldb = leveldb; diff --git a/libethereum/Client.h b/libethereum/Client.h index 283251e24..e8c460093 100644 --- a/libethereum/Client.h +++ b/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; diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 2546d484f..deb3da33e 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -19,11 +19,13 @@ * @date 2014 */ +#include "Executive.h" + #include #include +#include #include #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(_gas); - bytes const& c = m_s.code(_receiveAddress); - m_ext = make_shared(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(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(_gas); - m_ext = make_shared(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init); + m_vm = VMFactory::create(_gas); + m_ext = make_shared(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; } diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 1b2f3ad75..c5baada1d 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -25,30 +25,32 @@ #include #include #include -#include +#include #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 m_ext; - std::shared_ptr m_vm; - bytesConstRef m_out; + std::unique_ptr 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; diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index 4c343c162..fcd703b25 100644 --- a/libethereum/ExtVM.cpp +++ b/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(); +} + diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index ad1045d3f..9699a68ad 100644 --- a/libethereum/ExtVM.h +++ b/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; } diff --git a/libethereum/Interface.h b/libethereum/Interface.h index d598e1f9b..28ac26819 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -26,7 +26,7 @@ #include #include #include -#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 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; diff --git a/libethereum/MessageFilter.cpp b/libethereum/LogFilter.cpp similarity index 97% rename from libethereum/MessageFilter.cpp rename to libethereum/LogFilter.cpp index f44587620..81cb439ba 100644 --- a/libethereum/MessageFilter.cpp +++ b/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 . */ -/** @file MessageFilter.cpp +/** @file LogFilter.cpp * @author Gav Wood * @date 2014 */ -#include "MessageFilter.h" +#include "LogFilter.h" #include #include "State.h" diff --git a/libethereum/MessageFilter.h b/libethereum/LogFilter.h similarity index 98% rename from libethereum/MessageFilter.h rename to libethereum/LogFilter.h index e2c26d214..bda8e46a2 100644 --- a/libethereum/MessageFilter.h +++ b/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 . */ -/** @file MessageFilter.h +/** @file LogFilter.h * @author Gav Wood * @date 2014 */ diff --git a/libethereum/Manifest.cpp b/libethereum/Manifest.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/libethereum/Manifest.h b/libethereum/Manifest.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/libethereum/PastMessage.cpp b/libethereum/PastMessage.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/libethereum/PastMessage.h b/libethereum/PastMessage.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/libethereum/State.cpp b/libethereum/State.cpp index d5c71efe3..30290c315 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -29,10 +29,11 @@ #include #include #include -#include +#include #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(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 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; diff --git a/libethereum/State.h b/libethereum/State.h index 05e10df3e..afa1f1d2c 100644 --- a/libethereum/State.h +++ b/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 gas; - std::function exec; + std::function 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(); diff --git a/libethereum/TransactionReceipt.cpp b/libethereum/TransactionReceipt.cpp new file mode 100644 index 000000000..0fb104490 --- /dev/null +++ b/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 . +*/ +/** @file TransactionReceipt.cpp + * @author Gav Wood + * @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); +} diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index b990459d9..23995c75a 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -26,7 +26,6 @@ #include #include #include -#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(); } diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 206128e76..b8c4184ab 100644 --- a/libevm/ExtVMFace.h +++ b/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. }; } diff --git a/libevm/VM.cpp b/libevm/VM.cpp index bded9a10d..b8452e4f5 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -20,14 +20,14 @@ */ #include "VM.h" +#include -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(); } diff --git a/libevm/VM.h b/libevm/VM.h index fad9392f6..3eb330fcd 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -28,21 +28,13 @@ #include #include #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 - 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 m_onFail; }; -} - -// INLINE: -template 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 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 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 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 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 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 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 dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con BOOST_THROW_EXCEPTION(StepsDone()); return bytesConstRef(); } + +} } diff --git a/libevm/VMFace.h b/libevm/VMFace.h new file mode 100644 index 000000000..44ae03868 --- /dev/null +++ b/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 . +*/ +#pragma once + +#include +#include +#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; +}; + +} +} diff --git a/libevm/VMFactory.cpp b/libevm/VMFactory.cpp new file mode 100644 index 000000000..af37ec710 --- /dev/null +++ b/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 . +*/ + +#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 VMFactory::create(u256 _gas) +{ + asserts(g_kind == VMKind::Interpreter && "Only interpreter supported for now"); + return std::unique_ptr(new VM(_gas)); +} + +} +} diff --git a/libevm/VMFactory.h b/libevm/VMFactory.h new file mode 100644 index 000000000..d0d02e0c4 --- /dev/null +++ b/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 . +*/ +#pragma once + +#include "VMFace.h" + +namespace dev +{ +namespace eth +{ + +enum class VMKind: bool +{ + Interpreter, + JIT +}; + +class VMFactory +{ +public: + VMFactory() = delete; + + static std::unique_ptr create(u256 _gas); + static void setKind(VMKind _kind); +}; + +} +} diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index b094af194..258336917 100644 --- a/libsolidity/Compiler.cpp +++ b/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 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); +} + } } diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 4b8f02c5d..639e98410 100644 --- a/libsolidity/Compiler.h +++ b/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 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 m_breakTags; ///< tag to jump to for a "break" statement std::vector m_continueTags; ///< tag to jump to for a "continue" statement diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index d46754856..7aaa79b1c 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -101,10 +101,10 @@ void CompilerStack::compile(bool _optimize) if (ContractDefinition* contract = dynamic_cast(node.get())) { m_globalContext->setCurrentContract(*contract); - shared_ptr compiler = make_shared(); + shared_ptr compiler = make_shared(_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); } } diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index d4dfbe3c0..9f474896e 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/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)); diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h index 4da533752..928f0e2d3 100644 --- a/libsolidity/CompilerUtils.h +++ b/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. diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index a0b0a54a8..3beb423de 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/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(&rightExpression) + && !dynamic_cast(&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(*_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(_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.")); diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 41cb66763..c0b173d46 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/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; }; diff --git a/libsolidity/Token.h b/libsolidity/Token.h index eb9b4d426..22378ae07 100644 --- a/libsolidity/Token.h +++ b/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; } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 6c971a742..00e530c3f 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -53,6 +53,8 @@ shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) return make_shared(0, IntegerType::Modifier::ADDRESS); else if (_typeToken == Token::BOOL) return make_shared(); + else if (Token::STRING0 <= _typeToken && _typeToken <= Token::STRING32) + return make_shared(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::forLiteral(Literal const& _literal) case Token::NUMBER: return IntegerType::smallestTypeForLiteral(_literal.getValue()); case Token::STRING_LITERAL: - return shared_ptr(); // @todo add string literals + //@todo put larger strings into dynamic strings + return StaticStringType::smallestTypeForLiteral(_literal.getValue()); default: return shared_ptr(); } @@ -194,6 +197,44 @@ const MemberList IntegerType::AddressMemberList = {"send", make_shared(TypePointers({make_shared(256)}), TypePointers(), FunctionType::Location::SEND)}}); +shared_ptr StaticStringType::smallestTypeForLiteral(string const& _literal) +{ + if (_literal.length() <= 32) + return make_shared(_literal.length()); + return shared_ptr(); +} + +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(_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(_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 diff --git a/libsolidity/Types.h b/libsolidity/Types.h index dd89f311d..8e93bd31d 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -36,7 +36,7 @@ namespace dev namespace solidity { -// @todo realMxN, string +// @todo realMxN, dynamic strings, text, arrays class Type; // forward using TypePointer = std::shared_ptr; @@ -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 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. */ diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index ea0bf341c..e8940cc7a 100644 --- a/test/TestHelper.cpp +++ b/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 _expectedStore, map _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) diff --git a/test/createRandomTest.cpp b/test/createRandomTest.cpp index caeeb6b67..a11688457 100644 --- a/test/createRandomTest.cpp +++ b/test/createRandomTest.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #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) { diff --git a/test/jsonrpc.cpp b/test/jsonrpc.cpp index 5a9b94ebc..37377407c 100644 --- a/test/jsonrpc.cpp +++ b/test/jsonrpc.cpp @@ -19,7 +19,7 @@ * @date 2014 */ -#if ETH_JSONRPC +#if ETH_JSONRPC && 0 #include #include @@ -44,7 +44,7 @@ using namespace dev; using namespace dev::eth; namespace js = json_spirit; -WebThreeDirect *web3; +WebThreeDirect* web3; unique_ptr jsonrpcServer; unique_ptr jsonrpcClient; diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 004740b5e..eae8f3142 100644 --- a/test/solidityCompiler.cpp +++ b/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, diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 2cbb770f3..130e44a9a 100644 --- a/test/solidityEndToEndTest.cpp +++ b/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() } diff --git a/test/solidityExecutionFramework.h b/test/solidityExecutionFramework.h index 9d40e8a46..91ee7ad6a 100644 --- a/test/solidityExecutionFramework.h +++ b/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); diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 783972296..03eaebb3a 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/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" diff --git a/test/solidityOptimizerTest.cpp b/test/solidityOptimizerTest.cpp index f8d3b552c..388e0579c 100644 --- a/test/solidityOptimizerTest.cpp +++ b/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(sizeDiff) + " bytes, expected: " + boost::lexical_cast(_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() } diff --git a/test/state.cpp b/test/state.cpp index 07c8373e2..3fe0fe30b 100644 --- a/test/state.cpp +++ b/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); diff --git a/test/trie.cpp b/test/trie.cpp index 67f706917..0cf87c212 100644 --- a/test/trie.cpp +++ b/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; diff --git a/test/vm.cpp b/test/vm.cpp index 8fe378cef..f05981d9e 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -21,6 +21,8 @@ */ #include +#include +#include #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) { diff --git a/test/vm.h b/test/vm.h index 3d4b88d54..ff948cbf8 100644 --- a/test/vm.h +++ b/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 const& _storage, bytes const& _code); void set(Address _a, u256 _myBalance, u256 _myNonce, std::map const& _storage, bytes const& _code); diff --git a/test/whisperTopic.cpp b/test/whisperTopic.cpp index 941c790e5..79adf3d6a 100644 --- a/test/whisperTopic.cpp +++ b/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); }