diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 2a66a6d69..04fc7757c 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1626,7 +1626,7 @@ void Main::on_data_textChanged() catch (dev::Exception const& exception) { ostringstream error; - solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler.getScanner()); + solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); solidity = "

Solidity

" + QString::fromStdString(error.str()).toHtmlEscaped() + "
"; } catch (...) diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 50b9df413..fffc5843f 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 858fd53ff..766ca485d 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -52,6 +52,12 @@ void Secp256k1::decrypt(Secret const& _k, bytes& io_text) { CryptoPP::ECIES::Decryptor d; initializeDLScheme(_k, d); + + if (!io_text.size()) + { + io_text.resize(1); + io_text[0] = 0; + } size_t clen = io_text.size(); bytes plain; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index d130194cc..e3346754b 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -217,7 +217,7 @@ void Client::noteChanged(h256Set const& _filters) for (auto& i: m_watches) if (_filters.count(i.second.id)) { - cwatch << "!!!" << i.first << i.second.id; +// cwatch << "!!!" << i.first << i.second.id; i.second.changes++; } } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 42c063d3f..9e2f5ee07 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include "Interface.h" #include "Executive.h" @@ -32,13 +33,6 @@ using namespace dev::eth; #define ETH_VMTRACE 1 -Executive::~Executive() -{ - // TODO: Make safe. - delete m_ext; - delete m_vm; -} - u256 Executive::gasUsed() const { return m_t.gas() - m_endGas; @@ -112,9 +106,9 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu if (m_s.addressHasCode(_receiveAddress)) { - m_vm = VMFactory::create(VMFactory::Interpreter, _gas).release(); + m_vm = VMFactory::create(_gas); bytes const& c = m_s.code(_receiveAddress); - m_ext = 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, m_ms)); } else m_endGas = _gas; @@ -131,8 +125,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 = VMFactory::create(VMFactory::Interpreter, _gas).release(); - m_ext = new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_ms); + m_vm = VMFactory::create(_gas); + m_ext.reset(new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_ms)); return _init.empty(); } @@ -141,7 +135,7 @@ OnOpFunc Executive::simpleTrace() return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, void* voidVM, void const* voidExt) { ExtVM const& ext = *(ExtVM const*)voidExt; - VM& vm = *(VM*)voidVM; // TODO: Ok for now, because only interpeter/VM supports OnOp callback, but safer solution would be nice + VM& vm = *(VM*)voidVM; ostringstream o; o << endl << " STACK" << endl; diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 1a9a27d52..f2ee6a77d 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -25,17 +25,17 @@ #include #include #include +#include #include "Transaction.h" -#include "Manifest.h" +#include "ExtVM.h" namespace dev { namespace eth { -class VMFace; -class ExtVM; class State; +struct Manifest; struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; }; @@ -43,7 +43,9 @@ class Executive { public: Executive(State& _s, Manifest* o_ms = nullptr): m_s(_s), m_ms(o_ms) {} - ~Executive(); + ~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); @@ -68,8 +70,8 @@ public: private: State& m_s; - ExtVM* m_ext = nullptr; // TODO: make safe. - VMFace* m_vm = nullptr; + std::unique_ptr m_ext; + std::unique_ptr m_vm; Manifest* m_ms = nullptr; bytesConstRef m_out; Address m_newAddress; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 66d24a27b..539cb55f0 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; @@ -171,8 +172,7 @@ State::State(State const& _s): m_previousBlock(_s.m_previousBlock), m_currentBlock(_s.m_currentBlock), m_ourAddress(_s.m_ourAddress), - m_blockReward(_s.m_blockReward), - m_vmKind(_s.m_vmKind) + m_blockReward(_s.m_blockReward) { paranoia("after state cloning (copy cons).", true); } @@ -206,7 +206,6 @@ State& State::operator=(State const& _s) m_ourAddress = _s.m_ourAddress; m_blockReward = _s.m_blockReward; m_lastTx = _s.m_lastTx; - m_vmKind = _s.m_vmKind; paranoia("after state cloning (assignment op)", true); return *this; } @@ -1103,7 +1102,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(); @@ -1215,20 +1214,19 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA } else if (addressHasCode(_codeAddress)) { - auto vmObj = VMFactory::create(getVMKind(), *_gas); - auto& vm = *vmObj; + auto vm = VMFactory::create(*_gas); ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), o_ms, _level); bool revert = false; try { - auto out = vm.go(evm, _onOp); + 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(); + *_gas = vm->gas(); } catch (VMException const& _e) { @@ -1275,20 +1273,19 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, m_cache[newAddress] = Account(balance(newAddress) + _endowment, Account::ContractConception); // Execute init code. - auto vmObj = VMFactory::create(getVMKind(), *_gas); - auto& vm = *vmObj; + auto vm = VMFactory::create(*_gas); ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms, _level); bool revert = false; bytesConstRef out; try { - out = vm.go(evm, _onOp); + out = vm->go(evm, _onOp); if (o_ms) o_ms->output = out.toBytes(); if (o_sub) *o_sub += evm.sub; - *_gas = vm.gas(); + *_gas = vm->gas(); } catch (VMException const& _e) { diff --git a/libethereum/State.h b/libethereum/State.h index f7bc0d119..6b53ce925 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -38,9 +37,7 @@ #include "Account.h" #include "Transaction.h" #include "TransactionReceipt.h" -#include "Executive.h" #include "AccountDiff.h" -#include "VMFactory.h" namespace dev { @@ -262,12 +259,6 @@ public: /// the block since all state changes are ultimately reversed. void cleanup(bool _fullCommit); - /// Sets VM kind to be used by the state - void setVMKind(VMFactory::Kind _kind) { m_vmKind = _kind; } - - /// Get the kind of VM used by the state - VMFactory::Kind getVMKind() const { return m_vmKind; } - private: /// Undo the changes to the state for committing to mine. void uncommitToMine(); @@ -335,8 +326,6 @@ private: u256 m_blockReward; - VMFactory::Kind m_vmKind = VMFactory::Interpreter; ///< The kind of VM used by the state - static std::string c_defaultPath; static const std::map c_precompiled; diff --git a/libethereum/VMFactory.cpp b/libethereum/VMFactory.cpp deleted file mode 100644 index 3077ed546..000000000 --- a/libethereum/VMFactory.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include - -#if ETH_EVMJIT - #include -#endif - -#include "VMFactory.h" - -namespace dev -{ -namespace eth -{ - -std::unique_ptr VMFactory::create(VMFactory::Kind _kind, u256 _gas) -{ -#if ETH_EVMJIT - auto vm = _kind == Kind::JIT ? static_cast(new JitVM) - : static_cast(new VM); -#else - VMFace* vm = new VM; -#endif - - vm->reset(_gas); - return std::unique_ptr(vm); -} - -} -} diff --git a/libethereum/VMFactory.h b/libethereum/VMFactory.h deleted file mode 100644 index 5d8edc391..000000000 --- a/libethereum/VMFactory.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -namespace dev -{ -namespace eth -{ - -/** - */ - -class VMFactory -{ -public: - enum Kind: bool { - Interpreter, -#if ETH_EVMJIT - JIT -#endif - }; - - static std::unique_ptr create(Kind, u256 _gas = 0); -}; - - -} -} diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 4a175ff4e..52798cd89 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -150,7 +150,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 ba158de2a..44620a3d5 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -35,7 +35,7 @@ void VM::reset(u256 _gas) noexcept bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) { if (auto defaultExt = dynamic_cast(&_ext)) - return go(*defaultExt, _onOp, _steps); + return goImpl(*defaultExt, _onOp, _steps); else - return go(_ext, _onOp, _steps); + return goImpl(_ext, _onOp, _steps); } diff --git a/libevm/VM.h b/libevm/VM.h index c95242dfa..d03760a78 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -27,17 +27,14 @@ #include #include #include -#include "VMFace.h" #include "FeeStructure.h" -#include "ExtVMFace.h" +#include "VMFace.h" namespace dev { namespace eth { -class VMFactory; - // 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) @@ -52,7 +49,7 @@ inline u256 fromAddress(Address _a) /** */ -class VM : public VMFace +class VM: public VMFace { public: virtual void reset(u256 _gas = 0) noexcept override final; @@ -61,17 +58,20 @@ public: 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 curPC() const { return m_curPC; } bytes const& memory() const { return m_temp; } u256s const& stack() const { return m_stack; } private: - friend VMFactory; + friend class VMFactory; + + /// Construct VM object. explicit VM(u256 _gas = 0): VMFace(_gas) {} template - bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps); + bytesConstRef goImpl(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1); u256 m_curPC = 0; bytes m_temp; @@ -83,7 +83,7 @@ private: } // INLINE: -template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) +template dev::bytesConstRef dev::eth::VM::goImpl(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) { auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; @@ -518,12 +518,12 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::CALLDATALOAD: { - if ((unsigned)m_stack.back() + 31 < _ext.data.size()) + if ((unsigned)m_stack.back() + (uint64_t)31 < _ext.data.size()) m_stack.back() = (u256)*(h256 const*)(_ext.data.data() + (unsigned)m_stack.back()); else { h256 r; - for (unsigned i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + 32, j = 0; i < e; ++i, ++j) + for (uint64_t i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + (uint64_t)32, j = 0; i < e; ++i, ++j) r[j] = i < _ext.data.size() ? _ext.data[i] : 0; m_stack.back() = (u256)r; } @@ -532,51 +532,49 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::CALLDATASIZE: m_stack.push_back(_ext.data.size()); break; - case Instruction::CALLDATACOPY: - { - unsigned mf = (unsigned)m_stack.back(); - m_stack.pop_back(); - u256 cf = m_stack.back(); - m_stack.pop_back(); - unsigned l = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned el = cf + l > (u256)_ext.data.size() ? (u256)_ext.data.size() < cf ? 0 : _ext.data.size() - (unsigned)cf : l; - memcpy(m_temp.data() + mf, _ext.data.data() + (unsigned)cf, el); - memset(m_temp.data() + mf + el, 0, l - el); - break; - } case Instruction::CODESIZE: m_stack.push_back(_ext.code.size()); break; - case Instruction::CODECOPY: - { - unsigned mf = (unsigned)m_stack.back(); - m_stack.pop_back(); - u256 cf = (u256)m_stack.back(); - m_stack.pop_back(); - unsigned l = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned el = cf + l > (u256)_ext.code.size() ? (u256)_ext.code.size() < cf ? 0 : _ext.code.size() - (unsigned)cf : l; - memcpy(m_temp.data() + mf, _ext.code.data() + (unsigned)cf, el); - memset(m_temp.data() + mf + el, 0, l - el); - break; - } case Instruction::EXTCODESIZE: m_stack.back() = _ext.codeAt(asAddress(m_stack.back())).size(); break; + case Instruction::CALLDATACOPY: + case Instruction::CODECOPY: case Instruction::EXTCODECOPY: { - Address a = asAddress(m_stack.back()); - m_stack.pop_back(); - unsigned mf = (unsigned)m_stack.back(); + Address a; + if (inst == Instruction::EXTCODECOPY) + { + a = asAddress(m_stack.back()); + m_stack.pop_back(); + } + unsigned offset = (unsigned)m_stack.back(); m_stack.pop_back(); - u256 cf = m_stack.back(); + u256 index = m_stack.back(); m_stack.pop_back(); - unsigned l = (unsigned)m_stack.back(); + unsigned size = (unsigned)m_stack.back(); m_stack.pop_back(); - unsigned el = cf + l > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < cf ? 0 : _ext.codeAt(a).size() - (unsigned)cf : l; - memcpy(m_temp.data() + mf, _ext.codeAt(a).data() + (unsigned)cf, el); - memset(m_temp.data() + mf + el, 0, l - el); + unsigned sizeToBeCopied; + switch(inst) + { + case Instruction::CALLDATACOPY: + sizeToBeCopied = index + (bigint)size > (u256)_ext.data.size() ? (u256)_ext.data.size() < index ? 0 : _ext.data.size() - (unsigned)index : size; + memcpy(m_temp.data() + offset, _ext.data.data() + (unsigned)index, sizeToBeCopied); + break; + case Instruction::CODECOPY: + sizeToBeCopied = index + (bigint)size > (u256)_ext.code.size() ? (u256)_ext.code.size() < index ? 0 : _ext.code.size() - (unsigned)index : size; + memcpy(m_temp.data() + offset, _ext.code.data() + (unsigned)index, sizeToBeCopied); + break; + case Instruction::EXTCODECOPY: + sizeToBeCopied = index + (bigint)size > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < index ? 0 : _ext.codeAt(a).size() - (unsigned)index : size; + memcpy(m_temp.data() + offset, _ext.codeAt(a).data() + (unsigned)index, sizeToBeCopied); + break; + default: + // this is unreachable, but if someone introduces a bug in the future, he may get here. + BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("CALLDATACOPY, CODECOPY or EXTCODECOPY instruction requested.")); + break; + } + memset(m_temp.data() + offset + sizeToBeCopied, 0, size - sizeToBeCopied); break; } case Instruction::GASPRICE: diff --git a/libevm/VMFace.h b/libevm/VMFace.h index f4dd3096e..319da7fc1 100644 --- a/libevm/VMFace.h +++ b/libevm/VMFace.h @@ -32,7 +32,7 @@ struct BreakPointHit: virtual VMException {}; struct BadInstruction: virtual VMException {}; struct BadJumpDestination: virtual VMException {}; struct OutOfGas: virtual VMException {}; -struct StackTooSmall: virtual public VMException {}; +struct StackTooSmall: virtual VMException {}; /** */ @@ -47,7 +47,7 @@ public: VMFace(VMFace const&) = delete; void operator=(VMFace const&) = delete; - virtual void reset(u256 _gas = 0) noexcept; + virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; } virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0; diff --git a/libevm/VMFactory.cpp b/libevm/VMFactory.cpp new file mode 100644 index 000000000..90e17c553 --- /dev/null +++ b/libevm/VMFactory.cpp @@ -0,0 +1,45 @@ +/* + 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/VMFace.cpp b/libevm/VMFactory.h similarity index 74% rename from libevm/VMFace.cpp rename to libevm/VMFactory.h index 136bd5223..c5d9c4f65 100644 --- a/libevm/VMFace.cpp +++ b/libevm/VMFactory.h @@ -14,14 +14,31 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ +#pragma once #include "VMFace.h" -#include "VM.h" -using namespace dev; -using namespace dev::eth; +namespace dev +{ +namespace eth +{ -void VMFace::reset(u256 _gas) noexcept +enum class VMKind: bool { - m_gas = _gas; + Interpreter, + JIT +}; + +class VMFactory +{ +public: + VMFactory() = delete; + + static std::unique_ptr create(u256 _gas); + + static void setKind(VMKind _kind); + +}; + +} } diff --git a/libp2p/Common.h b/libp2p/Common.h index 34f47b236..d46c5eed1 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -57,7 +57,7 @@ class Session; struct NetWarn: public LogChannel { static const char* name() { return "!N!"; } static const int verbosity = 0; }; struct NetNote: public LogChannel { static const char* name() { return "*N*"; } static const int verbosity = 1; }; struct NetMessageSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 2; }; -struct NetConnect: public LogChannel { static const char* name() { return "+N+"; } static const int verbosity = 4; }; +struct NetConnect: public LogChannel { static const char* name() { return "+N+"; } static const int verbosity = 10; }; struct NetMessageDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 5; }; struct NetTriviaSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 10; }; struct NetTriviaDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 11; }; diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 7d08910aa..93a6ce672 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -15,22 +15,11 @@ along with cpp-ethereum. If not, see . */ /** @file Host.cpp - * @authors: - * Gav Wood - * Eric Lombrozo (Windows version of populateAddresses()) + * @author Alex Leverington + * @author Gav Wood * @date 2014 */ -#include "Host.h" - -#include -#ifdef _WIN32 -// winsock is already included -// #include -#else -#include -#endif - #include #include #include @@ -43,166 +32,18 @@ #include "Common.h" #include "Capability.h" #include "UPnP.h" +#include "Host.h" using namespace std; using namespace dev; using namespace dev::p2p; -std::vector Host::getInterfaceAddresses() -{ - std::vector addresses; - -#ifdef _WIN32 - WSAData wsaData; - if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) - BOOST_THROW_EXCEPTION(NoNetworking()); - - char ac[80]; - if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) - { - clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name."; - WSACleanup(); - BOOST_THROW_EXCEPTION(NoNetworking()); - } - - struct hostent* phe = gethostbyname(ac); - if (phe == 0) - { - clog(NetWarn) << "Bad host lookup."; - WSACleanup(); - BOOST_THROW_EXCEPTION(NoNetworking()); - } - - for (int i = 0; phe->h_addr_list[i] != 0; ++i) - { - struct in_addr addr; - memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); - char *addrStr = inet_ntoa(addr); - bi::address address(bi::address::from_string(addrStr)); - if (!isLocalHostAddress(address)) - addresses.push_back(address.to_v4()); - } - - WSACleanup(); -#else - ifaddrs* ifaddr; - if (getifaddrs(&ifaddr) == -1) - BOOST_THROW_EXCEPTION(NoNetworking()); - - for (auto ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) - { - if (!ifa->ifa_addr || string(ifa->ifa_name) == "lo0") - continue; - - if (ifa->ifa_addr->sa_family == AF_INET) - { - in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; - boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr)); - if (!isLocalHostAddress(address)) - addresses.push_back(address); - } - else if (ifa->ifa_addr->sa_family == AF_INET6) - { - sockaddr_in6* sockaddr = ((struct sockaddr_in6 *)ifa->ifa_addr); - in6_addr addr = sockaddr->sin6_addr; - boost::asio::ip::address_v6::bytes_type bytes; - memcpy(&bytes[0], addr.s6_addr, 16); - boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id); - if (!isLocalHostAddress(address)) - addresses.push_back(address); - } - } - - if (ifaddr!=NULL) - freeifaddrs(ifaddr); - -#endif - - return std::move(addresses); -} - -int Host::listen4(bi::tcp::acceptor* _acceptor, unsigned short _listenPort) -{ - int retport = -1; - for (unsigned i = 0; i < 2; ++i) - { - // try to connect w/listenPort, else attempt net-allocated port - bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _listenPort); - try - { - _acceptor->open(endpoint.protocol()); - _acceptor->set_option(ba::socket_base::reuse_address(true)); - _acceptor->bind(endpoint); - _acceptor->listen(); - retport = _acceptor->local_endpoint().port(); - break; - } - catch (...) - { - if (i) - { - // both attempts failed - cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); - } - - // first attempt failed - _acceptor->close(); - continue; - } - } - return retport; -} - -bi::tcp::endpoint Host::traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr) -{ - asserts(_listenPort != 0); - - UPnP* upnp = nullptr; - try - { - upnp = new UPnP; - } - // let m_upnp continue as null - we handle it properly. - catch (NoUPnPDevice) {} - - bi::tcp::endpoint upnpep; - if (upnp && upnp->isValid()) - { - bi::address paddr; - int extPort = 0; - for (auto const& addr: _ifAddresses) - if (addr.is_v4() && isPrivateAddress(addr) && (extPort = upnp->addRedirect(addr.to_string().c_str(), _listenPort))) - { - paddr = addr; - break; - } - - auto eip = upnp->externalIP(); - bi::address eipaddr(bi::address::from_string(eip)); - if (extPort && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr)) - { - clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << extPort << "."; - clog(NetNote) << "External addr:" << eip; - o_upnpifaddr = paddr; - upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)extPort); - } - else - clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place)."; - - if (upnp) - delete upnp; - } - - return upnpep; -} - Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool _start): Worker("p2p", 0), m_clientVersion(_clientVersion), m_netPrefs(_n), - m_ifAddresses(getInterfaceAddresses()), - m_ioService(new ba::io_service(2)), - m_acceptor(new bi::tcp::acceptor(*m_ioService)), - m_socket(new bi::tcp::socket(*m_ioService)), + m_ifAddresses(Network::getInterfaceAddresses()), + m_ioService(2), + m_acceptorV4(m_ioService), m_key(KeyPair::create()) { for (auto address: m_ifAddresses) @@ -216,7 +57,7 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool Host::~Host() { - quit(); + stop(); } void Host::start() @@ -226,30 +67,78 @@ void Host::start() void Host::stop() { + // called to force io_service to kill any remaining tasks it might have - + // such tasks may involve socket reads from Capabilities that maintain references + // to resources we're about to free. + { - // prevent m_run from being set to false at same time as set to true by start() + // Although m_run is set by stop() or start(), it effects m_runTimer so x_runTimer is used instead of a mutex for m_run. + // when m_run == false, run() will cause this::run() to stop() ioservice Guard l(x_runTimer); - // once m_run is false the scheduler will shutdown network and stopWorking() + // ignore if already stopped/stopping + if (!m_run) + return; m_run = false; } - // we know shutdown is complete when m_timer is reset - while (m_timer) + // wait for m_timer to reset (indicating network scheduler has stopped) + while (!!m_timer) this_thread::sleep_for(chrono::milliseconds(50)); + + // stop worker thread stopWorking(); } -void Host::quit() +void Host::doneWorking() { - // called to force io_service to kill any remaining tasks it might have - - // such tasks may involve socket reads from Capabilities that maintain references - // to resources we're about to free. - if (isWorking()) - stop(); - m_acceptor.reset(); - m_socket.reset(); + // reset ioservice (allows manually polling network, below) + m_ioService.reset(); + + // shutdown acceptor + m_acceptorV4.cancel(); + if (m_acceptorV4.is_open()) + m_acceptorV4.close(); + + // There maybe an incoming connection which started but hasn't finished. + // Wait for acceptor to end itself instead of assuming it's complete. + // This helps ensure a peer isn't stopped at the same time it's starting + // and that socket for pending connection is closed. + while (m_accepting) + m_ioService.poll(); + + // stop capabilities (eth: stops syncing or block/tx broadcast) + for (auto const& h: m_capabilities) + h.second->onStopping(); + + // disconnect peers + for (unsigned n = 0;; n = 0) + { + { + RecursiveGuard l(x_peers); + for (auto i: m_peers) + if (auto p = i.second.lock()) + if (p->isOpen()) + { + p->disconnect(ClientQuit); + n++; + } + } + if (!n) + break; + + // poll so that peers send out disconnect packets + m_ioService.poll(); + } + + // stop network (again; helpful to call before subsequent reset()) + m_ioService.stop(); + + // reset network (allows reusing ioservice in future) m_ioService.reset(); - // m_acceptor & m_socket are DANGEROUS now. + + // finally, clear out peers (in case they're lingering) + RecursiveGuard l(x_peers); + m_peers.clear(); } unsigned Host::protocolVersion() const @@ -407,7 +296,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) if (_upnp) { bi::address upnpifaddr; - bi::tcp::endpoint upnpep = traverseNAT(m_ifAddresses, m_listenPort, upnpifaddr); + bi::tcp::endpoint upnpep = Network::traverseNAT(m_ifAddresses, m_listenPort, upnpifaddr); if (!upnpep.address().is_unspecified() && !upnpifaddr.is_unspecified()) { if (!m_peerAddresses.count(upnpep.address())) @@ -431,18 +320,18 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) m_public = bi::tcp::endpoint(bi::address(), m_listenPort); } -void Host::ensureAccepting() +void Host::runAcceptor() { - // return if there's no io-server (quit called) or we're not listening - if (!m_ioService || m_listenPort < 1) - return; - - if (!m_accepting) + assert(m_listenPort > 0); + + if (m_run && !m_accepting) { clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")"; m_accepting = true; - m_acceptor->async_accept(*m_socket, [=](boost::system::error_code ec) + m_socket.reset(new bi::tcp::socket(m_ioService)); + m_acceptorV4.async_accept(*m_socket, [=](boost::system::error_code ec) { + bool success = false; if (!ec) { try @@ -452,8 +341,9 @@ void Host::ensureAccepting() } catch (...){} bi::address remoteAddress = m_socket->remote_endpoint().address(); // Port defaults to 0 - we let the hello tell us which port the peer listens to - auto p = std::make_shared(this, std::move(*m_socket), bi::tcp::endpoint(remoteAddress, 0)); + auto p = std::make_shared(this, std::move(*m_socket.release()), bi::tcp::endpoint(remoteAddress, 0)); p->start(); + success = true; } catch (Exception const& _e) { @@ -464,9 +354,17 @@ void Host::ensureAccepting() clog(NetWarn) << "ERROR: " << _e.what(); } } + + if (!success && m_socket->is_open()) + { + boost::system::error_code ec; + m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + m_socket->close(); + } + m_accepting = false; if (ec.value() < 1) - ensureAccepting(); + runAcceptor(); }); } } @@ -480,17 +378,16 @@ string Host::pocHost() void Host::connect(std::string const& _addr, unsigned short _port) noexcept { - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!m_ioService) + if (!m_run) return; - for (int i = 0; i < 2; ++i) + for (auto first: {true, false}) { try { - if (i == 0) + if (first) { - bi::tcp::resolver r(*m_ioService); + bi::tcp::resolver r(m_ioService); connect(r.resolve({_addr, toString(_port)})->endpoint()); } else @@ -512,12 +409,11 @@ void Host::connect(std::string const& _addr, unsigned short _port) noexcept void Host::connect(bi::tcp::endpoint const& _ep) { - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!m_ioService) + if (!m_run) return; clog(NetConnect) << "Attempting single-shot connection to " << _ep; - bi::tcp::socket* s = new bi::tcp::socket(*m_ioService); + bi::tcp::socket* s = new bi::tcp::socket(m_ioService); s->async_connect(_ep, [=](boost::system::error_code const& ec) { if (ec) @@ -534,8 +430,7 @@ void Host::connect(bi::tcp::endpoint const& _ep) void Host::connect(std::shared_ptr const& _n) { - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!m_ioService) + if (!m_run) return; // prevent concurrently connecting to a node; todo: better abstraction @@ -551,27 +446,32 @@ void Host::connect(std::shared_ptr const& _n) _n->lastAttempted = std::chrono::system_clock::now(); _n->failedAttempts++; m_ready -= _n->index; - bi::tcp::socket* s = new bi::tcp::socket(*m_ioService); - s->async_connect(_n->address, [=](boost::system::error_code const& ec) - { - if (ec) - { - clog(NetConnect) << "Connection refused to node" << _n->id.abridged() << "@" << _n->address << "(" << ec.message() << ")"; - _n->lastDisconnect = TCPError; - _n->lastAttempted = std::chrono::system_clock::now(); - m_ready += _n->index; - } - else + bi::tcp::socket* s = new bi::tcp::socket(m_ioService); + + auto n = node(_n->id); + if (n) + s->async_connect(_n->address, [=](boost::system::error_code const& ec) { - clog(NetConnect) << "Connected to" << _n->id.abridged() << "@" << _n->address; - _n->lastConnected = std::chrono::system_clock::now(); - auto p = make_shared(this, std::move(*s), node(_n->id), true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism. - p->start(); - } - delete s; - Guard l(x_pendingNodeConns); - m_pendingNodeConns.erase(nptr); - }); + if (ec) + { + clog(NetConnect) << "Connection refused to node" << _n->id.abridged() << "@" << _n->address << "(" << ec.message() << ")"; + _n->lastDisconnect = TCPError; + _n->lastAttempted = std::chrono::system_clock::now(); + m_ready += _n->index; + } + else + { + clog(NetConnect) << "Connected to" << _n->id.abridged() << "@" << _n->address; + _n->lastConnected = std::chrono::system_clock::now(); + auto p = make_shared(this, std::move(*s), n, true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism. + p->start(); + } + delete s; + Guard l(x_pendingNodeConns); + m_pendingNodeConns.erase(nptr); + }); + else + clog(NetWarn) << "Trying to connect to node not in node table."; } bool Host::havePeer(NodeId _id) const @@ -680,8 +580,7 @@ void Host::prunePeers() PeerInfos Host::peers(bool _updatePing) const { - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!m_ioService) + if (!m_run) return PeerInfos(); RecursiveGuard l(x_peers); @@ -698,152 +597,95 @@ PeerInfos Host::peers(bool _updatePing) const return ret; } -void Host::run(boost::system::error_code const& error) +void Host::run(boost::system::error_code const&) { - m_lastTick += c_timerInterval; - - if (error || !m_ioService) + if (!m_run) { - // timer died or io service went away, so stop here + // stopping io service allows running manual network operations for shutdown + // and also stops blocking worker thread, allowing worker thread to exit + m_ioService.stop(); + + // resetting timer signals network that nothing else can be scheduled to run m_timer.reset(); return; } - // network running - if (m_run) + m_lastTick += c_timerInterval; + if (m_lastTick >= c_timerInterval * 10) { - if (m_lastTick >= c_timerInterval * 10) - { - growPeers(); - prunePeers(); - m_lastTick = 0; - } - - if (m_hadNewNodes) - { - for (auto p: m_peers) - if (auto pp = p.second.lock()) - pp->serviceNodesRequest(); - - m_hadNewNodes = false; - } - - if (chrono::steady_clock::now() - m_lastPing > chrono::seconds(30)) // ping every 30s. - { - for (auto p: m_peers) - if (auto pp = p.second.lock()) - if (chrono::steady_clock::now() - pp->m_lastReceived > chrono::seconds(60)) - pp->disconnect(PingTimeout); - pingAll(); - } - - auto runcb = [this](boost::system::error_code const& error) -> void { run(error); }; - m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); - m_timer->async_wait(runcb); - - return; + growPeers(); + prunePeers(); + m_lastTick = 0; } - // network stopping - if (!m_run) + if (m_hadNewNodes) { - // close acceptor - if (m_acceptor->is_open()) - { - if (m_accepting) - m_acceptor->cancel(); - m_acceptor->close(); - m_accepting = false; - } - - // stop capabilities (eth: stops syncing or block/tx broadcast) - for (auto const& h: m_capabilities) - h.second->onStopping(); - - // disconnect peers - for (unsigned n = 0;; n = 0) - { - { - RecursiveGuard l(x_peers); - for (auto i: m_peers) - if (auto p = i.second.lock()) - if (p->isOpen()) - { - p->disconnect(ClientQuit); - n++; - } - } - if (!n) - break; - this_thread::sleep_for(chrono::milliseconds(100)); - } - - if (m_socket->is_open()) - m_socket->close(); + for (auto p: m_peers) + if (auto pp = p.second.lock()) + pp->serviceNodesRequest(); - // m_run is false, so we're stopping; kill timer - m_lastTick = 0; - - // causes parent thread's stop() to continue which calls stopWorking() - m_timer.reset(); - - // stop ioservice (stops blocking worker thread, allowing thread to join) - if (!!m_ioService) - m_ioService->stop(); - return; + m_hadNewNodes = false; + } + + if (chrono::steady_clock::now() - m_lastPing > chrono::seconds(30)) // ping every 30s. + { + for (auto p: m_peers) + if (auto pp = p.second.lock()) + if (chrono::steady_clock::now() - pp->m_lastReceived > chrono::seconds(60)) + pp->disconnect(PingTimeout); + pingAll(); } + + auto runcb = [this](boost::system::error_code const& error) -> void { run(error); }; + m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); + m_timer->async_wait(runcb); } void Host::startedWorking() { - if (!m_timer) + asserts(!m_timer); + { - // no timer means this is first run and network must be started - // (run once when host worker thread calls startedWorking()) - - { - // prevent m_run from being set to true at same time as set to false by stop() - // don't release mutex until m_timer is set so in case stop() is called at same - // time, stop will wait on m_timer and graceful network shutdown. - Guard l(x_runTimer); - // reset io service and create deadline timer - m_timer.reset(new boost::asio::deadline_timer(*m_ioService)); - m_run = true; - } - m_ioService->reset(); - - // try to open acceptor (todo: ipv6) - m_listenPort = listen4(m_acceptor.get(), m_netPrefs.listenPort); - - // start capability threads - for (auto const& h: m_capabilities) - h.second->onStarting(); - - // determine public IP, but only if we're able to listen for connections - // todo: GUI when listen is unavailable in UI - if (m_listenPort) - { - determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); - ensureAccepting(); - } - - // if m_public address is valid then add us to node list - // todo: abstract empty() and emplace logic - if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id())) - noteNode(id(), m_public, Origin::Perfect, false); + // prevent m_run from being set to true at same time as set to false by stop() + // don't release mutex until m_timer is set so in case stop() is called at same + // time, stop will wait on m_timer and graceful network shutdown. + Guard l(x_runTimer); + // create deadline timer + m_timer.reset(new boost::asio::deadline_timer(m_ioService)); + m_run = true; + } + + // try to open acceptor (todo: ipv6) + m_listenPort = Network::listen4(m_acceptorV4, m_netPrefs.listenPort); + + // start capability threads + for (auto const& h: m_capabilities) + h.second->onStarting(); + + // determine public IP, but only if we're able to listen for connections + // todo: GUI when listen is unavailable in UI + if (m_listenPort) + { + determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); - clog(NetNote) << "Id:" << id().abridged(); + if (m_listenPort > 0) + runAcceptor(); } + // if m_public address is valid then add us to node list + // todo: abstract empty() and emplace logic + if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id())) + noteNode(id(), m_public, Origin::Perfect, false); + + clog(NetNote) << "Id:" << id().abridged(); + run(boost::system::error_code()); } void Host::doWork() { - // no ioService means we've had quit() called - bomb out - we're not allowed in here. - if (asserts(!!m_ioService)) - return; - m_ioService->run(); + if (m_run) + m_ioService.run(); } void Host::pingAll() diff --git a/libp2p/Host.h b/libp2p/Host.h index 644afeb69..a146d6a66 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -14,7 +14,8 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file EthereumHost.h +/** @file Host.h + * @author Alex Leverington * @author Gav Wood * @date 2014 */ @@ -34,9 +35,10 @@ #include #include #include "HostCapability.h" +#include "Network.h" #include "Common.h" namespace ba = boost::asio; -namespace bi = boost::asio::ip; +namespace bi = ba::ip; namespace dev { @@ -101,16 +103,6 @@ struct Node using Nodes = std::vector; -struct NetworkPreferences -{ - NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), publicIP(i), upnp(u), localNetworking(l) {} - - unsigned short listenPort = 30303; - std::string publicIP; - bool upnp = true; - bool localNetworking = false; -}; - /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. @@ -120,17 +112,6 @@ class Host: public Worker friend class Session; friend class HostCapabilityFace; friend struct Node; - - /// Static network interface methods -public: - /// @returns public and private interface addresses - static std::vector getInterfaceAddresses(); - - /// Try to bind and listen on _listenPort, else attempt net-allocated port. - static int listen4(bi::tcp::acceptor* _acceptor, unsigned short _listenPort); - - /// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port. - static bi::tcp::endpoint traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr); public: /// Start server, listening for connections on the given port. @@ -187,14 +168,12 @@ public: void start(); /// Stop network. @threadsafe + /// Resets acceptor, socket, and IO service. Called by deallocator. void stop(); /// @returns if network is running. bool isStarted() const { return m_run; } - /// Reset acceptor, socket, and IO service. Called by deallocator. Maybe called by implementation when ordered deallocation is required. - void quit(); - NodeId id() const { return m_key.pub(); } void registerPeer(std::shared_ptr _s, CapDescs const& _caps); @@ -205,7 +184,8 @@ private: /// Populate m_peerAddresses with available public addresses. void determinePublic(std::string const& _publicAddress, bool _upnp); - void ensureAccepting(); + /// Called only from startedWorking(). + void runAcceptor(); void seal(bytes& _b); @@ -214,11 +194,14 @@ private: /// Called by Worker. Not thread-safe; to be called only by worker. virtual void startedWorking(); - /// Called by startedWorking. Not thread-safe; to be called only be worker callback. + /// Called by startedWorking. Not thread-safe; to be called only be Worker. void run(boost::system::error_code const& error); ///< Run network. Called serially via ASIO deadline timer. Manages connection state transitions. - /// Run network + /// Run network. Not thread-safe; to be called only by worker. virtual void doWork(); + + /// Shutdown network. Not thread-safe; to be called only by worker. + virtual void doneWorking(); std::shared_ptr noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = NodeId()); Nodes potentialPeers(RangeMask const& _known); @@ -235,8 +218,8 @@ private: int m_listenPort = -1; ///< What port are we listening on. -1 means binding failed or acceptor hasn't been initialized. - std::unique_ptr m_ioService; ///< IOService for network stuff. - std::unique_ptr m_acceptor; ///< Listening acceptor. + ba::io_service m_ioService; ///< IOService for network stuff. + bi::tcp::acceptor m_acceptorV4; ///< Listening acceptor. std::unique_ptr m_socket; ///< Listening socket. std::unique_ptr m_timer; ///< Timer which, when network is running, calls scheduler() every c_timerInterval ms. diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp new file mode 100644 index 000000000..8ca8dd135 --- /dev/null +++ b/libp2p/Network.cpp @@ -0,0 +1,187 @@ +/* + 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 Network.cpp + * @author Alex Leverington + * @author Gav Wood + * @author Eric Lombrozo (Windows version of getInterfaceAddresses()) + * @date 2014 + */ + +#include +#ifndef _WIN32 +#include +#endif + +#include +#include +#include +#include +#include "Common.h" +#include "UPnP.h" +#include "Network.h" + +using namespace std; +using namespace dev; +using namespace dev::p2p; + +std::vector Network::getInterfaceAddresses() +{ + std::vector addresses; + +#ifdef _WIN32 + WSAData wsaData; + if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) + BOOST_THROW_EXCEPTION(NoNetworking()); + + char ac[80]; + if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) + { + clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name."; + WSACleanup(); + BOOST_THROW_EXCEPTION(NoNetworking()); + } + + struct hostent* phe = gethostbyname(ac); + if (phe == 0) + { + clog(NetWarn) << "Bad host lookup."; + WSACleanup(); + BOOST_THROW_EXCEPTION(NoNetworking()); + } + + for (int i = 0; phe->h_addr_list[i] != 0; ++i) + { + struct in_addr addr; + memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); + char *addrStr = inet_ntoa(addr); + bi::address address(bi::address::from_string(addrStr)); + if (!isLocalHostAddress(address)) + addresses.push_back(address.to_v4()); + } + + WSACleanup(); +#else + ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) + BOOST_THROW_EXCEPTION(NoNetworking()); + + for (auto ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) + { + if (!ifa->ifa_addr || string(ifa->ifa_name) == "lo0") + continue; + + if (ifa->ifa_addr->sa_family == AF_INET) + { + in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr)); + if (!isLocalHostAddress(address)) + addresses.push_back(address); + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + sockaddr_in6* sockaddr = ((struct sockaddr_in6 *)ifa->ifa_addr); + in6_addr addr = sockaddr->sin6_addr; + boost::asio::ip::address_v6::bytes_type bytes; + memcpy(&bytes[0], addr.s6_addr, 16); + boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id); + if (!isLocalHostAddress(address)) + addresses.push_back(address); + } + } + + if (ifaddr!=NULL) + freeifaddrs(ifaddr); + +#endif + + return std::move(addresses); +} + +int Network::listen4(bi::tcp::acceptor& _acceptor, unsigned short _listenPort) +{ + int retport = -1; + for (unsigned i = 0; i < 2; ++i) + { + // try to connect w/listenPort, else attempt net-allocated port + bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _listenPort); + try + { + _acceptor.open(endpoint.protocol()); + _acceptor.set_option(ba::socket_base::reuse_address(true)); + _acceptor.bind(endpoint); + _acceptor.listen(); + retport = _acceptor.local_endpoint().port(); + break; + } + catch (...) + { + if (i) + { + // both attempts failed + cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); + } + + // first attempt failed + _acceptor.close(); + continue; + } + } + return retport; +} + +bi::tcp::endpoint Network::traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr) +{ + asserts(_listenPort != 0); + + UPnP* upnp = nullptr; + try + { + upnp = new UPnP; + } + // let m_upnp continue as null - we handle it properly. + catch (NoUPnPDevice) {} + + bi::tcp::endpoint upnpep; + if (upnp && upnp->isValid()) + { + bi::address paddr; + int extPort = 0; + for (auto const& addr: _ifAddresses) + if (addr.is_v4() && isPrivateAddress(addr) && (extPort = upnp->addRedirect(addr.to_string().c_str(), _listenPort))) + { + paddr = addr; + break; + } + + auto eip = upnp->externalIP(); + bi::address eipaddr(bi::address::from_string(eip)); + if (extPort && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr)) + { + clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << extPort << "."; + clog(NetNote) << "External addr:" << eip; + o_upnpifaddr = paddr; + upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)extPort); + } + else + clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place)."; + + if (upnp) + delete upnp; + } + + return upnpep; +} diff --git a/libp2p/Network.h b/libp2p/Network.h new file mode 100644 index 000000000..944d390c8 --- /dev/null +++ b/libp2p/Network.h @@ -0,0 +1,63 @@ +/* + 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 Network.h + * @author Alex Leverington + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +namespace ba = boost::asio; +namespace bi = ba::ip; + +namespace dev +{ + +namespace p2p +{ + +struct NetworkPreferences +{ + NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), publicIP(i), upnp(u), localNetworking(l) {} + + unsigned short listenPort = 30303; + std::string publicIP; + bool upnp = true; + bool localNetworking = false; +}; + +/** + * @brief Network Class + * Static network operations and interface(s). + */ +class Network +{ +public: + /// @returns public and private interface addresses + static std::vector getInterfaceAddresses(); + + /// Try to bind and listen on _listenPort, else attempt net-allocated port. + static int listen4(bi::tcp::acceptor& _acceptor, unsigned short _listenPort); + + /// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port. + static bi::tcp::endpoint traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr); +}; + +} +} diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 958473870..cb0a60a92 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -75,7 +75,11 @@ Session::~Session() try { if (m_socket.is_open()) + { + boost::system::error_code ec; + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); m_socket.close(); + } } catch (...){} } @@ -479,6 +483,8 @@ void Session::drop(DisconnectReason _reason) try { clogS(NetConnect) << "Closing " << m_socket.remote_endpoint() << "(" << reasonOf(_reason) << ")"; + boost::system::error_code ec; + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); m_socket.close(); } catch (...) {} diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 4bd0b2c0e..8174d138a 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -25,6 +25,7 @@ #include #include #include +#include using namespace std; @@ -33,38 +34,34 @@ namespace dev namespace solidity { -void ContractDefinition::accept(ASTVisitor& _visitor) +TypeError ASTNode::createTypeError(string const& _description) const { - if (_visitor.visit(*this)) - { - listAccept(m_definedStructs, _visitor); - listAccept(m_stateVariables, _visitor); - listAccept(m_definedFunctions, _visitor); - } - _visitor.endVisit(*this); + return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); } -void StructDefinition::accept(ASTVisitor& _visitor) +vector ContractDefinition::getInterfaceFunctions() const { - if (_visitor.visit(*this)) - listAccept(m_members, _visitor); - _visitor.endVisit(*this); -} + vector exportedFunctions; + for (ASTPointer const& f: m_definedFunctions) + if (f->isPublic() && f->getName() != getName()) + exportedFunctions.push_back(f.get()); + auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b) + { + return _a->getName().compare(_b->getName()) < 0; + }; -void StructDefinition::checkValidityOfMembers() -{ - checkMemberTypes(); - checkRecursion(); + sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames); + return exportedFunctions; } -void StructDefinition::checkMemberTypes() +void StructDefinition::checkMemberTypes() const { for (ASTPointer const& member: getMembers()) if (!member->getType()->canBeStored()) BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct.")); } -void StructDefinition::checkRecursion() +void StructDefinition::checkRecursion() const { set definitionsSeen; vector queue = {this}; @@ -79,239 +76,12 @@ void StructDefinition::checkRecursion() for (ASTPointer const& member: def->getMembers()) if (member->getType()->getCategory() == Type::Category::STRUCT) { - UserDefinedTypeName const& typeName = dynamic_cast(*member->getTypeName()); + UserDefinedTypeName const& typeName = dynamic_cast(*member->getTypeName()); queue.push_back(&dynamic_cast(*typeName.getReferencedDeclaration())); } } } -void ParameterList::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_parameters, _visitor); - _visitor.endVisit(*this); -} - -void FunctionDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_parameters->accept(_visitor); - if (m_returnParameters) - m_returnParameters->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void VariableDeclaration::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - if (m_typeName) - m_typeName->accept(_visitor); - _visitor.endVisit(*this); -} - -void TypeName::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ElementaryTypeName::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void UserDefinedTypeName::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Mapping::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_keyType->accept(_visitor); - m_valueType->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Statement::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Block::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_statements, _visitor); - _visitor.endVisit(*this); -} - -void IfStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_trueBody->accept(_visitor); - if (m_falseBody) - m_falseBody->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void BreakableStatement::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void WhileStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Continue::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Break::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Return::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - if (m_expression) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void ExpressionStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - if (m_expression) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void VariableDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_variable->accept(_visitor); - if (m_value) - m_value->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Assignment::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_leftHandSide->accept(_visitor); - m_rightHandSide->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void UnaryOperation::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - m_subExpression->accept(_visitor); - _visitor.endVisit(*this); -} - -void BinaryOperation::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_left->accept(_visitor); - m_right->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void FunctionCall::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_expression->accept(_visitor); - listAccept(m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void MemberAccess::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void IndexAccess::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_base->accept(_visitor); - m_index->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Identifier::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Literal::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -TypeError ASTNode::createTypeError(string const& _description) const -{ - return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); -} - -vector ContractDefinition::getInterfaceFunctions() const -{ - vector exportedFunctions; - for (ASTPointer const& f: m_definedFunctions) - if (f->isPublic() && f->getName() != getName()) - exportedFunctions.push_back(f.get()); - auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b) - { - return _a->getName().compare(_b->getName()) < 0; - }; - - sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames); - return exportedFunctions; -} - void FunctionDefinition::checkTypeRequirements() { for (ASTPointer const& var: getParameters() + getReturnParameters()) @@ -519,7 +289,7 @@ void Identifier::checkTypeRequirements() if (asserts(m_referencedDeclaration)) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier not resolved.")); - VariableDeclaration* variable = dynamic_cast(m_referencedDeclaration); + VariableDeclaration const* variable = dynamic_cast(m_referencedDeclaration); if (variable) { if (!variable->getType()) @@ -529,29 +299,29 @@ void Identifier::checkTypeRequirements() return; } //@todo can we unify these with TypeName::toType()? - StructDefinition* structDef = dynamic_cast(m_referencedDeclaration); + StructDefinition const* structDef = dynamic_cast(m_referencedDeclaration); if (structDef) { // note that we do not have a struct type here - m_type = make_shared(make_shared(*structDef)); + m_type = make_shared(make_shared(*structDef)); return; } - FunctionDefinition* functionDef = dynamic_cast(m_referencedDeclaration); + FunctionDefinition const* functionDef = dynamic_cast(m_referencedDeclaration); if (functionDef) { // a function reference is not a TypeType, because calling a TypeType converts to the type. // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type // conversion. - m_type = make_shared(*functionDef); + m_type = make_shared(*functionDef); return; } - ContractDefinition* contractDef = dynamic_cast(m_referencedDeclaration); + ContractDefinition const* contractDef = dynamic_cast(m_referencedDeclaration); if (contractDef) { - m_type = make_shared(make_shared(*contractDef)); + m_type = make_shared(make_shared(*contractDef)); return; } - MagicVariableDeclaration* magicVariable = dynamic_cast(m_referencedDeclaration); + MagicVariableDeclaration const* magicVariable = dynamic_cast(m_referencedDeclaration); if (magicVariable) { m_type = magicVariable->getType(); @@ -562,7 +332,7 @@ void Identifier::checkTypeRequirements() void ElementaryTypeNameExpression::checkTypeRequirements() { - m_type = make_shared(Type::fromElementaryTypeName(m_typeToken)); + m_type = make_shared(Type::fromElementaryTypeName(m_typeToken)); } void Literal::checkTypeRequirements() diff --git a/libsolidity/AST.h b/libsolidity/AST.h index d29e84a0a..0faea3fb9 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -39,6 +39,7 @@ namespace solidity { class ASTVisitor; +class ASTConstVisitor; /** @@ -54,12 +55,19 @@ public: virtual ~ASTNode() {} virtual void accept(ASTVisitor& _visitor) = 0; + virtual void accept(ASTConstVisitor& _visitor) const = 0; template static void listAccept(std::vector>& _list, ASTVisitor& _visitor) { for (ASTPointer& element: _list) element->accept(_visitor); } + template + static void listAccept(std::vector> const& _list, ASTConstVisitor& _visitor) + { + for (ASTPointer const& element: _list) + element->accept(_visitor); + } /// Returns the source code location of this node. Location const& getLocation() const { return m_location; } @@ -79,6 +87,44 @@ private: Location m_location; }; +/** + * Source unit containing import directives and contract definitions. + */ +class SourceUnit: public ASTNode +{ +public: + SourceUnit(Location const& _location, std::vector> const& _nodes): + ASTNode(_location), m_nodes(_nodes) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector> getNodes() const { return m_nodes; } + +private: + std::vector> m_nodes; +}; + +/** + * Import directive for referencing other files / source objects. + * Example: import "abc.sol" + * Source objects are identified by a string which can be a file name but does not have to be. + */ +class ImportDirective: public ASTNode +{ +public: + ImportDirective(Location const& _location, ASTPointer const& _identifier): + ASTNode(_location), m_identifier(_identifier) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + ASTString const& getIdentifier() const { return *m_identifier; } + +private: + ASTPointer m_identifier; +}; + /** * Abstract AST class for a declaration (contract, function, struct, variable). */ @@ -86,18 +132,18 @@ class Declaration: public ASTNode { public: Declaration(Location const& _location, ASTPointer const& _name): - ASTNode(_location), m_name(_name) {} + ASTNode(_location), m_name(_name), m_scope(nullptr) {} /// @returns the declared name. ASTString const& getName() const { return *m_name; } /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. /// Available only after name and type resolution step. - Declaration* getScope() const { return m_scope; } - void setScope(Declaration* const& _scope) { m_scope = _scope; } + Declaration const* getScope() const { return m_scope; } + void setScope(Declaration const* _scope) { m_scope = _scope; } private: ASTPointer m_name; - Declaration* m_scope; + Declaration const* m_scope; }; /** @@ -120,6 +166,7 @@ public: {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; std::vector> const& getDefinedStructs() const { return m_definedStructs; } std::vector> const& getStateVariables() const { return m_stateVariables; } @@ -142,16 +189,17 @@ public: std::vector> const& _members): Declaration(_location, _name), m_members(_members) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; std::vector> const& getMembers() const { return m_members; } /// Checks that the members do not include any recursive structs and have valid types /// (e.g. no functions). - void checkValidityOfMembers(); + void checkValidityOfMembers() const; private: - void checkMemberTypes(); - void checkRecursion(); + void checkMemberTypes() const; + void checkRecursion() const; std::vector> m_members; }; @@ -168,6 +216,7 @@ public: std::vector> const& _parameters): ASTNode(_location), m_parameters(_parameters) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; std::vector> const& getParameters() const { return m_parameters; } @@ -194,14 +243,15 @@ public: {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; bool isPublic() const { return m_isPublic; } bool isDeclaredConst() const { return m_isDeclaredConst; } std::vector> const& getParameters() const { return m_parameters->getParameters(); } - ParameterList& getParameterList() { return *m_parameters; } + ParameterList const& getParameterList() const { return *m_parameters; } std::vector> const& getReturnParameters() const { return m_returnParameters->getParameters(); } ASTPointer const& getReturnParameterList() const { return m_returnParameters; } - Block& getBody() { return *m_body; } + Block const& getBody() const { return *m_body; } /// @return A shared pointer of an ASTString. /// Can contain a nullptr in which case indicates absence of documentation ASTPointer const& getDocumentation() const { return m_documentation; } @@ -234,15 +284,16 @@ public: ASTPointer const& _name): Declaration(_location, _name), m_typeName(_type) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; - TypeName* getTypeName() const { return m_typeName.get(); } + TypeName const* getTypeName() const { return m_typeName.get(); } /// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly /// declared and there is no assignment to the variable that fixes the type. std::shared_ptr const& getType() const { return m_type; } void setType(std::shared_ptr const& _type) { m_type = _type; } - bool isLocalVariable() const { return !!dynamic_cast(getScope()); } + bool isLocalVariable() const { return !!dynamic_cast(getScope()); } private: ASTPointer m_typeName; ///< can be empty ("var") @@ -261,6 +312,8 @@ public: Declaration(Location(), std::make_shared(_name)), m_type(_type) {} virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST.")); } + virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError() + << errinfo_comment("MagicVariableDeclaration used inside real AST.")); } std::shared_ptr const& getType() const { return m_type; } @@ -279,11 +332,12 @@ class TypeName: public ASTNode public: explicit TypeName(Location const& _location): ASTNode(_location) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; /// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared /// pointer until the types have been resolved using the @ref NameAndTypeResolver. /// If it returns an empty shared pointer after that, this indicates that the type was not found. - virtual std::shared_ptr toType() const = 0; + virtual std::shared_ptr toType() const = 0; }; /** @@ -299,7 +353,8 @@ public: if (asserts(Token::isElementaryTypeName(_type))) BOOST_THROW_EXCEPTION(InternalCompilerError()); } virtual void accept(ASTVisitor& _visitor) override; - virtual std::shared_ptr toType() const override { return Type::fromElementaryTypeName(m_type); } + virtual void accept(ASTConstVisitor& _visitor) const override; + virtual std::shared_ptr toType() const override { return Type::fromElementaryTypeName(m_type); } Token::Value getTypeName() const { return m_type; } @@ -314,18 +369,19 @@ class UserDefinedTypeName: public TypeName { public: UserDefinedTypeName(Location const& _location, ASTPointer const& _name): - TypeName(_location), m_name(_name) {} + TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {} virtual void accept(ASTVisitor& _visitor) override; - virtual std::shared_ptr toType() const override { return Type::fromUserDefinedTypeName(*this); } + virtual void accept(ASTConstVisitor& _visitor) const override; + virtual std::shared_ptr toType() const override { return Type::fromUserDefinedTypeName(*this); } ASTString const& getName() const { return *m_name; } - void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } + void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } private: ASTPointer m_name; - Declaration* m_referencedDeclaration; + Declaration const* m_referencedDeclaration; }; /** @@ -338,7 +394,8 @@ public: ASTPointer const& _valueType): TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} virtual void accept(ASTVisitor& _visitor) override; - virtual std::shared_ptr toType() const override { return Type::fromMapping(*this); } + virtual void accept(ASTConstVisitor& _visitor) const override; + virtual std::shared_ptr toType() const override { return Type::fromMapping(*this); } ElementaryTypeName const& getKeyType() const { return *m_keyType; } TypeName const& getValueType() const { return *m_valueType; } @@ -361,7 +418,6 @@ class Statement: public ASTNode { public: explicit Statement(Location const& _location): ASTNode(_location) {} - virtual void accept(ASTVisitor& _visitor) override; /// Check all type requirements, throws exception if some requirement is not met. /// This includes checking that operators are applicable to their arguments but also that @@ -378,6 +434,7 @@ public: Block(Location const& _location, std::vector> const& _statements): Statement(_location), m_statements(_statements) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; @@ -397,12 +454,13 @@ public: Statement(_location), m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; - Expression& getCondition() const { return *m_condition; } - Statement& getTrueStatement() const { return *m_trueBody; } + Expression const& getCondition() const { return *m_condition; } + Statement const& getTrueStatement() const { return *m_trueBody; } /// @returns the "else" part of the if statement or nullptr if there is no "else" part. - Statement* getFalseStatement() const { return m_falseBody.get(); } + Statement const* getFalseStatement() const { return m_falseBody.get(); } private: ASTPointer m_condition; @@ -411,13 +469,12 @@ private: }; /** - * Statement in which a break statement is legal. + * Statement in which a break statement is legal (abstract class). */ class BreakableStatement: public Statement { public: BreakableStatement(Location const& _location): Statement(_location) {} - virtual void accept(ASTVisitor& _visitor) override; }; class WhileStatement: public BreakableStatement @@ -427,10 +484,11 @@ public: ASTPointer const& _body): BreakableStatement(_location), m_condition(_condition), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; - Expression& getCondition() const { return *m_condition; } - Statement& getBody() const { return *m_body; } + Expression const& getCondition() const { return *m_condition; } + Statement const& getBody() const { return *m_body; } private: ASTPointer m_condition; @@ -442,6 +500,7 @@ class Continue: public Statement public: Continue(Location const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override {} }; @@ -450,6 +509,7 @@ class Break: public Statement public: Break(Location const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override {} }; @@ -457,8 +517,9 @@ class Return: public Statement { public: Return(Location const& _location, ASTPointer _expression): - Statement(_location), m_expression(_expression) {} + Statement(_location), m_expression(_expression), m_returnParameters(nullptr) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } @@ -468,7 +529,7 @@ public: BOOST_THROW_EXCEPTION(InternalCompilerError()); return *m_returnParameters; } - Expression* getExpression() const { return m_expression.get(); } + Expression const* getExpression() const { return m_expression.get(); } private: ASTPointer m_expression; ///< value to return, optional @@ -489,10 +550,11 @@ public: ASTPointer _value): Statement(_location), m_variable(_variable), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; VariableDeclaration const& getDeclaration() const { return *m_variable; } - Expression* getExpression() const { return m_value.get(); } + Expression const* getExpression() const { return m_value.get(); } private: ASTPointer m_variable; @@ -508,9 +570,10 @@ public: ExpressionStatement(Location const& _location, ASTPointer _expression): Statement(_location), m_expression(_expression) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; - Expression& getExpression() const { return *m_expression; } + Expression const& getExpression() const { return *m_expression; } private: ASTPointer m_expression; @@ -572,11 +635,12 @@ public: if (asserts(Token::isAssignmentOp(_assignmentOperator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); } virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; - Expression& getLeftHandSide() const { return *m_leftHandSide; } + Expression const& getLeftHandSide() const { return *m_leftHandSide; } Token::Value getAssignmentOperator() const { return m_assigmentOperator; } - Expression& getRightHandSide() const { return *m_rightHandSide; } + Expression const& getRightHandSide() const { return *m_rightHandSide; } private: ASTPointer m_leftHandSide; @@ -599,11 +663,12 @@ public: if (asserts(Token::isUnaryOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); } virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; Token::Value getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } - Expression& getSubExpression() const { return *m_subExpression; } + Expression const& getSubExpression() const { return *m_subExpression; } private: Token::Value m_operator; @@ -625,10 +690,11 @@ public: if (asserts(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); } virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; - Expression& getLeftExpression() const { return *m_left; } - Expression& getRightExpression() const { return *m_right; } + Expression const& getLeftExpression() const { return *m_left; } + Expression const& getRightExpression() const { return *m_right; } Token::Value getOperator() const { return m_operator; } Type const& getCommonType() const { return *m_commonType; } @@ -652,10 +718,11 @@ public: std::vector> const& _arguments): Expression(_location), m_expression(_expression), m_arguments(_arguments) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; - Expression& getExpression() const { return *m_expression; } - std::vector> const& getArguments() const { return m_arguments; } + Expression const& getExpression() const { return *m_expression; } + std::vector> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; } /// Returns true if this is not an actual function call, but an explicit type conversion /// or constructor call. @@ -676,7 +743,8 @@ public: ASTPointer const& _memberName): Expression(_location), m_expression(_expression), m_memberName(_memberName) {} virtual void accept(ASTVisitor& _visitor) override; - Expression& getExpression() const { return *m_expression; } + virtual void accept(ASTConstVisitor& _visitor) const override; + Expression const& getExpression() const { return *m_expression; } ASTString const& getMemberName() const { return *m_memberName; } virtual void checkTypeRequirements() override; @@ -695,10 +763,11 @@ public: ASTPointer const& _index): Expression(_location), m_base(_base), m_index(_index) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; - Expression& getBaseExpression() const { return *m_base; } - Expression& getIndexExpression() const { return *m_index; } + Expression const& getBaseExpression() const { return *m_base; } + Expression const& getIndexExpression() const { return *m_index; } private: ASTPointer m_base; @@ -722,20 +791,21 @@ class Identifier: public PrimaryExpression { public: Identifier(Location const& _location, ASTPointer const& _name): - PrimaryExpression(_location), m_name(_name) {} + PrimaryExpression(_location), m_name(_name), m_referencedDeclaration(nullptr) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; ASTString const& getName() const { return *m_name; } - void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } - Declaration* getReferencedDeclaration() { return m_referencedDeclaration; } + void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } + Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } private: ASTPointer m_name; /// Declaration the name refers to. - Declaration* m_referencedDeclaration; + Declaration const* m_referencedDeclaration; }; /** @@ -752,6 +822,7 @@ public: if (asserts(Token::isElementaryTypeName(_typeToken))) BOOST_THROW_EXCEPTION(InternalCompilerError()); } virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; Token::Value getTypeToken() const { return m_typeToken; } @@ -769,6 +840,7 @@ public: Literal(Location const& _location, Token::Value _token, ASTPointer const& _value): PrimaryExpression(_location), m_token(_token), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; Token::Value getToken() const { return m_token; } diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index a369c8a79..8b4bac1ce 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -34,6 +34,8 @@ namespace solidity { class ASTNode; +class SourceUnit; +class ImportDirective; class Declaration; class ContractDefinition; class StructDefinition; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 987ad11cc..eddb51340 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -30,7 +30,7 @@ namespace dev namespace solidity { -ASTPrinter::ASTPrinter(ASTNode& _ast, string const& _source): +ASTPrinter::ASTPrinter(ASTNode const& _ast, string const& _source): m_indentation(0), m_source(_source), m_ast(&_ast) { } @@ -43,28 +43,35 @@ void ASTPrinter::print(ostream& _stream) } -bool ASTPrinter::visit(ContractDefinition& _node) +bool ASTPrinter::visit(ImportDirective const& _node) +{ + writeLine("ImportDirective \"" + _node.getIdentifier() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ContractDefinition const& _node) { writeLine("ContractDefinition \"" + _node.getName() + "\""); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(StructDefinition& _node) +bool ASTPrinter::visit(StructDefinition const& _node) { writeLine("StructDefinition \"" + _node.getName() + "\""); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(ParameterList& _node) +bool ASTPrinter::visit(ParameterList const& _node) { writeLine("ParameterList"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(FunctionDefinition& _node) +bool ASTPrinter::visit(FunctionDefinition const& _node) { writeLine("FunctionDefinition \"" + _node.getName() + "\"" + (_node.isPublic() ? " - public" : "") + @@ -73,112 +80,112 @@ bool ASTPrinter::visit(FunctionDefinition& _node) return goDeeper(); } -bool ASTPrinter::visit(VariableDeclaration& _node) +bool ASTPrinter::visit(VariableDeclaration const& _node) { writeLine("VariableDeclaration \"" + _node.getName() + "\""); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(TypeName& _node) +bool ASTPrinter::visit(TypeName const& _node) { writeLine("TypeName"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(ElementaryTypeName& _node) +bool ASTPrinter::visit(ElementaryTypeName const& _node) { writeLine(string("ElementaryTypeName ") + Token::toString(_node.getTypeName())); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(UserDefinedTypeName& _node) +bool ASTPrinter::visit(UserDefinedTypeName const& _node) { writeLine("UserDefinedTypeName \"" + _node.getName() + "\""); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(Mapping& _node) +bool ASTPrinter::visit(Mapping const& _node) { writeLine("Mapping"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(Statement& _node) +bool ASTPrinter::visit(Statement const& _node) { writeLine("Statement"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(Block& _node) +bool ASTPrinter::visit(Block const& _node) { writeLine("Block"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(IfStatement& _node) +bool ASTPrinter::visit(IfStatement const& _node) { writeLine("IfStatement"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(BreakableStatement& _node) +bool ASTPrinter::visit(BreakableStatement const& _node) { writeLine("BreakableStatement"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(WhileStatement& _node) +bool ASTPrinter::visit(WhileStatement const& _node) { writeLine("WhileStatement"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(Continue& _node) +bool ASTPrinter::visit(Continue const& _node) { writeLine("Continue"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(Break& _node) +bool ASTPrinter::visit(Break const& _node) { writeLine("Break"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(Return& _node) +bool ASTPrinter::visit(Return const& _node) { writeLine("Return"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(VariableDefinition& _node) +bool ASTPrinter::visit(VariableDefinition const& _node) { writeLine("VariableDefinition"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(ExpressionStatement& _node) +bool ASTPrinter::visit(ExpressionStatement const& _node) { writeLine("ExpressionStatement"); printSourcePart(_node); return goDeeper(); } -bool ASTPrinter::visit(Expression& _node) +bool ASTPrinter::visit(Expression const& _node) { writeLine("Expression"); printType(_node); @@ -186,7 +193,7 @@ bool ASTPrinter::visit(Expression& _node) return goDeeper(); } -bool ASTPrinter::visit(Assignment& _node) +bool ASTPrinter::visit(Assignment const& _node) { writeLine(string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator())); printType(_node); @@ -194,7 +201,7 @@ bool ASTPrinter::visit(Assignment& _node) return goDeeper(); } -bool ASTPrinter::visit(UnaryOperation& _node) +bool ASTPrinter::visit(UnaryOperation const& _node) { writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + ") " + Token::toString(_node.getOperator())); @@ -203,7 +210,7 @@ bool ASTPrinter::visit(UnaryOperation& _node) return goDeeper(); } -bool ASTPrinter::visit(BinaryOperation& _node) +bool ASTPrinter::visit(BinaryOperation const& _node) { writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); printType(_node); @@ -211,7 +218,7 @@ bool ASTPrinter::visit(BinaryOperation& _node) return goDeeper(); } -bool ASTPrinter::visit(FunctionCall& _node) +bool ASTPrinter::visit(FunctionCall const& _node) { writeLine("FunctionCall"); printType(_node); @@ -219,7 +226,7 @@ bool ASTPrinter::visit(FunctionCall& _node) return goDeeper(); } -bool ASTPrinter::visit(MemberAccess& _node) +bool ASTPrinter::visit(MemberAccess const& _node) { writeLine("MemberAccess to member " + _node.getMemberName()); printType(_node); @@ -227,7 +234,7 @@ bool ASTPrinter::visit(MemberAccess& _node) return goDeeper(); } -bool ASTPrinter::visit(IndexAccess& _node) +bool ASTPrinter::visit(IndexAccess const& _node) { writeLine("IndexAccess"); printType(_node); @@ -235,7 +242,7 @@ bool ASTPrinter::visit(IndexAccess& _node) return goDeeper(); } -bool ASTPrinter::visit(PrimaryExpression& _node) +bool ASTPrinter::visit(PrimaryExpression const& _node) { writeLine("PrimaryExpression"); printType(_node); @@ -243,7 +250,7 @@ bool ASTPrinter::visit(PrimaryExpression& _node) return goDeeper(); } -bool ASTPrinter::visit(Identifier& _node) +bool ASTPrinter::visit(Identifier const& _node) { writeLine(string("Identifier ") + _node.getName()); printType(_node); @@ -251,7 +258,7 @@ bool ASTPrinter::visit(Identifier& _node) return goDeeper(); } -bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) +bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node) { writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken())); printType(_node); @@ -259,7 +266,7 @@ bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) return goDeeper(); } -bool ASTPrinter::visit(Literal& _node) +bool ASTPrinter::visit(Literal const& _node) { char const* tokenString = Token::toString(_node.getToken()); if (!tokenString) @@ -270,157 +277,157 @@ bool ASTPrinter::visit(Literal& _node) return goDeeper(); } -void ASTPrinter::endVisit(ASTNode&) +void ASTPrinter::endVisit(ImportDirective const&) { m_indentation--; } -void ASTPrinter::endVisit(ContractDefinition&) +void ASTPrinter::endVisit(ContractDefinition const&) { m_indentation--; } -void ASTPrinter::endVisit(StructDefinition&) +void ASTPrinter::endVisit(StructDefinition const&) { m_indentation--; } -void ASTPrinter::endVisit(ParameterList&) +void ASTPrinter::endVisit(ParameterList const&) { m_indentation--; } -void ASTPrinter::endVisit(FunctionDefinition&) +void ASTPrinter::endVisit(FunctionDefinition const&) { m_indentation--; } -void ASTPrinter::endVisit(VariableDeclaration&) +void ASTPrinter::endVisit(VariableDeclaration const&) { m_indentation--; } -void ASTPrinter::endVisit(TypeName&) +void ASTPrinter::endVisit(TypeName const&) { m_indentation--; } -void ASTPrinter::endVisit(ElementaryTypeName&) +void ASTPrinter::endVisit(ElementaryTypeName const&) { m_indentation--; } -void ASTPrinter::endVisit(UserDefinedTypeName&) +void ASTPrinter::endVisit(UserDefinedTypeName const&) { m_indentation--; } -void ASTPrinter::endVisit(Mapping&) +void ASTPrinter::endVisit(Mapping const&) { m_indentation--; } -void ASTPrinter::endVisit(Statement&) +void ASTPrinter::endVisit(Statement const&) { m_indentation--; } -void ASTPrinter::endVisit(Block&) +void ASTPrinter::endVisit(Block const&) { m_indentation--; } -void ASTPrinter::endVisit(IfStatement&) +void ASTPrinter::endVisit(IfStatement const&) { m_indentation--; } -void ASTPrinter::endVisit(BreakableStatement&) +void ASTPrinter::endVisit(BreakableStatement const&) { m_indentation--; } -void ASTPrinter::endVisit(WhileStatement&) +void ASTPrinter::endVisit(WhileStatement const&) { m_indentation--; } -void ASTPrinter::endVisit(Continue&) +void ASTPrinter::endVisit(Continue const&) { m_indentation--; } -void ASTPrinter::endVisit(Break&) +void ASTPrinter::endVisit(Break const&) { m_indentation--; } -void ASTPrinter::endVisit(Return&) +void ASTPrinter::endVisit(Return const&) { m_indentation--; } -void ASTPrinter::endVisit(VariableDefinition&) +void ASTPrinter::endVisit(VariableDefinition const&) { m_indentation--; } -void ASTPrinter::endVisit(ExpressionStatement&) +void ASTPrinter::endVisit(ExpressionStatement const&) { m_indentation--; } -void ASTPrinter::endVisit(Expression&) +void ASTPrinter::endVisit(Expression const&) { m_indentation--; } -void ASTPrinter::endVisit(Assignment&) +void ASTPrinter::endVisit(Assignment const&) { m_indentation--; } -void ASTPrinter::endVisit(UnaryOperation&) +void ASTPrinter::endVisit(UnaryOperation const&) { m_indentation--; } -void ASTPrinter::endVisit(BinaryOperation&) +void ASTPrinter::endVisit(BinaryOperation const&) { m_indentation--; } -void ASTPrinter::endVisit(FunctionCall&) +void ASTPrinter::endVisit(FunctionCall const&) { m_indentation--; } -void ASTPrinter::endVisit(MemberAccess&) +void ASTPrinter::endVisit(MemberAccess const&) { m_indentation--; } -void ASTPrinter::endVisit(IndexAccess&) +void ASTPrinter::endVisit(IndexAccess const&) { m_indentation--; } -void ASTPrinter::endVisit(PrimaryExpression&) +void ASTPrinter::endVisit(PrimaryExpression const&) { m_indentation--; } -void ASTPrinter::endVisit(Identifier&) +void ASTPrinter::endVisit(Identifier const&) { m_indentation--; } -void ASTPrinter::endVisit(ElementaryTypeNameExpression&) +void ASTPrinter::endVisit(ElementaryTypeNameExpression const&) { m_indentation--; } -void ASTPrinter::endVisit(Literal&) +void ASTPrinter::endVisit(Literal const&) { m_indentation--; } diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index e0757fbc4..5a9187154 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -33,77 +33,78 @@ namespace solidity /** * Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes. */ -class ASTPrinter: public ASTVisitor +class ASTPrinter: public ASTConstVisitor { public: /// Create a printer for the given abstract syntax tree. If the source is specified, /// the corresponding parts of the source are printed with each node. - ASTPrinter(ASTNode& _ast, std::string const& _source = std::string()); + ASTPrinter(ASTNode const& _ast, std::string const& _source = std::string()); /// Output the string representation of the AST to _stream. void print(std::ostream& _stream); - bool visit(ContractDefinition& _node) override; - bool visit(StructDefinition& _node) override; - bool visit(ParameterList& _node) override; - bool visit(FunctionDefinition& _node) override; - bool visit(VariableDeclaration& _node) override; - bool visit(TypeName& _node) override; - bool visit(ElementaryTypeName& _node) override; - bool visit(UserDefinedTypeName& _node) override; - bool visit(Mapping& _node) override; - bool visit(Statement& _node) override; - bool visit(Block& _node) override; - bool visit(IfStatement& _node) override; - bool visit(BreakableStatement& _node) override; - bool visit(WhileStatement& _node) override; - bool visit(Continue& _node) override; - bool visit(Break& _node) override; - bool visit(Return& _node) override; - bool visit(VariableDefinition& _node) override; - bool visit(ExpressionStatement& _node) override; - bool visit(Expression& _node) override; - bool visit(Assignment& _node) override; - bool visit(UnaryOperation& _node) override; - bool visit(BinaryOperation& _node) override; - bool visit(FunctionCall& _node) override; - bool visit(MemberAccess& _node) override; - bool visit(IndexAccess& _node) override; - bool visit(PrimaryExpression& _node) override; - bool visit(Identifier& _node) override; - bool visit(ElementaryTypeNameExpression& _node) override; - bool visit(Literal& _node) override; + bool visit(ImportDirective const& _node) override; + bool visit(ContractDefinition const& _node) override; + bool visit(StructDefinition const& _node) override; + bool visit(ParameterList const& _node) override; + bool visit(FunctionDefinition const& _node) override; + bool visit(VariableDeclaration const& _node) override; + bool visit(TypeName const& _node) override; + bool visit(ElementaryTypeName const& _node) override; + bool visit(UserDefinedTypeName const& _node) override; + bool visit(Mapping const& _node) override; + bool visit(Statement const& _node) override; + bool visit(Block const& _node) override; + bool visit(IfStatement const& _node) override; + bool visit(BreakableStatement const& _node) override; + bool visit(WhileStatement const& _node) override; + bool visit(Continue const& _node) override; + bool visit(Break const& _node) override; + bool visit(Return const& _node) override; + bool visit(VariableDefinition const& _node) override; + bool visit(ExpressionStatement const& _node) override; + bool visit(Expression const& _node) override; + bool visit(Assignment const& _node) override; + bool visit(UnaryOperation const& _node) override; + bool visit(BinaryOperation const& _node) override; + bool visit(FunctionCall const& _node) override; + bool visit(MemberAccess const& _node) override; + bool visit(IndexAccess const& _node) override; + bool visit(PrimaryExpression const& _node) override; + bool visit(Identifier const& _node) override; + bool visit(ElementaryTypeNameExpression const& _node) override; + bool visit(Literal const& _node) override; - void endVisit(ASTNode& _node) override; - void endVisit(ContractDefinition&) override; - void endVisit(StructDefinition&) override; - void endVisit(ParameterList&) override; - void endVisit(FunctionDefinition&) override; - void endVisit(VariableDeclaration&) override; - void endVisit(TypeName&) override; - void endVisit(ElementaryTypeName&) override; - void endVisit(UserDefinedTypeName&) override; - void endVisit(Mapping&) override; - void endVisit(Statement&) override; - void endVisit(Block&) override; - void endVisit(IfStatement&) override; - void endVisit(BreakableStatement&) override; - void endVisit(WhileStatement&) override; - void endVisit(Continue&) override; - void endVisit(Break&) override; - void endVisit(Return&) override; - void endVisit(VariableDefinition&) override; - void endVisit(ExpressionStatement&) override; - void endVisit(Expression&) override; - void endVisit(Assignment&) override; - void endVisit(UnaryOperation&) override; - void endVisit(BinaryOperation&) override; - void endVisit(FunctionCall&) override; - void endVisit(MemberAccess&) override; - void endVisit(IndexAccess&) override; - void endVisit(PrimaryExpression&) override; - void endVisit(Identifier&) override; - void endVisit(ElementaryTypeNameExpression&) override; - void endVisit(Literal&) override; + void endVisit(ImportDirective const&) override; + void endVisit(ContractDefinition const&) override; + void endVisit(StructDefinition const&) override; + void endVisit(ParameterList const&) override; + void endVisit(FunctionDefinition const&) override; + void endVisit(VariableDeclaration const&) override; + void endVisit(TypeName const&) override; + void endVisit(ElementaryTypeName const&) override; + void endVisit(UserDefinedTypeName const&) override; + void endVisit(Mapping const&) override; + void endVisit(Statement const&) override; + void endVisit(Block const&) override; + void endVisit(IfStatement const&) override; + void endVisit(BreakableStatement const&) override; + void endVisit(WhileStatement const&) override; + void endVisit(Continue const&) override; + void endVisit(Break const&) override; + void endVisit(Return const&) override; + void endVisit(VariableDefinition const&) override; + void endVisit(ExpressionStatement const&) override; + void endVisit(Expression const&) override; + void endVisit(Assignment const&) override; + void endVisit(UnaryOperation const&) override; + void endVisit(BinaryOperation const&) override; + void endVisit(FunctionCall const&) override; + void endVisit(MemberAccess const&) override; + void endVisit(IndexAccess const&) override; + void endVisit(PrimaryExpression const&) override; + void endVisit(Identifier const&) override; + void endVisit(ElementaryTypeNameExpression const&) override; + void endVisit(Literal const&) override; private: void printSourcePart(ASTNode const& _node); @@ -114,7 +115,7 @@ private: int m_indentation; std::string m_source; - ASTNode* m_ast; + ASTNode const* m_ast; std::ostream* m_ostream; }; diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index 6e579f358..4e1a49458 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -42,6 +42,8 @@ class ASTVisitor { public: virtual bool visit(ASTNode&) { return true; } + virtual bool visit(SourceUnit&) { return true; } + virtual bool visit(ImportDirective&) { return true; } virtual bool visit(ContractDefinition&) { return true; } virtual bool visit(StructDefinition&) { return true; } virtual bool visit(ParameterList&) { return true; } @@ -74,6 +76,8 @@ public: virtual bool visit(Literal&) { return true; } virtual void endVisit(ASTNode&) { } + virtual void endVisit(SourceUnit&) { } + virtual void endVisit(ImportDirective&) { } virtual void endVisit(ContractDefinition&) { } virtual void endVisit(StructDefinition&) { } virtual void endVisit(ParameterList&) { } @@ -106,5 +110,77 @@ public: virtual void endVisit(Literal&) { } }; +class ASTConstVisitor +{ +public: + virtual bool visit(ASTNode const&) { return true; } + virtual bool visit(SourceUnit const&) { return true; } + virtual bool visit(ImportDirective const&) { return true; } + virtual bool visit(ContractDefinition const&) { return true; } + virtual bool visit(StructDefinition const&) { return true; } + virtual bool visit(ParameterList const&) { return true; } + virtual bool visit(FunctionDefinition const&) { return true; } + virtual bool visit(VariableDeclaration const&) { return true; } + virtual bool visit(TypeName const&) { return true; } + virtual bool visit(ElementaryTypeName const&) { return true; } + virtual bool visit(UserDefinedTypeName const&) { return true; } + virtual bool visit(Mapping const&) { return true; } + virtual bool visit(Statement const&) { return true; } + virtual bool visit(Block const&) { return true; } + virtual bool visit(IfStatement const&) { return true; } + virtual bool visit(BreakableStatement const&) { return true; } + virtual bool visit(WhileStatement const&) { return true; } + virtual bool visit(Continue const&) { return true; } + virtual bool visit(Break const&) { return true; } + virtual bool visit(Return const&) { return true; } + virtual bool visit(VariableDefinition const&) { return true; } + virtual bool visit(ExpressionStatement const&) { return true; } + virtual bool visit(Expression const&) { return true; } + virtual bool visit(Assignment const&) { return true; } + virtual bool visit(UnaryOperation const&) { return true; } + virtual bool visit(BinaryOperation const&) { return true; } + virtual bool visit(FunctionCall const&) { return true; } + virtual bool visit(MemberAccess const&) { return true; } + virtual bool visit(IndexAccess const&) { return true; } + virtual bool visit(PrimaryExpression const&) { return true; } + virtual bool visit(Identifier const&) { return true; } + virtual bool visit(ElementaryTypeNameExpression const&) { return true; } + virtual bool visit(Literal const&) { return true; } + + virtual void endVisit(ASTNode const&) { } + virtual void endVisit(SourceUnit const&) { } + virtual void endVisit(ImportDirective const&) { } + virtual void endVisit(ContractDefinition const&) { } + virtual void endVisit(StructDefinition const&) { } + virtual void endVisit(ParameterList const&) { } + virtual void endVisit(FunctionDefinition const&) { } + virtual void endVisit(VariableDeclaration const&) { } + virtual void endVisit(TypeName const&) { } + virtual void endVisit(ElementaryTypeName const&) { } + virtual void endVisit(UserDefinedTypeName const&) { } + virtual void endVisit(Mapping const&) { } + virtual void endVisit(Statement const&) { } + virtual void endVisit(Block const&) { } + virtual void endVisit(IfStatement const&) { } + virtual void endVisit(BreakableStatement const&) { } + virtual void endVisit(WhileStatement const&) { } + virtual void endVisit(Continue const&) { } + virtual void endVisit(Break const&) { } + virtual void endVisit(Return const&) { } + virtual void endVisit(VariableDefinition const&) { } + virtual void endVisit(ExpressionStatement const&) { } + virtual void endVisit(Expression const&) { } + virtual void endVisit(Assignment const&) { } + virtual void endVisit(UnaryOperation const&) { } + virtual void endVisit(BinaryOperation const&) { } + virtual void endVisit(FunctionCall const&) { } + virtual void endVisit(MemberAccess const&) { } + virtual void endVisit(IndexAccess const&) { } + virtual void endVisit(PrimaryExpression const&) { } + virtual void endVisit(Identifier const&) { } + virtual void endVisit(ElementaryTypeNameExpression const&) { } + virtual void endVisit(Literal const&) { } +}; + } } diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h new file mode 100644 index 000000000..173273c6d --- /dev/null +++ b/libsolidity/AST_accept.h @@ -0,0 +1,493 @@ +/* + 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 . +*/ +/** + * @author Christian + * @date 2014 + * Implementation of the accept functions of AST nodes, included by AST.cpp to not clutter that + * file with these mechanical implementations. + */ + +#pragma once + +#include +#include + +namespace dev +{ +namespace solidity +{ + +void SourceUnit::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_nodes, _visitor); + _visitor.endVisit(*this); +} + +void SourceUnit::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + listAccept(m_nodes, _visitor); + _visitor.endVisit(*this); +} + +void ImportDirective::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ImportDirective::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ContractDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + listAccept(m_definedStructs, _visitor); + listAccept(m_stateVariables, _visitor); + listAccept(m_definedFunctions, _visitor); + } + _visitor.endVisit(*this); +} + +void ContractDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + listAccept(m_definedStructs, _visitor); + listAccept(m_stateVariables, _visitor); + listAccept(m_definedFunctions, _visitor); + } + _visitor.endVisit(*this); +} + +void StructDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_members, _visitor); + _visitor.endVisit(*this); +} + +void StructDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + listAccept(m_members, _visitor); + _visitor.endVisit(*this); +} + +void StructDefinition::checkValidityOfMembers() const +{ + checkMemberTypes(); + checkRecursion(); +} + +void ParameterList::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_parameters, _visitor); + _visitor.endVisit(*this); +} + +void ParameterList::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + listAccept(m_parameters, _visitor); + _visitor.endVisit(*this); +} + +void FunctionDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_parameters->accept(_visitor); + if (m_returnParameters) + m_returnParameters->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void FunctionDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_parameters->accept(_visitor); + if (m_returnParameters) + m_returnParameters->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void VariableDeclaration::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + if (m_typeName) + m_typeName->accept(_visitor); + _visitor.endVisit(*this); +} + +void VariableDeclaration::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + if (m_typeName) + m_typeName->accept(_visitor); + _visitor.endVisit(*this); +} + +void TypeName::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void TypeName::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ElementaryTypeName::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ElementaryTypeName::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void UserDefinedTypeName::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void UserDefinedTypeName::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Mapping::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_keyType->accept(_visitor); + m_valueType->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Mapping::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_keyType->accept(_visitor); + m_valueType->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Block::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_statements, _visitor); + _visitor.endVisit(*this); +} + +void Block::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + listAccept(m_statements, _visitor); + _visitor.endVisit(*this); +} + +void IfStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_condition->accept(_visitor); + m_trueBody->accept(_visitor); + if (m_falseBody) + m_falseBody->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void IfStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_condition->accept(_visitor); + m_trueBody->accept(_visitor); + if (m_falseBody) + m_falseBody->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void WhileStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_condition->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void WhileStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_condition->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Continue::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Continue::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Break::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Break::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Return::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + if (m_expression) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void Return::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + if (m_expression) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void ExpressionStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + if (m_expression) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void ExpressionStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + if (m_expression) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void VariableDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_variable->accept(_visitor); + if (m_value) + m_value->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void VariableDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_variable->accept(_visitor); + if (m_value) + m_value->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Assignment::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_leftHandSide->accept(_visitor); + m_rightHandSide->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Assignment::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_leftHandSide->accept(_visitor); + m_rightHandSide->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void UnaryOperation::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_subExpression->accept(_visitor); + _visitor.endVisit(*this); +} + +void UnaryOperation::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_subExpression->accept(_visitor); + _visitor.endVisit(*this); +} + +void BinaryOperation::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_left->accept(_visitor); + m_right->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void BinaryOperation::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_left->accept(_visitor); + m_right->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void FunctionCall::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_expression->accept(_visitor); + listAccept(m_arguments, _visitor); + } + _visitor.endVisit(*this); +} + +void FunctionCall::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_expression->accept(_visitor); + listAccept(m_arguments, _visitor); + } + _visitor.endVisit(*this); +} + +void MemberAccess::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void MemberAccess::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void IndexAccess::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_base->accept(_visitor); + m_index->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void IndexAccess::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_base->accept(_visitor); + m_index->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Identifier::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Identifier::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Literal::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Literal::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +} +} diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h index d1ffd7bbc..a8fd77c86 100644 --- a/libsolidity/BaseTypes.h +++ b/libsolidity/BaseTypes.h @@ -22,6 +22,8 @@ #pragma once +#include +#include #include namespace dev @@ -35,19 +37,19 @@ namespace solidity */ struct Location { - Location(int _start, int _end): start(_start), end(_end) { } + Location(int _start, int _end, std::shared_ptr _sourceName): + start(_start), end(_end), sourceName(_sourceName) { } Location(): start(-1), end(-1) { } - bool IsValid() const { return start >= 0 && end >= start; } - int start; int end; + std::shared_ptr sourceName; }; /// Stream output for Location (used e.g. in boost exceptions). inline std::ostream& operator<<(std::ostream& _out, Location const& _location) { - return _out << "[" << _location.start << "," << _location.end << ")"; + return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")"; } } diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 17ad4fd16..b094af194 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -26,13 +26,14 @@ #include #include #include +#include using namespace std; namespace dev { namespace solidity { -void Compiler::compileContract(ContractDefinition& _contract, vector const& _magicGlobals) +void Compiler::compileContract(ContractDefinition const& _contract, vector const& _magicGlobals) { m_context = CompilerContext(); // clear it just in case @@ -135,7 +136,7 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b for (ASTPointer const& var: _function.getParameters()) { unsigned const numBytes = var->getType()->getCalldataEncodedSize(); - if (numBytes == 0) + if (numBytes == 0 || numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(var->getLocation()) << errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); @@ -154,18 +155,20 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function) //@todo this can be also done more efficiently unsigned dataOffset = 0; vector> const& parameters = _function.getReturnParameters(); + unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters); for (unsigned i = 0; i < parameters.size(); ++i) { Type const& paramType = *parameters[i]->getType(); unsigned numBytes = paramType.getCalldataEncodedSize(); - if (numBytes == 0) + if (numBytes == 0 || numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(parameters[i]->getLocation()) << errinfo_comment("Type " + paramType.toString() + " not yet supported.")); - m_context << eth::dupInstruction(parameters.size() - i); + 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; + stackDepth -= paramType.getSizeOnStack(); dataOffset += numBytes; } // note that the stack is not cleaned up here @@ -179,7 +182,7 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract) m_context.addStateVariable(*variable); } -bool Compiler::visit(FunctionDefinition& _function) +bool Compiler::visit(FunctionDefinition const& _function) { //@todo to simplify this, the calling convention could by changed such that // caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn] @@ -195,15 +198,12 @@ bool Compiler::visit(FunctionDefinition& _function) // stack upon entry: [return address] [arg0] [arg1] ... [argn] // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] - unsigned const numArguments = _function.getParameters().size(); - unsigned const numReturnValues = _function.getReturnParameters().size(); - unsigned const numLocalVariables = _function.getLocalVariables().size(); - - for (ASTPointer const& variable: _function.getParameters() + _function.getReturnParameters()) + for (ASTPointer const& variable: _function.getParameters()) m_context.addVariable(*variable); + for (ASTPointer const& variable: _function.getReturnParameters()) + m_context.addAndInitializeVariable(*variable); for (VariableDeclaration const* localVariable: _function.getLocalVariables()) - m_context.addVariable(*localVariable); - m_context.initializeLocalVariables(numReturnValues + numLocalVariables); + m_context.addAndInitializeVariable(*localVariable); _function.getBody().accept(*this); @@ -215,12 +215,16 @@ bool Compiler::visit(FunctionDefinition& _function) // Note that the fact that the return arguments are of increasing index is vital for this // algorithm to work. + unsigned const argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters()); + unsigned const returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters()); + unsigned const localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables()); + vector stackLayout; - stackLayout.push_back(numReturnValues); // target of return address - stackLayout += vector(numArguments, -1); // discard all arguments - for (unsigned i = 0; i < numReturnValues; ++i) + stackLayout.push_back(returnValuesSize); // target of return address + stackLayout += vector(argumentsSize, -1); // discard all arguments + for (unsigned i = 0; i < returnValuesSize; ++i) stackLayout.push_back(i); - stackLayout += vector(numLocalVariables, -1); + stackLayout += vector(localVariablesSize, -1); while (stackLayout.back() != int(stackLayout.size() - 1)) if (stackLayout.back() < 0) @@ -240,7 +244,7 @@ bool Compiler::visit(FunctionDefinition& _function) return false; } -bool Compiler::visit(IfStatement& _ifStatement) +bool Compiler::visit(IfStatement const& _ifStatement) { ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition()); eth::AssemblyItem trueTag = m_context.appendConditionalJump(); @@ -253,7 +257,7 @@ bool Compiler::visit(IfStatement& _ifStatement) return false; } -bool Compiler::visit(WhileStatement& _whileStatement) +bool Compiler::visit(WhileStatement const& _whileStatement) { eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag(); @@ -275,58 +279,53 @@ bool Compiler::visit(WhileStatement& _whileStatement) return false; } -bool Compiler::visit(Continue&) +bool Compiler::visit(Continue const&) { if (!m_continueTags.empty()) m_context.appendJumpTo(m_continueTags.back()); return false; } -bool Compiler::visit(Break&) +bool Compiler::visit(Break const&) { if (!m_breakTags.empty()) m_context.appendJumpTo(m_breakTags.back()); return false; } -bool Compiler::visit(Return& _return) +bool Compiler::visit(Return const& _return) { //@todo modifications are needed to make this work with functions returning multiple values - if (Expression* expression = _return.getExpression()) + if (Expression const* expression = _return.getExpression()) { ExpressionCompiler::compileExpression(m_context, *expression); VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front(); ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType()); - unsigned stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(firstVariable)); - m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP; + CompilerUtils(m_context).moveToStackVariable(firstVariable); } m_context.appendJumpTo(m_returnTag); return false; } -bool Compiler::visit(VariableDefinition& _variableDefinition) +bool Compiler::visit(VariableDefinition const& _variableDefinition) { - if (Expression* expression = _variableDefinition.getExpression()) + if (Expression const* expression = _variableDefinition.getExpression()) { ExpressionCompiler::compileExpression(m_context, *expression); ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *_variableDefinition.getDeclaration().getType()); - unsigned baseStackOffset = m_context.getBaseStackOffsetOfVariable(_variableDefinition.getDeclaration()); - unsigned stackPosition = m_context.baseToCurrentStackOffset(baseStackOffset); - m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP; + CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration()); } return false; } -bool Compiler::visit(ExpressionStatement& _expressionStatement) +bool Compiler::visit(ExpressionStatement const& _expressionStatement) { - Expression& expression = _expressionStatement.getExpression(); + Expression const& expression = _expressionStatement.getExpression(); ExpressionCompiler::compileExpression(m_context, expression); -// Type::Category category = expression.getType()->getCategory(); - for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i) - m_context << eth::Instruction::POP; + CompilerUtils(m_context).popStackElement(*expression.getType()); return false; } diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 70e6c44f2..4b8f02c5d 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -27,12 +27,12 @@ namespace dev { namespace solidity { -class Compiler: private ASTVisitor +class Compiler: private ASTConstVisitor { public: Compiler(): m_returnTag(m_context.newTag()) {} - void compileContract(ContractDefinition& _contract, std::vector const& _magicGlobals); + void compileContract(ContractDefinition const& _contract, std::vector const& _magicGlobals); bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); } void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } @@ -48,14 +48,14 @@ private: void registerStateVariables(ContractDefinition const& _contract); - virtual bool visit(FunctionDefinition& _function) override; - virtual bool visit(IfStatement& _ifStatement) override; - virtual bool visit(WhileStatement& _whileStatement) override; - virtual bool visit(Continue& _continue) override; - virtual bool visit(Break& _break) override; - virtual bool visit(Return& _return) override; - virtual bool visit(VariableDefinition& _variableDefinition) override; - virtual bool visit(ExpressionStatement& _expressionStatement) override; + virtual bool visit(FunctionDefinition const& _function) override; + virtual bool visit(IfStatement const& _ifStatement) override; + virtual bool visit(WhileStatement const& _whileStatement) override; + virtual bool visit(Continue const& _continue) override; + virtual bool visit(Break const& _break) override; + virtual bool visit(Return const& _return) override; + virtual bool visit(VariableDefinition const& _variableDefinition) override; + virtual bool visit(ExpressionStatement const& _expressionStatement) override; CompilerContext m_context; diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index b89a8e5b5..cd22c4e8b 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -41,20 +41,30 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration) m_stateVariablesSize += _declaration.getType()->getStorageSize(); } -void CompilerContext::initializeLocalVariables(unsigned _numVariables) +void CompilerContext::addVariable(VariableDeclaration const& _declaration) { - if (_numVariables > 0) - { + m_localVariables[&_declaration] = m_localVariablesSize; + m_localVariablesSize += _declaration.getType()->getSizeOnStack(); +} + +void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration) +{ + addVariable(_declaration); + + unsigned const size = _declaration.getType()->getSizeOnStack(); + for (unsigned i = 0; i < size; ++i) *this << u256(0); - for (unsigned i = 1; i < _numVariables; ++i) - *this << eth::Instruction::DUP1; - m_asm.adjustDeposit(-_numVariables); - } + m_asm.adjustDeposit(-size); +} + +void CompilerContext::addFunction(FunctionDefinition const& _function) +{ + m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); } bool CompilerContext::isLocalVariable(Declaration const* _declaration) const { - return std::find(m_localVariables.begin(), m_localVariables.end(), _declaration) != m_localVariables.end(); + return m_localVariables.count(_declaration) > 0; } eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const @@ -67,10 +77,10 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition cons unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const { - auto res = find(begin(m_localVariables), end(m_localVariables), &_declaration); + auto res = m_localVariables.find(&_declaration); if (asserts(res != m_localVariables.end())) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack.")); - return unsigned(end(m_localVariables) - res - 1); + return m_localVariablesSize - res->second - 1; } unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 6a48e1485..652e65a63 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace dev { @@ -43,9 +44,9 @@ public: void addMagicGlobal(MagicVariableDeclaration const& _declaration); void addStateVariable(VariableDeclaration const& _declaration); void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); } - void initializeLocalVariables(unsigned _numVariables); - void addVariable(VariableDeclaration const& _declaration) { m_localVariables.push_back(&_declaration); } - void addFunction(FunctionDefinition const& _function) { m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); } + void addVariable(VariableDeclaration const& _declaration); + void addAndInitializeVariable(VariableDeclaration const& _declaration); + void addFunction(FunctionDefinition const& _function); void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } @@ -98,8 +99,10 @@ private: u256 m_stateVariablesSize; /// Storage offsets of state variables std::map m_stateVariables; - /// Offsets of local variables on the stack. - std::vector m_localVariables; + /// Offsets of local variables on the stack (relative to stack base). + std::map m_localVariables; + /// Sum of stack sizes of local variables + unsigned m_localVariablesSize; /// Labels pointing to the entry points of funcitons. std::map m_functionEntryLabels; }; diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index c83257a1d..d46754856 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -36,22 +36,43 @@ namespace dev namespace solidity { -CompilerStack::CompilerStack(): m_interfaceHandler(make_shared()) {} +void CompilerStack::addSource(string const& _name, string const& _content) +{ + if (m_sources.count(_name)) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source by given name already exists.")); + + reset(true); + m_sources[_name].scanner = make_shared(CharStream(_content), _name); +} void CompilerStack::setSource(string const& _sourceCode) { reset(); - m_scanner = make_shared(CharStream(_sourceCode)); + addSource("", _sourceCode); } void CompilerStack::parse() { - if (!m_scanner) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available.")); - m_contractASTNode = Parser().parse(m_scanner); + for (auto& sourcePair: m_sources) + { + sourcePair.second.scanner->reset(); + sourcePair.second.ast = Parser().parse(sourcePair.second.scanner); + } + resolveImports(); + m_globalContext = make_shared(); - m_globalContext->setCurrentContract(*m_contractASTNode); - NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*m_contractASTNode); + NameAndTypeResolver resolver(m_globalContext->getDeclarations()); + for (Source const* source: m_sourceOrder) + resolver.registerDeclarations(*source->ast); + for (Source const* source: m_sourceOrder) + for (ASTPointer const& node: source->ast->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + m_globalContext->setCurrentContract(*contract); + resolver.updateDeclaration(*m_globalContext->getCurrentThis()); + resolver.resolveNamesAndTypes(*contract); + m_contracts[contract->getName()].contract = contract; + } m_parseSuccessful = true; } @@ -61,54 +82,90 @@ void CompilerStack::parse(string const& _sourceCode) parse(); } -bytes const& CompilerStack::compile(bool _optimize) +vector CompilerStack::getContractNames() const { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - m_bytecode.clear(); - m_compiler = make_shared(); - m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables()); - return m_bytecode = m_compiler->getAssembledBytecode(_optimize); + vector contractNames; + for (auto const& contract: m_contracts) + contractNames.push_back(contract.first); + return contractNames; +} + +void CompilerStack::compile(bool _optimize) +{ + if (!m_parseSuccessful) + parse(); + for (Source const* source: m_sourceOrder) + for (ASTPointer const& node: source->ast->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + m_globalContext->setCurrentContract(*contract); + shared_ptr compiler = make_shared(); + compiler->compileContract(*contract, m_globalContext->getMagicVariables()); + Contract& compiledContract = m_contracts[contract->getName()]; + compiledContract.bytecode = compiler->getAssembledBytecode(_optimize); + compiledContract.compiler = move(compiler); + } } bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize) { parse(_sourceCode); - return compile(_optimize); + compile(_optimize); + return getBytecode(); } -void CompilerStack::streamAssembly(ostream& _outStream) +bytes const& CompilerStack::getBytecode(string const& _contractName) const { - if (!m_compiler || m_bytecode.empty()) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); - m_compiler->streamAssembly(_outStream); + return getContract(_contractName).bytecode; } -std::string const& CompilerStack::getJsonDocumentation(enum DocumentationType _type) +void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const +{ + getContract(_contractName).compiler->streamAssembly(_outStream); +} + +string const& CompilerStack::getInterface(string const& _contractName) const +{ + return getJsonDocumentation(_contractName, DocumentationType::ABI_INTERFACE); +} + +string const& CompilerStack::getJsonDocumentation(string const& _contractName, DocumentationType _type) const { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - auto createDocIfNotThere = [this, _type](std::unique_ptr& _doc) - { - if (!_doc) - _doc = m_interfaceHandler->getDocumentation(m_contractASTNode, _type); - }; + Contract const& contract = getContract(_contractName); + std::unique_ptr* doc; switch (_type) { - case NATSPEC_USER: - createDocIfNotThere(m_userDocumentation); - return *m_userDocumentation; - case NATSPEC_DEV: - createDocIfNotThere(m_devDocumentation); - return *m_devDocumentation; - case ABI_INTERFACE: - createDocIfNotThere(m_interface); - return *m_interface; + case DocumentationType::NATSPEC_USER: + doc = &contract.userDocumentation; + break; + case DocumentationType::NATSPEC_DEV: + doc = &contract.devDocumentation; + break; + case DocumentationType::ABI_INTERFACE: + doc = &contract.interface; + break; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); } + if (!*doc) + *doc = contract.interfaceHandler->getDocumentation(*contract.contract, _type); + return *(*doc); +} - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); +Scanner const& CompilerStack::getScanner(string const& _sourceName) const +{ + return *getSource(_sourceName).scanner; +} + +SourceUnit const& CompilerStack::getAST(string const& _sourceName) const +{ + return *getSource(_sourceName).ast; } bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) @@ -117,7 +174,70 @@ bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimiz return stack.compile(_sourceCode, _optimize); } +void CompilerStack::reset(bool _keepSources) +{ + m_parseSuccessful = false; + if (_keepSources) + for (auto sourcePair: m_sources) + sourcePair.second.reset(); + else + m_sources.clear(); + m_globalContext.reset(); + m_sourceOrder.clear(); + m_contracts.clear(); +} + +void CompilerStack::resolveImports() +{ + // topological sorting (depth first search) of the import graph, cutting potential cycles + vector sourceOrder; + set sourcesSeen; + + function toposort = [&](Source const* _source) + { + if (sourcesSeen.count(_source)) + return; + sourcesSeen.insert(_source); + for (ASTPointer const& node: _source->ast->getNodes()) + if (ImportDirective const* import = dynamic_cast(node.get())) + { + string const& id = import->getIdentifier(); + if (!m_sources.count(id)) + BOOST_THROW_EXCEPTION(ParserError() + << errinfo_sourceLocation(import->getLocation()) + << errinfo_comment("Source not found.")); + toposort(&m_sources[id]); + } + sourceOrder.push_back(_source); + }; + + for (auto const& sourcePair: m_sources) + toposort(&sourcePair.second); + + swap(m_sourceOrder, sourceOrder); +} + +CompilerStack::Contract const& CompilerStack::getContract(string const& _contractName) const +{ + if (m_contracts.empty()) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); + if (_contractName.empty()) + return m_contracts.begin()->second; + auto it = m_contracts.find(_contractName); + if (it == m_contracts.end()) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found.")); + return it->second; +} + +CompilerStack::Source const& CompilerStack::getSource(string const& _sourceName) const +{ + auto it = m_sources.find(_sourceName); + if (it == m_sources.end()) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Given source file not found.")); + return it->second; +} +CompilerStack::Contract::Contract(): interfaceHandler(make_shared()) {} } } diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 8dc546fbe..b6c34f1ac 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace dev { @@ -33,11 +34,12 @@ namespace solidity { // forward declarations class Scanner; class ContractDefinition; +class SourceUnit; class Compiler; class GlobalContext; class InterfaceHandler; -enum DocumentationType: unsigned short +enum class DocumentationType: uint8_t { NATSPEC_USER = 1, NATSPEC_DEV, @@ -49,52 +51,85 @@ enum DocumentationType: unsigned short * It holds state and can be used to either step through the compilation stages (and abort e.g. * before compilation to bytecode) or run the whole compilation in one call. */ -class CompilerStack +class CompilerStack: boost::noncopyable { public: - CompilerStack(); - void reset() { *this = CompilerStack(); } + CompilerStack(): m_parseSuccessful(false) {} + + /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. + void addSource(std::string const& _name, std::string const& _content); void setSource(std::string const& _sourceCode); + /// Parses all source units that were added void parse(); + /// Sets the given source code as the only source unit and parses it. void parse(std::string const& _sourceCode); - /// Compiles the contract that was previously parsed. - bytes const& compile(bool _optimize = false); + /// Returns a list of the contract names in the sources. + std::vector getContractNames() const; + + /// Compiles the source units that were previously added and parsed. + void compile(bool _optimize = false); /// Parses and compiles the given source code. + /// @returns the compiled bytecode bytes const& compile(std::string const& _sourceCode, bool _optimize = false); - bytes const& getBytecode() const { return m_bytecode; } + bytes const& getBytecode(std::string const& _contractName = "") const; /// Streams a verbose version of the assembly to @a _outStream. /// Prerequisite: Successful compilation. - void streamAssembly(std::ostream& _outStream); + void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const; /// Returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. - std::string const& getInterface(); + std::string const& getInterface(std::string const& _contractName = "") const; /// Returns a string representing the contract's documentation in JSON. /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. - /// Can be one of 3 types defined at @c documentation_type - std::string const& getJsonDocumentation(enum DocumentationType type); + /// Can be one of 3 types defined at @c DocumentationType + std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const; /// Returns the previously used scanner, useful for counting lines during error reporting. - Scanner const& getScanner() const { return *m_scanner; } - ContractDefinition& getAST() const { return *m_contractASTNode; } + Scanner const& getScanner(std::string const& _sourceName = "") const; + SourceUnit const& getAST(std::string const& _sourceName = "") const; /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for /// scanning the source code - this is useful for printing exception information. static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false); private: - std::shared_ptr m_scanner; - std::shared_ptr m_globalContext; - std::shared_ptr m_contractASTNode; + /** + * Information pertaining to one source unit, filled gradually during parsing and compilation. + */ + struct Source + { + std::shared_ptr scanner; + std::shared_ptr ast; + std::string interface; + void reset() { scanner.reset(); ast.reset(); interface.clear(); } + }; + + struct Contract + { + ContractDefinition* contract; + std::shared_ptr compiler; + bytes bytecode; + std::shared_ptr interfaceHandler; + mutable std::unique_ptr interface; + mutable std::unique_ptr userDocumentation; + mutable std::unique_ptr devDocumentation; + + Contract(); + }; + + void reset(bool _keepSources = false); + void resolveImports(); + + Contract const& getContract(std::string const& _contractName = "") const; + Source const& getSource(std::string const& _sourceName = "") const; + bool m_parseSuccessful; - std::unique_ptr m_interface; - std::unique_ptr m_userDocumentation; - std::unique_ptr m_devDocumentation; - std::shared_ptr m_compiler; - std::shared_ptr m_interfaceHandler; - bytes m_bytecode; + std::map m_sources; + std::shared_ptr m_globalContext; + std::vector m_sourceOrder; + std::map m_contracts; }; } diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp new file mode 100644 index 000000000..d4dfbe3c0 --- /dev/null +++ b/libsolidity/CompilerUtils.cpp @@ -0,0 +1,71 @@ +/* + 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 . +*/ +/** + * @author Christian + * @date 2014 + * Routines used by both the compiler and the expression compiler. + */ + +#include +#include +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ + +void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) +{ + unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable)); + unsigned const size = _variable.getType()->getSizeOnStack(); + // move variable starting from its top end in the stack + if (stackPosition - size + 1 > 16) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation()) + << errinfo_comment("Stack too deep.")); + for (unsigned i = 0; i < size; ++i) + m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP; +} + +void CompilerUtils::copyToStackTop(unsigned _stackDepth, Type const& _type) +{ + if (_stackDepth > 16) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep.")); + unsigned const size = _type.getSizeOnStack(); + for (unsigned i = 0; i < size; ++i) + m_context << eth::dupInstruction(_stackDepth); +} + +void CompilerUtils::popStackElement(Type const& _type) +{ + unsigned const size = _type.getSizeOnStack(); + for (unsigned i = 0; i < size; ++i) + m_context << eth::Instruction::POP; +} + +unsigned CompilerUtils::getSizeOnStack(vector> const& _variableTypes) +{ + unsigned size = 0; + for (shared_ptr const& type: _variableTypes) + size += type->getSizeOnStack(); + return size; +} + +} +} diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h new file mode 100644 index 000000000..4da533752 --- /dev/null +++ b/libsolidity/CompilerUtils.h @@ -0,0 +1,63 @@ +/* + 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 . +*/ +/** + * @author Christian + * @date 2014 + * Routines used by both the compiler and the expression compiler. + */ + +#pragma once + +#include +#include + +namespace dev { +namespace solidity { + +class Type; // forward + +class CompilerUtils +{ +public: + CompilerUtils(CompilerContext& _context): m_context(_context) {} + + /// 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. + void copyToStackTop(unsigned _stackDepth, Type const& _type); + /// Removes the current value from the top of the stack. + void popStackElement(Type const& _type); + + template + static unsigned getSizeOnStack(std::vector const& _variables); + static unsigned getSizeOnStack(std::vector> const& _variableTypes); + +private: + CompilerContext& m_context; +}; + +template +unsigned CompilerUtils::getSizeOnStack(std::vector const& _variables) +{ + unsigned size = 0; + for (T const& variable: _variables) + size += variable->getType()->getSizeOnStack(); + return size; +} + +} +} diff --git a/libsolidity/DeclarationContainer.cpp b/libsolidity/DeclarationContainer.cpp index 6ea9c28c5..c7081bc78 100644 --- a/libsolidity/DeclarationContainer.cpp +++ b/libsolidity/DeclarationContainer.cpp @@ -28,15 +28,15 @@ namespace dev namespace solidity { -bool DeclarationContainer::registerDeclaration(Declaration& _declaration) +bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _update) { - if (m_declarations.find(_declaration.getName()) != m_declarations.end()) + if (!_update && m_declarations.find(_declaration.getName()) != m_declarations.end()) return false; m_declarations[_declaration.getName()] = &_declaration; return true; } -Declaration* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const +Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const { auto result = m_declarations.find(_name); if (result != m_declarations.end()) diff --git a/libsolidity/DeclarationContainer.h b/libsolidity/DeclarationContainer.h index db6812890..c0a0b42c7 100644 --- a/libsolidity/DeclarationContainer.h +++ b/libsolidity/DeclarationContainer.h @@ -39,18 +39,19 @@ namespace solidity class DeclarationContainer { public: - explicit DeclarationContainer(Declaration* _enclosingDeclaration = nullptr, DeclarationContainer* _enclosingContainer = nullptr): + explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr, + DeclarationContainer const* _enclosingContainer = nullptr): m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} /// Registers the declaration in the scope unless its name is already declared. Returns true iff /// it was not yet declared. - bool registerDeclaration(Declaration& _declaration); - Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; - Declaration* getEnclosingDeclaration() const { return m_enclosingDeclaration; } + bool registerDeclaration(Declaration const& _declaration, bool _update = false); + Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const; + Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; } private: - Declaration* m_enclosingDeclaration; - DeclarationContainer* m_enclosingContainer; - std::map m_declarations; + Declaration const* m_enclosingDeclaration; + DeclarationContainer const* m_enclosingContainer; + std::map m_declarations; }; } diff --git a/libsolidity/Exceptions.h b/libsolidity/Exceptions.h index 8298c9810..14f919772 100644 --- a/libsolidity/Exceptions.h +++ b/libsolidity/Exceptions.h @@ -38,7 +38,6 @@ struct CompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {}; struct DocstringParsingError: virtual Exception {}; -typedef boost::error_info errinfo_sourcePosition; typedef boost::error_info errinfo_sourceLocation; } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index c3c7116e4..a0b0a54a8 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -26,13 +26,14 @@ #include #include #include +#include using namespace std; namespace dev { namespace solidity { -void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression& _expression) +void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression) { ExpressionCompiler compiler(_context); _expression.accept(compiler); @@ -45,7 +46,7 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, compiler.appendTypeConversion(_typeOnStack, _targetType); } -bool ExpressionCompiler::visit(Assignment& _assignment) +bool ExpressionCompiler::visit(Assignment const& _assignment) { _assignment.getRightHandSide().accept(*this); appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType()); @@ -67,7 +68,7 @@ bool ExpressionCompiler::visit(Assignment& _assignment) return false; } -void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation) +void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation) { //@todo type checking and creating code for an operator should be in the same place: // the operator should know how to convert itself and to which types it applies, so @@ -128,10 +129,10 @@ void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation) } } -bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation) +bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) { - Expression& leftExpression = _binaryOperation.getLeftExpression(); - Expression& rightExpression = _binaryOperation.getRightExpression(); + Expression const& leftExpression = _binaryOperation.getLeftExpression(); + Expression const& rightExpression = _binaryOperation.getRightExpression(); Type const& commonType = _binaryOperation.getCommonType(); Token::Value const op = _binaryOperation.getOperator(); @@ -158,7 +159,7 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation) return false; } -bool ExpressionCompiler::visit(FunctionCall& _functionCall) +bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { using Location = FunctionType::Location; if (_functionCall.isTypeConversion()) @@ -166,7 +167,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) //@todo struct construction if (asserts(_functionCall.getArguments().size() == 1)) BOOST_THROW_EXCEPTION(InternalCompilerError()); - Expression& firstArgument = *_functionCall.getArguments().front(); + Expression const& firstArgument = *_functionCall.getArguments().front(); firstArgument.accept(*this); if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT && _functionCall.getType()->getCategory() == Type::Category::INTEGER) @@ -174,18 +175,18 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) // explicit type conversion contract -> address, nothing to do. } else - { appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); - } } else { FunctionType const& function = dynamic_cast(*_functionCall.getExpression().getType()); - std::vector> const& arguments = _functionCall.getArguments(); + std::vector> arguments = _functionCall.getArguments(); if (asserts(arguments.size() == function.getParameterTypes().size())) BOOST_THROW_EXCEPTION(InternalCompilerError()); - if (function.getLocation() == Location::INTERNAL) + switch (function.getLocation()) + { + case Location::INTERNAL: { // Calling convention: Caller pushes return address and arguments // Callee removes them and pushes return values @@ -201,74 +202,104 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) m_context.appendJump(); m_context << returnLabel; + unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes()); // callee adds return parameters, but removes arguments and return label - m_context.adjustStackOffset(function.getReturnParameterTypes().size() - arguments.size() - 1); + m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(arguments) - 1); // @todo for now, the return value of a function is its first return value, so remove // all others for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i) - m_context << eth::Instruction::POP; + CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]); + break; } - else if (function.getLocation() == Location::EXTERNAL) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet.")); - else + case Location::EXTERNAL: { - switch (function.getLocation()) - { - case Location::SEND: - m_context << u256(0) << u256(0) << u256(0) << u256(0); - arguments.front()->accept(*this); - //@todo might not be necessary - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); - _functionCall.getExpression().accept(*this); - m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB - << eth::Instruction::CALL - << eth::Instruction::POP; - break; - case Location::SUICIDE: - arguments.front()->accept(*this); - //@todo might not be necessary - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); - m_context << eth::Instruction::SUICIDE; - break; - case Location::SHA3: - 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; - break; - case Location::ECRECOVER: - case Location::SHA256: - case Location::RIPEMD160: + unsigned dataOffset = 1; // reserve one byte for the function index + for (unsigned i = 0; i < arguments.size(); ++i) { - static const map contractAddresses{{Location::ECRECOVER, 1}, - {Location::SHA256, 2}, - {Location::RIPEMD160, 3}}; - u256 contractAddress = contractAddresses.find(function.getLocation())->second; - // @todo later, combine this code with external function call - for (unsigned i = 0; i < arguments.size(); ++i) - { - 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; - } - 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; - break; + arguments[i]->accept(*this); + Type const& type = *function.getParameterTypes()[i]; + appendTypeConversion(*arguments[i]->getType(), type); + unsigned const numBytes = type.getCalldataEncodedSize(); + if (numBytes == 0 || numBytes > 32) + 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; + dataOffset += numBytes; } - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented.")); + //@todo only return the first return value for now + unsigned retSize = function.getReturnParameterTypes().empty() ? 0 + : function.getReturnParameterTypes().front()->getCalldataEncodedSize(); + // 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 + m_context << u256(0) << eth::Instruction::MSTORE8 + << 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; + break; + } + case Location::SEND: + m_context << u256(0) << u256(0) << u256(0) << u256(0); + arguments.front()->accept(*this); + //@todo might not be necessary + appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); + _functionCall.getExpression().accept(*this); + m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB + << eth::Instruction::CALL + << eth::Instruction::POP; + break; + case Location::SUICIDE: + arguments.front()->accept(*this); + //@todo might not be necessary + appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); + m_context << eth::Instruction::SUICIDE; + break; + case Location::SHA3: + 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; + break; + case Location::ECRECOVER: + case Location::SHA256: + case Location::RIPEMD160: + { + static const map contractAddresses{{Location::ECRECOVER, 1}, + {Location::SHA256, 2}, + {Location::RIPEMD160, 3}}; + u256 contractAddress = contractAddresses.find(function.getLocation())->second; + // @todo later, combine this code with external function call + for (unsigned i = 0; i < arguments.size(); ++i) + { + 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; } + 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; + break; + } + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type.")); } } return false; } -void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) +void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) { ASTString const& member = _memberAccess.getMemberName(); switch (_memberAccess.getExpression().getType()->getCategory()) @@ -289,9 +320,11 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); break; case Type::Category::CONTRACT: - // call function - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented.")); + { + ContractType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); + m_context << type.getFunctionIndex(member); break; + } case Type::Category::MAGIC: // we can ignore the kind of magic and only look at the name of the member if (member == "coinbase") @@ -323,7 +356,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) { StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; - m_currentLValue = LValue(m_context, LValue::STORAGE); + m_currentLValue = LValue(m_context, LValue::STORAGE, *_memberAccess.getType()); m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); break; } @@ -332,7 +365,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) } } -bool ExpressionCompiler::visit(IndexAccess& _indexAccess) +bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) { _indexAccess.getBaseExpression().accept(*this); _indexAccess.getIndexExpression().accept(*this); @@ -343,36 +376,36 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess) m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE; m_context << u256(64) << u256(0) << eth::Instruction::SHA3; - m_currentLValue = LValue(m_context, LValue::STORAGE); + m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType()); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); return false; } -void ExpressionCompiler::endVisit(Identifier& _identifier) +void ExpressionCompiler::endVisit(Identifier const& _identifier) { - Declaration* declaration = _identifier.getReferencedDeclaration(); - if (MagicVariableDeclaration* magicVar = dynamic_cast(declaration)) + Declaration const* declaration = _identifier.getReferencedDeclaration(); + if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this" m_context << eth::Instruction::ADDRESS; return; } - if (FunctionDefinition* functionDef = dynamic_cast(declaration)) + if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) { m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag(); return; } - if (/*VariableDeclaration* varDef = */dynamic_cast(declaration)) + if (dynamic_cast(declaration)) { - m_currentLValue.fromIdentifier(_identifier, *_identifier.getReferencedDeclaration()); + m_currentLValue.fromIdentifier(_identifier, *declaration); m_currentLValue.retrieveValueIfLValueNotRequested(_identifier); return; } BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); } -void ExpressionCompiler::endVisit(Literal& _literal) +void ExpressionCompiler::endVisit(Literal const& _literal) { switch (_literal.getType()->getCategory()) { @@ -385,7 +418,7 @@ void ExpressionCompiler::endVisit(Literal& _literal) } } -void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperation) +void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation) { Token::Value const op = _binaryOperation.getOperator(); if (asserts(op == Token::OR || op == Token::AND)) @@ -532,6 +565,13 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack) m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND; } +ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, + unsigned _baseStackOffset): + m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset), + m_stackSize(_dataType.getSizeOnStack()) +{ +} + void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const { switch (m_type) @@ -542,7 +582,8 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) << errinfo_comment("Stack too deep.")); - *m_context << eth::dupInstruction(stackPos + 1); + for (unsigned i = 0; i < m_stackSize; ++i) + *m_context << eth::dupInstruction(stackPos + 1); break; } case STORAGE: @@ -550,7 +591,17 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo break; // no distinction between value and reference for non-value types if (!_remove) *m_context << eth::Instruction::DUP1; - *m_context << eth::Instruction::SLOAD; + if (m_stackSize == 1) + *m_context << eth::Instruction::SLOAD; + else + for (unsigned i = 0; i < m_stackSize; ++i) + { + *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1; + if (i + 1 < m_stackSize) + *m_context << u256(1) << eth::Instruction::ADD; + else + *m_context << eth::Instruction::POP; + } break; case MEMORY: if (!_expression.getType()->isValueType()) @@ -571,12 +622,13 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool { case STACK: { - unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); - if (stackPos > 16) + unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1; + if (stackDiff > 16) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) << errinfo_comment("Stack too deep.")); - else if (stackPos > 0) - *m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP; + else if (stackDiff > 0) + for (unsigned i = 0; i < m_stackSize; ++i) + *m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP; if (!_move) retrieveValue(_expression); break; @@ -584,9 +636,27 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool case LValue::STORAGE: if (!_expression.getType()->isValueType()) break; // no distinction between value and reference for non-value types - if (!_move) - *m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1; - *m_context << eth::Instruction::SSTORE; + // stack layout: value value ... value ref + if (!_move) // copy values + { + if (m_stackSize + 1 > 16) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) + << errinfo_comment("Stack too deep.")); + for (unsigned i = 0; i < m_stackSize; ++i) + *m_context << eth::dupInstruction(m_stackSize + 1) << eth::Instruction::SWAP1; + } + if (m_stackSize > 0) // store high index value first + *m_context << u256(m_stackSize - 1) << eth::Instruction::ADD; + for (unsigned i = 0; i < m_stackSize; ++i) + { + if (i + 1 >= m_stackSize) + *m_context << eth::Instruction::SSTORE; + else + // v v ... v v r+x + *m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 + << eth::Instruction::SSTORE + << u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB; + } break; case LValue::MEMORY: if (!_expression.getType()->isValueType()) @@ -612,6 +682,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration) { + m_stackSize = _identifier.getType()->getSizeOnStack(); if (m_context->isLocalVariable(&_declaration)) { m_type = STACK; diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 83d7cdc6c..41cb66763 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -31,20 +31,21 @@ class AssemblyItem; // forward } namespace solidity { -class CompilerContext; // forward -class Type; // forward -class IntegerType; // forward +// forward declarations +class CompilerContext; +class Type; +class IntegerType; /** * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream * of EVM instructions. It needs a compiler context that is the same for the whole compilation * unit. */ -class ExpressionCompiler: private ASTVisitor +class ExpressionCompiler: private ASTConstVisitor { public: /// Compile the given @a _expression into the @a _context. - static void compileExpression(CompilerContext& _context, Expression& _expression); + static void compileExpression(CompilerContext& _context, Expression const& _expression); /// 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); @@ -53,18 +54,18 @@ private: ExpressionCompiler(CompilerContext& _compilerContext): m_context(_compilerContext), m_currentLValue(m_context) {} - virtual bool visit(Assignment& _assignment) override; - virtual void endVisit(UnaryOperation& _unaryOperation) override; - virtual bool visit(BinaryOperation& _binaryOperation) override; - virtual bool visit(FunctionCall& _functionCall) override; - virtual void endVisit(MemberAccess& _memberAccess) override; - virtual bool visit(IndexAccess& _indexAccess) override; - virtual void endVisit(Identifier& _identifier) override; - virtual void endVisit(Literal& _literal) override; + virtual bool visit(Assignment const& _assignment) override; + virtual void endVisit(UnaryOperation const& _unaryOperation) override; + virtual bool visit(BinaryOperation const& _binaryOperation) override; + virtual bool visit(FunctionCall const& _functionCall) override; + virtual void endVisit(MemberAccess const& _memberAccess) override; + virtual bool visit(IndexAccess const& _indexAccess) override; + virtual void endVisit(Identifier const& _identifier) override; + virtual void endVisit(Literal const& _literal) override; ///@{ ///@name Append code for various operator types - void appendAndOrOperatorCode(BinaryOperation& _binaryOperation); + void appendAndOrOperatorCode(BinaryOperation const& _binaryOperation); void appendCompareOperatorCode(Token::Value _operator, Type const& _type); void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type); @@ -92,8 +93,7 @@ private: enum LValueType { NONE, STACK, MEMORY, STORAGE }; explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); } - LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0): - m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {} + LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0); /// Set type according to the declaration and retrieve the reference. /// @a _expression is the current expression @@ -128,14 +128,12 @@ private: /// If m_type is STACK, this is base stack offset (@see /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. unsigned m_baseStackOffset; + /// Size of the value of this lvalue on the stack. + unsigned m_stackSize; }; CompilerContext& m_context; LValue m_currentLValue; - /// If a "virtual" function (i.e. a bulit-in function without jump tag) is encountered, the - /// actual function is stored here. @todo prevent assignment or store it with assignment - enum class SpecialFunction { NONE, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 }; - SpecialFunction m_currentSpecialFunction; }; diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index d8b637076..915d06bf2 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -68,17 +68,16 @@ void GlobalContext::setCurrentContract(ContractDefinition const& _contract) m_currentContract = &_contract; } -vector GlobalContext::getDeclarations() const +vector GlobalContext::getDeclarations() const { - vector declarations; + vector declarations; declarations.reserve(m_magicVariables.size() + 1); - for (ASTPointer const& variable: m_magicVariables) + for (ASTPointer const& variable: m_magicVariables) declarations.push_back(variable.get()); - declarations.push_back(getCurrentThis()); return declarations; } -MagicVariableDeclaration*GlobalContext::getCurrentThis() const +MagicVariableDeclaration const* GlobalContext::getCurrentThis() const { if (!m_thisPointer[m_currentContract]) m_thisPointer[m_currentContract] = make_shared( diff --git a/libsolidity/GlobalContext.h b/libsolidity/GlobalContext.h index 0166734ca..50a21f702 100644 --- a/libsolidity/GlobalContext.h +++ b/libsolidity/GlobalContext.h @@ -47,15 +47,17 @@ class GlobalContext: private boost::noncopyable public: GlobalContext(); void setCurrentContract(ContractDefinition const& _contract); + MagicVariableDeclaration const* getCurrentThis() const; + /// @returns all magic variables. std::vector getMagicVariables() const; - std::vector getDeclarations() const; + /// @returns a vector of all implicit global declarations excluding "this". + std::vector getDeclarations() const; private: - MagicVariableDeclaration* getCurrentThis() const; - std::vector> m_magicVariables; + std::vector> m_magicVariables; ContractDefinition const* m_currentContract; - std::map> mutable m_thisPointer; + std::map> mutable m_thisPointer; }; } diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 0115c7f59..c3e62cad0 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -12,19 +12,19 @@ namespace solidity InterfaceHandler::InterfaceHandler() { - m_lastTag = DOCTAG_NONE; + m_lastTag = DocTagType::NONE; } -std::unique_ptr InterfaceHandler::getDocumentation(std::shared_ptr _contractDef, - enum DocumentationType _type) +std::unique_ptr InterfaceHandler::getDocumentation(ContractDefinition& _contractDef, + DocumentationType _type) { switch(_type) { - case NATSPEC_USER: + case DocumentationType::NATSPEC_USER: return getUserDocumentation(_contractDef); - case NATSPEC_DEV: + case DocumentationType::NATSPEC_DEV: return getDevDocumentation(_contractDef); - case ABI_INTERFACE: + case DocumentationType::ABI_INTERFACE: return getABIInterface(_contractDef); } @@ -32,11 +32,11 @@ std::unique_ptr InterfaceHandler::getDocumentation(std::shared_ptr< return nullptr; } -std::unique_ptr InterfaceHandler::getABIInterface(std::shared_ptr _contractDef) +std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinition& _contractDef) { Json::Value methods(Json::arrayValue); - for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) + for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) { Json::Value method; Json::Value inputs(Json::arrayValue); @@ -63,12 +63,12 @@ std::unique_ptr InterfaceHandler::getABIInterface(std::shared_ptr(new std::string(m_writer.write(methods))); } -std::unique_ptr InterfaceHandler::getUserDocumentation(std::shared_ptr _contractDef) +std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefinition& _contractDef) { Json::Value doc; Json::Value methods(Json::objectValue); - for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) + for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) { Json::Value user; auto strPtr = f->getDocumentation(); @@ -88,14 +88,14 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(std::shared_ return std::unique_ptr(new std::string(m_writer.write(doc))); } -std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_ptr _contractDef) +std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefinition& _contractDef) { // LTODO: Somewhere in this function warnings for mismatch of param names // should be thrown Json::Value doc; Json::Value methods(Json::objectValue); - for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) + for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) { Json::Value method; auto strPtr = f->getDocumentation(); @@ -146,7 +146,7 @@ static inline std::string::const_iterator skipLineOrEOS(std::string::const_itera std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos, std::string::const_iterator _end, std::string& _tagString, - enum DocTagType _tagType) + DocTagType _tagType) { auto nlPos = std::find(_pos, _end, '\n'); std::copy(_pos, nlPos, back_inserter(_tagString)); @@ -170,7 +170,7 @@ std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::cons auto paramDesc = std::string(currPos, nlPos); m_params.push_back(std::make_pair(paramName, paramDesc)); - m_lastTag = DOCTAG_PARAM; + m_lastTag = DocTagType::PARAM; return skipLineOrEOS(nlPos, _end); } @@ -197,14 +197,14 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite { // LTODO: need to check for @(start of a tag) between here and the end of line // for all cases - if (m_lastTag == DOCTAG_NONE || _tag != "") + if (m_lastTag == DocTagType::NONE || _tag != "") { if (_tag == "dev") - return parseDocTagLine(_pos, _end, m_dev, DOCTAG_DEV); + return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); else if (_tag == "notice") - return parseDocTagLine(_pos, _end, m_notice, DOCTAG_NOTICE); + return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); else if (_tag == "return") - return parseDocTagLine(_pos, _end, m_return, DOCTAG_RETURN); + return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); else if (_tag == "param") return parseDocTagParam(_pos, _end); else @@ -222,16 +222,16 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it { switch (m_lastTag) { - case DOCTAG_DEV: + case DocTagType::DEV: m_dev += " "; - return parseDocTagLine(_pos, _end, m_dev, DOCTAG_DEV); - case DOCTAG_NOTICE: + return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); + case DocTagType::NOTICE: m_notice += " "; - return parseDocTagLine(_pos, _end, m_notice, DOCTAG_NOTICE); - case DOCTAG_RETURN: + return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); + case DocTagType::RETURN: m_return += " "; - return parseDocTagLine(_pos, _end, m_return, DOCTAG_RETURN); - case DOCTAG_PARAM: + 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")); @@ -267,7 +267,7 @@ void InterfaceHandler::parseDocString(std::string const& _string) currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos)); } - else if (m_lastTag != DOCTAG_NONE) // continuation of the previous tag + else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag currPos = appendDocTag(currPos + 1, end); else if (currPos != end) // skip the line if a newline was found currPos = nlPos + 1; diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index 7a5ee66db..e6be9e6a7 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -37,15 +37,15 @@ namespace solidity // Forward declarations class ContractDefinition; -enum DocumentationType: unsigned short; +enum class DocumentationType: uint8_t; -enum DocTagType +enum class DocTagType: uint8_t { - DOCTAG_NONE = 0, - DOCTAG_DEV, - DOCTAG_NOTICE, - DOCTAG_PARAM, - DOCTAG_RETURN + NONE = 0, + DEV, + NOTICE, + PARAM, + RETURN }; class InterfaceHandler @@ -59,23 +59,23 @@ public: /// types provided by @c DocumentationType /// @return A unique pointer contained string with the json /// representation of provided type - std::unique_ptr getDocumentation(std::shared_ptr _contractDef, - enum DocumentationType _type); + std::unique_ptr getDocumentation(ContractDefinition& _contractDef, + DocumentationType _type); /// Get the ABI Interface of the contract /// @param _contractDef The contract definition /// @return A unique pointer contained string with the json /// representation of the contract's ABI Interface - std::unique_ptr getABIInterface(std::shared_ptr _contractDef); + std::unique_ptr getABIInterface(ContractDefinition& _contractDef); /// Get the User documentation of the contract /// @param _contractDef The contract definition /// @return A unique pointer contained string with the json /// representation of the contract's user documentation - std::unique_ptr getUserDocumentation(std::shared_ptr _contractDef); + std::unique_ptr getUserDocumentation(ContractDefinition& _contractDef); /// Get the Developer's documentation of the contract /// @param _contractDef The contract definition /// @return A unique pointer contained string with the json /// representation of the contract's developer documentation - std::unique_ptr getDevDocumentation(std::shared_ptr _contractDef); + std::unique_ptr getDevDocumentation(ContractDefinition& _contractDef); private: void resetUser(); @@ -84,7 +84,7 @@ private: std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos, std::string::const_iterator _end, std::string& _tagString, - enum DocTagType _tagType); + DocTagType _tagType); std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos, std::string::const_iterator _end); std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos, @@ -99,7 +99,7 @@ private: Json::StyledWriter m_writer; // internal state - enum DocTagType m_lastTag; + DocTagType m_lastTag; std::string m_notice; std::string m_dev; std::string m_return; diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index d473348b8..540b066eb 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -32,15 +32,20 @@ namespace solidity { -NameAndTypeResolver::NameAndTypeResolver(std::vector const& _globals) +NameAndTypeResolver::NameAndTypeResolver(std::vector const& _globals) { - for (Declaration* declaration: _globals) + for (Declaration const* declaration: _globals) m_scopes[nullptr].registerDeclaration(*declaration); } +void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) +{ + // The helper registers all declarations in m_scopes as a side-effect of its construction. + DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit); +} + void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) { - DeclarationRegistrationHelper registrar(m_scopes, _contract); m_currentScope = &m_scopes[&_contract]; for (ASTPointer const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, nullptr); @@ -65,7 +70,14 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) m_currentScope = &m_scopes[nullptr]; } -Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const +void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) +{ + m_scopes[nullptr].registerDeclaration(_declaration, true); + if (asserts(_declaration.getScope() == nullptr)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Updated declaration outside global scope.")); +} + +Declaration const* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const { auto iterator = m_scopes.find(_scope); if (iterator == end(m_scopes)) @@ -73,7 +85,7 @@ Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaratio return iterator->second.resolveName(_name, false); } -Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) +Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) { return m_currentScope->resolveName(_name, _recursive); } @@ -135,7 +147,7 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) return true; } -void DeclarationRegistrationHelper::enterNewSubScope(Declaration& _declaration) +void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) { map::iterator iter; bool newlyAdded; @@ -193,15 +205,14 @@ bool ReferencesResolver::visit(Return& _return) return true; } -bool ReferencesResolver::visit(Mapping& _mapping) +bool ReferencesResolver::visit(Mapping&) { - (void)_mapping; return true; } bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) { - Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); + Declaration const* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); if (!declaration) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) << errinfo_comment("Undeclared identifier.")); @@ -211,7 +222,7 @@ bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) bool ReferencesResolver::visit(Identifier& _identifier) { - Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); + Declaration const* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); if (!declaration) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) << errinfo_comment("Undeclared identifier.")); diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 797eca605..1ff9febf0 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -41,21 +41,25 @@ namespace solidity class NameAndTypeResolver: private boost::noncopyable { public: - explicit NameAndTypeResolver(std::vector const& _globals); + explicit NameAndTypeResolver(std::vector const& _globals); + /// Registers all declarations found in the source unit. + void registerDeclarations(SourceUnit& _sourceUnit); + /// Resolves all names and types referenced from the given contract. void resolveNamesAndTypes(ContractDefinition& _contract); + /// Updates the given global declaration (used for "this"). Not to be used with declarations + /// that create their own scope. + void updateDeclaration(Declaration const& _declaration); /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, /// the global scope is used (i.e. the one containing only the contract). /// @returns a pointer to the declaration on success or nullptr on failure. - Declaration* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const; + Declaration const* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const; /// Resolves a name in the "current" scope. Should only be called during the initial /// resolving phase. - Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); + Declaration const* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); private: - /// Throws if @a _struct contains a recursive loop. Note that recursion via mappings is fine. - void checkForRecursion(StructDefinition const& _struct); void reset(); /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration, @@ -85,12 +89,12 @@ private: void endVisit(VariableDefinition& _variableDefinition); bool visit(VariableDeclaration& _declaration); - void enterNewSubScope(Declaration& _declaration); + void enterNewSubScope(Declaration const& _declaration); void closeCurrentScope(); void registerDeclaration(Declaration& _declaration, bool _opensScope); std::map& m_scopes; - Declaration* m_currentScope; + Declaration const* m_currentScope; FunctionDefinition* m_currentFunction; }; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 0506bc3e3..ddab489b6 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -20,30 +20,27 @@ * Solidity parser. */ +#include #include #include #include #include #include +using namespace std; + namespace dev { namespace solidity { -ASTPointer Parser::parse(std::shared_ptr const& _scanner) -{ - m_scanner = _scanner; - return parseContractDefinition(); -} - - /// AST node factory that also tracks the begin and end position of an AST node /// while it is being parsed class Parser::ASTNodeFactory { public: - ASTNodeFactory(Parser const& _parser): m_parser(_parser), m_location(_parser.getPosition(), -1) {} + ASTNodeFactory(Parser const& _parser): + m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {} void markEndPosition() { m_location.end = m_parser.getEndPosition(); } void setLocationEmpty() { m_location.end = m_location.start; } @@ -55,7 +52,7 @@ public: { if (m_location.end < 0) markEndPosition(); - return std::make_shared(m_location, std::forward(_args)...); + return make_shared(m_location, forward(_args)...); } private: @@ -63,6 +60,33 @@ private: Location m_location; }; +ASTPointer Parser::parse(shared_ptr const& _scanner) +{ + m_scanner = _scanner; + ASTNodeFactory nodeFactory(*this); + vector> nodes; + while (_scanner->getCurrentToken() != Token::EOS) + { + switch (m_scanner->getCurrentToken()) + { + case Token::IMPORT: + nodes.push_back(parseImportDirective()); + break; + case Token::CONTRACT: + nodes.push_back(parseContractDefinition()); + break; + default: + BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition."))); + } + } + return nodeFactory.createNode(nodes); +} + +std::shared_ptr const& Parser::getSourceName() const +{ + return m_scanner->getSourceName(); +} + int Parser::getPosition() const { return m_scanner->getCurrentLocation().start; @@ -73,15 +97,27 @@ int Parser::getEndPosition() const return m_scanner->getCurrentLocation().end; } +ASTPointer Parser::parseImportDirective() +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::IMPORT); + if (m_scanner->getCurrentToken() != Token::STRING_LITERAL) + BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL).")); + ASTPointer url = getLiteralAndAdvance(); + nodeFactory.markEndPosition(); + expectToken(Token::SEMICOLON); + return nodeFactory.createNode(url); +} + ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); expectToken(Token::CONTRACT); ASTPointer name = expectIdentifierToken(); expectToken(Token::LBRACE); - std::vector> structs; - std::vector> stateVariables; - std::vector> functions; + vector> structs; + vector> stateVariables; + vector> functions; bool visibilityIsPublic = true; while (true) { @@ -110,7 +146,6 @@ ASTPointer Parser::parseContractDefinition() } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); - expectToken(Token::EOS); return nodeFactory.createNode(name, structs, stateVariables, functions); } @@ -119,7 +154,7 @@ ASTPointer Parser::parseFunctionDefinition(bool _isPublic) ASTNodeFactory nodeFactory(*this); ASTPointer docstring; if (m_scanner->getCurrentCommentLiteral() != "") - docstring = std::make_shared(m_scanner->getCurrentCommentLiteral()); + docstring = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::FUNCTION); ASTPointer name(expectIdentifierToken()); @@ -142,7 +177,7 @@ ASTPointer Parser::parseFunctionDefinition(bool _isPublic) // create an empty parameter list at a zero-length location ASTNodeFactory nodeFactory(*this); nodeFactory.setLocationEmpty(); - returnParameters = nodeFactory.createNode(std::vector>()); + returnParameters = nodeFactory.createNode(vector>()); } ASTPointer block = parseBlock(); nodeFactory.setEndPositionFromNode(block); @@ -156,7 +191,7 @@ ASTPointer Parser::parseStructDefinition() ASTNodeFactory nodeFactory(*this); expectToken(Token::STRUCT); ASTPointer name = expectIdentifierToken(); - std::vector> members; + vector> members; expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { @@ -228,7 +263,7 @@ ASTPointer Parser::parseMapping() ASTPointer Parser::parseParameterList(bool _allowEmpty) { ASTNodeFactory nodeFactory(*this); - std::vector> parameters; + vector> parameters; expectToken(Token::LPAREN); if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { @@ -249,7 +284,7 @@ ASTPointer Parser::parseBlock() { ASTNodeFactory nodeFactory(*this); expectToken(Token::LBRACE); - std::vector> statements; + vector> statements; while (m_scanner->getCurrentToken() != Token::RBRACE) statements.push_back(parseStatement()); nodeFactory.markEndPosition(); @@ -447,7 +482,7 @@ ASTPointer Parser::parseLeftHandSideExpression() case Token::LPAREN: { m_scanner->next(); - std::vector> arguments = parseFunctionCallArguments(); + vector> arguments = parseFunctionCallArguments(); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); expression = nodeFactory.createNode(expression, arguments); @@ -503,9 +538,9 @@ ASTPointer Parser::parsePrimaryExpression() return expression; } -std::vector> Parser::parseFunctionCallArguments() +vector> Parser::parseFunctionCallArguments() { - std::vector> arguments; + vector> arguments; if (m_scanner->getCurrentToken() != Token::RPAREN) { arguments.push_back(parseExpression()); @@ -521,7 +556,7 @@ std::vector> Parser::parseFunctionCallArguments() void Parser::expectToken(Token::Value _value) { if (m_scanner->getCurrentToken() != _value) - BOOST_THROW_EXCEPTION(createParserError(std::string("Expected token ") + std::string(Token::getName(_value)))); + BOOST_THROW_EXCEPTION(createParserError(string("Expected token ") + string(Token::getName(_value)))); m_scanner->next(); } @@ -543,14 +578,15 @@ ASTPointer Parser::expectIdentifierToken() ASTPointer Parser::getLiteralAndAdvance() { - ASTPointer identifier = std::make_shared(m_scanner->getCurrentLiteral()); + ASTPointer identifier = make_shared(m_scanner->getCurrentLiteral()); m_scanner->next(); return identifier; } -ParserError Parser::createParserError(std::string const& _description) const +ParserError Parser::createParserError(string const& _description) const { - return ParserError() << errinfo_sourcePosition(getPosition()) << errinfo_comment(_description); + return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName())) + << errinfo_comment(_description); } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 307a0d6a1..52a374e03 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -34,7 +34,8 @@ class Scanner; class Parser { public: - ASTPointer parse(std::shared_ptr const& _scanner); + ASTPointer parse(std::shared_ptr const& _scanner); + std::shared_ptr const& getSourceName() const; private: class ASTNodeFactory; @@ -46,6 +47,7 @@ private: ///@{ ///@name Parsing functions for the AST nodes + ASTPointer parseImportDirective(); ASTPointer parseContractDefinition(); ASTPointer parseFunctionDefinition(bool _isPublic); ASTPointer parseStructDefinition(); diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 2f5f8d37a..08bf744d4 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -143,17 +143,22 @@ private: }; // end of LiteralScope class -void Scanner::reset(CharStream const& _source) +void Scanner::reset(CharStream const& _source, string const& _sourceName) { m_source = _source; + m_sourceName = make_shared(_sourceName); + reset(); +} + +void Scanner::reset() +{ + m_source.reset(); m_char = m_source.get(); skipWhitespace(); scanToken(); - next(); } - bool Scanner::scanHexByte(char& o_scannedByte) { char x = 0; diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 49ac3651c..18b1f5d3a 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -79,6 +79,8 @@ public: char advanceAndGet(size_t _chars=1); char rollback(size_t _amount); + void reset() { m_pos = 0; } + ///@{ ///@name Error printing helper functions /// Functions that help pretty-printing parse errors @@ -99,11 +101,12 @@ class Scanner friend class LiteralScope; public: - Scanner() { reset(CharStream()); } - explicit Scanner(CharStream const& _source) { reset(_source); } + explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); } - /// Resets the scanner as if newly constructed with _input as input. - void reset(CharStream const& _source); + /// Resets the scanner as if newly constructed with _source and _sourceName as input. + void reset(CharStream const& _source, std::string const& _sourceName); + /// Resets scanner to the start of input. + void reset(); /// Returns the next token and advances input Token::Value next(); @@ -139,6 +142,8 @@ public: std::string const& peekLiteral() const { return m_nextToken.literal; } ///@} + std::shared_ptr const& getSourceName() const { return m_sourceName; } + ///@{ ///@name Error printing helper functions /// Functions that help pretty-printing parse errors @@ -203,6 +208,7 @@ private: TokenDesc m_nextToken; // desc for next token (one token look-ahead) CharStream m_source; + std::shared_ptr m_sourceName; /// one character look-ahead, equals 0 at end of input char m_char; diff --git a/libsolidity/SourceReferenceFormatter.cpp b/libsolidity/SourceReferenceFormatter.cpp index d3f2152a6..c61f9b685 100644 --- a/libsolidity/SourceReferenceFormatter.cpp +++ b/libsolidity/SourceReferenceFormatter.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -38,7 +39,6 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream, int startLine; int startColumn; tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start); - _stream << "starting at line " << (startLine + 1) << ", column " << (startColumn + 1) << "\n"; int endLine; int endColumn; tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end); @@ -58,37 +58,28 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream, << "Spanning multiple lines.\n"; } -void SourceReferenceFormatter::printSourcePosition(ostream& _stream, - int _position, - const Scanner& _scanner) -{ - int line; - int column; - tie(line, column) = _scanner.translatePositionToLineColumn(_position); - _stream << "at line " << (line + 1) << ", column " << (column + 1) << endl - << _scanner.getLineAtPosition(_position) << endl - << string(column, ' ') << "^" << endl; -} - void SourceReferenceFormatter::printExceptionInformation(ostream& _stream, Exception const& _exception, string const& _name, - Scanner const& _scanner) + CompilerStack const& _compiler) { - _stream << _name; - if (string const* description = boost::get_error_info(_exception)) - _stream << ": " << *description; + Location const* location = boost::get_error_info(_exception); + Scanner const* scanner; - if (int const* position = boost::get_error_info(_exception)) + if (location) { - _stream << " "; - printSourcePosition(_stream, *position, _scanner); - } - if (Location const* location = boost::get_error_info(_exception)) - { - _stream << " "; - printSourceLocation(_stream, *location, _scanner); + scanner = &_compiler.getScanner(*location->sourceName); + int startLine; + int startColumn; + tie(startLine, startColumn) = scanner->translatePositionToLineColumn(location->start); + _stream << *location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": "; } + _stream << _name; + if (string const* description = boost::get_error_info(_exception)) + _stream << ": " << *description << endl; + + if (location) + printSourceLocation(_stream, *location, *scanner); } } diff --git a/libsolidity/SourceReferenceFormatter.h b/libsolidity/SourceReferenceFormatter.h index 4736066fd..8e3f126f0 100644 --- a/libsolidity/SourceReferenceFormatter.h +++ b/libsolidity/SourceReferenceFormatter.h @@ -34,14 +34,14 @@ namespace solidity { class Scanner; // forward +class CompilerStack; // forward struct SourceReferenceFormatter { public: static void printSourceLocation(std::ostream& _stream, Location const& _location, Scanner const& _scanner); - static void printSourcePosition(std::ostream& _stream, int _position, Scanner const& _scanner); static void printExceptionInformation(std::ostream& _stream, Exception const& _exception, - std::string const& _name, Scanner const& _scanner); + std::string const& _name, CompilerStack const& _compiler); }; } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 7e07b1162..6c971a742 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -32,7 +32,7 @@ namespace dev namespace solidity { -shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) +shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) { if (asserts(Token::isElementaryTypeName(_typeToken))) BOOST_THROW_EXCEPTION(InternalCompilerError()); @@ -44,33 +44,33 @@ shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) if (bytes == 0) bytes = 32; int modifier = offset / 33; - return make_shared(bytes * 8, + return make_shared(bytes * 8, modifier == 0 ? IntegerType::Modifier::SIGNED : modifier == 1 ? IntegerType::Modifier::UNSIGNED : IntegerType::Modifier::HASH); } else if (_typeToken == Token::ADDRESS) - return make_shared(0, IntegerType::Modifier::ADDRESS); + return make_shared(0, IntegerType::Modifier::ADDRESS); else if (_typeToken == Token::BOOL) - return make_shared(); + return make_shared(); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type.")); } -shared_ptr Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) +shared_ptr Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) { Declaration const* declaration = _typeName.getReferencedDeclaration(); if (StructDefinition const* structDef = dynamic_cast(declaration)) - return make_shared(*structDef); + return make_shared(*structDef); else if (FunctionDefinition const* function = dynamic_cast(declaration)) - return make_shared(*function); + return make_shared(*function); else if (ContractDefinition const* contract = dynamic_cast(declaration)) - return make_shared(*contract); - return shared_ptr(); + return make_shared(*contract); + return shared_ptr(); } -shared_ptr Type::fromMapping(Mapping const& _typeName) +shared_ptr Type::fromMapping(Mapping const& _typeName) { shared_ptr keyType = _typeName.getKeyType().toType(); if (!keyType) @@ -78,28 +78,28 @@ shared_ptr Type::fromMapping(Mapping const& _typeName) shared_ptr valueType = _typeName.getValueType().toType(); if (!valueType) BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); - return make_shared(keyType, valueType); + return make_shared(keyType, valueType); } -shared_ptr Type::forLiteral(Literal const& _literal) +shared_ptr Type::forLiteral(Literal const& _literal) { switch (_literal.getToken()) { case Token::TRUE_LITERAL: case Token::FALSE_LITERAL: - return make_shared(); + return make_shared(); case Token::NUMBER: return IntegerType::smallestTypeForLiteral(_literal.getValue()); case Token::STRING_LITERAL: - return shared_ptr(); // @todo add string literals + return shared_ptr(); // @todo add string literals default: - return shared_ptr(); + return shared_ptr(); } } const MemberList Type::EmptyMemberList = MemberList(); -shared_ptr IntegerType::smallestTypeForLiteral(string const& _literal) +shared_ptr IntegerType::smallestTypeForLiteral(string const& _literal) { bigint value(_literal); bool isSigned = value < 0 || (!_literal.empty() && _literal.front() == '-'); @@ -108,8 +108,8 @@ shared_ptr IntegerType::smallestTypeForLiteral(string const& _liter value = ((-value) - 1) << 1; unsigned bytes = max(bytesRequired(value), 1u); if (bytes > 32) - return shared_ptr(); - return make_shared(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED); + return shared_ptr(); + return make_shared(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED); } IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): @@ -140,7 +140,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return _convertTo.getCategory() == getCategory(); + return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT; } bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const @@ -247,6 +247,31 @@ string ContractType::toString() const return "contract " + m_contract.getName(); } +MemberList const& ContractType::getMembers() const +{ + // We need to lazy-initialize it because of recursive references. + if (!m_members) + { + map> members; + for (FunctionDefinition const* function: m_contract.getInterfaceFunctions()) + members[function->getName()] = make_shared(*function, false); + m_members.reset(new MemberList(members)); + } + return *m_members; +} + +unsigned ContractType::getFunctionIndex(string const& _functionName) const +{ + unsigned index = 0; + for (FunctionDefinition const* function: m_contract.getInterfaceFunctions()) + { + if (function->getName() == _functionName) + return index; + ++index; + } + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index of non-existing contract function requested.")); +} + bool StructType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -302,7 +327,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); } -FunctionType::FunctionType(FunctionDefinition const& _function) +FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal) { TypePointers params; TypePointers retParams; @@ -314,7 +339,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function) retParams.push_back(var->getType()); swap(params, m_parameterTypes); swap(retParams, m_returnParameterTypes); - m_location = Location::INTERNAL; + m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL; } bool FunctionType::operator==(Type const& _other) const @@ -323,6 +348,8 @@ bool FunctionType::operator==(Type const& _other) const return false; FunctionType const& other = dynamic_cast(_other); + if (m_location != other.m_location) + return false; if (m_parameterTypes.size() != other.m_parameterTypes.size() || m_returnParameterTypes.size() != other.m_returnParameterTypes.size()) return false; diff --git a/libsolidity/Types.h b/libsolidity/Types.h index b655f9e0d..887a1ee16 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -80,15 +80,15 @@ public: ///@{ ///@name Factory functions /// Factory functions that convert an AST @ref TypeName to a Type. - static std::shared_ptr fromElementaryTypeName(Token::Value _typeToken); - static std::shared_ptr fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); - static std::shared_ptr fromMapping(Mapping const& _typeName); - static std::shared_ptr fromFunction(FunctionDefinition const& _function); + static std::shared_ptr fromElementaryTypeName(Token::Value _typeToken); + static std::shared_ptr fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); + static std::shared_ptr fromMapping(Mapping const& _typeName); + static std::shared_ptr fromFunction(FunctionDefinition const& _function); /// @} /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does /// not fit any type. - static std::shared_ptr forLiteral(Literal const& _literal); + static std::shared_ptr forLiteral(Literal const& _literal); virtual Category getCategory() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } @@ -148,7 +148,7 @@ public: /// @returns the smallest integer type for the given literal or an empty pointer /// if no type fits. - static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); + static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED); @@ -214,10 +214,17 @@ public: virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool operator==(Type const& _other) const override; virtual u256 getStorageSize() const override; + virtual bool isValueType() const override { return true; } virtual std::string toString() const override; + virtual MemberList const& getMembers() const override; + + unsigned getFunctionIndex(std::string const& _functionName) const; + private: ContractDefinition const& m_contract; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr m_members; }; /** @@ -263,7 +270,7 @@ public: enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 }; virtual Category getCategory() const override { return Category::FUNCTION; } - explicit FunctionType(FunctionDefinition const& _function); + explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, Location _location = Location::INTERNAL): m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), @@ -279,7 +286,7 @@ public: virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override; - Location getLocation() const { return m_location; } + Location const& getLocation() const { return m_location; } private: TypePointers m_parameterTypes; diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 7607b7a95..84a0ae42a 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -510,7 +510,7 @@ std::string WebThreeStubServer::eth_solidity(std::string const& _code) catch (dev::Exception const& exception) { ostringstream error; - solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler.getScanner()); + solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); cwarn << "Solidity compilation error: " << error.str(); } catch (...) diff --git a/libwebthree/WebThree.cpp b/libwebthree/WebThree.cpp index b5256ac22..c9f9d56e3 100644 --- a/libwebthree/WebThree.cpp +++ b/libwebthree/WebThree.cpp @@ -57,11 +57,11 @@ WebThreeDirect::~WebThreeDirect() // eth::Client (owned by us via a unique_ptr) uses eth::EthereumHost (via a weak_ptr). // Really need to work out a clean way of organising ownership and guaranteeing startup/shutdown is perfect. - // Have to call quit here to get the Host to kill its io_service otherwise we end up with left-over reads, + // Have to call stop here to get the Host to kill its io_service otherwise we end up with left-over reads, // still referencing Sessions getting deleted *after* m_ethereum is reset, causing bad things to happen, since // the guarantee is that m_ethereum is only reset *after* all sessions have ended (sessions are allowed to // use bits of data owned by m_ethereum). - m_net.quit(); + m_net.stop(); m_ethereum.reset(); } diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index a135f77ff..ec7bf2406 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.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 Client.h +/** @file WebThree.h * @author Gav Wood * @date 2014 */ @@ -92,9 +92,6 @@ public: /// Connect to a particular peer. void connect(std::string const& _seedHost, unsigned short _port = 30303); - /// Is the network subsystem up? - bool haveNetwork() { return peerCount() != 0; } - /// Save peers dev::bytes saveNodes(); diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp index 65da72f9d..07620a7cf 100644 --- a/libwhisper/Message.cpp +++ b/libwhisper/Message.cpp @@ -89,6 +89,15 @@ Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigne return ret; } +Envelope::Envelope(RLP const& _m) +{ + m_expiry = _m[0].toInt(); + m_ttl = _m[1].toInt(); + m_topic = _m[2].toVector>(); + m_data = _m[3].toBytes(); + m_nonce = _m[4].toInt(); +} + Message Envelope::open(Secret const& _s) const { return Message(*this, _s); diff --git a/libwhisper/Message.h b/libwhisper/Message.h index 954aed4a0..f582b14c7 100644 --- a/libwhisper/Message.h +++ b/libwhisper/Message.h @@ -51,14 +51,7 @@ class Envelope public: Envelope() {} - Envelope(RLP const& _m) - { - m_expiry = _m[0].toInt(); - m_ttl = _m[1].toInt(); - m_topic = _m[2].toVector>(); - m_data = _m[3].toBytes(); - m_nonce = _m[4].toInt(); - } + Envelope(RLP const& _m); operator bool() const { return !!m_expiry; } diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 769b39057..589f8f047 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -68,7 +68,7 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p) return; UpgradeGuard ll(l); m_messages[h] = _m; - m_expiryQueue[_m.expiry()] = h; + m_expiryQueue.insert(make_pair(_m.expiry(), h)); } // if (_p) diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index b38964f8e..1a4ec2a71 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -78,7 +78,7 @@ private: mutable dev::SharedMutex x_messages; std::map m_messages; - std::map m_expiryQueue; + std::multimap m_expiryQueue; mutable dev::Mutex m_filterLock; std::map m_filters; diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index 0951a3726..1dbd6e16e 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -82,21 +82,21 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r) void WhisperPeer::sendMessages() { - if (m_unseen.size()) + RLPStream amalg; + unsigned msgCount = 0; { - RLPStream amalg; - unsigned msgCount; + Guard l(x_unseen); + msgCount = m_unseen.size(); + while (m_unseen.size()) { - Guard l(x_unseen); - msgCount = m_unseen.size(); - while (m_unseen.size()) - { - auto p = *m_unseen.begin(); - m_unseen.erase(m_unseen.begin()); - host()->streamMessage(p.second, amalg); - } + auto p = *m_unseen.begin(); + m_unseen.erase(m_unseen.begin()); + host()->streamMessage(p.second, amalg); } - + } + + if (msgCount) + { RLPStream s; prep(s, MessagesPacket, msgCount).appendRaw(amalg.out(), msgCount); sealAndSend(s); @@ -106,5 +106,5 @@ void WhisperPeer::sendMessages() void WhisperPeer::noteNewMessage(h256 _h, Message const& _m) { Guard l(x_unseen); - m_unseen[rating(_m)] = _h; + m_unseen.insert(make_pair(rating(_m), _h)); } diff --git a/libwhisper/WhisperPeer.h b/libwhisper/WhisperPeer.h index faac2d870..d21f33725 100644 --- a/libwhisper/WhisperPeer.h +++ b/libwhisper/WhisperPeer.h @@ -67,7 +67,7 @@ private: void noteNewMessage(h256 _h, Message const& _m); mutable dev::Mutex x_unseen; - std::map m_unseen; ///< Rated according to what they want. + std::multimap m_unseen; ///< Rated according to what they want. std::chrono::system_clock::time_point m_timer = std::chrono::system_clock::now(); }; diff --git a/mix/ConstantCompilationModel.cpp b/mix/ConstantCompilationModel.cpp index e06734f59..ea12a267c 100644 --- a/mix/ConstantCompilationModel.cpp +++ b/mix/ConstantCompilationModel.cpp @@ -46,7 +46,7 @@ CompilerResult ConstantCompilationModel::compile(QString _code) catch (dev::Exception const& _exception) { ostringstream error; - solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler.getScanner()); + solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler); res.success = false; res.comment = QString::fromStdString(error.str()).toHtmlEscaped(); res.hexCode = ""; diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index 386d4a1a8..84f87eb53 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -8,6 +8,8 @@ set(EXECUTABLE solc) add_executable(${EXECUTABLE} ${SRC_LIST}) +target_link_libraries(${EXECUTABLE} boost_filesystem) +target_link_libraries(${EXECUTABLE} boost_program_options) target_link_libraries(${EXECUTABLE} solidity) install( TARGETS ${EXECUTABLE} DESTINATION bin ) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp new file mode 100644 index 000000000..d3dd39459 --- /dev/null +++ b/solc/CommandLineInterface.cpp @@ -0,0 +1,368 @@ +/* + 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 . +*/ +/** + * @author Lefteris + * @date 2014 + * Solidity command line interface. + */ +#include "CommandLineInterface.h" + +#include +#include +#include + +#include + +#include "BuildInfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +namespace po = boost::program_options; + +namespace dev +{ +namespace solidity +{ + +static void version() +{ + cout << "solc, the solidity complier commandline interface " << dev::Version << endl + << " by Christian and Lefteris , (c) 2014." << endl + << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; + exit(0); +} + +static inline bool argToStdout(po::variables_map const& _args, const char* _name) +{ + return _args.count(_name) && _args[_name].as() != OutputType::FILE; +} + +static bool needStdout(po::variables_map const& _args) +{ + return argToStdout(_args, "abi") || argToStdout(_args, "natspec-user") || argToStdout(_args, "natspec-dev") || + argToStdout(_args, "asm") || argToStdout(_args, "opcodes") || argToStdout(_args, "binary"); +} + +static inline bool outputToFile(OutputType type) +{ + return type == OutputType::FILE || type == OutputType::BOTH; +} + +static inline bool outputToStdout(OutputType type) +{ + return type == OutputType::STDOUT || type == OutputType::BOTH; +} + +static std::istream& operator>>(std::istream& _in, OutputType& io_output) +{ + std::string token; + _in >> token; + if (token == "stdout") + io_output = OutputType::STDOUT; + else if (token == "file") + io_output = OutputType::FILE; + else if (token == "both") + io_output = OutputType::BOTH; + else + throw boost::program_options::invalid_option_value(token); + return _in; +} + +void CommandLineInterface::handleBinary(string const& _contract) +{ + auto choice = m_args["binary"].as(); + if (outputToStdout(choice)) + { + cout << "Binary: " << endl; + cout << toHex(m_compiler.getBytecode(_contract)) << endl; + } + + if (outputToFile(choice)) + { + ofstream outFile(_contract + ".binary"); + outFile << toHex(m_compiler.getBytecode(_contract)); + outFile.close(); + } +} + +void CommandLineInterface::handleOpcode(string const& _contract) +{ + // TODO: Figure out why the wrong operator << (from boost) is used here + auto choice = m_args["opcode"].as(); + if (outputToStdout(choice)) + { + cout << "Opcodes: " << endl; + dev::operator<<(cout, m_compiler.getBytecode(_contract)); + cout << endl; + } + + if (outputToFile(choice)) + { + ofstream outFile(_contract + ".opcode"); + dev::operator<<(outFile, m_compiler.getBytecode(_contract)); + outFile.close(); + } +} + +void CommandLineInterface::handleBytecode(string const& _contract) +{ + if (m_args.count("opcodes")) + handleOpcode(_contract); + if (m_args.count("binary")) + handleBinary(_contract); +} + +void CommandLineInterface::handleJson(DocumentationType _type, + string const& _contract) +{ + std::string argName; + std::string suffix; + std::string title; + switch(_type) + { + case DocumentationType::ABI_INTERFACE: + argName = "abi"; + suffix = ".abi"; + title = "Contract ABI"; + break; + case DocumentationType::NATSPEC_USER: + argName = "natspec-user"; + suffix = ".docuser"; + title = "User Documentation"; + break; + case DocumentationType::NATSPEC_DEV: + argName = "natspec-dev"; + suffix = ".docdev"; + title = "Developer Documentation"; + break; + default: + // should never happen + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation _type")); + } + + if (m_args.count(argName)) + { + auto choice = m_args[argName].as(); + if (outputToStdout(choice)) + { + cout << title << endl; + cout << m_compiler.getJsonDocumentation(_contract, _type); + } + + if (outputToFile(choice)) + { + ofstream outFile(_contract + suffix); + outFile << m_compiler.getJsonDocumentation(_contract, _type); + outFile.close(); + } + } +} + +bool CommandLineInterface::parseArguments(int argc, char** argv) +{ +#define OUTPUT_TYPE_STR "Legal values:\n" \ + "\tstdout: Print it to standard output\n" \ + "\tfile: Print it to a file with same name\n" \ + "\tboth: Print both to a file and the stdout\n" + // Declare the supported options. + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "Show help message and exit") + ("version", "Show version and exit") + ("optimize", po::value()->default_value(false), "Optimize bytecode for size") + ("input-file", po::value>(), "input file") + ("ast", po::value(), + "Request to output the AST of the contract. " OUTPUT_TYPE_STR) + ("asm", po::value(), + "Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR) + ("opcodes", po::value(), + "Request to output the Opcodes of the contract. " OUTPUT_TYPE_STR) + ("binary", po::value(), + "Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR) + ("abi", po::value(), + "Request to output the contract's ABI interface. " OUTPUT_TYPE_STR) + ("natspec-user", po::value(), + "Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR) + ("natspec-dev", po::value(), + "Request to output the contract's Natspec developer documentation. " OUTPUT_TYPE_STR); +#undef OUTPUT_TYPE_STR + + // All positional options should be interpreted as input files + po::positional_options_description p; + p.add("input-file", -1); + + // parse the compiler arguments + try + { + po::store(po::command_line_parser(argc, argv).options(desc).positional(p).allow_unregistered().run(), m_args); + } + catch (po::error const& exception) + { + cout << exception.what() << endl; + return false; + } + po::notify(m_args); + + if (m_args.count("help")) + { + cout << desc; + return false; + } + + if (m_args.count("version")) + { + version(); + return false; + } + + return true; +} + +bool CommandLineInterface::processInput() +{ + if (!m_args.count("input-file")) + { + string s; + while (!cin.eof()) + { + getline(cin, s); + m_sourceCodes[""].append(s); + } + } + else + for (string const& infile: m_args["input-file"].as>()) + m_sourceCodes[infile] = asString(dev::contents(infile)); + + try + { + for (auto const& sourceCode: m_sourceCodes) + m_compiler.addSource(sourceCode.first, sourceCode.second); + // TODO: Perhaps we should not compile unless requested + m_compiler.compile(m_args["optimize"].as()); + } + catch (ParserError const& exception) + { + SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", m_compiler); + return false; + } + catch (DeclarationError const& exception) + { + SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", m_compiler); + return false; + } + catch (TypeError const& exception) + { + SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", m_compiler); + return false; + } + catch (CompilerError const& exception) + { + SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", m_compiler); + return false; + } + catch (InternalCompilerError const& exception) + { + SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", m_compiler); + return false; + } + catch (Exception const& exception) + { + cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl; + return false; + } + catch (...) + { + cerr << "Unknown exception during compilation." << endl; + return false; + } + + return true; +} + +void CommandLineInterface::actOnInput() +{ + // do we need AST output? + if (m_args.count("ast")) + { + auto choice = m_args["ast"].as(); + if (outputToStdout(choice)) + { + cout << "Syntax trees:" << endl << endl; + for (auto const& sourceCode: m_sourceCodes) + { + cout << endl << "======= " << sourceCode.first << " =======" << endl; + ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second); + printer.print(cout); + } + } + + if (outputToFile(choice)) + { + for (auto const& sourceCode: m_sourceCodes) + { + boost::filesystem::path p(sourceCode.first); + ofstream outFile(p.stem().string() + ".ast"); + ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second); + printer.print(outFile); + outFile.close(); + } + } + } + + vector contracts = m_compiler.getContractNames(); + for (string const& contract: contracts) + { + if (needStdout(m_args)) + cout << endl << "======= " << contract << " =======" << endl; + + // do we need EVM assembly? + if (m_args.count("asm")) + { + auto choice = m_args["asm"].as(); + if (outputToStdout(choice)) + { + cout << "EVM assembly:" << endl; + m_compiler.streamAssembly(cout, contract); + } + + if (outputToFile(choice)) + { + ofstream outFile(contract + ".evm"); + m_compiler.streamAssembly(outFile, contract); + outFile.close(); + } + } + + handleBytecode(contract); + handleJson(DocumentationType::ABI_INTERFACE, contract); + handleJson(DocumentationType::NATSPEC_DEV, contract); + handleJson(DocumentationType::NATSPEC_USER, contract); + } // end of contracts iteration +} + +} +} diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h new file mode 100644 index 000000000..7e3ad2502 --- /dev/null +++ b/solc/CommandLineInterface.h @@ -0,0 +1,71 @@ +/* + 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 . +*/ +/** + * @author Lefteris + * @date 2014 + * Solidity command line interface. + */ +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +//forward declaration +enum class DocumentationType: uint8_t; + +enum class OutputType: uint8_t +{ + STDOUT, + FILE, + BOTH +}; + +class CommandLineInterface +{ +public: + CommandLineInterface() {} + + /// Parse command line arguments and return false if we should not continue + bool parseArguments(int argc, char** argv); + /// Parse the files and create source code objects + bool processInput(); + /// Perform actions on the input depending on provided compiler arguments + void actOnInput(); + +private: + void handleBinary(std::string const& _contract); + void handleOpcode(std::string const& _contract); + void handleBytecode(std::string const& _contract); + void handleJson(DocumentationType _type, + std::string const& _contract); + + /// Compiler arguments variable map + boost::program_options::variables_map m_args; + /// map of input files to source code strings + std::map m_sourceCodes; + /// Solidity compiler stack + dev::solidity::CompilerStack m_compiler; +}; + +} +} diff --git a/solc/main.cpp b/solc/main.cpp index daeb2707c..c5f72980d 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -20,124 +20,16 @@ * Solidity commandline compiler. */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace dev; -using namespace solidity; - -void help() -{ - cout << "Usage solc [OPTIONS] " << endl - << "Options:" << endl - << " -o,--optimize Optimize the bytecode for size." << endl - << " -h,--help Show this help message and exit." << endl - << " -V,--version Show the version and exit." << endl; - exit(0); -} - -void version() -{ - cout << "solc, the solidity complier commandline interface " << dev::Version << endl - << " by Christian , (c) 2014." << endl - << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; - exit(0); -} +#include "CommandLineInterface.h" int main(int argc, char** argv) { - string infile; - bool optimize = false; - for (int i = 1; i < argc; ++i) - { - string arg = argv[i]; - if (arg == "-o" || arg == "--optimize") - optimize = true; - else if (arg == "-h" || arg == "--help") - help(); - else if (arg == "-V" || arg == "--version") - version(); - else - infile = argv[i]; - } - string sourceCode; - if (infile.empty()) - { - string s; - while (!cin.eof()) - { - getline(cin, s); - sourceCode.append(s); - } - } - else - sourceCode = asString(dev::contents(infile)); - - CompilerStack compiler; - try - { - compiler.compile(sourceCode, optimize); - } - catch (ParserError const& exception) - { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", compiler.getScanner()); - return -1; - } - catch (DeclarationError const& exception) - { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", compiler.getScanner()); - return -1; - } - catch (TypeError const& exception) - { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", compiler.getScanner()); - return -1; - } - catch (CompilerError const& exception) - { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", compiler.getScanner()); - return -1; - } - catch (InternalCompilerError const& exception) - { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", compiler.getScanner()); - return -1; - } - catch (Exception const& exception) - { - cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl; - return -1; - } - catch (...) - { - cerr << "Unknown exception during compilation." << endl; - return -1; - } - - cout << "Syntax tree for the contract:" << endl; - ASTPrinter printer(compiler.getAST(), sourceCode); - printer.print(cout); - cout << "EVM assembly:" << endl; - compiler.streamAssembly(cout); - cout << "Opcodes:" << endl; - cout << eth::disassemble(compiler.getBytecode()) << endl; - cout << "Binary: " << toHex(compiler.getBytecode()) << endl; - cout << "Interface specification: " << compiler.getJsonDocumentation(ABI_INTERFACE) << endl; - cout << "Natspec user documentation: " << compiler.getJsonDocumentation(NATSPEC_USER) << endl; - cout << "Natspec developer documentation: " << compiler.getJsonDocumentation(NATSPEC_DEV) << endl; + dev::solidity::CommandLineInterface cli; + if (!cli.parseArguments(argc, argv)) + return 1; + if (!cli.processInput()) + return 1; + cli.actOnInput(); return 0; } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 1ece875ea..b0935d0b6 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -72,7 +72,7 @@ ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller): m_TestObject(_o if (!isFiller) { importState(_o["post"].get_obj(), m_statePost); - m_environment.sub.logs = importLog(_o["logs"].get_obj()); + m_environment.sub.logs = importLog(_o["logs"].get_array()); } } @@ -259,16 +259,17 @@ bytes importCode(json_spirit::mObject& _o) return code; } -LogEntries importLog(json_spirit::mObject& _o) +LogEntries importLog(json_spirit::mArray& _a) { LogEntries logEntries; - for (auto const& l: _o) + for (auto const& l: _a) { - json_spirit::mObject o = l.second.get_obj(); + json_spirit::mObject o = l.get_obj(); // cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest) assert(o.count("address") > 0); assert(o.count("topics") > 0); assert(o.count("data") > 0); + assert(o.count("bloom") > 0); LogEntry log; log.address = Address(o["address"].get_str()); for (auto const& t: o["topics"].get_array()) @@ -279,9 +280,9 @@ LogEntries importLog(json_spirit::mObject& _o) return logEntries; } -json_spirit::mObject exportLog(eth::LogEntries _logs) +json_spirit::mArray exportLog(eth::LogEntries _logs) { - json_spirit::mObject ret; + json_spirit::mArray ret; if (_logs.size() == 0) return ret; for (LogEntry const& l: _logs) { @@ -292,7 +293,8 @@ json_spirit::mObject exportLog(eth::LogEntries _logs) topics.push_back(toString(t)); o["topics"] = topics; o["data"] = "0x" + toHex(l.data); - ret[toString(l.bloom())] = o; + o["bloom"] = toString(l.bloom()); + ret.push_back(o); } return ret; } diff --git a/test/TestHelper.h b/test/TestHelper.h index c5c3a083d..3203eae83 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -68,8 +68,8 @@ u256 toInt(json_spirit::mValue const& _v); byte toByte(json_spirit::mValue const& _v); bytes importCode(json_spirit::mObject& _o); bytes importData(json_spirit::mObject& _o); -eth::LogEntries importLog(json_spirit::mObject& _o); -json_spirit::mObject exportLog(eth::LogEntries _logs); +eth::LogEntries importLog(json_spirit::mArray& _o); +json_spirit::mArray exportLog(eth::LogEntries _logs); void checkOutput(bytes const& _output, json_spirit::mObject& _o); void checkStorage(std::map _expectedStore, std::map _resultStore, Address _expectedAddr); void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs); diff --git a/test/createRandomTest.cpp b/test/createRandomTest.cpp index a01eec65c..1af12f64d 100644 --- a/test/createRandomTest.cpp +++ b/test/createRandomTest.cpp @@ -31,9 +31,8 @@ #include #include #include -#include #include -#include +#include #include "vm.h" using namespace std; @@ -129,9 +128,6 @@ void doMyTests(json_spirit::mValue& v) assert(o.count("pre") > 0); assert(o.count("exec") > 0); - - auto vmObj = eth::VMFactory::create(eth::VMFactory::Interpreter); - auto& vm = *vmObj; dev::test::FakeExtVM fev; fev.importEnv(o["env"].get_obj()); fev.importState(o["pre"].get_obj()); @@ -145,14 +141,15 @@ void doMyTests(json_spirit::mValue& v) fev.code = fev.thisTxCode; } - vm.reset(fev.gas); bytes output; + 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) { @@ -192,7 +189,7 @@ void doMyTests(json_spirit::mValue& v) o["callcreates"] = fev.exportCallCreates(); o["out"] = "0x" + toHex(output); fev.push(o, "gas", gas); - o["logs"] = mValue(test::exportLog(fev.sub.logs)); + o["logs"] = test::exportLog(fev.sub.logs); } } } diff --git a/test/crypto.cpp b/test/crypto.cpp index 08236135a..466015ad7 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -44,6 +44,14 @@ static CryptoPP::OID s_curveOID(CryptoPP::ASN1::secp256k1()); static CryptoPP::DL_GroupParameters_EC s_params(s_curveOID); static CryptoPP::DL_GroupParameters_EC::EllipticCurve s_curve(s_params.GetCurve()); +BOOST_AUTO_TEST_CASE(cryptopp_patch) +{ + KeyPair k = KeyPair::create(); + bytes io_text; + s_secp256k1.decrypt(k.sec(), io_text); + BOOST_REQUIRE_EQUAL(io_text.size(), 0); +} + BOOST_AUTO_TEST_CASE(verify_secert) { h256 empty; diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 3e86919a6..004740b5e 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -46,16 +46,23 @@ namespace bytes compileContract(const string& _sourceCode) { Parser parser; - ASTPointer contract; - BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared(CharStream(_sourceCode)))); + ASTPointer sourceUnit; + BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(CharStream(_sourceCode)))); NameAndTypeResolver resolver({}); - BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); - - Compiler compiler; - compiler.compileContract(*contract, {}); - // debug - //compiler.streamAssembly(cout); - return compiler.getAssembledBytecode(); + resolver.registerDeclarations(*sourceUnit); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); + + Compiler compiler; + compiler.compileContract(*contract, {}); + // debug + //compiler.streamAssembly(cout); + return compiler.getAssembledBytecode(); + } + BOOST_FAIL("No contract found in source."); + return bytes(); } /// Checks that @a _compiledCode is present starting from offset @a _offset in @a _expectation. @@ -118,8 +125,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers) byte(Instruction::JUMP), // end of f byte(Instruction::JUMPDEST), // beginning of g byte(Instruction::PUSH1), 0x0, - byte(Instruction::DUP1), // initialized e and h - byte(Instruction::PUSH1), byte(0x29 + shift), // ret address + byte(Instruction::PUSH1), 0x0, // initialized e and h + byte(Instruction::PUSH1), byte(0x2a + shift), // ret address byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 8b25d4031..3c2bb0814 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -47,9 +47,11 @@ class ExecutionFramework public: ExecutionFramework() { g_logVerbosity = 0; } - bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0) + bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0, string const& _contractName = "") { - bytes code = dev::solidity::CompilerStack::staticCompile(_sourceCode); + dev::solidity::CompilerStack compiler; + compiler.compile(_sourceCode); + bytes code = compiler.getBytecode(_contractName); sendMessage(code, true, _value); BOOST_REQUIRE(!m_output.empty()); return m_output; @@ -115,6 +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::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()); @@ -127,7 +130,7 @@ private: catch (...) {} if (_isCreation) { - BOOST_REQUIRE(!executive.create(Address(), _value, m_gasPrice, m_gas, &_data, Address())); + 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)); @@ -135,14 +138,16 @@ private: else { BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); - BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), _value, m_gasPrice, &_data, m_gas, Address())); + 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; @@ -898,6 +903,163 @@ BOOST_AUTO_TEST_CASE(ecrecover) BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr)); } +BOOST_AUTO_TEST_CASE(inter_contract_calls) +{ + char const* sourceCode = R"( + contract Helper { + function multiply(uint a, uint b) returns (uint c) { + return a * b; + } + } + contract Main { + Helper h; + function callHelper(uint a, uint b) returns (uint c) { + return h.multiply(a, b); + } + function getHelper() returns (address haddress) { + return address(h); + } + function setHelper(address haddress) { + h = Helper(haddress); + } + })"; + 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)); + u256 a(3456789); + u256 b("0x282837623374623234aa74"); + BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b)); +} + +BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters) +{ + char const* sourceCode = R"( + contract Helper { + function sel(uint a, bool select, uint b) returns (uint c) { + if (select) return a; else return b; + } + } + contract Main { + Helper h; + function callHelper(uint a, bool select, uint b) returns (uint c) { + return h.sel(a, select, b) * 3; + } + function getHelper() returns (address haddress) { + return address(h); + } + function setHelper(address haddress) { + h = Helper(haddress); + } + })"; + 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)); + u256 a(3456789); + u256 b("0x282837623374623234aa74"); + BOOST_REQUIRE(callContractFunction(0, a, true, b) == toBigEndian(a * 3)); + BOOST_REQUIRE(callContractFunction(0, a, false, b) == toBigEndian(b * 3)); +} + +BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this) +{ + char const* sourceCode = R"( + contract Helper { + function getAddress() returns (address addr) { + return address(this); + } + } + contract Main { + Helper h; + function callHelper() returns (address addr) { + return h.getAddress(); + } + 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_REQUIRE(callContractFunction(0) == toBigEndian(helperAddress)); +} + +BOOST_AUTO_TEST_CASE(calls_to_this) +{ + char const* sourceCode = R"( + contract Helper { + function invoke(uint a, uint b) returns (uint c) { + return this.multiply(a, b, 10); + } + function multiply(uint a, uint b, uint8 c) returns (uint ret) { + return a * b + c; + } + } + contract Main { + Helper h; + function callHelper(uint a, uint b) returns (uint ret) { + return h.invoke(a, b); + } + 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)); + u256 a(3456789); + u256 b("0x282837623374623234aa74"); + BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 10)); +} + +BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars) +{ + // note that a reference to another contract's function occupies two stack slots, + // so this tests correct stack slot allocation + char const* sourceCode = R"( + contract Helper { + function multiply(uint a, uint b) returns (uint c) { + return a * b; + } + } + contract Main { + Helper h; + function callHelper(uint a, uint b) returns (uint c) { + var fu = h.multiply; + var y = 9; + var ret = fu(a, b); + return ret + y; + } + function getHelper() returns (address haddress) { + return address(h); + } + function setHelper(address haddress) { + h = Helper(haddress); + } + })"; + 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)); + u256 a(3456789); + u256 b("0x282837623374623234aa74"); + BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 9)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/solidityExpressionCompiler.cpp b/test/solidityExpressionCompiler.cpp index 6ea66badb..486b46ebb 100644 --- a/test/solidityExpressionCompiler.cpp +++ b/test/solidityExpressionCompiler.cpp @@ -86,27 +86,34 @@ bytes compileFirstExpression(const string& _sourceCode, vector> _ vector> _localVariables = {}) { Parser parser; - ASTPointer contract; - BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared(CharStream(_sourceCode)))); + ASTPointer sourceUnit; + BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(CharStream(_sourceCode)))); NameAndTypeResolver resolver({}); - BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); - FirstExpressionExtractor extractor(*contract); - BOOST_REQUIRE(extractor.getExpression() != nullptr); - - CompilerContext context; - for (vector const& function: _functions) - context.addFunction(dynamic_cast(resolveDeclaration(function, resolver))); - for (vector const& variable: _localVariables) - context.addVariable(dynamic_cast(resolveDeclaration(variable, resolver))); - - ExpressionCompiler::compileExpression(context, *extractor.getExpression()); - - for (vector const& function: _functions) - context << context.getFunctionEntryLabel(dynamic_cast(resolveDeclaration(function, resolver))); - bytes instructions = context.getAssembledBytecode(); - // debug - // cout << eth::disassemble(instructions) << endl; - return instructions; + resolver.registerDeclarations(*sourceUnit); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); + FirstExpressionExtractor extractor(*contract); + BOOST_REQUIRE(extractor.getExpression() != nullptr); + + CompilerContext context; + for (vector const& function: _functions) + context.addFunction(dynamic_cast(resolveDeclaration(function, resolver))); + for (vector const& variable: _localVariables) + context.addVariable(dynamic_cast(resolveDeclaration(variable, resolver))); + + ExpressionCompiler::compileExpression(context, *extractor.getExpression()); + + for (vector const& function: _functions) + context << context.getFunctionEntryLabel(dynamic_cast(resolveDeclaration(function, resolver))); + bytes instructions = context.getAssembledBytecode(); + // debug + // cout << eth::disassemble(instructions) << endl; + return instructions; + } + BOOST_FAIL("No contract found in source."); + return bytes(); } } // end anonymous namespace diff --git a/test/solidityJSONInterfaceTest.cpp b/test/solidityJSONInterfaceTest.cpp index 407f05d03..487508bb9 100644 --- a/test/solidityJSONInterfaceTest.cpp +++ b/test/solidityJSONInterfaceTest.cpp @@ -50,7 +50,7 @@ public: msg += *extra; BOOST_FAIL(msg); } - std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation(ABI_INTERFACE); + std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation("", DocumentationType::ABI_INTERFACE); Json::Value generatedInterface; m_reader.parse(generatedInterfaceString, generatedInterface); Json::Value expectedInterface; diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 8804c519c..783972296 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -41,10 +41,12 @@ namespace void parseTextAndResolveNames(std::string const& _source) { Parser parser; - ASTPointer contract = parser.parse( - std::make_shared(CharStream(_source))); + ASTPointer sourceUnit = parser.parse(std::make_shared(CharStream(_source))); NameAndTypeResolver resolver({}); - resolver.resolveNamesAndTypes(*contract); + resolver.registerDeclarations(*sourceUnit); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + resolver.resolveNamesAndTypes(*contract); } } diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index 2ccedf7a2..f1795fe1c 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -55,9 +55,9 @@ public: } if (_userDocumentation) - generatedDocumentationString = m_compilerStack.getJsonDocumentation(NATSPEC_USER); + generatedDocumentationString = m_compilerStack.getJsonDocumentation("", DocumentationType::NATSPEC_USER); else - generatedDocumentationString = m_compilerStack.getJsonDocumentation(NATSPEC_DEV); + generatedDocumentationString = m_compilerStack.getJsonDocumentation("", DocumentationType::NATSPEC_DEV); Json::Value generatedDocumentation; m_reader.parse(generatedDocumentationString, generatedDocumentation); Json::Value expectedDocumentation; diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 6a97a5d99..a9e2d531f 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -21,13 +21,15 @@ */ #include - +#include #include #include #include #include #include +using namespace std; + namespace dev { namespace solidity @@ -40,7 +42,12 @@ namespace ASTPointer parseText(std::string const& _source) { Parser parser; - return parser.parse(std::make_shared(CharStream(_source))); + ASTPointer sourceUnit = parser.parse(std::make_shared(CharStream(_source))); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ASTPointer contract = dynamic_pointer_cast(node)) + return contract; + BOOST_FAIL("No contract found in source."); + return ASTPointer(); } } @@ -380,6 +387,50 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(import_directive) +{ + char const* text = "import \"abc\";\n" + "contract test {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(multiple_contracts) +{ + char const* text = "contract test {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "}\n" + "contract test2 {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(multiple_contracts_and_imports) +{ + char const* text = "import \"abc\";\n" + "contract test {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "}\n" + "import \"def\";\n" + "contract test2 {\n" + " function fun() {\n" + " uint64(2);\n" + " }\n" + "}\n" + "import \"ghi\";\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/stLogTestsFiller.json b/test/stLogTestsFiller.json new file mode 100644 index 000000000..34758ff83 --- /dev/null +++ b/test/stLogTestsFiller.json @@ -0,0 +1,1851 @@ +{ + "log0_emptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log0_nonEmptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG0 0 32) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log0_nonEmptyMem_logMemSize1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG0 0 1) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + + "log0_nonEmptyMem_logMemSize1_logMemStart31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG0 31 1) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log0_logMemStartTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 1) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log0_logMemsizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG0 1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log0_logMemsizeZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG0 1 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log1_emptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG1 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log1_nonEmptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG1 0 32 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log1_nonEmptyMem_logMemSize1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 0 1 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + + "log1_nonEmptyMem_logMemSize1_logMemStart31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 31 1 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log1_logMemStartTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 1 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log1_logMemsizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log1_logMemsizeZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 1 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log1_MaxTopic": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 0 32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log1_Caller": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG1 0 32 (CALLER)) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log2_emptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG2 0 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log2_nonEmptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG2 0 32 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log2_nonEmptyMem_logMemSize1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 0 1 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + + "log2_nonEmptyMem_logMemSize1_logMemStart31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 31 1 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log2_logMemStartTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 1 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log2_logMemsizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log2_logMemsizeZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 1 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log2_MaxTopic": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 0 32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log2_Caller": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG2 0 32 0 (CALLER) ) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log3_emptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG3 0 0 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log3_nonEmptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG3 0 32 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log3_nonEmptyMem_logMemSize1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 0 1 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + + "log3_nonEmptyMem_logMemSize1_logMemStart31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 31 1 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log3_logMemStartTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 1 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log3_logMemsizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log3_logMemsizeZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 1 0 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log3_MaxTopic": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 0 32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log3_Caller": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG3 0 32 0 0 (CALLER) ) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log3_PC": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG3 0 32 (PC) (PC) (PC) ) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log4_emptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG4 0 0 0 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log4_nonEmptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG4 0 32 0 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log4_nonEmptyMem_logMemSize1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 0 1 0 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + + "log4_nonEmptyMem_logMemSize1_logMemStart31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 31 1 0 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log4_logMemStartTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 1 0 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log4_logMemsizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log4_logMemsizeZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 1 0 0 0 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log4_MaxTopic": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 0 32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log4_Caller": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG3 0 32 0 0 0 (CALLER) ) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log4_PC": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALL 1000 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 23 0 0 0 0) }", + "storage": {} + }, + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG3 0 32 (PC) (PC) (PC) (PC) ) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + } +} diff --git a/test/stPreCompiledContractsFiller.json b/test/stPreCompiledContractsFiller.json index 9c65ad37b..62a3a1623 100644 --- a/test/stPreCompiledContractsFiller.json +++ b/test/stPreCompiledContractsFiller.json @@ -33,6 +33,41 @@ } }, + "CallEcrecover0_overlappingInputOutput": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "20000000", + "nonce" : 0, + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 64 32) [[ 0 ]] (MOD (MLOAD 64) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "365224", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "CallEcrecover0_completeReturnValue": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/test/stSystemOperationsTestFiller.json b/test/stSystemOperationsTestFiller.json index edd803641..9fc8db773 100644 --- a/test/stSystemOperationsTestFiller.json +++ b/test/stSystemOperationsTestFiller.json @@ -634,6 +634,86 @@ } }, + "CallRecursiveBombLog": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "20000000", + "nonce" : 0, + "code" : "{ (CALL 100000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 0 0 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG0 0 32) [[ 0 ]] (+ (SLOAD 0) 1) [[ 1 ]] (CALL (- (GAS) 224) (ADDRESS) 0 0 0 0 0) } ", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "1000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "CallRecursiveBombLog2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "20000000", + "nonce" : 0, + "code" : "{ (CALL 100000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 0 0 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 (GAS)) (LOG0 0 32) [[ 0 ]] (+ (SLOAD 0) 1) [[ 1 ]] (CALL (- (GAS) 224) (ADDRESS) 0 0 0 0 0) } ", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "1000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "CallRecursiveBomb1": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/test/state.cpp b/test/state.cpp index 5fc23f149..07c8373e2 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -125,6 +125,11 @@ BOOST_AUTO_TEST_CASE(stPreCompiledContracts) dev::test::executeTests("stPreCompiledContracts", "/StateTests", dev::test::doStateTests); } +BOOST_AUTO_TEST_CASE(stLogTests) +{ + dev::test::executeTests("stLogTests", "/StateTests", dev::test::doStateTests); +} + BOOST_AUTO_TEST_CASE(stSpecialTest) { dev::test::executeTests("stSpecialTest", "/StateTests", dev::test::doStateTests); diff --git a/test/vm.cpp b/test/vm.cpp index 354defe44..015492d8d 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -22,8 +22,10 @@ #include #include -#include +#include +#include #include "vm.h" + using namespace std; using namespace json_spirit; using namespace dev; @@ -121,41 +123,6 @@ void FakeExtVM::importEnv(mObject& _o) currentBlock.coinbaseAddress = Address(_o["currentCoinbase"].get_str()); } -mObject FakeExtVM::exportLog() -{ - mObject ret; - for (LogEntry const& l: sub.logs) - { - mObject o; - o["address"] = toString(l.address); - mArray topics; - for (auto const& t: l.topics) - topics.push_back(toString(t)); - o["topics"] = topics; - o["data"] = "0x" + toHex(l.data); - ret[toString(l.bloom())] = o; - } - return ret; -} - -void FakeExtVM::importLog(mObject& _o) -{ - for (auto const& l: _o) - { - mObject o = l.second.get_obj(); - // cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest) - assert(o.count("address") > 0); - assert(o.count("topics") > 0); - assert(o.count("data") > 0); - LogEntry log; - log.address = Address(o["address"].get_str()); - for (auto const& t: o["topics"].get_array()) - log.topics.push_back(h256(t.get_str())); - log.data = importData(o); - sub.logs.push_back(log); - } -} - mObject FakeExtVM::exportState() { mObject ret; @@ -320,14 +287,16 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) auto argc = boost::unit_test::framework::master_test_suite().argc; auto argv = boost::unit_test::framework::master_test_suite().argv; - auto useJit = false; - for (auto i = 0; i < argc && !useJit; ++i) - useJit |= std::string(argv[i]) == "--jit"; -#if ETH_EVMJIT - auto vmKind = useJit ? VMFactory::JIT : VMFactory::Interpreter; -#else - auto vmKind == VMFactory::Interpreter; -#endif + + for (auto i = 0; i < argc; ++i) + { + if (std::string(argv[i]) == "--jit") + { + VMFactory::setKind(VMKind::JIT); + break; + } + } + dev::test::FakeExtVM fev; fev.importEnv(o["env"].get_obj()); @@ -343,14 +312,13 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) fev.code = fev.thisTxCode; } - auto vm = VMFactory::create(vmKind, fev.gas); bytes output; - - auto startTime = std::chrono::high_resolution_clock::now(); u256 gas; bool vmExceptionOccured = false; + auto startTime = std::chrono::high_resolution_clock::now(); try { + auto vm = eth::VMFactory::create(fev.gas); output = vm->go(fev, fev.simpleTrace()).toBytes(); gas = vm->gas(); } @@ -410,7 +378,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) o["callcreates"] = fev.exportCallCreates(); o["out"] = "0x" + toHex(output); fev.push(o, "gas", gas); - o["logs"] = mValue(exportLog(fev.sub.logs)); + o["logs"] = exportLog(fev.sub.logs); } } else @@ -428,7 +396,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) dev::test::FakeExtVM test; test.importState(o["post"].get_obj()); test.importCallCreates(o["callcreates"].get_array()); - test.sub.logs = importLog(o["logs"].get_obj()); + test.sub.logs = importLog(o["logs"].get_array()); checkOutput(output, o); @@ -514,6 +482,26 @@ BOOST_AUTO_TEST_CASE(vmLogTest) dev::test::executeTests("vmLogTest", "/VMTests", dev::test::doVMTests); } +BOOST_AUTO_TEST_CASE(vmPerformanceTest) +{ + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + { + string arg = boost::unit_test::framework::master_test_suite().argv[i]; + if (arg == "--performance") + dev::test::executeTests("vmPerformanceTest", "/VMTests", dev::test::doVMTests); + } +} + +BOOST_AUTO_TEST_CASE(vmArithPerformanceTest) +{ + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + { + string arg = boost::unit_test::framework::master_test_suite().argv[i]; + if (arg == "--performance") + dev::test::executeTests("vmArithPerformanceTest", "/VMTests", dev::test::doVMTests); + } +} + BOOST_AUTO_TEST_CASE(vmRandom) { string testPath = getTestPath(); diff --git a/test/vm.h b/test/vm.h index fb0346d51..a52a02e31 100644 --- a/test/vm.h +++ b/test/vm.h @@ -72,8 +72,6 @@ public: void importExec(json_spirit::mObject& _o); json_spirit::mArray exportCallCreates(); void importCallCreates(json_spirit::mArray& _callcreates); - json_spirit::mObject exportLog(); - void importLog(json_spirit::mObject& _o); eth::OnOpFunc simpleTrace(); diff --git a/test/vmEnvironmentalInfoTestFiller.json b/test/vmEnvironmentalInfoTestFiller.json index 95e7936aa..abeed9945 100644 --- a/test/vmEnvironmentalInfoTestFiller.json +++ b/test/vmEnvironmentalInfoTestFiller.json @@ -338,6 +338,33 @@ } }, + "calldataloadSizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALLDATALOAD 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x123456789abcdef0000000000000000000000000000000000000000000000000024", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, "calldatasize0": { "env" : { @@ -451,6 +478,62 @@ } }, + "calldatacopy_DataIndexTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (CALLDATACOPY 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 0xff ) [[ 0 ]] @0}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + + "calldatacopy_DataIndexTooHigh2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (CALLDATACOPY 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 9 ) [[ 0 ]] @0}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + "calldatacopy1": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -535,6 +618,34 @@ } }, + "codecopy_DataIndexTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (CODECOPY 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 8 ) [[ 0 ]] @0}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + "codecopy0": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -686,6 +797,35 @@ } }, + "extcodecopy_DataIndexTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (EXTCODECOPY (ADDRESS) 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 8 ) [[ 0 ]] @0}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + + "extcodecopy0": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/test/vmLogTestFiller.json b/test/vmLogTestFiller.json index f4fb48b52..4f3d26f52 100644 --- a/test/vmLogTestFiller.json +++ b/test/vmLogTestFiller.json @@ -55,6 +55,34 @@ } }, + "log_2logs": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG0 0 32) (LOG0 2 16) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "log0_nonEmptyMem_logMemSize1": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/windows/LibEthereum.vcxproj.filters b/windows/LibEthereum.vcxproj.filters index 85f1fdedc..76201d823 100644 --- a/windows/LibEthereum.vcxproj.filters +++ b/windows/LibEthereum.vcxproj.filters @@ -196,14 +196,8 @@ libevmcore - - libevm - - - libethereum - @@ -443,9 +437,6 @@ libevm - - libethereum -