diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 04fc7757c..a240f64e1 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1261,9 +1261,12 @@ void Main::on_blocks_currentItemChanged() s << "
Gas used/limit: " << info.gasUsed << "/" << info.gasLimit << ""; s << "
Coinbase: " << pretty(info.coinbaseAddress).toHtmlEscaped().toStdString() << " " << info.coinbaseAddress; s << "
Nonce: " << info.nonce << ""; - s << "
Hash w/o nonce: " << info.headerHashWithoutNonce() << ""; + s << "
Hash w/o nonce: " << info.headerHash(WithoutNonce) << ""; s << "
Difficulty: " << info.difficulty << ""; - s << "
Proof-of-Work: " << ProofOfWork::eval(info.headerHashWithoutNonce(), info.nonce) << " <= " << (h256)u256((bigint(1) << 256) / info.difficulty) << ""; + if (info.number) + s << "
Proof-of-Work: " << ProofOfWork::eval(info.headerHash(WithoutNonce), info.nonce) << " <= " << (h256)u256((bigint(1) << 256) / info.difficulty) << ""; + else + s << "
Proof-of-Work: Phil has nothing to prove"; s << "
Parent: " << info.parentHash << ""; // s << "
Bloom: " << details.bloom << ""; s << "
Log Bloom: " << info.logBloom << ""; @@ -1280,7 +1283,7 @@ void Main::on_blocks_currentItemChanged() if (info.parentHash) s << "
Pre: " << BlockInfo(ethereum()->blockChain().block(info.parentHash)).stateRoot << ""; else - s << "
Pre: Nothing is before the Gensesis"; + s << "
Pre: Nothing is before Phil"; for (auto const& i: block[1]) s << "
" << sha3(i.data()).abridged();// << ": " << i[1].toHash() << " [" << i[2].toInt() << " used]"; s << "
Post: " << info.stateRoot << ""; @@ -1395,10 +1398,10 @@ void Main::populateDebugger(dev::bytesConstRef _r) bytesConstRef lastData; h256 lastHash; h256 lastDataHash; - auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) + auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, dev::eth::VM* voidVM, dev::eth::ExtVMFace const* voidExt) { - dev::eth::VM& vm = *(dev::eth::VM*)voidVM; - dev::eth::ExtVM const& ext = *(dev::eth::ExtVM const*)voidExt; + dev::eth::VM& vm = *voidVM; + dev::eth::ExtVM const& ext = *static_cast(voidExt); if (ext.code != lastExtCode) { lastExtCode = ext.code; 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/eth/main.cpp b/eth/main.cpp index 9485a11bf..0113fa1a5 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -630,10 +630,10 @@ int main(int argc, char** argv) OnOpFunc oof; if (format == "pretty") - oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, void* vvm, void const* vextVM) + oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) { - dev::eth::VM* vm = (VM*)vvm; - dev::eth::ExtVM const* ext = (ExtVM const*)vextVM; + dev::eth::VM* vm = vvm; + dev::eth::ExtVM const* ext = static_cast(vextVM); f << endl << " STACK" << endl; for (auto i: vm->stack()) f << (h256)i << endl; @@ -644,17 +644,17 @@ int main(int argc, char** argv) f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << vm->gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32"; }; else if (format == "standard") - oof = [&](uint64_t, Instruction instr, bigint, bigint, void* vvm, void const* vextVM) + oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) { - dev::eth::VM* vm = (VM*)vvm; - dev::eth::ExtVM const* ext = (ExtVM const*)vextVM; + dev::eth::VM* vm = vvm; + dev::eth::ExtVM const* ext = static_cast(vextVM); f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl; }; else if (format == "standard+") - oof = [&](uint64_t, Instruction instr, bigint, bigint, void* vvm, void const* vextVM) + oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) { dev::eth::VM* vm = (VM*)vvm; - dev::eth::ExtVM const* ext = (ExtVM const*)vextVM; + dev::eth::ExtVM const* ext = static_cast(vextVM); if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE) for (auto const& i: ext->state().storage(ext->myAddress)) f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 015f8dad6..fbf056867 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -49,19 +49,19 @@ BlockInfo BlockInfo::fromHeader(bytesConstRef _block) return ret; } -h256 BlockInfo::headerHashWithoutNonce() const +h256 BlockInfo::headerHash(IncludeNonce _n) const { RLPStream s; - streamRLP(s, false); + streamRLP(s, _n); return sha3(s.out()); } -void BlockInfo::streamRLP(RLPStream& _s, bool _nonce) const +void BlockInfo::streamRLP(RLPStream& _s, IncludeNonce _n) const { - _s.appendList(_nonce ? 14 : 13) + _s.appendList(_n == WithNonce ? 14 : 13) << parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot << receiptsRoot << logBloom << difficulty << number << gasLimit << gasUsed << timestamp << extraData; - if (_nonce) + if (_n == WithNonce) _s << nonce; } @@ -100,8 +100,8 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce) } // check it hashes according to proof of work or that it's the genesis block. - if (_checkNonce && parentHash && !ProofOfWork::verify(headerHashWithoutNonce(), nonce, difficulty)) - BOOST_THROW_EXCEPTION(InvalidBlockNonce(headerHashWithoutNonce(), nonce, difficulty)); + if (_checkNonce && parentHash && !ProofOfWork::verify(headerHash(WithoutNonce), nonce, difficulty)) + BOOST_THROW_EXCEPTION(InvalidBlockNonce(headerHash(WithoutNonce), nonce, difficulty)); if (gasUsed > gasLimit) BOOST_THROW_EXCEPTION(TooMuchGasUsed()); diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index aa7456f72..fc52880fa 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -32,6 +32,12 @@ namespace eth extern u256 c_genesisDifficulty; +enum IncludeNonce +{ + WithoutNonce = 0, + WithNonce = 1 +}; + /** @brief Encapsulation of a block header. * Class to contain all of a block header's data. It is able to parse a block header and populate * from some given RLP block serialisation with the static fromHeader(), through the method @@ -48,7 +54,7 @@ extern u256 c_genesisDifficulty; * * The difficulty and gas-limit derivations may be calculated with the calculateDifficulty() * and calculateGasLimit() and the object serialised to RLP with streamRLP. To determine the - * header hash without the nonce (for mining), the method headerHashWithoutNonce() is provided. + * header hash without the nonce (for mining), the method headerHash(WithoutNonce) is provided. * * The default constructor creates an empty object, which can be tested against with the boolean * conversion operator. @@ -113,8 +119,8 @@ public: u256 calculateGasLimit(BlockInfo const& _parent) const; /// No-nonce sha3 of the header only. - h256 headerHashWithoutNonce() const; - void streamRLP(RLPStream& _s, bool _nonce) const; + h256 headerHash(IncludeNonce _n) const; + void streamRLP(RLPStream& _s, IncludeNonce _n) const; }; inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi) diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index 5f9332ad8..36726dae4 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -33,7 +33,7 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 48; +const unsigned c_protocolVersion = 49; const unsigned c_databaseVersion = 5; static const vector> g_units = diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 531005fb2..6ed0a2ca6 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -145,7 +145,7 @@ void BlockChain::open(std::string _path, bool _killExisting) if (!details(m_genesisHash)) { // Insert details of genesis block. - m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {}, h256()); + m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {}); auto r = m_details[m_genesisHash].rlp(); m_extrasDB->Put(m_writeOptions, ldb::Slice((char const*)&m_genesisHash, 32), (ldb::Slice)dev::ref(r)); } @@ -303,7 +303,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) // Get total difficulty increase and update state, checking it. State s(bi.coinbaseAddress, _db); auto tdIncrease = s.enactOn(&_block, bi, *this); - auto b = s.oldBloom(); BlockLogBlooms blb; BlockReceipts br; for (unsigned i = 0; i < s.pending().size(); ++i) @@ -320,7 +319,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) // All ok - insert into DB { WriteGuard l(x_details); - m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}, b); + m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}); m_details[bi.parentHash].children.push_back(newHash); } { diff --git a/libethereum/BlockDetails.cpp b/libethereum/BlockDetails.cpp index 5a61eb037..58f37b7aa 100644 --- a/libethereum/BlockDetails.cpp +++ b/libethereum/BlockDetails.cpp @@ -32,10 +32,9 @@ BlockDetails::BlockDetails(RLP const& _r) totalDifficulty = _r[1].toInt(); parent = _r[2].toHash(); children = _r[3].toVector(); - bloom = _r[4].toHash(); } bytes BlockDetails::rlp() const { - return rlpList(number, totalDifficulty, parent, children, bloom); + return rlpList(number, totalDifficulty, parent, children); } diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h index 0c3af5b33..2fd0d3048 100644 --- a/libethereum/BlockDetails.h +++ b/libethereum/BlockDetails.h @@ -40,7 +40,7 @@ namespace eth struct BlockDetails { BlockDetails(): number(0), totalDifficulty(0) {} - BlockDetails(unsigned _n, u256 _tD, h256 _p, h256s _c, h256 _bloom): number(_n), totalDifficulty(_tD), parent(_p), children(_c), bloom(_bloom) {} + BlockDetails(unsigned _n, u256 _tD, h256 _p, h256s _c): number(_n), totalDifficulty(_tD), parent(_p), children(_c) {} BlockDetails(RLP const& _r); bytes rlp() const; @@ -51,7 +51,6 @@ struct BlockDetails u256 totalDifficulty; h256 parent; h256s children; - h256 bloom; }; struct BlockLogBlooms diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 6a123875c..007896d75 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; @@ -91,14 +85,6 @@ bool Executive::setup(bytesConstRef _rlp) clog(StateDetail) << "Paying" << formatBalance(cost) << "from sender (includes" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")"; m_s.subBalance(m_sender, cost); - if (m_ms) - { - m_ms->from = m_sender; - m_ms->to = m_t.receiveAddress(); - m_ms->value = m_t.value(); - m_ms->input = m_t.data(); - } - if (m_t.isCreation()) return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasCost, &m_t.data(), m_sender); else @@ -110,11 +96,24 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu // cnote << "Transferring" << formatBalance(_value) << "to receiver."; m_s.addBalance(_receiveAddress, _value); - if (m_s.addressHasCode(_receiveAddress)) + auto it = !(_receiveAddress & ~h160(0xffffffff)) ? State::precompiled().find((unsigned)(u160)_receiveAddress) : State::precompiled().end(); + if (it != State::precompiled().end()) { - m_vm = new VM(_gas); + bigint g = it->second.gas(_data); + if (_gas < g) + { + m_endGas = 0; + return false; + } + m_endGas = (u256)(_gas - g); + it->second.exec(_data, bytesRef()); + return true; + } + else if (m_s.addressHasCode(_receiveAddress)) + { + m_vm = VMFactory::create(_gas); bytes const& c = m_s.code(_receiveAddress); - m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms); + m_ext.reset(new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c)); } else m_endGas = _gas; @@ -131,17 +130,17 @@ 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 = new VM(_gas); - 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)); return _init.empty(); } OnOpFunc Executive::simpleTrace() { - return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, void* voidVM, void const* voidExt) + return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt) { - ExtVM const& ext = *(ExtVM const*)voidExt; - VM& vm = *(VM*)voidVM; + ExtVM const& ext = *static_cast(voidExt); + VM& vm = *voidVM; ostringstream o; o << endl << " STACK" << endl; @@ -162,16 +161,16 @@ bool Executive::go(OnOpFunc const& _onOp) { boost::timer t; auto sgas = m_vm->gas(); - bool revert = false; try { m_out = m_vm->go(*m_ext, _onOp); - if (m_ext) - { - m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds); - m_logs = m_ext->sub.logs; - } m_endGas = m_vm->gas(); + m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds); + m_logs = m_ext->sub.logs; + if (m_out.size() * c_createDataGas <= m_endGas) + m_endGas -= m_out.size() * c_createDataGas; + else + m_out.reset(); } catch (StepsDone const&) { @@ -181,7 +180,16 @@ bool Executive::go(OnOpFunc const& _onOp) { clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e); m_endGas = 0;//m_vm->gas(); - revert = true; + + // Write state out only in the case of a non-excepted transaction. + m_ext->revert(); + + // Explicitly delete a newly created address - this will still be in the reverted state. +/* if (m_newAddress) + { + m_s.m_cache.erase(m_newAddress); + m_newAddress = Address(); + }*/ } catch (Exception const& _e) { @@ -194,18 +202,6 @@ bool Executive::go(OnOpFunc const& _onOp) cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what(); } cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas); - - // Write state out only in the case of a non-excepted transaction. - if (revert) - { - m_ext->revert(); - // Explicitly delete a newly created address - this will still be in the reverted state. - if (m_newAddress) - { - m_s.m_cache.erase(m_newAddress); - m_newAddress = Address(); - } - } } return true; } @@ -217,9 +213,11 @@ u256 Executive::gas() const void Executive::finalize(OnOpFunc const&) { - if (m_t.isCreation() && m_newAddress && m_out.size()) - // non-reverted creation - put code in place. + if (m_t.isCreation() && !m_ext->sub.suicides.count(m_newAddress)) + { + // creation - put code in place. m_s.m_cache[m_newAddress].setCode(m_out); + } // cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")"; m_s.addBalance(m_sender, m_endGas * m_t.gasPrice()); @@ -228,9 +226,6 @@ void Executive::finalize(OnOpFunc const&) // cnote << "Transferring" << formatBalance(gasSpent) << "to miner."; m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned); - if (m_ms) - m_ms->output = m_out.toBytes(); - // Suicides... if (m_ext) for (auto a: m_ext->sub.suicides) diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 930c2859b..9e47bbfbf 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -25,26 +25,27 @@ #include #include #include -#include +#include #include "Transaction.h" -#include "Manifest.h" +#include "ExtVM.h" namespace dev { namespace eth { -class VM; -class ExtVM; class State; +struct Manifest; struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; }; class Executive { public: - Executive(State& _s, Manifest* o_ms = nullptr): m_s(_s), m_ms(o_ms) {} - ~Executive(); + Executive(State& _s): m_s(_s) {} + ~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); @@ -63,15 +64,14 @@ public: h160 newAddress() const { return m_newAddress; } LogEntries const& logs() const { return m_logs; } - VM const& vm() const { return *m_vm; } + VMFace const& vm() const { return *m_vm; } State const& state() const { return m_s; } ExtVM const& ext() const { return *m_ext; } private: State& m_s; - ExtVM* m_ext = nullptr; // TODO: make safe. - VM* m_vm = nullptr; - Manifest* m_ms = nullptr; + std::unique_ptr m_ext; + std::unique_ptr m_vm; bytesConstRef m_out; Address m_newAddress; diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index 24ce618ed..ad1045d3f 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -39,8 +39,8 @@ class ExtVM: public ExtVMFace { public: /// Full constructor. - ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, Manifest* o_ms, unsigned _depth = 0): - ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _s.m_previousBlock, _s.m_currentBlock, _depth), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms) + ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, unsigned _depth = 0): + ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _s.m_previousBlock, _s.m_currentBlock, _depth), m_s(_s), m_origCache(_s.m_cache) { m_s.ensureCached(_myAddress, true, true); } @@ -49,7 +49,7 @@ public: virtual u256 store(u256 _n) override final { return m_s.storage(myAddress, _n); } /// Write a value in storage. - virtual void setStore(u256 _n, u256 _v) override final { m_s.setStorage(myAddress, _n, _v); if (m_ms) m_ms->altered.push_back(_n); } + virtual void setStore(u256 _n, u256 _v) override final { m_s.setStorage(myAddress, _n, _v); } /// Read address's code. virtual bytes const& codeAt(Address _a) override final { return m_s.code(_a); } @@ -59,23 +59,13 @@ public: { // Increment associated nonce for sender. m_s.noteSending(myAddress); - if (m_ms) - m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); - if (m_ms && !m_ms->internal.back().from) - m_ms->internal.pop_back(); - return ret; + return m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, _onOp, depth + 1); } /// Create a new message call. Leave _myAddressOverride as the default to use the present address as caller. virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final { - if (m_ms) - m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); - if (m_ms && !m_ms->internal.back().from) - m_ms->internal.pop_back(); - return ret; + return m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, _onOp, depth + 1); } /// Read address's balance. @@ -96,14 +86,13 @@ public: /// Revert any changes made (by any of the other calls). /// @TODO check call site for the parent manifest being discarded. - virtual void revert() override final { if (m_ms) *m_ms = Manifest(); m_s.m_cache = m_origCache; } + virtual void revert() override final { m_s.m_cache = m_origCache; } State& state() const { return m_s; } private: State& m_s; ///< A reference to the base state. std::map m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution. - Manifest* m_ms; }; } diff --git a/libethereum/Manifest.cpp b/libethereum/Manifest.cpp index 9e6d2f651..e69de29bb 100644 --- a/libethereum/Manifest.cpp +++ b/libethereum/Manifest.cpp @@ -1,46 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** @file Manifest.cpp - * @author Gav Wood - * @date 2014 - */ - -#include "Manifest.h" -using namespace std; -using namespace dev; -using namespace dev::eth; - -Manifest::Manifest(bytesConstRef _r) -{ - RLP r(_r); - from = r[0].toHash
(); - to = r[1].toHash
(); - value = r[2].toInt(); - altered = r[3].toVector(); - input = r[4].toBytes(); - output = r[5].toBytes(); - for (auto const& i: r[6]) - internal.emplace_back(i.data()); -} - -void Manifest::streamRLP(RLPStream& _s) const -{ - _s.appendList(7) << from << to << value << altered << input << output; - _s.appendList(internal.size()); - for (auto const& i: internal) - i.streamRLP(_s); -} diff --git a/libethereum/Manifest.h b/libethereum/Manifest.h index e0e512c92..e69de29bb 100644 --- a/libethereum/Manifest.h +++ b/libethereum/Manifest.h @@ -1,73 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** @file Manifest.h - * @author Gav Wood - * @date 2014 - */ - -#pragma once - -#include -#include -#include -#include - -namespace dev -{ -namespace eth -{ - -struct Manifest; -using Manifests = std::vector; - -/** - * @brief A record of the state-interaction of a transaction/call/create. - */ -struct Manifest -{ - Manifest() {} - Manifest(bytesConstRef _r); - void streamRLP(RLPStream& _s) const; - - h256 bloom() const { h256 ret = from.bloom() | to.bloom(); for (auto const& i: internal) ret |= i.bloom(); for (auto const& i: altered) ret |= h256(i).bloom(); return ret; } - - std::string toString(unsigned _indent = 0) const - { - std::ostringstream oss; - oss << std::string(_indent * 3, ' ') << from << " -> " << to << " [" << value << "]: {"; - if (internal.size()) - { - oss << std::endl; - for (auto const& m: internal) - oss << m.toString(_indent + 1) << std::endl; - oss << std::string(_indent * 3, ' '); - } - oss << "} I:" << toHex(input) << "; O:" << toHex(output); - return oss.str(); - } - - Address from; - Address to; - u256 value; - u256s altered; - bytes input; - bytes output; - Manifests internal; -}; - -} -} diff --git a/libethereum/MessageFilter.cpp b/libethereum/MessageFilter.cpp index 0519fe28b..f44587620 100644 --- a/libethereum/MessageFilter.cpp +++ b/libethereum/MessageFilter.cpp @@ -27,131 +27,6 @@ using namespace std; using namespace dev; using namespace dev::eth; -void MessageFilter::streamRLP(RLPStream& _s) const -{ - _s.appendList(8) << m_from << m_to << m_stateAltered << m_altered << m_earliest << m_latest << m_max << m_skip; -} - -h256 MessageFilter::sha3() const -{ - RLPStream s; - streamRLP(s); - return dev::sha3(s.out()); -} - -bool MessageFilter::matches(h256 _bloom) const -{ - auto have = [=](Address const& a) { return _bloom.contains(a.bloom()); }; - if (m_from.size()) - { - for (auto i: m_from) - if (have(i)) - goto OK1; - return false; - } - OK1: - if (m_to.size()) - { - for (auto i: m_to) - if (have(i)) - goto OK2; - return false; - } - OK2: - if (m_stateAltered.size() || m_altered.size()) - { - for (auto i: m_altered) - if (have(i)) - goto OK3; - for (auto i: m_stateAltered) - if (have(i.first) && _bloom.contains(h256(i.second).bloom())) - goto OK3; - return false; - } - OK3: - return true; -} - -bool MessageFilter::matches(State const& _s, unsigned _i) const -{ - h256 b = _s.changesFromPending(_i).bloom(); - if (!matches(b)) - return false; - - Transaction t = _s.pending()[_i]; - if (!m_to.empty() && !m_to.count(t.receiveAddress())) - return false; - if (!m_from.empty() && !m_from.count(t.sender())) - return false; - if (m_stateAltered.empty() && m_altered.empty()) - return true; - StateDiff d = _s.pendingDiff(_i); - if (!m_altered.empty()) - { - for (auto const& s: m_altered) - if (d.accounts.count(s)) - return true; - return false; - } - if (!m_stateAltered.empty()) - { - for (auto const& s: m_stateAltered) - if (d.accounts.count(s.first) && d.accounts.at(s.first).storage.count(s.second)) - return true; - return false; - } - return true; -} - -PastMessages MessageFilter::matches(Manifest const& _m, unsigned _i) const -{ - PastMessages ret; - matches(_m, vector(1, _i), _m.from, PastMessages(), ret); - return ret; -} - -bool MessageFilter::matches(Manifest const& _m, vector _p, Address _o, PastMessages _limbo, PastMessages& o_ret) const -{ - bool ret; - - if ((m_from.empty() || m_from.count(_m.from)) && (m_to.empty() || m_to.count(_m.to))) - _limbo.push_back(PastMessage(_m, _p, _o)); - - // Handle limbos, by checking against all addresses in alteration. - bool alters = m_altered.empty() && m_stateAltered.empty(); - alters = alters || m_altered.count(_m.from) || m_altered.count(_m.to); - - if (!alters) - for (auto const& i: _m.altered) - if (m_altered.count(_m.to) || m_stateAltered.count(make_pair(_m.to, i))) - { - alters = true; - break; - } - // If we do alter stuff, - if (alters) - { - o_ret += _limbo; - _limbo.clear(); - ret = true; - } - - _p.push_back(0); - for (auto const& m: _m.internal) - { - if (matches(m, _p, _o, _limbo, o_ret)) - { - _limbo.clear(); - ret = true; - } - _p.back()++; - } - - return ret; -} - - - void LogFilter::streamRLP(RLPStream& _s) const { _s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip; diff --git a/libethereum/MessageFilter.h b/libethereum/MessageFilter.h index 482c68ef6..e2c26d214 100644 --- a/libethereum/MessageFilter.h +++ b/libethereum/MessageFilter.h @@ -24,7 +24,6 @@ #include #include #include -#include "PastMessage.h" #include "TransactionReceipt.h" namespace dev @@ -32,47 +31,8 @@ namespace dev namespace eth { -struct Manifest; class State; -class MessageFilter -{ -public: - MessageFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {} - - void streamRLP(RLPStream& _s) const; - h256 sha3() const; - - int earliest() const { return m_earliest; } - int latest() const { return m_latest; } - unsigned max() const { return m_max; } - unsigned skip() const { return m_skip; } - bool matches(h256 _bloom) const; - bool matches(State const& _s, unsigned _i) const; - PastMessages matches(Manifest const& _m, unsigned _i) const; - - MessageFilter from(Address _a) { m_from.insert(_a); return *this; } - MessageFilter to(Address _a) { m_to.insert(_a); return *this; } - MessageFilter altered(Address _a, u256 _l) { m_stateAltered.insert(std::make_pair(_a, _l)); return *this; } - MessageFilter altered(Address _a) { m_altered.insert(_a); return *this; } - MessageFilter withMax(unsigned _m) { m_max = _m; return *this; } - MessageFilter withSkip(unsigned _m) { m_skip = _m; return *this; } - MessageFilter withEarliest(int _e) { m_earliest = _e; return *this; } - MessageFilter withLatest(int _e) { m_latest = _e; return *this; } - -private: - bool matches(Manifest const& _m, std::vector _p, Address _o, PastMessages _limbo, PastMessages& o_ret) const; - - std::set
m_from; - std::set
m_to; - std::set> m_stateAltered; - std::set
m_altered; - int m_earliest = 0; - int m_latest = -1; - unsigned m_max; - unsigned m_skip; -}; - class LogFilter { public: diff --git a/libethereum/PastMessage.cpp b/libethereum/PastMessage.cpp index d81ae672c..e69de29bb 100644 --- a/libethereum/PastMessage.cpp +++ b/libethereum/PastMessage.cpp @@ -1,28 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** @file PastMessage.cpp - * @author Gav Wood - * @date 2014 - */ - -#include "PastMessage.h" -using namespace std; -using namespace dev; -using namespace dev::eth; - -#pragma GCC diagnostic ignored "-Wunused-variable" -namespace { char dummy; }; diff --git a/libethereum/PastMessage.h b/libethereum/PastMessage.h index 6446eff5a..e69de29bb 100644 --- a/libethereum/PastMessage.h +++ b/libethereum/PastMessage.h @@ -1,56 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** @file PastMessage.h - * @author Gav Wood - * @date 2014 - */ - -#pragma once - -#include -#include -#include "Manifest.h" - -namespace dev -{ -namespace eth -{ - -struct PastMessage -{ - PastMessage(Manifest const& _m, std::vector _path, Address _o): to(_m.to), from(_m.from), value(_m.value), input(_m.input), output(_m.output), path(_path), origin(_o) {} - - PastMessage& polish(h256 _b, u256 _ts, unsigned _n, Address _coinbase) { block = _b; timestamp = _ts; number = _n; coinbase = _coinbase; return *this; } - - Address to; ///< The receiving address of the transaction. Address() in the case of a creation. - Address from; ///< The receiving address of the transaction. Address() in the case of a creation. - u256 value; ///< The value associated with the call. - bytes input; ///< The data associated with the message, or the initialiser if it's a creation transaction. - bytes output; ///< The data returned by the message, or the body code if it's a creation transaction. - - std::vector path; ///< Call path into the block transaction. size() is always > 0. First item is the transaction index in the block. - Address origin; ///< Originating sender of the transaction. - Address coinbase; ///< Block coinbase. - h256 block; ///< Block hash. - u256 timestamp; ///< Block timestamp. - unsigned number; ///< Block number. -}; - -typedef std::vector PastMessages; - -} -} diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 9a0426e18..1d6ad3480 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; @@ -89,9 +90,9 @@ void ripemd160Code(bytesConstRef _in, bytesRef _out) const std::map State::c_precompiled = { - { 1, { 500, ecrecoverCode }}, - { 2, { 100, sha256Code }}, - { 3, { 100, ripemd160Code }} + { 1, { [](bytesConstRef) -> bigint { return (bigint)500; }, ecrecoverCode }}, + { 2, { [](bytesConstRef i) -> bigint { return (bigint)50 + (i.size() + 31) / 32 * 50; }, sha256Code }}, + { 3, { [](bytesConstRef i) -> bigint { return (bigint)50 + (i.size() + 31) / 32 * 50; }, ripemd160Code }} }; OverlayDB State::openDB(std::string _path, bool _killExisting) @@ -764,7 +765,7 @@ bool State::amIJustParanoid(BlockChain const& _bc) // Compile block: RLPStream block; block.appendList(3); - m_currentBlock.streamRLP(block, true); + m_currentBlock.streamRLP(block, WithNonce); block.appendRaw(m_currentTxs); block.appendRaw(m_currentUncles); @@ -791,14 +792,6 @@ bool State::amIJustParanoid(BlockChain const& _bc) return false; } -h256 State::oldBloom() const -{ - h256 ret = m_currentBlock.coinbaseAddress.bloom(); - for (auto const& i: m_receipts) - ret |= i.changes().bloom(); - return ret; -} - LogBloom State::logBloom() const { LogBloom ret; @@ -839,7 +832,7 @@ void State::commitToMine(BlockChain const& _bc) if (!knownUncles.count(u)) // ignore any uncles/mainline blocks that we know about. { BlockInfo ubi(_bc.block(u)); - ubi.streamRLP(unclesData, true); + ubi.streamRLP(unclesData, WithNonce); ++unclesCount; uncleAddresses.push_back(ubi.coinbaseAddress); } @@ -903,13 +896,13 @@ MineInfo State::mine(unsigned _msTimeout, bool _turbo) m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); // TODO: Miner class that keeps dagger between mine calls (or just non-polling mining). - auto ret = m_pow.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHashWithoutNonce(), m_currentBlock.difficulty, _msTimeout, true, _turbo); + auto ret = m_pow.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHash(WithoutNonce), m_currentBlock.difficulty, _msTimeout, true, _turbo); if (!ret.completed) m_currentBytes.clear(); else { - cnote << "Completed" << m_currentBlock.headerHashWithoutNonce().abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHashWithoutNonce(), m_currentBlock.nonce, m_currentBlock.difficulty); + cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHash(WithoutNonce), m_currentBlock.nonce, m_currentBlock.difficulty); } return ret; @@ -923,7 +916,7 @@ void State::completeMine() // Compile block: RLPStream ret; ret.appendList(3); - m_currentBlock.streamRLP(ret, true); + m_currentBlock.streamRLP(ret, WithNonce); ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentUncles); ret.swapOut(m_currentBytes); @@ -1101,7 +1094,7 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const return false; } } - catch (InvalidTrie) + catch (InvalidTrie const&) { cwarn << "BAD TRIE" << (e ? "[enforced" : "[unenforced") << "refs]"; cnote << m_db.keys(); @@ -1126,9 +1119,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) auto h = rootHash(); #endif - Manifest ms; - - Executive e(*this, &ms); + Executive e(*this); e.setup(_rlp); u256 startGasUsed = gasUsed(); @@ -1178,12 +1169,12 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) // Add to the user-originated transactions that we've executed. m_transactions.push_back(e.t()); - m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs(), ms)); + m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs())); m_transactionSet.insert(e.t().sha3()); return e.gasUsed(); } -bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, SubState* o_sub, OnOpFunc const& _onOp, unsigned _level) { if (!_originAddress) _originAddress = _senderAddress; @@ -1191,132 +1182,108 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA // cnote << "Transferring" << formatBalance(_value) << "to receiver."; addBalance(_receiveAddress, _value); - if (o_ms) - { - o_ms->from = _senderAddress; - o_ms->to = _receiveAddress; - o_ms->value = _value; - o_ms->input = _data.toBytes(); - } - auto it = !(_codeAddress & ~h160(0xffffffff)) ? c_precompiled.find((unsigned)(u160)_codeAddress) : c_precompiled.end(); if (it != c_precompiled.end()) { - if (*_gas < it->second.gas) + bigint g = it->second.gas(_data); + if (*_gas < g) { *_gas = 0; return false; } - *_gas -= it->second.gas; + *_gas -= (u256)g; it->second.exec(_data, _out); } else if (addressHasCode(_codeAddress)) { - VM vm(*_gas); - ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), o_ms, _level); - bool revert = false; - + auto vm = VMFactory::create(*_gas); + ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), _level); 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(); + // Write state out only in the case of a non-excepted transaction. + return true; } catch (VMException const& _e) { clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e); - revert = true; + evm.revert(); *_gas = 0; + return false; } catch (Exception const& _e) { cwarn << "Unexpected exception in VM: " << diagnostic_information(_e) << ". This is exceptionally bad."; // TODO: use fallback known-safe VM. + // AUDIT: THIS SHOULD NEVER HAPPEN! PROVE IT! + throw; } catch (std::exception const& _e) { cwarn << "Unexpected exception in VM: " << _e.what() << ". This is exceptionally bad."; // TODO: use fallback known-safe VM. + // AUDIT: THIS SHOULD NEVER HAPPEN! PROVE IT! + throw; } - - // Write state out only in the case of a non-excepted transaction. - if (revert) - evm.revert(); - - return !revert; } return true; } -h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, OnOpFunc const& _onOp, unsigned _level) { if (!_origin) _origin = _sender; - if (o_ms) - { - o_ms->from = _sender; - o_ms->to = Address(); - o_ms->value = _endowment; - o_ms->input = _code.toBytes(); - } - Address newAddress = right160(sha3(rlpList(_sender, transactionsFrom(_sender) - 1))); // Set up new account... m_cache[newAddress] = Account(balance(newAddress) + _endowment, Account::ContractConception); // Execute init code. - VM vm(*_gas); - ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms, _level); - bool revert = false; + auto vm = VMFactory::create(*_gas); + ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, _level); bytesConstRef out; try { - out = vm.go(evm, _onOp); - if (o_ms) - o_ms->output = out.toBytes(); + out = vm->go(evm, _onOp); if (o_sub) *o_sub += evm.sub; - *_gas = vm.gas(); + *_gas = vm->gas(); + + if (out.size() * c_createDataGas <= *_gas) + *_gas -= out.size() * c_createDataGas; + else + out.reset(); + + // Set code. + if (!evm.sub.suicides.count(newAddress)) + m_cache[newAddress].setCode(out); } catch (VMException const& _e) { clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e); - revert = true; + evm.revert(); *_gas = 0; } catch (Exception const& _e) { // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does. cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e); + throw; } catch (std::exception const& _e) { // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does. cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what(); + throw; } - // TODO: CHECK: AUDIT: IS THIS CORRECT?! (esp. given account created prior to revertion init.) - - // Write state out only in the case of a non-out-of-gas transaction. - if (revert) - { - evm.revert(); - m_cache.erase(newAddress); - newAddress = Address(); - } - else - // Set code. - if (addressInUse(newAddress)) - m_cache[newAddress].setCode(out); - return newAddress; } diff --git a/libethereum/State.h b/libethereum/State.h index 9c41843ff..10d73f671 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -36,7 +36,6 @@ #include "Account.h" #include "Transaction.h" #include "TransactionReceipt.h" -#include "Executive.h" #include "AccountDiff.h" namespace dev @@ -55,7 +54,7 @@ struct StateDetail: public LogChannel { static const char* name() { return "/S/" struct PrecompiledAddress { - unsigned gas; + std::function gas; std::function exec; }; @@ -211,15 +210,6 @@ public: /// Get the list of pending transactions. Transactions const& pending() const { return m_transactions; } - /// Get the list of pending transactions. TODO: PoC-7: KILL - Manifest changesFromPending(unsigned _i) const { return m_receipts[_i].changes(); } - - /// Get the bloom filter of all changes happened in the block. TODO: PoC-7: KILL - h256 oldBloom() const; - - /// Get the bloom filter of a particular transaction that happened in the block. TODO: PoC-7: KILL - h256 oldBloom(unsigned _i) const { return m_receipts[_i].changes().bloom(); } - /// Get the transaction receipt for the transaction of the given index. TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; } @@ -259,6 +249,9 @@ public: /// the block since all state changes are ultimately reversed. void cleanup(bool _fullCommit); + /// Info on precompiled contract accounts baked into the protocol. + static std::map const& precompiled() { return c_precompiled; } + private: /// Undo the changes to the state for committing to mine. void uncommitToMine(); @@ -283,12 +276,12 @@ private: // We assume all instrinsic fees are paid up before this point. /// Execute a contract-creation transaction. - h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), SubState* o_sub = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Execute a call. /// @a _gas points to the amount of gas to use for the call, and will lower it accordingly. /// @returns false if the call ran out of gas before completion. true otherwise. - bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), SubState* o_sub = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index e24c224e7..b990459d9 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -38,9 +38,7 @@ class TransactionReceipt { public: TransactionReceipt(bytesConstRef _rlp) { RLP r(_rlp); m_stateRoot = (h256)r[0]; m_gasUsed = (u256)r[1]; m_bloom = (LogBloom)r[2]; for (auto const& i: r[3]) m_log.emplace_back(i); } - TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log, Manifest const& _ms): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log), m_changes(_ms) {} - - Manifest const& changes() const { return m_changes; } + TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log) {} h256 const& stateRoot() const { return m_stateRoot; } u256 const& gasUsed() const { return m_gasUsed; } @@ -62,8 +60,6 @@ private: u256 m_gasUsed; LogBloom m_bloom; LogEntries m_log; - - Manifest m_changes; ///< TODO: PoC-7: KILL }; using TransactionReceipts = std::vector; diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 4a175ff4e..24c7dfcda 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -85,7 +85,10 @@ struct SubState } }; -using OnOpFunc = std::function; +class ExtVMFace; +class VM; + +using OnOpFunc = std::function; /** * @brief Interface and null implementation of the class for specifying VM externalities. @@ -146,11 +149,11 @@ public: u256 value; ///< Value (in Wei) that was passed to this address. u256 gasPrice; ///< Price of gas (that we already paid). bytesConstRef data; ///< Current input data. - bytes code; ///< Current code that is executing. + bytes code; ///< Current code that is executing. BlockInfo previousBlock; ///< The previous block's information. BlockInfo currentBlock; ///< The current block's information. SubState sub; ///< Sub-band VM state (suicides, refund counter, logs). - unsigned depth; ///< Depth of the present call. + unsigned depth = 0; ///< Depth of the present call. }; } diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp index 3e957118e..94eb956af 100644 --- a/libevm/FeeStructure.cpp +++ b/libevm/FeeStructure.cpp @@ -27,12 +27,14 @@ using namespace dev::eth; u256 const dev::eth::c_stepGas = 1; u256 const dev::eth::c_balanceGas = 20; -u256 const dev::eth::c_sha3Gas = 20; +u256 const dev::eth::c_sha3Gas = 10; +u256 const dev::eth::c_sha3WordGas = 10; u256 const dev::eth::c_sloadGas = 20; u256 const dev::eth::c_sstoreSetGas = 300; u256 const dev::eth::c_sstoreResetGas = 100; u256 const dev::eth::c_sstoreRefundGas = 100; u256 const dev::eth::c_createGas = 100; +u256 const dev::eth::c_createDataGas = 5; u256 const dev::eth::c_callGas = 20; u256 const dev::eth::c_expGas = 1; u256 const dev::eth::c_expByteGas = 1; diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 3fb8175e6..8ef384265 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -31,11 +31,13 @@ namespace eth extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. extern u256 const c_balanceGas; ///< Once per BALANCE operation. extern u256 const c_sha3Gas; ///< Once per SHA3 operation. +extern u256 const c_sha3WordGas; extern u256 const c_sloadGas; ///< Once per SLOAD operation. extern u256 const c_sstoreSetGas; ///< Once per SSTORE operation if the zeroness changes from zero. extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness doesn't change. extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero. extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction. +extern u256 const c_createDataGas; extern u256 const c_callGas; ///< Once per CALL operation & message call transaction. extern u256 const c_expGas; ///< Once per EXP instuction. extern u256 const c_expByteGas; ///< Times ceil(log256(exponent)) for the EXP instruction. diff --git a/libevm/VM.cpp b/libevm/VM.cpp index bded9a10d..b8452e4f5 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -20,14 +20,14 @@ */ #include "VM.h" +#include -using namespace std; using namespace dev; using namespace dev::eth; -void VM::reset(u256 _gas) +void VM::reset(u256 _gas) noexcept { - m_gas = _gas; + VMFace::reset(_gas); m_curPC = 0; m_jumpDests.clear(); } diff --git a/libevm/VM.h b/libevm/VM.h index 2885deb3b..6f3229920 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -28,21 +28,13 @@ #include #include #include "FeeStructure.h" -#include "ExtVMFace.h" +#include "VMFace.h" namespace dev { namespace eth { -struct VMException: virtual Exception {}; -struct StepsDone: virtual VMException {}; -struct BreakPointHit: virtual VMException {}; -struct BadInstruction: virtual VMException {}; -struct BadJumpDestination: virtual VMException {}; -struct OutOfGas: virtual VMException {}; -struct StackTooSmall: virtual public VMException {}; - // Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. // Currently we just pull out the right (low-order in BE) 160-bits. inline Address asAddress(u256 _item) @@ -57,28 +49,27 @@ inline u256 fromAddress(Address _a) /** */ -class VM +class VM: public VMFace { public: - /// Construct VM object. - explicit VM(u256 _gas = 0) { reset(_gas); } - - void reset(u256 _gas = 0); + virtual void reset(u256 _gas = 0) noexcept override final; - template - bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1); + virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; void require(u256 _n) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackTooSmall() << RequirementError((bigint)_n, (bigint)m_stack.size())); } } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } } - u256 gas() const { return m_gas; } u256 curPC() const { return m_curPC; } bytes const& memory() const { return m_temp; } u256s const& stack() const { return m_stack; } private: - u256 m_gas = 0; + friend class VMFactory; + + /// Construct VM object. + explicit VM(u256 _gas): VMFace(_gas) {} + u256 m_curPC = 0; bytes m_temp; u256s m_stack; @@ -86,10 +77,8 @@ private: std::function m_onFail; }; -} - -// INLINE: -template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) +// TODO: Move it to cpp file. Not done to make review easier. +inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) { auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; @@ -164,7 +153,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::SLOAD: require(1); - runGas = c_sloadGas; + runGas = c_sloadGas; break; // These all operate on memory and therefore potentially expand it: @@ -186,7 +175,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::SHA3: require(2); - runGas = c_sha3Gas; + runGas = c_sha3Gas + (m_stack[m_stack.size() - 2] + 31) / 32 * c_sha3WordGas; newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); break; case Instruction::CALLDATACOPY: @@ -412,7 +401,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con m_stack.pop_back(); break; case Instruction::SDIV: - m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0; + m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0; m_stack.pop_back(); break; case Instruction::MOD: @@ -420,7 +409,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con m_stack.pop_back(); break; case Instruction::SMOD: - m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0; + m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0; m_stack.pop_back(); break; case Instruction::EXP: @@ -443,11 +432,11 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con m_stack.pop_back(); break; case Instruction::SLT: - m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; + m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; m_stack.pop_back(); break; case Instruction::SGT: - m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; + m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; m_stack.pop_back(); break; case Instruction::EQ: @@ -870,4 +859,6 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con BOOST_THROW_EXCEPTION(StepsDone()); return bytesConstRef(); } + +} } diff --git a/libevm/VMFace.h b/libevm/VMFace.h new file mode 100644 index 000000000..44ae03868 --- /dev/null +++ b/libevm/VMFace.h @@ -0,0 +1,55 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +#pragma once + +#include +#include +#include "ExtVMFace.h" + +namespace dev +{ +namespace eth +{ + +struct VMException: virtual Exception {}; +struct StepsDone: virtual VMException {}; +struct BreakPointHit: virtual VMException {}; +struct BadInstruction: virtual VMException {}; +struct BadJumpDestination: virtual VMException {}; +struct OutOfGas: virtual VMException {}; +struct StackTooSmall: virtual VMException {}; + +/// EVM Virtual Machine interface +class VMFace +{ +public: + explicit VMFace(u256 _gas): m_gas(_gas) {} + virtual ~VMFace() = default; + VMFace(VMFace const&) = delete; + void operator=(VMFace const&) = delete; + + virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; } + u256 gas() const noexcept { return m_gas; } + + virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0; + +protected: + u256 m_gas = 0; +}; + +} +} diff --git a/libevm/VMFactory.cpp b/libevm/VMFactory.cpp new file mode 100644 index 000000000..af37ec710 --- /dev/null +++ b/libevm/VMFactory.cpp @@ -0,0 +1,42 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ + +#include "VMFactory.h" +#include "VM.h" + +namespace dev +{ +namespace eth +{ +namespace +{ + VMKind g_kind = VMKind::Interpreter; +} + +void VMFactory::setKind(VMKind _kind) +{ + g_kind = _kind; +} + +std::unique_ptr VMFactory::create(u256 _gas) +{ + asserts(g_kind == VMKind::Interpreter && "Only interpreter supported for now"); + return std::unique_ptr(new VM(_gas)); +} + +} +} diff --git a/libevm/VMFactory.h b/libevm/VMFactory.h new file mode 100644 index 000000000..d0d02e0c4 --- /dev/null +++ b/libevm/VMFactory.h @@ -0,0 +1,42 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +#pragma once + +#include "VMFace.h" + +namespace dev +{ +namespace eth +{ + +enum class VMKind: bool +{ + Interpreter, + JIT +}; + +class VMFactory +{ +public: + VMFactory() = delete; + + static std::unique_ptr create(u256 _gas); + static void setKind(VMKind _kind); +}; + +} +} diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp index c3dc2dda3..63351bc4c 100644 --- a/liblll/CompilerState.cpp +++ b/liblll/CompilerState.cpp @@ -72,6 +72,11 @@ void CompilerState::populateStandard() "(def 'regname (name) { [32]'register [64]name (call allgas namereg 0 32 64 0 0) })" "(def 'regcoin (name) { [32]name (call allgas coinreg 0 32 32 0 0) })" "(def 'regcoin (name denom) { [32]name [64]denom (call allgas coinreg 0 32 64 0 0) })" + "(def 'ecrecover (r s v hash) { [0] r [32] s [64] v [96] hash (msg allgas 1 0 0 128) })" + "(def 'sha256 (data datasize) (msg allgas 2 0 data datasize))" + "(def 'ripemd160 (data datasize) (msg allgas 3 0 data datasize))" + "(def 'sha256 (val) { [0]:val (sha256 0 32) })" + "(def 'ripemd160 (val) { [0]:val (ripemd160 0 32) })" "}"; CodeFragment::compile(s, *this); } diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 974313418..258336917 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -130,21 +130,17 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b { // We do not check the calldata size, everything is zero-padded. unsigned dataOffset = 1; - eth::Instruction load = _fromMemory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD; //@todo this can be done more efficiently, saving some CALLDATALOAD calls for (ASTPointer const& var: _function.getParameters()) { unsigned const numBytes = var->getType()->getCalldataEncodedSize(); - if (numBytes == 0 || numBytes > 32) + if (numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(var->getLocation()) << errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); - if (numBytes == 32) - m_context << u256(dataOffset) << load; - else - m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset) - << load << eth::Instruction::DIV; + bool leftAligned = var->getType()->getCategory() == Type::Category::STRING; + CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory); dataOffset += numBytes; } return dataOffset; @@ -160,14 +156,13 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function) { Type const& paramType = *parameters[i]->getType(); unsigned numBytes = paramType.getCalldataEncodedSize(); - if (numBytes == 0 || numBytes > 32) + if (numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(parameters[i]->getLocation()) << errinfo_comment("Type " + paramType.toString() + " not yet supported.")); CompilerUtils(m_context).copyToStackTop(stackDepth, paramType); - if (numBytes != 32) - m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; - m_context << u256(dataOffset) << eth::Instruction::MSTORE; + bool const leftAligned = paramType.getCategory() == Type::Category::STRING; + CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned); stackDepth -= paramType.getSizeOnStack(); dataOffset += numBytes; } diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index d4dfbe3c0..9f474896e 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -31,6 +31,46 @@ namespace dev namespace solidity { +void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata) +{ + if (_bytes == 0) + { + m_context << u256(0); + return; + } + eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD; + if (asserts(_bytes <= 32)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory load of more than 32 bytes requested.")); + if (_bytes == 32) + m_context << u256(_offset) << load; + else + { + // load data and add leading or trailing zeros by dividing/multiplying depending on alignment + u256 shiftFactor = u256(1) << ((32 - _bytes) * 8); + m_context << shiftFactor; + if (_leftAligned) + m_context << eth::Instruction::DUP1; + m_context << u256(_offset) << load << eth::Instruction::DIV; + if (_leftAligned) + m_context << eth::Instruction::MUL; + } +} + +void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned) +{ + if (_bytes == 0) + { + m_context << eth::Instruction::POP; + return; + } + if (asserts(_bytes <= 32)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory store of more than 32 bytes requested.")); + if (_bytes != 32 && !_leftAligned) + // shift the value accordingly before storing + m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL; + m_context << u256(_offset) << eth::Instruction::MSTORE; +} + void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) { unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable)); diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h index 4da533752..928f0e2d3 100644 --- a/libsolidity/CompilerUtils.h +++ b/libsolidity/CompilerUtils.h @@ -35,6 +35,18 @@ class CompilerUtils public: CompilerUtils(CompilerContext& _context): m_context(_context) {} + /// Loads data from memory to the stack. + /// @param _offset offset in memory (or calldata) + /// @param _bytes number of bytes to load + /// @param _leftAligned if true, store left aligned on stack (otherwise right aligned) + /// @param _fromCalldata if true, load from calldata, not from memory + void loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, bool _fromCalldata = false); + /// Stores data from stack in memory. + /// @param _offset offset in memory + /// @param _bytes number of bytes to store + /// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned) + void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false); + /// Moves the value that is at the top of the stack to a stack variable. void moveToStackVariable(VariableDeclaration const& _variable); /// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack. diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index a19c9cfd8..3beb423de 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -239,14 +239,14 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(arguments[i]->getLocation()) << errinfo_comment("Type " + type.toString() + " not yet supported.")); - if (numBytes != 32) - m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; - m_context << u256(dataOffset) << eth::Instruction::MSTORE; + bool const leftAligned = type.getCategory() == Type::Category::STRING; + CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned); dataOffset += numBytes; } //@todo only return the first return value for now - unsigned retSize = function.getReturnParameterTypes().empty() ? 0 - : function.getReturnParameterTypes().front()->getCalldataEncodedSize(); + Type const* firstType = function.getReturnParameterTypes().empty() ? nullptr : + function.getReturnParameterTypes().front().get(); + unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0; // CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top) m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0); _functionCall.getExpression().accept(*this); // pushes addr and function index @@ -254,11 +254,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB << eth::Instruction::CALL << eth::Instruction::POP; // @todo do not ignore failure indicator - if (retSize == 32) - m_context << u256(0) << eth::Instruction::MLOAD; - else if (retSize > 0) - m_context << (u256(1) << ((32 - retSize) * 8)) - << u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV; + if (retSize > 0) + { + bool const leftAligned = firstType->getCategory() == Type::Category::STRING; + CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned); + } break; } case Location::SEND: @@ -281,7 +281,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arguments.front()->accept(*this); appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); // @todo move this once we actually use memory - m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3; + CompilerUtils(m_context).storeInMemory(0); + m_context << u256(32) << u256(0) << eth::Instruction::SHA3; break; case Location::ECRECOVER: case Location::SHA256: @@ -297,13 +298,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arguments[i]->accept(*this); appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true); // @todo move this once we actually use memory - m_context << u256(i * 32) << eth::Instruction::MSTORE; + CompilerUtils(m_context).storeInMemory(i * 32); } m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0) << contractAddress << u256(500) //@todo determine actual gas requirement << eth::Instruction::CALL - << eth::Instruction::POP - << u256(0) << eth::Instruction::MLOAD; + << eth::Instruction::POP; + CompilerUtils(m_context).loadFromMemory(0); break; } default: @@ -387,7 +388,8 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) *dynamic_cast(*_indexAccess.getBaseExpression().getType()).getKeyType(), true); // @todo move this once we actually use memory - m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE; + CompilerUtils(m_context).storeInMemory(0); + CompilerUtils(m_context).storeInMemory(32); m_context << u256(64) << u256(0) << eth::Instruction::SHA3; m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType()); @@ -425,10 +427,11 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { case Type::Category::INTEGER: case Type::Category::BOOL: + case Type::Category::STRING: m_context << _literal.getType()->literalValue(_literal); break; default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer and boolean literals implemented for now.")); + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now.")); } } @@ -564,6 +567,11 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con return; if (_typeOnStack.getCategory() == Type::Category::INTEGER) appendHighBitsCleanup(dynamic_cast(_typeOnStack)); + else if (_typeOnStack.getCategory() == Type::Category::STRING) + { + // nothing to do, strings are high-order-bit-aligned + //@todo clear lower-order bytes if we allow explicit conversion to shorter strings + } else if (_typeOnStack != _targetType) // All other types should not be convertible to non-equal types. BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested.")); diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index fb4577c8c..c0b173d46 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -35,6 +35,7 @@ namespace solidity { class CompilerContext; class Type; class IntegerType; +class StaticStringType; /** * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream @@ -75,7 +76,7 @@ private: /// @} /// Appends an implicit or explicit type conversion. For now this comprises only erasing - /// higher-order bits (@see appendHighBitCleanup) when widening integer types. + /// higher-order bits (@see appendHighBitCleanup) when widening integer. /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be /// necessary. void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index c748d9892..51450f1a2 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -269,6 +269,39 @@ namespace solidity K(ADDRESS, "address", 0) \ K(BOOL, "bool", 0) \ K(STRING_TYPE, "string", 0) \ + K(STRING0, "string0", 0) \ + K(STRING1, "string1", 0) \ + K(STRING2, "string2", 0) \ + K(STRING3, "string3", 0) \ + K(STRING4, "string4", 0) \ + K(STRING5, "string5", 0) \ + K(STRING6, "string6", 0) \ + K(STRING7, "string7", 0) \ + K(STRING8, "string8", 0) \ + K(STRING9, "string9", 0) \ + K(STRING10, "string10", 0) \ + K(STRING11, "string11", 0) \ + K(STRING12, "string12", 0) \ + K(STRING13, "string13", 0) \ + K(STRING14, "string14", 0) \ + K(STRING15, "string15", 0) \ + K(STRING16, "string16", 0) \ + K(STRING17, "string17", 0) \ + K(STRING18, "string18", 0) \ + K(STRING19, "string19", 0) \ + K(STRING20, "string20", 0) \ + K(STRING21, "string21", 0) \ + K(STRING22, "string22", 0) \ + K(STRING23, "string23", 0) \ + K(STRING24, "string24", 0) \ + K(STRING25, "string25", 0) \ + K(STRING26, "string26", 0) \ + K(STRING27, "string27", 0) \ + K(STRING28, "string28", 0) \ + K(STRING29, "string29", 0) \ + K(STRING30, "string30", 0) \ + K(STRING31, "string31", 0) \ + K(STRING32, "string32", 0) \ K(TEXT, "text", 0) \ K(REAL, "real", 0) \ K(UREAL, "ureal", 0) \ diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 6c971a742..00e530c3f 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -53,6 +53,8 @@ shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) return make_shared(0, IntegerType::Modifier::ADDRESS); else if (_typeToken == Token::BOOL) return make_shared(); + else if (Token::STRING0 <= _typeToken && _typeToken <= Token::STRING32) + return make_shared(int(_typeToken) - int(Token::STRING0)); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type.")); @@ -91,7 +93,8 @@ shared_ptr Type::forLiteral(Literal const& _literal) case Token::NUMBER: return IntegerType::smallestTypeForLiteral(_literal.getValue()); case Token::STRING_LITERAL: - return shared_ptr(); // @todo add string literals + //@todo put larger strings into dynamic strings + return StaticStringType::smallestTypeForLiteral(_literal.getValue()); default: return shared_ptr(); } @@ -194,6 +197,44 @@ const MemberList IntegerType::AddressMemberList = {"send", make_shared(TypePointers({make_shared(256)}), TypePointers(), FunctionType::Location::SEND)}}); +shared_ptr StaticStringType::smallestTypeForLiteral(string const& _literal) +{ + if (_literal.length() <= 32) + return make_shared(_literal.length()); + return shared_ptr(); +} + +StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes) +{ + if (asserts(m_bytes >= 0 && m_bytes <= 32)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid byte number for static string type: " + + dev::toString(m_bytes))); +} + +bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (_convertTo.getCategory() != getCategory()) + return false; + StaticStringType const& convertTo = dynamic_cast(_convertTo); + return convertTo.m_bytes >= m_bytes; +} + +bool StaticStringType::operator==(Type const& _other) const +{ + if (_other.getCategory() != getCategory()) + return false; + StaticStringType const& other = dynamic_cast(_other); + return other.m_bytes == m_bytes; +} + +u256 StaticStringType::literalValue(const Literal& _literal) const +{ + u256 value = 0; + for (char c: _literal.getValue()) + value = (value << 8) | byte(c); + return value << ((32 - _literal.getValue().length()) * 8); +} + bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const { // conversion to integer is fine, but not to address diff --git a/libsolidity/Types.h b/libsolidity/Types.h index dd89f311d..8e93bd31d 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -36,7 +36,7 @@ namespace dev namespace solidity { -// @todo realMxN, string +// @todo realMxN, dynamic strings, text, arrays class Type; // forward using TypePointer = std::shared_ptr; @@ -178,6 +178,35 @@ private: static const MemberList AddressMemberList; }; +/** + * String type with fixed length, up to 32 bytes. + */ +class StaticStringType: public Type +{ +public: + virtual Category getCategory() const override { return Category::STRING; } + + /// @returns the smallest string type for the given literal or an empty pointer + /// if no type fits. + static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); + + StaticStringType(int _bytes); + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool operator==(Type const& _other) const override; + + virtual unsigned getCalldataEncodedSize() const override { return m_bytes; } + virtual bool isValueType() const override { return true; } + + virtual std::string toString() const override { return "string" + dev::toString(m_bytes); } + virtual u256 literalValue(Literal const& _literal) const override; + + int getNumBytes() const { return m_bytes; } + +private: + int m_bytes; +}; + /** * The boolean type. */ diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index 0d54af089..afeca48a8 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -54,10 +54,16 @@ bool TopicFilter::matches(Envelope const& _e) const { for (TopicMask const& t: m_topicMasks) { - if (_e.topics().size() == t.size()) - for (unsigned i = 0; i < t.size(); ++i) - if (((t[i].first ^ _e.topics()[i]) & t[i].second) != 0) - goto NEXT_TOPICMASK; + for (unsigned i = 0; i < t.size(); ++i) + { + for (auto et: _e.topics()) + if (((t[i].first ^ et) & t[i].second) == 0) + goto NEXT_TOPICPART; + // failed to match topicmask against any topics: move on to next mask + goto NEXT_TOPICMASK; + NEXT_TOPICPART:; + } + // all topicmasks matched. return true; NEXT_TOPICMASK:; } diff --git a/test/createRandomTest.cpp b/test/createRandomTest.cpp index caeeb6b67..a11688457 100644 --- a/test/createRandomTest.cpp +++ b/test/createRandomTest.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include "vm.h" using namespace std; @@ -142,14 +142,14 @@ void doMyTests(json_spirit::mValue& v) } bytes output; - eth::VM vm(fev.gas); + auto vm = eth::VMFactory::create(fev.gas); u256 gas; bool vmExceptionOccured = false; try { - output = vm.go(fev, fev.simpleTrace()).toBytes(); - gas = vm.gas(); + output = vm->go(fev, fev.simpleTrace()).toBytes(); + gas = vm->gas(); } catch (eth::VMException const& _e) { diff --git a/test/genesis.cpp b/test/genesis.cpp index 6839d42e2..2cd9221a8 100644 --- a/test/genesis.cpp +++ b/test/genesis.cpp @@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(genesis_tests) BOOST_CHECK_EQUAL(BlockChain::genesis().stateRoot, h256(o["genesis_state_root"].get_str())); BOOST_CHECK_EQUAL(toHex(BlockChain::createGenesisBlock()), toHex(fromHex(o["genesis_rlp_hex"].get_str()))); - BOOST_CHECK_EQUAL(sha3(BlockChain::createGenesisBlock()), h256(o["genesis_hash"].get_str())); + BOOST_CHECK_EQUAL(BlockInfo::headerHash(BlockChain::createGenesisBlock()), h256(o["genesis_hash"].get_str())); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/jsonrpc.cpp b/test/jsonrpc.cpp index d17c5a594..20ffc6d54 100644 --- a/test/jsonrpc.cpp +++ b/test/jsonrpc.cpp @@ -19,7 +19,7 @@ * @date 2014 */ -#if ETH_JSONRPC +#if ETH_JSONRPC && 0 #include #include diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 3c77492a5..af8b58baa 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -363,6 +363,48 @@ BOOST_AUTO_TEST_CASE(small_signed_types) testSolidityAgainstCpp(0, small_signed_types_cpp); } +BOOST_AUTO_TEST_CASE(strings) +{ + char const* sourceCode = "contract test {\n" + " function fixed() returns(string32 ret) {\n" + " return \"abc\\x00\\xff__\";\n" + " }\n" + " function pipeThrough(string2 small, bool one) returns(string16 large, bool oneRet) {\n" + " oneRet = one;\n" + " large = small;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + bytes expectation(32, 0); + expectation[0] = byte('a'); + expectation[1] = byte('b'); + expectation[2] = byte('c'); + expectation[3] = byte(0); + expectation[4] = byte(0xff); + expectation[5] = byte('_'); + expectation[6] = byte('_'); + BOOST_CHECK(callContractFunction(0, bytes()) == expectation); + expectation = bytes(17, 0); + expectation[0] = 0; + expectation[1] = 2; + expectation[16] = 1; + BOOST_CHECK(callContractFunction(1, bytes({0x00, 0x02, 0x01})) == expectation); +} + +BOOST_AUTO_TEST_CASE(empty_string_on_stack) +{ + char const* sourceCode = "contract test {\n" + " function run(string0 empty, uint8 inp) returns(uint16 a, string0 b, string4 c) {\n" + " var x = \"abc\";\n" + " var y = \"\";\n" + " var z = inp;\n" + " a = z; b = y; c = x;" + " }\n" + "}\n"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction(0, bytes({0x02})) == bytes({0x00, 0x02, 'a', 'b', 'c', 0x00})); +} + BOOST_AUTO_TEST_CASE(state_smoke_test) { char const* sourceCode = "contract test {\n" @@ -941,6 +983,34 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars) BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 9)); } +BOOST_AUTO_TEST_CASE(strings_in_calls) +{ + char const* sourceCode = R"( + contract Helper { + function invoke(string3 x, bool stop) returns (string4 ret) { + return x; + } + } + contract Main { + Helper h; + function callHelper(string2 x, bool stop) returns (string5 ret) { + return h.invoke(x, stop); + } + function getHelper() returns (address addr) { + return address(h); + } + function setHelper(address addr) { + h = Helper(addr); + } + })"; + compileAndRun(sourceCode, 0, "Helper"); + u160 const helperAddress = m_contractAddress; + compileAndRun(sourceCode, 0, "Main"); + BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); + BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0})); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 783972296..03eaebb3a 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -226,6 +226,14 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } +BOOST_AUTO_TEST_CASE(large_string_literal) +{ + char const* text = "contract test {\n" + " function f() { var x = \"123456789012345678901234567890123\"; }" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + BOOST_AUTO_TEST_CASE(balance) { char const* text = "contract test {\n" diff --git a/test/solidityOptimizerTest.cpp b/test/solidityOptimizerTest.cpp index 69000a57f..388e0579c 100644 --- a/test/solidityOptimizerTest.cpp +++ b/test/solidityOptimizerTest.cpp @@ -48,7 +48,7 @@ public: m_optimize = true; bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName); int sizeDiff = nonOptimizedBytecode.size() - optimizedBytecode.size(); - BOOST_CHECK_MESSAGE(sizeDiff == _expectedSizeDecrease, "Bytecode did only shrink by " + BOOST_CHECK_MESSAGE(sizeDiff == int(_expectedSizeDecrease), "Bytecode did only shrink by " + boost::lexical_cast(sizeDiff) + " bytes, expected: " + boost::lexical_cast(_expectedSizeDecrease)); m_optimizedContract = m_contractAddress; diff --git a/test/trie.cpp b/test/trie.cpp index 67f706917..3f072a6d1 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -59,8 +59,8 @@ BOOST_AUTO_TEST_CASE(trie_tests) cnote << "Testing Trie..."; js::mValue v; - string s = asString(contents(testPath + "/trietest.json")); - BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'trietest.json' is empty. Have you cloned the 'tests' repo branch develop?"); + string s = asString(contents(testPath + "/trieanyorder.json")); + BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'trieanyorder.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); for (auto& i: v.get_obj()) { @@ -88,12 +88,11 @@ BOOST_AUTO_TEST_CASE(trie_tests) BOOST_REQUIRE(t.check(true)); } BOOST_REQUIRE(!o["root"].is_null()); - BOOST_CHECK_EQUAL(o["root"].get_str(), toHex(t.root().asArray())); + BOOST_CHECK_EQUAL(o["root"].get_str(), "0x" + toHex(t.root().asArray())); } } } - inline h256 stringMapHash256(StringMap const& _s) { return hash256(_s); diff --git a/test/vm.cpp b/test/vm.cpp index 5c270f789..d8e85383c 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -21,6 +21,8 @@ */ #include +#include +#include #include "vm.h" using namespace std; @@ -241,10 +243,11 @@ void FakeExtVM::importCallCreates(mArray& _callcreates) eth::OnOpFunc FakeExtVM::simpleTrace() { - return [](uint64_t steps, eth::Instruction inst, bigint newMemSize, bigint gasCost, void* voidVM, void const* voidExt) + + return [](uint64_t steps, eth::Instruction inst, bigint newMemSize, bigint gasCost, dev::eth::VM* voidVM, dev::eth::ExtVMFace const* voidExt) { - FakeExtVM const& ext = *(FakeExtVM const*)voidExt; - eth::VM& vm = *(eth::VM*)voidVM; + FakeExtVM const& ext = *static_cast(voidExt); + eth::VM& vm = *voidVM; std::ostringstream o; o << std::endl << " STACK" << std::endl; @@ -297,14 +300,14 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) } bytes output; - VM vm(fev.gas); + auto vm = eth::VMFactory::create(fev.gas); u256 gas; bool vmExceptionOccured = false; try { - output = vm.go(fev, fev.simpleTrace()).toBytes(); - gas = vm.gas(); + output = vm->go(fev, fev.simpleTrace()).toBytes(); + gas = vm->gas(); } catch (VMException const& _e) { diff --git a/test/vm.h b/test/vm.h index a52a02e31..3d4b88d54 100644 --- a/test/vm.h +++ b/test/vm.h @@ -80,9 +80,6 @@ public: bytes thisTxData; bytes thisTxCode; u256 gas; - -private: - eth::Manifest m_ms; }; diff --git a/test/whisperTopic.cpp b/test/whisperTopic.cpp index 493b37bc2..c5e59332d 100644 --- a/test/whisperTopic.cpp +++ b/test/whisperTopic.cpp @@ -32,7 +32,8 @@ BOOST_AUTO_TEST_SUITE(whisper) BOOST_AUTO_TEST_CASE(topic) { - g_logVerbosity = 0; + cnote << "Testing Whisper..."; +// g_logVerbosity = 0; bool started = false; unsigned result = 0; @@ -40,16 +41,16 @@ BOOST_AUTO_TEST_CASE(topic) { setThreadName("other"); - Host ph("Test", NetworkPreferences(30303, "", false, true)); + Host ph("Test", NetworkPreferences(50303, "", false, true)); auto wh = ph.registerCapability(new WhisperHost()); ph.start(); started = true; /// Only interested in odd packets - auto w = wh->installWatch(BuildTopicMask()("odd")); + auto w = wh->installWatch(BuildTopicMask("odd")); - for (int i = 0, last = 0; i < 100 && last < 81; ++i) + for (int i = 0, last = 0; i < 200 && last < 81; ++i) { for (auto i: wh->checkWatch(w)) { @@ -65,10 +66,12 @@ BOOST_AUTO_TEST_CASE(topic) while (!started) this_thread::sleep_for(chrono::milliseconds(50)); - Host ph("Test", NetworkPreferences(30300, "", false, true)); + Host ph("Test", NetworkPreferences(50300, "", false, true)); auto wh = ph.registerCapability(new WhisperHost()); + this_thread::sleep_for(chrono::milliseconds(500)); ph.start(); - ph.connect("127.0.0.1", 30303); + this_thread::sleep_for(chrono::milliseconds(500)); + ph.connect("127.0.0.1", 50303); KeyPair us = KeyPair::create(); for (int i = 0; i < 10; ++i) @@ -78,6 +81,8 @@ BOOST_AUTO_TEST_CASE(topic) } listener.join(); +// g_logVerbosity = 0; + BOOST_REQUIRE_EQUAL(result, 1 + 9 + 25 + 49 + 81); } diff --git a/windows/LibEthereum.vcxproj b/windows/LibEthereum.vcxproj index 06f868023..cbacd51b5 100644 --- a/windows/LibEthereum.vcxproj +++ b/windows/LibEthereum.vcxproj @@ -368,6 +368,7 @@ true true + @@ -568,4 +569,4 @@ - + \ No newline at end of file diff --git a/windows/LibEthereum.vcxproj.filters b/windows/LibEthereum.vcxproj.filters index 114364008..76201d823 100644 --- a/windows/LibEthereum.vcxproj.filters +++ b/windows/LibEthereum.vcxproj.filters @@ -190,18 +190,14 @@ libdevcrypto - - libdevcrypto - - - libdevcrypto - libethereum libevmcore + + @@ -438,6 +434,9 @@ libevmcore + + libevm +