diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 14f3f5b67..6c01f57f9 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -570,7 +570,7 @@ - Qt::NoFocus + Qt::WheelFocus QFrame::NoFrame @@ -637,7 +637,7 @@ - Qt::NoFocus + Qt::WheelFocus QFrame::NoFrame @@ -715,7 +715,7 @@ - Qt::NoFocus + Qt::WheelFocus QFrame::NoFrame diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 33ced7e98..94a58dec0 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1156,7 +1156,7 @@ void Main::refreshBlockChain() auto b = bc.block(h); for (auto const& i: RLP(b)[1]) { - Transaction t(i.data(), CheckSignature::Sender); + Transaction t(i.data(), CheckTransaction::Everything); QString s = t.receiveAddress() ? QString(" %2 %5> %3: %1 [%4]") .arg(formatBalance(t.value()).c_str()) @@ -1536,7 +1536,7 @@ void Main::on_blocks_currentItemChanged() else { unsigned txi = item->data(Qt::UserRole + 1).toInt(); - Transaction tx(block[1][txi].data(), CheckSignature::Sender); + Transaction tx(block[1][txi].data(), CheckTransaction::Everything); auto ss = tx.safeSender(); h256 th = sha3(rlpList(ss, tx.nonce())); TransactionReceipt receipt = ethereum()->blockChain().receipts(h).receipts[txi]; @@ -1597,7 +1597,7 @@ void Main::on_debugCurrent_triggered() State s(ethereum()->state(txi, h)); Executive e(s, ethereum()->blockChain()); Debugger dw(this, this); - dw.populate(e, Transaction(t, CheckSignature::Sender)); + dw.populate(e, Transaction(t, CheckTransaction::Everything)); dw.exec(); } } diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 1ff3427e3..f1f7477fe 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -306,7 +306,7 @@ void Transact::rejigData() htmlInfo += "

Hex

" + QString(Div(Mono)) + QString::fromStdString(toHex(m_data)) + ""; // Determine the minimum amount of gas we need to play... - qint64 baseGas = (qint64)Interface::txGas(m_data, 0); + qint64 baseGas = (qint64)Transaction::gasRequired(m_data, 0); qint64 gasNeeded = 0; if (b < value() + baseGas * gasPrice()) diff --git a/eth/main.cpp b/eth/main.cpp index 9ec6dec98..6308c6cfb 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -44,6 +44,7 @@ #include #include #endif +#include #include "BuildInfo.h" using namespace std; using namespace dev; @@ -112,6 +113,7 @@ void help() << " -c,--client-name Add a name to your client's version string (default: blank)." << endl << " -d,--db-path Load database from path (default: ~/.ethereum " << endl << " /Etherum or Library/Application Support/Ethereum)." << endl + << " -D,--initdag Initialize DAG for mining and exit." << endl << " -e,--ether-price Set the ether price in the reference unit e.g. ¢ (Default: 30.679)." << endl << " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl << " -h,--help Show this help message and exit." << endl @@ -200,6 +202,7 @@ enum class NodeMode int main(int argc, char** argv) { + bool initDAG = false; string listenIP; unsigned short listenPort = 30303; string publicIP; @@ -304,6 +307,8 @@ int main(int argc, char** argv) structuredLogging = true; else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) dbPath = argv[++i]; + else if (arg == "-D" || arg == "--initdag") + initDAG = true; else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) { try @@ -436,6 +441,14 @@ int main(int argc, char** argv) &nodesState, miners ); + + if (initDAG) + { + cout << "Initializing DAG. (This will take awhile)" << endl; + Ethasher::get()->full(web3.ethereum()->blockChain().info()); + return 0; + } + web3.setIdealPeerCount(peers); std::shared_ptr gasPricer = make_shared(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr; @@ -654,7 +667,7 @@ int main(int argc, char** argv) cnote << ssbd.str(); int ssize = sechex.length(); int size = hexAddr.length(); - u256 minGas = (u256)Client::txGas(data, 0); + u256 minGas = (u256)Transaction::gasRequired(data, 0); if (size < 40) { if (size > 0) @@ -733,7 +746,7 @@ int main(int argc, char** argv) auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); - u256 minGas = (u256)Client::txGas(bytes(), 0); + u256 minGas = (u256)Transaction::gasRequired(bytes(), 0); try { Address dest = h160(fromHex(hexAddr, WhenError::Throw)); @@ -798,7 +811,7 @@ int main(int argc, char** argv) cnote << "Init:"; cnote << ssc.str(); } - u256 minGas = (u256)Client::txGas(init, 0); + u256 minGas = (u256)Transaction::gasRequired(init, 0); if (!init.size()) cwarn << "Contract creation aborted, no init code."; else if (endowment < 0) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index dc0f42bf7..75780fb10 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.9.5"; +char const* Version = "0.9.6"; } diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 6468e250f..38ccd71f0 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -236,6 +236,34 @@ inline std::vector<_T>& operator+=(std::vector std::set& operator+=(std::set& _a, U const& _b) +{ + for (auto const& i: _b) + _a.insert(i); + return _a; +} + +/// Concatenate the contents of a container onto a vector +template std::vector& operator+=(std::vector& _a, U const& _b) +{ + for (auto const& i: _b) + _a.push_back(i); + return _a; +} + +/// Insert the contents of a container into a set +template std::set operator+(std::set _a, U const& _b) +{ + return _a += _b; +} + +/// Concatenate the contents of a container onto a vector +template std::vector operator+(std::vector _a, U const& _b) +{ + return _a += _b; +} + /// Concatenate two vectors of elements. template inline std::vector<_T> operator+(std::vector<_T> const& _a, std::vector<_T> const& _b) diff --git a/libethcore/CommonJS.cpp b/libethcore/CommonJS.cpp index ef9ac9f65..167879db3 100644 --- a/libethcore/CommonJS.cpp +++ b/libethcore/CommonJS.cpp @@ -65,5 +65,22 @@ std::string prettyU256(u256 _n, bool _abridged) return s.str(); } +namespace eth +{ + +BlockNumber jsToBlockNumber(std::string const& _js) +{ + if (_js == "latest") + return LatestBlock; + else if (_js == "earliest") + return 0; + else if (_js == "pending") + return PendingBlock; + else + return (unsigned)jsToInt(_js); +} + +} + } diff --git a/libethcore/CommonJS.h b/libethcore/CommonJS.h index 7d3071c68..185cd3191 100644 --- a/libethcore/CommonJS.h +++ b/libethcore/CommonJS.h @@ -67,5 +67,8 @@ struct TransactionSkeleton u256 gasPrice; }; +/// Convert to a block number, a bit like jsToInt, except that it correctly recognises "pending" and "latest". +BlockNumber jsToBlockNumber(std::string const& _js); + } } diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index db2718fc4..3471a958f 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -38,6 +38,7 @@ using errinfo_difficulty = boost::error_info; using BadFieldError = boost::tuple; struct DatabaseAlreadyOpen: virtual dev::Exception {}; +struct OutOfGasBase: virtual dev::Exception {}; struct NotEnoughAvailableSpace: virtual dev::Exception {}; struct NotEnoughCash: virtual dev::Exception {}; struct GasPriceTooLow: virtual dev::Exception {}; diff --git a/libethcore/Params.cpp b/libethcore/Params.cpp index d70443fd9..029f8b47a 100644 --- a/libethcore/Params.cpp +++ b/libethcore/Params.cpp @@ -46,7 +46,6 @@ u256 const c_sha3WordGas = 6; u256 const c_sloadGas = 50; u256 const c_sstoreSetGas = 20000; u256 const c_sstoreResetGas = 5000; -u256 const c_sstoreClearGas = 5000; u256 const c_sstoreRefundGas = 15000; u256 const c_jumpdestGas = 1; u256 const c_logGas = 375; diff --git a/libethcore/Params.h b/libethcore/Params.h index 62cf6b2d8..46b30e2c3 100644 --- a/libethcore/Params.h +++ b/libethcore/Params.h @@ -48,8 +48,7 @@ extern u256 const c_sha3WordGas; ///< Once per word of the SHA3 operation's da extern u256 const c_copyGas; ///< Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. 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_sstoreClearGas; ///< Once per SSTORE operation if the zeroness changes to zero. +extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness does not change from zero. NOTE: when c_sstoreSetGas does not apply. extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero. extern u256 const c_jumpdestGas; ///< Once per JUMPDEST operation. extern u256 const c_logGas; ///< Per LOG* operation. diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index a4061a1a0..48ec55077 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -68,20 +68,6 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) return _out; } -ldb::Slice dev::eth::oldToSlice(h256 const& _h, unsigned _sub) -{ -#if ALL_COMPILERS_ARE_CPP11_COMPLIANT - static thread_local h256 h = _h ^ sha3(h256(u256(_sub))); - return ldb::Slice((char const*)&h, 32); -#else - static boost::thread_specific_ptr t_h; - if (!t_h.get()) - t_h.reset(new h256); - *t_h = _h ^ sha3(h256(u256(_sub))); - return ldb::Slice((char const*)t_h.get(), 32); -#endif -} - ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub) { #if ALL_COMPILERS_ARE_CPP11_COMPLIANT @@ -988,41 +974,11 @@ bytes BlockChain::block(h256 const& _hash) const return bytes(); } - WriteGuard l(x_blocks); - m_blocks[_hash].resize(d.size()); - memcpy(m_blocks[_hash].data(), d.data(), d.size()); - noteUsed(_hash); - return m_blocks[_hash]; -} - -bytes BlockChain::oldBlock(h256 const& _hash) const -{ - if (_hash == m_genesisHash) - return m_genesisBlock; - - { - ReadGuard l(x_blocks); - auto it = m_blocks.find(_hash); - if (it != m_blocks.end()) - return it->second; - } - - string d; - m_blocksDB->Get(m_readOptions, oldToSlice(_hash), &d); - - if (!d.size()) - { - cwarn << "Couldn't find requested block:" << _hash.abridged(); - return bytes(); - } - WriteGuard l(x_blocks); m_blocks[_hash].resize(d.size()); memcpy(m_blocks[_hash].data(), d.data(), d.size()); - noteUsed(_hash); - return m_blocks[_hash]; } diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 70432f19c..e20ee8f6a 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -64,7 +64,6 @@ struct BlockChainWarn: public LogChannel { static const char* name() { return "= std::map const& genesisState(); ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); -ldb::Slice oldToSlice(h256 const& _h, unsigned _sub = 0); using BlocksHash = std::map; using TransactionHashes = h256s; @@ -274,30 +273,6 @@ private: return ret.first->second; } - template T oldQueryExtras(h256 const& _h, std::map& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const - { - { - ReadGuard l(_x); - auto it = _m.find(_h); - if (it != _m.end()) - return it->second; - } - - std::string s; - (_extrasDB ? _extrasDB : m_extrasDB)->Get(m_readOptions, oldToSlice(_h, N), &s); - if (s.empty()) - { -// cout << "Not found in DB: " << _h << endl; - return _n; - } - - noteUsed(_h, N); - - WriteGuard l(_x); - auto ret = _m.insert(std::make_pair(_h, T(RLP(s)))); - return ret.first->second; - } - void checkConsistency(); /// The caches of the disk DB and their locks. diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index 25f6ad438..6f8c64827 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -107,14 +107,6 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) } } -namespace dev { -template std::set& operator+=(std::set& _a, U const& _b) -{ - for (auto const& i: _b) - _a.insert(i); - return _a; -} } - bool BlockQueue::doneDrain(h256s const& _bad) { WriteGuard l(m_lock); diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 4ad20b403..c703c6e95 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -94,7 +94,7 @@ void BasicGasPricer::update(BlockChain const& _bc) for (unsigned i = 0; i < r[1].size(); ++i) { auto gu = brs.receipts[i].gasUsed(); - dist[Transaction(r[1][i].data(), CheckSignature::None).gasPrice()] += (unsigned)brs.receipts[i].gasUsed(); + dist[Transaction(r[1][i].data(), CheckTransaction::None).gasPrice()] += (unsigned)brs.receipts[i].gasUsed(); total += (unsigned)gu; } } @@ -511,7 +511,7 @@ void Client::doWork() clog(ClientNote) << "Dead block:" << h.abridged(); for (auto const& t: m_bc.transactions(h)) { - clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckSignature::None); + clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None); m_tq.import(t); } } @@ -519,7 +519,7 @@ void Client::doWork() // remove transactions from m_tq nicely rather than relying on out of date nonce later on. for (auto const& h: fresh) { - clog(ClientChat) << "Mined block:" << h.abridged(); + clog(ClientChat) << "Live block:" << h.abridged(); for (auto const& th: m_bc.transactionHashes(h)) { clog(ClientNote) << "Safely dropping transaction " << th; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index b9c2fa878..d1d9bc27f 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -285,11 +285,6 @@ LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId) return ret; } -h256 ClientBase::hashFromNumber(unsigned _number) const -{ - return bc().numberHash(_number); -} - BlockInfo ClientBase::blockInfo(h256 _hash) const { return BlockInfo(bc().block(_hash)); @@ -302,7 +297,7 @@ BlockDetails ClientBase::blockDetails(h256 _hash) const Transaction ClientBase::transaction(h256 _transactionHash) const { - return Transaction(bc().transaction(_transactionHash), CheckSignature::Range); + return Transaction(bc().transaction(_transactionHash), CheckTransaction::Cheap); } Transaction ClientBase::transaction(h256 _blockHash, unsigned _i) const @@ -310,7 +305,7 @@ Transaction ClientBase::transaction(h256 _blockHash, unsigned _i) const auto bl = bc().block(_blockHash); RLP b(bl); if (_i < b[1].itemCount()) - return Transaction(b[1][_i].data(), CheckSignature::Range); + return Transaction(b[1][_i].data(), CheckTransaction::Cheap); else return Transaction(); } @@ -321,7 +316,7 @@ Transactions ClientBase::transactions(h256 _blockHash) const RLP b(bl); Transactions res; for (unsigned i = 0; i < b[1].itemCount(); i++) - res.emplace_back(b[1][i].data(), CheckSignature::Range); + res.emplace_back(b[1][i].data(), CheckTransaction::Cheap); return res; } @@ -369,6 +364,11 @@ Transactions ClientBase::pending() const return postMine().pending(); } +h256s ClientBase::pendingHashes() const +{ + return h256s() + postMine().pendingHashes(); +} + StateDiff ClientBase::diff(unsigned _txi, h256 _block) const { @@ -399,3 +399,12 @@ Address ClientBase::address() const { return preMine().address(); } + +h256 ClientBase::hashFromNumber(BlockNumber _number) const +{ + if (_number == PendingBlock) + return h256(); + if (_number == LatestBlock) + return bc().currentHash(); + return bc().numberHash(_number); +} diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 10e4ca84b..2d5b4a802 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -63,7 +63,7 @@ struct WatchChannel: public LogChannel { static const char* name() { return "(o) #define cwatch dev::LogOutputStream() struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; }; struct WorkOutChannel: public LogChannel { static const char* name() { return "() #define cworkin dev::LogOutputStream() #define cworkout dev::LogOutputStream() @@ -109,7 +109,7 @@ public: virtual LocalisedLogEntries checkWatch(unsigned _watchId) override; // TODO: switch all the _blockHash arguments to also accept _blockNumber - virtual h256 hashFromNumber(unsigned _number) const override; + virtual h256 hashFromNumber(BlockNumber _number) const override; virtual eth::BlockInfo blockInfo(h256 _hash) const override; virtual eth::BlockDetails blockDetails(h256 _hash) const override; virtual eth::Transaction transaction(h256 _transactionHash) const override; @@ -122,6 +122,7 @@ public: virtual unsigned uncleCount(h256 _blockHash) const override; virtual unsigned number() const override; virtual eth::Transactions pending() const override; + virtual h256s pendingHashes() const override; using Interface::diff; virtual StateDiff diff(unsigned _txi, h256 _block) const override; diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 6a69a6d55..6b50ddbae 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -191,6 +191,8 @@ void EthereumHost::maintainTransactions() for (auto const& p: randomSelection(25, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); })) peerTransactions[p].push_back(i.first); } + for (auto const& t: ts) + m_transactionsSent.insert(t.first); for (auto p: peerSessions()) if (auto ep = p.first->cap()) { @@ -198,11 +200,10 @@ void EthereumHost::maintainTransactions() unsigned n = 0; for (auto const& h: peerTransactions[ep]) { + ep->m_knownTransactions.insert(h); b += ts[h].rlp(); ++n; } - for (auto const& t: ts) - m_transactionsSent.insert(t.first); ep->clearKnownTransactions(); diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 3bd7ecab3..3da4402ac 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -334,7 +334,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) case GetTransactionsPacket: break; // DEPRECATED. case TransactionsPacket: { - clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)"; + clogS(NetAllDetail) << "Transactions (" << dec << _r.itemCount() << "entries)"; Guard l(x_knownTransactions); for (unsigned i = 0; i < _r.itemCount(); ++i) { @@ -348,11 +348,11 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) break; case ImportResult::AlreadyKnown: // if we already had the transaction, then don't bother sending it on. + host()->m_transactionsSent.insert(h); addRating(0); break; case ImportResult::Success: addRating(100); - host()->m_transactionsSent.insert(h); break; default:; } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 0b75a07af..d4ad0ef1d 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -31,8 +31,6 @@ using namespace std; using namespace dev; using namespace dev::eth; -#define ETH_VMTRACE 1 - Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level): m_s(_s), m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)), @@ -69,12 +67,11 @@ void Executive::initialize(Transaction const& _transaction) } // Check gas cost is enough. - m_gasRequired = Interface::txGas(m_t.data()); - if (m_t.gas() < m_gasRequired) + if (!m_t.checkPayment()) { - clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_gasRequired << " Got" << m_t.gas(); + clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas(); m_excepted = TransactionException::OutOfGas; - BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)m_gasRequired, (bigint)m_t.gas())); + BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(m_t.gasRequired(), (bigint)m_t.gas())); } // Avoid invalid transactions. @@ -119,9 +116,9 @@ bool Executive::execute() m_s.subBalance(m_t.sender(), m_gasCost); if (m_t.isCreation()) - return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_gasRequired, &m_t.data(), m_t.sender()); + return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_t.gasRequired(), &m_t.data(), m_t.sender()); else - return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_gasRequired, m_t.sender()); + return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired(), m_t.sender()); } bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 3efdf6f0f..c55341cb8 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -71,7 +71,7 @@ public: void operator=(Executive) = delete; /// Initializes the executive for evaluating a transaction. You must call finalize() at some point following this. - void initialize(bytesConstRef _transaction) { initialize(Transaction(_transaction, CheckSignature::None)); } + void initialize(bytesConstRef _transaction) { initialize(Transaction(_transaction, CheckTransaction::None)); } void initialize(Transaction const& _transaction); /// Finalise a transaction previously set up with initialize(). /// @warning Only valid after initialize() and execute(), and possibly go(). diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 529ddc093..0151a8ccb 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -112,10 +112,11 @@ public: // [BLOCK QUERY API] - virtual h256 hashFromNumber(unsigned _number) const = 0; + virtual Transaction transaction(h256 _transactionHash) const = 0; + virtual h256 hashFromNumber(BlockNumber _number) const = 0; + virtual BlockInfo blockInfo(h256 _hash) const = 0; virtual BlockDetails blockDetails(h256 _hash) const = 0; - virtual Transaction transaction(h256 _transactionHash) const = 0; virtual Transaction transaction(h256 _blockHash, unsigned _i) const = 0; virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const = 0; virtual UncleHashes uncleHashes(h256 _blockHash) const = 0; @@ -124,6 +125,16 @@ public: virtual Transactions transactions(h256 _blockHash) const = 0; virtual TransactionHashes transactionHashes(h256 _blockHash) const = 0; + BlockInfo blockInfo(BlockNumber _block) const { return blockInfo(hashFromNumber(_block)); } + BlockDetails blockDetails(BlockNumber _block) const { return blockDetails(hashFromNumber(_block)); } + Transaction transaction(BlockNumber _block, unsigned _i) const { if (_block == PendingBlock) { auto p = pending(); return _i < p.size() ? p[_i] : Transaction(); } return transaction(hashFromNumber(_block)); } + unsigned transactionCount(BlockNumber _block) const { if (_block == PendingBlock) { auto p = pending(); return p.size(); } return transactionCount(hashFromNumber(_block)); } + Transactions transactions(BlockNumber _block) const { if (_block == PendingBlock) return pending(); return transactions(hashFromNumber(_block)); } + TransactionHashes transactionHashes(BlockNumber _block) const { if (_block == PendingBlock) return pendingHashes(); return transactionHashes(hashFromNumber(_block)); } + BlockInfo uncle(BlockNumber _block, unsigned _i) const { return uncle(hashFromNumber(_block), _i); } + UncleHashes uncleHashes(BlockNumber _block) const { return uncleHashes(hashFromNumber(_block)); } + unsigned uncleCount(BlockNumber _block) const { return uncleCount(hashFromNumber(_block)); } + // [EXTRA API]: /// @returns The height of the chain. @@ -132,6 +143,7 @@ public: /// Get a map containing each of the pending transactions. /// @TODO: Remove in favour of transactions(). virtual Transactions pending() const = 0; + virtual h256s pendingHashes() const = 0; /// Differences between transactions. StateDiff diff(unsigned _txi) const { return diff(_txi, m_default); } @@ -143,9 +155,6 @@ public: virtual Addresses addresses() const { return addresses(m_default); } virtual Addresses addresses(BlockNumber _block) const = 0; - /// Get the fee associated for a transaction with the given data. - template static bigint txGas(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; } - /// Get the remaining gas limit in this block. virtual u256 gasLimitRemaining() const = 0; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index a58ba9d0f..c1f37a553 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -574,7 +574,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) k << i; transactionsTrie.insert(&k.out(), tr.data()); - execute(lh, Transaction(tr.data(), CheckSignature::Sender)); + execute(lh, Transaction(tr.data(), CheckTransaction::Everything)); RLPStream receiptrlp; m_receipts.back().streamRLP(receiptrlp); diff --git a/libethereum/State.h b/libethereum/State.h index 662426e17..2fa4d4af4 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -274,6 +274,9 @@ public: /// Get the list of pending transactions. Transactions const& pending() const { return m_transactions; } + /// Get the list of hashes of pending transactions. + h256Set const& pendingHashes() const { return m_transactionSet; } + /// Get the transaction receipt for the transaction of the given index. TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; } diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 3228eca70..411c8bd99 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -25,6 +25,7 @@ #include #include #include +#include "Interface.h" #include "Transaction.h" using namespace std; using namespace dev; @@ -53,7 +54,7 @@ TransactionException dev::eth::toTransactionException(VMException const& _e) return TransactionException::Unknown; } -Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) +Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig) { int field = 0; RLP rlp(_rlpData); @@ -81,9 +82,9 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("to many fields in the transaction RLP")); m_vrs = SignatureStruct{ r, s, v }; - if (_checkSig >= CheckSignature::Range && !m_vrs.isValid()) + if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid()) BOOST_THROW_EXCEPTION(InvalidSignature()); - if (_checkSig == CheckSignature::Sender) + if (_checkSig == CheckTransaction::Everything) m_sender = sender(); } catch (Exception& _e) @@ -91,6 +92,8 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) _e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes())); throw; } + if (_checkSig >= CheckTransaction::Cheap && !checkPayment()) + BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(gasRequired(), (bigint)gas())); } Address const& Transaction::safeSender() const noexcept @@ -118,6 +121,13 @@ Address const& Transaction::sender() const return m_sender; } +bigint Transaction::gasRequired() const +{ + if (!m_gasRequired) + m_gasRequired = Transaction::gasRequired(m_data); + return m_gasRequired; +} + void Transaction::sign(Secret _priv) { auto sig = dev::sign(_priv, sha3(WithoutSignature)); diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index ab2c12b50..faf5ea07a 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -24,7 +24,7 @@ #include #include #include - +#include namespace dev { namespace eth @@ -37,11 +37,11 @@ enum IncludeSignature WithSignature = 1, ///< Do include a signature. }; -enum class CheckSignature +enum class CheckTransaction { None, - Range, - Sender + Cheap, + Everything }; enum class TransactionException @@ -119,10 +119,10 @@ public: Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} /// Constructs a transaction from the given RLP. - explicit Transaction(bytesConstRef _rlp, CheckSignature _checkSig); + explicit Transaction(bytesConstRef _rlp, CheckTransaction _checkSig); /// Constructs a transaction from the given RLP. - explicit Transaction(bytes const& _rlp, CheckSignature _checkSig): Transaction(&_rlp, _checkSig) {} + explicit Transaction(bytes const& _rlp, CheckTransaction _checkSig): Transaction(&_rlp, _checkSig) {} /// Checks equality of transactions. @@ -178,27 +178,37 @@ public: /// @returns the signature of the transaction. Encodes the sender. SignatureStruct const& signature() const { return m_vrs; } + /// @returns true if the transaction contains enough gas for the basic payment. + bool checkPayment() const { return m_gas >= gasRequired(); } + + /// @returns the gas required to run this transaction. + bigint gasRequired() const; + + /// Get the fee associated for a transaction with the given data. + template static bigint gasRequired(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; } + private: /// Type of transaction. enum Type { - NullTransaction, ///< Null transaction. - ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored. - MessageCall ///< Transaction to invoke a message call - receiveAddress() is used. + NullTransaction, ///< Null transaction. + ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored. + MessageCall ///< Transaction to invoke a message call - receiveAddress() is used. }; - void sign(Secret _priv); ///< Sign the transaction. + void sign(Secret _priv); ///< Sign the transaction. - Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction? - u256 m_nonce; ///< The transaction-count of the sender. - u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions. - Address m_receiveAddress; ///< The receiving address of the transaction. - u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS. - u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended. - bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction. - SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender. + Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction? + u256 m_nonce; ///< The transaction-count of the sender. + u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions. + Address m_receiveAddress; ///< The receiving address of the transaction. + u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS. + u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended. + bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction. + SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender. - mutable Address m_sender; ///< Cached sender, determined from signature. + mutable Address m_sender; ///< Cached sender, determined from signature. + mutable bigint m_gasRequired = 0; ///< Memoised amount required for the transaction to run. }; /// Nice name for vector of Transaction. diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 26962ed90..38f3b9429 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -44,21 +44,23 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP) // Check validity of _transactionRLP as a transaction. To do this we just deserialise and attempt to determine the sender. // If it doesn't work, the signature is bad. // The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction). - Transaction t(_transactionRLP, CheckSignature::Sender); + Transaction t(_transactionRLP, CheckTransaction::Everything); UpgradeGuard ul(l); // If valid, append to blocks. m_current[h] = t; m_known.insert(h); + + ctxq << "Queued vaguely legit-looking transaction" << h.abridged(); } catch (Exception const& _e) { - cwarn << "Ignoring invalid transaction: " << diagnostic_information(_e); + ctxq << "Ignoring invalid transaction: " << diagnostic_information(_e); return ImportResult::Malformed; } catch (std::exception const& _e) { - cwarn << "Ignoring invalid transaction: " << _e.what(); + ctxq << "Ignoring invalid transaction: " << _e.what(); return ImportResult::Malformed; } diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index cf40e1209..73ce24fbd 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "libethcore/Common.h" #include "Transaction.h" @@ -34,6 +35,8 @@ namespace eth class BlockChain; +struct TransactionQueueChannel: public LogChannel { static const char* name() { return "->Q"; } static const int verbosity = 4; }; +#define ctxq dev::LogOutputStream() /** * @brief A queue of Transactions, each stored as RLP. diff --git a/libevm/VM.cpp b/libevm/VM.cpp index 33c943938..853ac25f6 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -108,7 +108,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) runGas = c_sstoreSetGas; else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) { - runGas = c_sstoreClearGas; + runGas = c_sstoreResetGas; _ext.sub.refunds += c_sstoreRefundGas; } else diff --git a/libevmcore/CMakeLists.txt b/libevmcore/CMakeLists.txt index b57d52fa3..6a834936b 100644 --- a/libevmcore/CMakeLists.txt +++ b/libevmcore/CMakeLists.txt @@ -25,6 +25,7 @@ else() endif() target_link_libraries(${EXECUTABLE} devcore) +target_link_libraries(${EXECUTABLE} devcrypto) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index caeab4619..d857158b9 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -34,8 +35,8 @@ vector CommonSubexpressionEliminator::getOptimizedItems() { optimizeBreakingItem(); - map initialStackContents; - map targetStackContents; + map initialStackContents; + map targetStackContents; int minHeight = m_stackHeight + 1; if (!m_stackElements.empty()) minHeight = min(minHeight, m_stackElements.begin()->first); @@ -58,18 +59,18 @@ vector CommonSubexpressionEliminator::getOptimizedItems() ostream& CommonSubexpressionEliminator::stream( ostream& _out, - map _initialStack, - map _targetStack + map _initialStack, + map _targetStack ) const { - auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id) + auto streamExpressionClass = [this](ostream& _out, Id _id) { auto const& expr = m_expressionClasses.representative(_id); _out << " " << dec << _id << ": " << *expr.item; if (expr.sequenceNumber) _out << "@" << dec << expr.sequenceNumber; _out << "("; - for (ExpressionClasses::Id arg: expr.arguments) + for (Id arg: expr.arguments) _out << dec << arg << ","; _out << ")" << endl; }; @@ -77,7 +78,7 @@ ostream& CommonSubexpressionEliminator::stream( _out << "Optimizer analysis:" << endl; _out << "Final stack height: " << dec << m_stackHeight << endl; _out << "Equivalence classes: " << endl; - for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass) + for (Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass) streamExpressionClass(_out, eqClass); _out << "Initial stack: " << endl; @@ -119,7 +120,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co ); else if (instruction != Instruction::POP) { - vector arguments(info.args); + vector arguments(info.args); for (int i = 0; i < info.args; ++i) arguments[i] = stackElement(m_stackHeight - i); if (_item.instruction() == Instruction::SSTORE) @@ -130,6 +131,8 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co storeInMemory(arguments[0], arguments[1]); else if (_item.instruction() == Instruction::MLOAD) setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0])); + else if (_item.instruction() == Instruction::SHA3) + setStackElement(m_stackHeight + _item.deposit(), applySha3(arguments.at(0), arguments.at(1))); else setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem)); } @@ -142,7 +145,6 @@ void CommonSubexpressionEliminator::optimizeBreakingItem() if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI)) return; - using Id = ExpressionClasses::Id; static AssemblyItem s_jump = Instruction::JUMP; Id condition = stackElement(m_stackHeight - 1); @@ -163,7 +165,7 @@ void CommonSubexpressionEliminator::optimizeBreakingItem() } } -void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class) +void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Id _class) { m_stackElements[_stackHeight] = _class; } @@ -194,26 +196,28 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); } -void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value) +void CommonSubexpressionEliminator::storeInStorage(Id _slot, Id _value) { if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value) // do not execute the storage if we know that the value is already there return; m_sequenceNumber++; decltype(m_storageContent) storageContents; - // copy over values at points where we know that they are different from _slot + // Copy over all values (i.e. retain knowledge about them) where we know that this store + // operation will not destroy the knowledge. Specifically, we copy storage locations we know + // are different from _slot or locations where we know that the stored value is equal to _value. for (auto const& storageItem: m_storageContent) - if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot)) + if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot) || storageItem.second == _value) storageContents.insert(storageItem); m_storageContent = move(storageContents); - ExpressionClasses::Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber); + Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber); m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id)); m_storageContent[_slot] = _value; // increment a second time so that we get unique sequence numbers for writes m_sequenceNumber++; } -ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionClasses::Id _slot) +ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(Id _slot) { if (m_storageContent.count(_slot)) return m_storageContent.at(_slot); @@ -221,7 +225,7 @@ ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionC return m_storageContent[_slot] = m_expressionClasses.find(Instruction::SLOAD, {_slot}, true, m_sequenceNumber); } -void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value) +void CommonSubexpressionEliminator::storeInMemory(Id _slot, Id _value) { if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value) // do not execute the store if we know that the value is already there @@ -233,14 +237,14 @@ void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, E if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot)) memoryContents.insert(memoryItem); m_memoryContent = move(memoryContents); - ExpressionClasses::Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber); + Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber); m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id)); m_memoryContent[_slot] = _value; // increment a second time so that we get unique sequence numbers for writes m_sequenceNumber++; } -ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionClasses::Id _slot) +ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(Id _slot) { if (m_memoryContent.count(_slot)) return m_memoryContent.at(_slot); @@ -248,6 +252,37 @@ ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionCl return m_memoryContent[_slot] = m_expressionClasses.find(Instruction::MLOAD, {_slot}, true, m_sequenceNumber); } +CommonSubexpressionEliminator::Id CommonSubexpressionEliminator::applySha3(Id _start, Id _length) +{ + // Special logic if length is a short constant, otherwise we cannot tell. + u256 const* l = m_expressionClasses.knownConstant(_length); + // unknown or too large length + if (!l || *l > 128) + return m_expressionClasses.find(Instruction::SHA3, {_start, _length}, true, m_sequenceNumber); + + vector arguments; + for (u256 i = 0; i < *l; i += 32) + { + Id slot = m_expressionClasses.find(Instruction::ADD, {_start, m_expressionClasses.find(i)}); + arguments.push_back(loadFromMemory(slot)); + } + if (m_knownSha3Hashes.count(arguments)) + return m_knownSha3Hashes.at(arguments); + Id v; + // If all arguments are known constants, compute the sha3 here + if (all_of(arguments.begin(), arguments.end(), [this](Id _a) { return !!m_expressionClasses.knownConstant(_a); })) + { + bytes data; + for (Id a: arguments) + data += toBigEndian(*m_expressionClasses.knownConstant(a)); + data.resize(size_t(*l)); + v = m_expressionClasses.find(u256(sha3(data))); + } + else + v = m_expressionClasses.find(Instruction::SHA3, {_start, _length}, true, m_sequenceNumber); + return m_knownSha3Hashes[arguments] = v; +} + CSECodeGenerator::CSECodeGenerator( ExpressionClasses& _expressionClasses, vector const& _storeOperations @@ -259,8 +294,8 @@ CSECodeGenerator::CSECodeGenerator( } AssemblyItems CSECodeGenerator::generateCode( - map const& _initialStack, - map const& _targetStackContents + map const& _initialStack, + map const& _targetStackContents ) { m_stack = _initialStack; @@ -280,7 +315,7 @@ AssemblyItems CSECodeGenerator::generateCode( } // store all needed sequenced expressions - set> sequencedExpressions; + set> sequencedExpressions; for (auto const& p: m_neededBy) for (auto id: {p.first, p.second}) if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber) @@ -327,19 +362,20 @@ AssemblyItems CSECodeGenerator::generateCode( return m_generatedItems; } -void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) +void CSECodeGenerator::addDependencies(Id _c) { if (m_neededBy.count(_c)) return; // we already computed the dependencies for _c ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); - for (ExpressionClasses::Id argument: expr.arguments) + for (Id argument: expr.arguments) { addDependencies(argument); m_neededBy.insert(make_pair(argument, _c)); } if (expr.item->type() == Operation && ( expr.item->instruction() == Instruction::SLOAD || - expr.item->instruction() == Instruction::MLOAD + expr.item->instruction() == Instruction::MLOAD || + expr.item->instruction() == Instruction::SHA3 )) { // this loads an unknown value from storage or memory and thus, in addition to its @@ -347,22 +383,52 @@ void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) // they are different that occur before this load StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ? StoreOperation::Storage : StoreOperation::Memory; - ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0); + Id slotToLoadFrom = expr.arguments.at(0); for (auto const& p: m_storeOperations) { if (p.first.first != target) continue; - ExpressionClasses::Id slot = p.first.second; + Id slot = p.first.second; StoreOperations const& storeOps = p.second; if (storeOps.front().sequenceNumber > expr.sequenceNumber) continue; - if ( - (target == StoreOperation::Memory && m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom)) || - (target == StoreOperation::Storage && m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom)) - ) + bool knownToBeIndependent = false; + switch (expr.item->instruction()) + { + case Instruction::SLOAD: + knownToBeIndependent = m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom); + break; + case Instruction::MLOAD: + knownToBeIndependent = m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom); + break; + case Instruction::SHA3: + { + Id length = expr.arguments.at(1); + Id offsetToStart = m_expressionClasses.find(Instruction::SUB, {slot, slotToLoadFrom}); + u256 const* o = m_expressionClasses.knownConstant(offsetToStart); + u256 const* l = m_expressionClasses.knownConstant(length); + if (l && *l == 0) + knownToBeIndependent = true; + else if (o) + { + // We could get problems here if both *o and *l are larger than 2**254 + // but it is probably ok for the optimizer to produce wrong code for such cases + // which cannot be executed anyway because of the non-payable price. + if (u2s(*o) <= -32) + knownToBeIndependent = true; + else if (l && u2s(*o) >= 0 && *o >= *l) + knownToBeIndependent = true; + } + break; + } + default: + break; + } + if (knownToBeIndependent) continue; + // note that store and load never have the same sequence number - ExpressionClasses::Id latestStore = storeOps.front().expression; + Id latestStore = storeOps.front().expression; for (auto it = ++storeOps.begin(); it != storeOps.end(); ++it) if (it->sequenceNumber < expr.sequenceNumber) latestStore = it->expression; @@ -372,7 +438,7 @@ void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) } } -int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced) +int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) { // do some cleanup removeStackTopIfPossible(); @@ -392,8 +458,8 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allow OptimizerException, "Sequence constrained operation requested out of sequence." ); - ExpressionClasses::Ids const& arguments = expr.arguments; - for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments)) + vector const& arguments = expr.arguments; + for (Id arg: boost::adaptors::reverse(arguments)) generateClassElement(arg); // The arguments are somewhere on the stack now, so it remains to move them at the correct place. @@ -478,7 +544,7 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allow } } -int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const +int CSECodeGenerator::classElementPosition(Id _id) const { assertThrow( m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition, @@ -488,7 +554,7 @@ int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const return m_classPositions.at(_id); } -bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result) +bool CSECodeGenerator::canBeRemoved(Id _element, Id _result) { // Returns false if _element is finally needed or is needed by a class that has not been // computed yet. Note that m_classPositions also includes classes that were deleted in the meantime. @@ -507,7 +573,7 @@ bool CSECodeGenerator::removeStackTopIfPossible() if (m_stack.empty()) return false; assertThrow(m_stack.count(m_stackHeight) > 0, OptimizerException, ""); - ExpressionClasses::Id top = m_stack[m_stackHeight]; + Id top = m_stack[m_stackHeight]; if (!canBeRemoved(top)) return false; m_generatedItems.push_back(AssemblyItem(Instruction::POP)); diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index 0dbb47b29..660e9dacc 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -57,19 +57,20 @@ using AssemblyItems = std::vector; class CommonSubexpressionEliminator { public: + using Id = ExpressionClasses::Id; struct StoreOperation { enum Target { Memory, Storage }; StoreOperation( Target _target, - ExpressionClasses::Id _slot, + Id _slot, unsigned _sequenceNumber, - ExpressionClasses::Id _expression + Id _expression ): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {} Target target; - ExpressionClasses::Id slot; + Id slot; unsigned sequenceNumber; - ExpressionClasses::Id expression; + Id expression; }; /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first @@ -83,8 +84,8 @@ public: /// Streams debugging information to @a _out. std::ostream& stream( std::ostream& _out, - std::map _initialStack = std::map(), - std::map _targetStack = std::map() + std::map _initialStack = std::map(), + std::map _targetStack = std::map() ) const; private: @@ -96,39 +97,42 @@ private: /// Simplifies the given item using /// Assigns a new equivalence class to the next sequence number of the given stack element. - void setStackElement(int _stackHeight, ExpressionClasses::Id _class); + void setStackElement(int _stackHeight, Id _class); /// Swaps the given stack elements in their next sequence number. void swapStackElements(int _stackHeightA, int _stackHeightB); /// Retrieves the current equivalence class fo the given stack element (or generates a new /// one if it does not exist yet). - ExpressionClasses::Id stackElement(int _stackHeight); + Id stackElement(int _stackHeight); /// @returns the equivalence class id of the special initial stack element at the given height /// (must not be positive). - ExpressionClasses::Id initialStackElement(int _stackHeight); + Id initialStackElement(int _stackHeight); /// Increments the sequence number, deletes all storage information that might be overwritten /// and stores the new value at the given slot. - void storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value); + void storeInStorage(Id _slot, Id _value); /// Retrieves the current value at the given slot in storage or creates a new special sload class. - ExpressionClasses::Id loadFromStorage(ExpressionClasses::Id _slot); + Id loadFromStorage(Id _slot); /// Increments the sequence number, deletes all memory information that might be overwritten /// and stores the new value at the given slot. - void storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value); + void storeInMemory(Id _slot, Id _value); /// Retrieves the current value at the given slot in memory or creates a new special mload class. - ExpressionClasses::Id loadFromMemory(ExpressionClasses::Id _slot); - + Id loadFromMemory(Id _slot); + /// Finds or creates a new expression that applies the sha3 hash function to the contents in memory. + Id applySha3(Id _start, Id _length); /// Current stack height, can be negative. int m_stackHeight = 0; /// Current stack layout, mapping stack height -> equivalence class - std::map m_stackElements; + std::map m_stackElements; /// Current sequence number, this is incremented with each modification to storage or memory. unsigned m_sequenceNumber = 1; /// Knowledge about storage content. - std::map m_storageContent; + std::map m_storageContent; /// Knowledge about memory content. Keys are memory addresses, note that the values overlap /// and are not contained here if they are not completely known. - std::map m_memoryContent; + std::map m_memoryContent; + /// Keeps record of all sha3 hashes that are computed. + std::map, Id> m_knownSha3Hashes; /// Keeps information about which storage or memory slots were written to at which sequence /// number with what instruction. std::vector m_storeOperations; @@ -149,6 +153,7 @@ class CSECodeGenerator public: using StoreOperation = CommonSubexpressionEliminator::StoreOperation; using StoreOperations = std::vector; + using Id = ExpressionClasses::Id; /// Initializes the code generator with the given classes and store operations. /// The store operations have to be sorted by sequence number in ascending order. @@ -159,25 +164,25 @@ public: /// @param _targetStackContents final contents of the stack, by stack height relative to initial /// @note should only be called once on each object. AssemblyItems generateCode( - std::map const& _initialStack, - std::map const& _targetStackContents + std::map const& _initialStack, + std::map const& _targetStackContents ); private: /// Recursively discovers all dependencies to @a m_requests. - void addDependencies(ExpressionClasses::Id _c); + void addDependencies(Id _c); /// Produce code that generates the given element if it is not yet present. /// @returns the stack position of the element or c_invalidPosition if it does not actually /// generate a value on the stack. /// @param _allowSequenced indicates that sequence-constrained operations are allowed - int generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced = false); + int generateClassElement(Id _c, bool _allowSequenced = false); /// @returns the position of the representative of the given id on the stack. /// @note throws an exception if it is not on the stack. - int classElementPosition(ExpressionClasses::Id _id) const; + int classElementPosition(Id _id) const; /// @returns true if @a _element can be removed - in general or, if given, while computing @a _result. - bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1)); + bool canBeRemoved(Id _element, Id _result = Id(-1)); /// Appends code to remove the topmost stack element if it can be removed. bool removeStackTopIfPossible(); @@ -196,19 +201,19 @@ private: /// Current height of the stack relative to the start. int m_stackHeight = 0; /// If (b, a) is in m_requests then b is needed to compute a. - std::multimap m_neededBy; + std::multimap m_neededBy; /// Current content of the stack. - std::map m_stack; + std::map m_stack; /// Current positions of equivalence classes, equal to c_invalidPosition if already deleted. - std::map m_classPositions; + std::map m_classPositions; /// The actual eqivalence class items and how to compute them. ExpressionClasses& m_expressionClasses; /// Keeps information about which storage or memory slots were written to by which operations. /// The operations are sorted ascendingly by sequence number. - std::map, StoreOperations> m_storeOperations; + std::map, StoreOperations> m_storeOperations; /// The set of equivalence classes that should be present on the stack at the end. - std::set m_finalClasses; + std::set m_finalClasses; }; template diff --git a/libevmcore/ExpressionClasses.cpp b/libevmcore/ExpressionClasses.cpp index 1c7a79e6b..81beaac7e 100644 --- a/libevmcore/ExpressionClasses.cpp +++ b/libevmcore/ExpressionClasses.cpp @@ -84,24 +84,35 @@ ExpressionClasses::Id ExpressionClasses::find( bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b) { // Try to simplify "_a - _b" and return true iff the value is a non-zero constant. - map matchGroups; - Pattern constant(Push); - constant.setMatchGroup(1, matchGroups); - Id difference = find(Instruction::SUB, {_a, _b}); - return constant.matches(representative(difference), *this) && constant.d() != u256(0); + return knownNonZero(find(Instruction::SUB, {_a, _b})); } bool ExpressionClasses::knownToBeDifferentBy32(ExpressionClasses::Id _a, ExpressionClasses::Id _b) { // Try to simplify "_a - _b" and return true iff the value is at least 32 away from zero. + u256 const* v = knownConstant(find(Instruction::SUB, {_a, _b})); + // forbidden interval is ["-31", 31] + return v && *v + 31 > u256(62); +} + +bool ExpressionClasses::knownZero(Id _c) +{ + return Pattern(u256(0)).matches(representative(_c), *this); +} + +bool ExpressionClasses::knownNonZero(Id _c) +{ + return Pattern(u256(0)).matches(representative(find(Instruction::ISZERO, {_c})), *this); +} + +u256 const* ExpressionClasses::knownConstant(Id _c) +{ map matchGroups; Pattern constant(Push); constant.setMatchGroup(1, matchGroups); - Id difference = find(Instruction::SUB, {_a, _b}); - if (!constant.matches(representative(difference), *this)) - return false; - // forbidden interval is ["-31", 31] - return constant.d() + 31 > u256(62); + if (!constant.matches(representative(_c), *this)) + return nullptr; + return &constant.d(); } string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const diff --git a/libevmcore/ExpressionClasses.h b/libevmcore/ExpressionClasses.h index 5179845f6..dba7384ec 100644 --- a/libevmcore/ExpressionClasses.h +++ b/libevmcore/ExpressionClasses.h @@ -78,6 +78,15 @@ public: bool knownToBeDifferent(Id _a, Id _b); /// Similar to @a knownToBeDifferent but require that abs(_a - b) >= 32. bool knownToBeDifferentBy32(Id _a, Id _b); + /// @returns true if the value of the given class is known to be zero. + /// @note that this is not the negation of knownNonZero + bool knownZero(Id _c); + /// @returns true if the value of the given class is known to be nonzero. + /// @note that this is not the negation of knownZero + bool knownNonZero(Id _c); + /// @returns a pointer to the value if the given class is known to be a constant, + /// and a nullptr otherwise. + u256 const* knownConstant(Id _c); std::string fullDAGToString(Id _id) const; @@ -131,7 +140,7 @@ public: /// @returns the id of the matched expression if this pattern is part of a match group. Id id() const { return matchGroupValue().id; } /// @returns the data of the matched expression if this pattern is part of a match group. - u256 d() const { return matchGroupValue().item->data(); } + u256 const& d() const { return matchGroupValue().item->data(); } std::string toString() const; diff --git a/libevmcore/Instruction.cpp b/libevmcore/Instruction.cpp index 23f19ac94..03b6ccf2f 100644 --- a/libevmcore/Instruction.cpp +++ b/libevmcore/Instruction.cpp @@ -21,6 +21,7 @@ #include "Instruction.h" +#include #include #include #include @@ -294,27 +295,42 @@ static const std::map c_instructionInfo = { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true, ZeroTier } } }; -string dev::eth::disassemble(bytes const& _mem) +void dev::eth::eachInstruction( + bytes const& _mem, + function const& _onInstruction +) { - stringstream ret; - unsigned numerics = 0; for (auto it = _mem.begin(); it != _mem.end(); ++it) { - byte n = *it; - auto iit = c_instructionInfo.find((Instruction)n); - if (numerics || iit == c_instructionInfo.end() || (byte)iit->first != n) // not an instruction or expecting an argument... + Instruction instr = Instruction(*it); + size_t additional = 0; + if (isValidInstruction(instr)) + additional = instructionInfo(instr).additional; + u256 data; + for (size_t i = 0; i < additional; ++i) { - if (numerics) - numerics--; - ret << "0x" << hex << (int)n << " "; + data <<= 8; + if (it != _mem.end() && ++it != _mem.end()) + data |= *it; } + _onInstruction(instr, data); + } +} + +string dev::eth::disassemble(bytes const& _mem) +{ + stringstream ret; + eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) { + if (!isValidInstruction(_instr)) + ret << "0x" << hex << int(_instr) << " "; else { - auto const& ii = iit->second; - ret << ii.name << " "; - numerics = ii.additional; + InstructionInfo info = instructionInfo(_instr); + ret << info.name << " "; + if (info.additional) + ret << "0x" << hex << _data << " "; } - } + }); return ret.str(); } diff --git a/libevmcore/Instruction.h b/libevmcore/Instruction.h index 07c7b52fd..2ccf5bc34 100644 --- a/libevmcore/Instruction.h +++ b/libevmcore/Instruction.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -253,6 +254,9 @@ bool isValidInstruction(Instruction _inst); /// Convert from string mnemonic to Instruction type. extern const std::map c_instructions; +/// Iterate through EVM code and call a function on each instruction. +void eachInstruction(bytes const& _mem, std::function const& _onInstruction); + /// Convert from EVM code to simple EVM assembly language. std::string disassemble(bytes const& _mem); diff --git a/libevmcore/SemanticInformation.cpp b/libevmcore/SemanticInformation.cpp index e561e7554..139c99ce2 100644 --- a/libevmcore/SemanticInformation.cpp +++ b/libevmcore/SemanticInformation.cpp @@ -52,8 +52,6 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item) return true; // GAS and PC assume a specific order of opcodes if (_item.instruction() == Instruction::MSIZE) return true; // msize is modified already by memory access, avoid that for now - if (_item.instruction() == Instruction::SHA3) - return true; //@todo: we have to compare sha3's not based on their memory addresses but on the memory content. InstructionInfo info = instructionInfo(_item.instruction()); if (_item.instruction() == Instruction::SSTORE) return false; diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index e00f56962..8c5a50750 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -245,7 +245,7 @@ bool Session::interpret(PacketType _t, RLP const& _r) break; default: for (auto const& i: m_capabilities) - if (_t >= i.second->m_idOffset && _t - i.second->m_idOffset < i.second->hostCapability()->messageCount()) + if (_t >= (int)i.second->m_idOffset && _t - i.second->m_idOffset < i.second->hostCapability()->messageCount()) { if (i.second->m_enabled) return i.second->interpret(_t - i.second->m_idOffset, _r); diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index b42b6e5c0..0abd188c1 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -27,7 +27,6 @@ endif() target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} evmcore) -target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} devcrypto) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 79fd75d77..072d96331 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -170,18 +170,6 @@ static Json::Value toJson(map const& _storage) return res; } -static unsigned toBlockNumber(std::string const& _js) -{ - if (_js == "latest") - return LatestBlock; - else if (_js == "earliest") - return 0; - else if (_js == "pending") - return PendingBlock; - else - return (unsigned)jsToInt(_js); -} - static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to avoid warning. Uncomment once in use @ PoC-7. { dev::eth::LogFilter filter; @@ -190,9 +178,9 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to // check only !empty. it should throw exceptions if input params are incorrect if (!_json["fromBlock"].empty()) - filter.withEarliest(toBlockNumber(_json["fromBlock"].asString())); + filter.withEarliest(jsToBlockNumber(_json["fromBlock"].asString())); if (!_json["toBlock"].empty()) - filter.withLatest(toBlockNumber(_json["toBlock"].asString())); + filter.withLatest(jsToBlockNumber(_json["toBlock"].asString())); if (!_json["address"].empty()) { if (_json["address"].isArray()) @@ -336,7 +324,7 @@ string WebThreeStubServerBase::eth_getBalance(string const& _address, string con { try { - return toJS(client()->balanceAt(jsToAddress(_address), toBlockNumber(_blockNumber))); + return toJS(client()->balanceAt(jsToAddress(_address), jsToBlockNumber(_blockNumber))); } catch (...) { @@ -348,7 +336,7 @@ string WebThreeStubServerBase::eth_getStorageAt(string const& _address, string c { try { - return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_position), toBlockNumber(_blockNumber))); + return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_position), jsToBlockNumber(_blockNumber))); } catch (...) { @@ -360,7 +348,7 @@ string WebThreeStubServerBase::eth_getTransactionCount(string const& _address, s { try { - return toJS(client()->countAt(jsToAddress(_address), toBlockNumber(_blockNumber))); + return toJS(client()->countAt(jsToAddress(_address), jsToBlockNumber(_blockNumber))); } catch (...) { @@ -385,7 +373,7 @@ string WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const { try { - return toJS(_blockNumber == "pending" ? client()->pending().size() : client()->transactionCount(client()->hashFromNumber(toBlockNumber(_blockNumber)))); + return toJS(client()->transactionCount(jsToBlockNumber(_blockNumber))); } catch (...) { @@ -409,7 +397,7 @@ string WebThreeStubServerBase::eth_getUncleCountByBlockNumber(string const& _blo { try { - return toJS(client()->uncleCount(client()->hashFromNumber(toBlockNumber(_blockNumber)))); + return toJS(client()->uncleCount(jsToBlockNumber(_blockNumber))); } catch (...) { @@ -421,7 +409,7 @@ string WebThreeStubServerBase::eth_getCode(string const& _address, string const& { try { - return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber))); + return toJS(client()->codeAt(jsToAddress(_address), jsToBlockNumber(_blockNumber))); } catch (...) { @@ -499,7 +487,7 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const& try { t = toTransaction(_json); - number = toBlockNumber(_blockNumber); + number = jsToBlockNumber(_blockNumber); } catch (...) { @@ -546,7 +534,7 @@ Json::Value WebThreeStubServerBase::eth_getBlockByNumber(string const& _blockNum { try { - auto h = client()->hashFromNumber(jsToInt(_blockNumber)); + auto h = jsToBlockNumber(_blockNumber); if (_includeTransactions) return toJson(client()->blockInfo(h), client()->uncleHashes(h), client()->transactions(h)); else @@ -586,7 +574,7 @@ Json::Value WebThreeStubServerBase::eth_getTransactionByBlockNumberAndIndex(stri { try { - return toJson(client()->transaction(client()->hashFromNumber(jsToInt(_blockNumber)), jsToInt(_transactionIndex))); + return toJson(client()->transaction(jsToBlockNumber(_blockNumber), jsToInt(_transactionIndex))); } catch (...) { @@ -610,7 +598,7 @@ Json::Value WebThreeStubServerBase::eth_getUncleByBlockNumberAndIndex(string con { try { - return toJson(client()->uncle(client()->hashFromNumber(toBlockNumber(_blockNumber)), jsToInt(_uncleIndex))); + return toJson(client()->uncle(jsToBlockNumber(_blockNumber), jsToInt(_uncleIndex))); } catch (...) { diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 731a5cb7e..41aa55249 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -154,6 +154,14 @@ QVariantMap ClientModel::contractAddresses() const return res; } +QVariantMap ClientModel::gasCosts() const +{ + QVariantMap res; + for (auto const& c: m_gasCosts) + res.insert(c.first, QVariant::fromValue(static_cast(c.second))); + return res; +} + void ClientModel::setupState(QVariantMap _state) { QVariantList balances = _state.value("accounts").toList(); @@ -173,6 +181,7 @@ void ClientModel::setupState(QVariantMap _state) QString contractId = transaction.value("contractId").toString(); QString functionId = transaction.value("functionId").toString(); u256 gas = boost::get(qvariant_cast(transaction.value("gas"))->internalValue()); + bool gasAuto = transaction.value("gasAuto").toBool(); u256 value = (qvariant_cast(transaction.value("value")))->toU256Wei(); u256 gasPrice = (qvariant_cast(transaction.value("gasPrice")))->toU256Wei(); QString sender = transaction.value("sender").toString(); @@ -183,7 +192,7 @@ void ClientModel::setupState(QVariantMap _state) contractId = functionId; TransactionSettings transactionSettings(contractId, transaction.value("url").toString()); transactionSettings.gasPrice = 10000000000000; - transactionSettings.gas = 125000; + transactionSettings.gasAuto = true; transactionSettings.value = 0; transactionSettings.sender = Secret(sender.toStdString()); transactionSequence.push_back(transactionSettings); @@ -192,7 +201,7 @@ void ClientModel::setupState(QVariantMap _state) { if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later contractId = m_codeModel->contracts().keys()[0]; - TransactionSettings transactionSettings(contractId, functionId, value, gas, gasPrice, Secret(sender.toStdString())); + TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString())); transactionSettings.parameterValues = transaction.value("parameters").toMap(); if (contractId == functionId || functionId == "Constructor") @@ -228,7 +237,7 @@ void ClientModel::executeSequence(vector const& _sequence, //std contract bytes const& stdContractCode = m_codeModel->getStdContractCode(transaction.contractId, transaction.stdContractUrl); TransactionSettings stdTransaction = transaction; - stdTransaction.gas = 500000;// TODO: get this from std contracts library + stdTransaction.gasAuto = true; Address address = deployContract(stdContractCode, stdTransaction); m_stdContractAddresses[stdTransaction.contractId] = address; m_stdContractNames[address] = stdTransaction.contractId; @@ -277,6 +286,8 @@ void ClientModel::executeSequence(vector const& _sequence, m_contractNames[newAddress] = transaction.contractId; contractAddressesChanged(); } + gasCostsChanged(); + m_gasCosts[transaction.contractId] = m_client->lastExecution().gasUsed; } else { @@ -507,13 +518,13 @@ void ClientModel::debugRecord(unsigned _index) Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction) { - Address newAddress = m_client->submitTransaction(_ctrTransaction.sender, _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice); + Address newAddress = m_client->submitTransaction(_ctrTransaction.sender, _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice, _ctrTransaction.gasAuto); return newAddress; } void ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) { - m_client->submitTransaction(_tr.sender, _tr.value, _contract, _data, _tr.gas, _tr.gasPrice); + m_client->submitTransaction(_tr.sender, _tr.value, _contract, _data, _tr.gas, _tr.gasPrice, _tr.gasAuto); } RecordLogEntry* ClientModel::lastBlock() const @@ -523,7 +534,7 @@ RecordLogEntry* ClientModel::lastBlock() const strGas << blockInfo.gasUsed; stringstream strNumber; strNumber << blockInfo.number; - RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash().ref()))), tr("Gas Used: ") + QString::fromStdString(strGas.str()), QString(), QString(), false, RecordLogEntry::RecordType::Block); + RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str())); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); return record; } @@ -544,13 +555,17 @@ void ClientModel::onNewTransaction() unsigned recordIndex = tr.executonIndex; QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex); QString address = QString::fromStdString(toJS(tr.address)); - QString value = QString::fromStdString(toString(tr.value)); + QString value = QString::fromStdString(toString(tr.value)); QString contract = address; QString function; QString returned; + QString gasUsed; bool creation = (bool)tr.contractAddress; + if (!tr.isCall()) + gasUsed = QString::fromStdString(toString(tr.gasUsed)); + //TODO: handle value transfer FixedHash<4> functionHash; bool abi = false; @@ -605,7 +620,7 @@ void ClientModel::onNewTransaction() } } - RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction); + RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, gasUsed); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); emit newRecord(log); } diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 4b597a1ea..ddc0e6cf1 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -48,10 +48,10 @@ struct SolidityType; struct TransactionSettings { TransactionSettings() {} - TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice, Secret _sender): - contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice), sender(_sender) {} + TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender): + contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender) {} TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl): - contractId(_stdContractName), stdContractUrl(_stdContractUrl) {} + contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl) {} /// Contract name QString contractId; @@ -61,6 +61,8 @@ struct TransactionSettings u256 value; /// Gas u256 gas; + /// Calculate gas automatically + bool gasAuto = true; /// Gas price u256 gasPrice; /// Mapping from contract function parameter name to value @@ -95,6 +97,8 @@ class RecordLogEntry: public QObject Q_PROPERTY(bool call MEMBER m_call CONSTANT) /// @returns record type Q_PROPERTY(RecordType type MEMBER m_type CONSTANT) + /// Gas used + Q_PROPERTY(QString gasUsed MEMBER m_gasUsed CONSTANT) public: enum RecordType @@ -105,8 +109,8 @@ public: RecordLogEntry(): m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {} - RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type): - m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type) {} + RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed): + m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed) {} private: unsigned m_recordIndex; @@ -118,6 +122,7 @@ private: QString m_returned; bool m_call; RecordType m_type; + QString m_gasUsed; }; /** @@ -136,6 +141,8 @@ public: Q_PROPERTY(bool mining MEMBER m_mining NOTIFY miningStateChanged) /// @returns deployed contracts addresses Q_PROPERTY(QVariantMap contractAddresses READ contractAddresses NOTIFY contractAddressesChanged) + /// @returns deployed contracts gas costs + Q_PROPERTY(QVariantMap gasCosts READ gasCosts NOTIFY gasCostsChanged) // @returns the last block Q_PROPERTY(RecordLogEntry* lastBlock READ lastBlock CONSTANT) /// ethereum.js RPC request entry point @@ -180,6 +187,8 @@ signals: void runFailed(QString const& _message); /// Contract address changed void contractAddressesChanged(); + /// Gas costs updated + void gasCostsChanged(); /// Execution state changed void newBlock(); /// Execution state changed @@ -197,6 +206,7 @@ signals: private: RecordLogEntry* lastBlock() const; QVariantMap contractAddresses() const; + QVariantMap gasCosts() const; void executeSequence(std::vector const& _sequence, std::map const& _balances); dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings()); void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); @@ -212,6 +222,7 @@ private: std::unique_ptr m_client; std::unique_ptr m_rpcConnector; std::unique_ptr m_web3Server; + std::map m_gasCosts; std::map m_contractAddresses; std::map m_contractNames; std::map m_stdContractAddresses; diff --git a/mix/MachineStates.h b/mix/MachineStates.h index f0346ba1e..afd2b990a 100644 --- a/mix/MachineStates.h +++ b/mix/MachineStates.h @@ -80,6 +80,7 @@ namespace mix dev::Address sender; dev::Address contractAddress; dev::u256 value; + dev::u256 gasUsed; unsigned transactionIndex; unsigned executonIndex = 0; diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index 54c860a8d..4822b8da5 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -52,13 +52,8 @@ ApplicationService::ApplicationService() MixApplication::MixApplication(int& _argc, char* _argv[]): QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()) { + setWindowIcon(QIcon(":/res/mix_256x256x32.png")); m_engine->load(QUrl("qrc:/qml/Application.qml")); - if (!m_engine->rootObjects().empty()) - { - QWindow *window = qobject_cast(m_engine->rootObjects().at(0)); - if (window) - window->setIcon(QIcon(":/res/mix_256x256x32.png")); - } } void MixApplication::initialize() diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 58cab151d..60d97e5b7 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -40,10 +40,8 @@ namespace dev namespace mix { -// TODO: merge as much as possible with the Client.cpp into a mutually inherited base class. - -const Secret c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); -const u256 c_mixGenesisDifficulty = c_minimumDifficulty; //TODO: make it lower for Mix somehow +Secret const c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); +u256 const c_mixGenesisDifficulty = c_minimumDifficulty; //TODO: make it lower for Mix somehow bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) { @@ -100,9 +98,18 @@ void MixClient::resetState(std::map _accounts) m_executions.clear(); } -void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call) +Transaction MixClient::replaceGas(Transaction const& _t, Secret const& _secret, u256 const& _gas) +{ + if (_t.isCreation()) + return Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret); + else + return Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret); +} + +void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret) { - bytes rlp = _t.rlp(); + Transaction t = _gasAuto ? replaceGas(_t, _secret, m_state.gasLimitRemaining()) : _t; + bytes rlp = t.rlp(); // do debugging run first LastHashes lastHashes(256); @@ -167,6 +174,31 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c execution.go(onOp); execution.finalize(); + dev::eth::ExecutionResult er = execution.executionResult(); + + switch (er.excepted) + { + case TransactionException::None: + break; + case TransactionException::NotEnoughCash: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Insufficient balance for contract deployment")); + case TransactionException::OutOfGasBase: + case TransactionException::OutOfGas: + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas")); + case TransactionException::BlockGasLimitReached: + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached")); + case TransactionException::OutOfStack: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack")); + case TransactionException::StackUnderflow: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Stack underflow")); + //these should not happen in mix + case TransactionException::Unknown: + case TransactionException::BadInstruction: + case TransactionException::BadJumpDestination: + case TransactionException::InvalidSignature: + case TransactionException::InvalidNonce: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Internal execution error")); + }; ExecutionResult d; d.result = execution.executionResult(); @@ -176,6 +208,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c d.address = _t.receiveAddress(); d.sender = _t.sender(); d.value = _t.value(); + d.gasUsed = er.gasUsed + er.gasRefunded; if (_t.isCreation()) d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); if (!_call) @@ -185,9 +218,11 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c // execute on a state if (!_call) { - dev::eth::ExecutionResult er =_state.execute(lastHashes, _t); - if (_t.isCreation() && _state.code(d.contractAddress).empty()) + t = _gasAuto ? replaceGas(_t, _secret, d.gasUsed) : _t; + er =_state.execute(lastHashes, t); + if (t.isCreation() && _state.code(d.contractAddress).empty()) BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); + d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit; // collect watches h256Set changed; Guard l(x_filtersWatches); @@ -242,25 +277,25 @@ State MixClient::asOf(h256 const& _block) const return State(m_stateDB, bc(), _block); } -void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto) { WriteGuard l(x_state); u256 n = m_state.transactionsFrom(toAddress(_secret)); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - executeTransaction(t, m_state, false); + executeTransaction(t, m_state, false, _gasAuto, _secret); } -Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) +Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto) { WriteGuard l(x_state); u256 n = m_state.transactionsFrom(toAddress(_secret)); eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); - executeTransaction(t, m_state, false); + executeTransaction(t, m_state, false, _gasAuto, _secret); Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); return address; } -dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) +dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto) { (void)_blockNumber; State temp = asOf(eth::PendingBlock); @@ -268,10 +303,25 @@ dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _ Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); bytes rlp = t.rlp(); WriteGuard lw(x_state); //TODO: lock is required only for last execution state - executeTransaction(t, temp, true); + executeTransaction(t, temp, true, _gasAuto, _secret); return lastExecution().result; } +void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +{ + submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, false); +} + +Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) +{ + return submitTransaction(_secret, _endowment, _init, _gas, _gasPrice, false); +} + +dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) +{ + return call(_secret, _value, _dest, _data, _gas, _gasPrice, _blockNumber,false); +} + dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) { (void)_blockNumber; @@ -285,7 +335,7 @@ dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes c Transaction t(_value, _gasPrice, _gas, _data, n, _secret); bytes rlp = t.rlp(); WriteGuard lw(x_state); //TODO: lock is required only for last execution state - executeTransaction(t, temp, true); + executeTransaction(t, temp, true, false, _secret); return lastExecution().result; } diff --git a/mix/MixClient.h b/mix/MixClient.h index abd9445c7..c52e7bd88 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -58,6 +58,10 @@ public: dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock) override; dev::eth::ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock) override; + void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto); + Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto); + dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto); + void setAddress(Address _us) override; void setMiningThreads(unsigned _threads) override; unsigned miningThreads() const override; @@ -86,8 +90,9 @@ protected: virtual void prepareForTransaction() override {} private: - void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call); + void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret); void noteChanged(h256Set const& _filters); + dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::Secret const& _secret, dev::u256 const& _gas); std::vector m_userAccounts; eth::State m_state; diff --git a/mix/qml/DeploymentDialog.qml b/mix/qml/DeploymentDialog.qml index 6ed7afed5..1e230f088 100644 --- a/mix/qml/DeploymentDialog.qml +++ b/mix/qml/DeploymentDialog.qml @@ -73,6 +73,12 @@ Dialog { balance.text = comboAccounts.balances[0]; }); }); + + var gas = 0; + var gasCosts = clientModel.gasCosts; + for (var g in gasCosts) + gas += gasCosts[g]; + gasToUse = gas; } function stopForInputError(inError) @@ -421,7 +427,7 @@ Dialog { Layout.preferredWidth: 350 id: localPackageUrl readOnly: true - enabled: rowRegister.isOkToRegister() + } DefaultLabel diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index c90692572..7b7f33361 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -44,6 +44,7 @@ Item { value: QEtherHelper.createEther(t.value.value, t.value.unit), gas: QEtherHelper.createBigInt(t.gas.value), gasPrice: QEtherHelper.createEther(t.gasPrice.value, t.gasPrice.unit), + gasAuto: t.gasAuto, stdContract: t.stdContract ? true : false, parameters: {}, sender: t.sender @@ -91,6 +92,7 @@ Item { url: t.url, value: { value: t.value.value, unit: t.value.unit }, gas: { value: t.gas.value() }, + gasAuto: t.gasAuto, gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit }, stdContract: t.stdContract, parameters: {} diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index f53e16a06..2c36bf2ea 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -17,6 +17,7 @@ Dialog { title: qsTr("Edit Transaction") property int transactionIndex property alias gas: gasValueEdit.gasValue; + property alias gasAuto: gasAutoCheck.checked; property alias gasPrice: gasPriceField.value; property alias transactionValue: valueField.value; property string contractId: contractComboBox.currentValue(); @@ -39,6 +40,7 @@ Dialog { transactionIndex = index; gasValueEdit.gasValue = item.gas; + gasAutoCheck.checked = item.gasAuto ? true : false; gasPriceField.value = item.gasPrice; valueField.value = item.value; var contractId = item.contractId; @@ -164,6 +166,7 @@ Dialog { contractId: transactionDialog.contractId, functionId: transactionDialog.functionId, gas: transactionDialog.gas, + gasAuto: transactionDialog.gasAuto, gasPrice: transactionDialog.gasPrice, value: transactionDialog.transactionValue, parameters: {}, @@ -314,8 +317,16 @@ Dialog { onGasValueChanged: text = gasValue.value(); onTextChanged: gasValue.setValue(text); implicitWidth: 200 + enabled: !gasAutoCheck.checked id: gasValueEdit; } + + CheckBox + { + id: gasAutoCheck + checked: true + text: qsTr("Auto"); + } } CommonSeparator diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index 5668c6e05..ad97846ae 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -127,6 +127,11 @@ Item { title: qsTr("Returned") width: 120 } + TableViewColumn { + role: "gasUsed" + title: qsTr("Gas Used") + width: 120 + } onActivated: { var item = logTable.model.get(row); if (item.type === RecordLogEntry.Transaction) diff --git a/mix/qml/js/TransactionHelper.js b/mix/qml/js/TransactionHelper.js index 566b04910..f0b4991fc 100644 --- a/mix/qml/js/TransactionHelper.js +++ b/mix/qml/js/TransactionHelper.js @@ -6,6 +6,7 @@ function defaultTransaction() value: createEther("0", QEther.Wei), functionId: "", gas: createBigInt("250000"), + gasAuto: true, gasPrice: createEther("100000", QEther.Wei), parameters: {}, stdContract: false diff --git a/neth/main.cpp b/neth/main.cpp index 7ee4962e9..91b02fc24 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -40,6 +40,7 @@ #include #include #endif +#include #include "BuildInfo.h" #undef KEY_EVENT // from windows.h @@ -76,6 +77,7 @@ void help() << " -c,--client-name Add a name to your client's version string (default: blank)." << endl << " -d,--db-path Load database from path (default: ~/.ethereum " << endl << " /Etherum or Library/Application Support/Ethereum)." << endl + << " -D,--initdag Initialize DAG for mining and exit." << endl << " -e,--ether-price Set the ether price in the reference unit e.g. ¢ (Default: 30.679)." << endl << " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl << " -h,--help Show this help message and exit." << endl @@ -321,6 +323,7 @@ enum class NodeMode int main(int argc, char** argv) { + bool initDAG = true; string listenIP; unsigned short listenPort = 30303; string publicIP; @@ -424,6 +427,8 @@ int main(int argc, char** argv) structuredLogging = true; else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) dbPath = argv[++i]; + else if (arg == "-D" || arg == "--initdag") + initDAG = true; else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) { try @@ -556,6 +561,14 @@ int main(int argc, char** argv) &nodesState, miners ); + + if (initDAG) + { + cout << "Initialize DAG. (This will take awhile)" << endl; + Ethasher::get()->full(web3.ethereum()->blockChain().info()); + return 0; + } + web3.setIdealPeerCount(peers); std::shared_ptr gasPricer = make_shared(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr; @@ -873,7 +886,7 @@ int main(int argc, char** argv) ssbd << bbd; cnote << ssbd.str(); int ssize = fields[4].length(); - u256 minGas = (u256)Client::txGas(data, 0); + u256 minGas = (u256)Transaction::gasRequired(data, 0); if (size < 40) { if (size > 0) @@ -939,7 +952,7 @@ int main(int argc, char** argv) auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); - u256 minGas = (u256)Client::txGas(bytes(), 0); + u256 minGas = (u256)Transaction::gasRequired(bytes(), 0); try { Address dest = h160(fromHex(fields[0], WhenError::Throw)); @@ -1027,7 +1040,7 @@ int main(int argc, char** argv) cnote << "Init:"; cnote << ssc.str(); } - u256 minGas = (u256)Client::txGas(init, 0); + u256 minGas = (u256)Transaction::gasRequired(init, 0); if (!init.size()) cwarn << "Contract creation aborted, no init code."; else if (endowment < 0) @@ -1113,7 +1126,7 @@ int main(int argc, char** argv) auto b = bc.block(h); for (auto const& i: RLP(b)[1]) { - Transaction t(i.data(), CheckSignature::Sender); + Transaction t(i.data(), CheckTransaction::Everything); auto s = t.receiveAddress() ? boost::format(" %1% %2%> %3%: %4% [%5%]") % toString(t.safeSender()) % diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index 4fedd642d..f523847f1 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -56,8 +56,16 @@ public: m_nonOptimizedContract = m_contractAddress; m_optimize = true; bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName); + size_t nonOptimizedSize = 0; + eth::eachInstruction(nonOptimizedBytecode, [&](Instruction, u256 const&) { + nonOptimizedSize++; + }); + size_t optimizedSize = 0; + eth::eachInstruction(optimizedBytecode, [&](Instruction, u256 const&) { + optimizedSize++; + }); BOOST_CHECK_MESSAGE( - nonOptimizedBytecode.size() > optimizedBytecode.size(), + nonOptimizedSize > optimizedSize, "Optimizer did not reduce bytecode size." ); m_optimizedContract = m_contractAddress; @@ -75,11 +83,16 @@ public: "\nOptimized: " + toHex(optimizedOutput)); } - void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation) + AssemblyItems getCSE(AssemblyItems const& _input) { eth::CommonSubexpressionEliminator cse; BOOST_REQUIRE(cse.feedItems(_input.begin(), _input.end()) == _input.end()); - AssemblyItems output = cse.getOptimizedItems(); + return cse.getOptimizedItems(); + } + + void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation) + { + AssemblyItems output = getCSE(_input); BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); } @@ -569,6 +582,155 @@ BOOST_AUTO_TEST_CASE(cse_jumpi_jump) }); } +BOOST_AUTO_TEST_CASE(cse_empty_sha3) +{ + AssemblyItems input{ + u256(0), + Instruction::DUP2, + Instruction::SHA3 + }; + checkCSE(input, { + u256(sha3(bytesConstRef())) + }); +} + +BOOST_AUTO_TEST_CASE(cse_partial_sha3) +{ + AssemblyItems input{ + u256(0xabcd) << (256 - 16), + u256(0), + Instruction::MSTORE, + u256(2), + u256(0), + Instruction::SHA3 + }; + checkCSE(input, { + u256(0xabcd) << (256 - 16), + u256(0), + Instruction::MSTORE, + u256(sha3(bytes{0xab, 0xcd})) + }); +} + +BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_location) +{ + // sha3 twice from same dynamic location + AssemblyItems input{ + Instruction::DUP2, + Instruction::DUP1, + Instruction::MSTORE, + u256(64), + Instruction::DUP2, + Instruction::SHA3, + u256(64), + Instruction::DUP3, + Instruction::SHA3 + }; + checkCSE(input, { + Instruction::DUP2, + Instruction::DUP1, + Instruction::MSTORE, + u256(64), + Instruction::DUP2, + Instruction::SHA3, + Instruction::DUP1 + }); +} + +BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content) +{ + // sha3 twice from different dynamic location but with same content + AssemblyItems input{ + Instruction::DUP1, + u256(0x80), + Instruction::MSTORE, // m[128] = DUP1 + u256(0x20), + u256(0x80), + Instruction::SHA3, // sha3(m[128..(128+32)]) + Instruction::DUP2, + u256(12), + Instruction::MSTORE, // m[12] = DUP1 + u256(0x20), + u256(12), + Instruction::SHA3 // sha3(m[12..(12+32)]) + }; + checkCSE(input, { + u256(0x80), + Instruction::DUP2, + Instruction::DUP2, + Instruction::MSTORE, + u256(0x20), + Instruction::SWAP1, + Instruction::SHA3, + u256(12), + Instruction::DUP3, + Instruction::SWAP1, + Instruction::MSTORE, + Instruction::DUP1 + }); +} + +BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_dynamic_store_in_between) +{ + // sha3 twice from different dynamic location but with same content, + // dynamic mstore in between, which forces us to re-calculate the sha3 + AssemblyItems input{ + u256(0x80), + Instruction::DUP2, + Instruction::DUP2, + Instruction::MSTORE, // m[128] = DUP1 + u256(0x20), + Instruction::DUP1, + Instruction::DUP3, + Instruction::SHA3, // sha3(m[128..(128+32)]) + u256(12), + Instruction::DUP5, + Instruction::DUP2, + Instruction::MSTORE, // m[12] = DUP1 + Instruction::DUP12, + Instruction::DUP14, + Instruction::MSTORE, // destroys memory knowledge + Instruction::SWAP2, + Instruction::SWAP1, + Instruction::SWAP2, + Instruction::SHA3 // sha3(m[12..(12+32)]) + }; + checkCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_noninterfering_store_in_between) +{ + // sha3 twice from different dynamic location but with same content, + // dynamic mstore in between, but does not force us to re-calculate the sha3 + AssemblyItems input{ + u256(0x80), + Instruction::DUP2, + Instruction::DUP2, + Instruction::MSTORE, // m[128] = DUP1 + u256(0x20), + Instruction::DUP1, + Instruction::DUP3, + Instruction::SHA3, // sha3(m[128..(128+32)]) + u256(12), + Instruction::DUP5, + Instruction::DUP2, + Instruction::MSTORE, // m[12] = DUP1 + Instruction::DUP12, + u256(12 + 32), + Instruction::MSTORE, // does not destoy memory knowledge + Instruction::DUP13, + u256(128 - 32), + Instruction::MSTORE, // does not destoy memory knowledge + u256(0x20), + u256(12), + Instruction::SHA3 // sha3(m[12..(12+32)]) + }; + // if this changes too often, only count the number of SHA3 and MSTORE instructions + AssemblyItems output = getCSE(input); + BOOST_CHECK_EQUAL(4, count(output.begin(), output.end(), AssemblyItem(Instruction::MSTORE))); + BOOST_CHECK_EQUAL(1, count(output.begin(), output.end(), AssemblyItem(Instruction::SHA3))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index dd7c09eab..757a6e7a6 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -172,7 +172,7 @@ void ImportTest::importTransaction(json_spirit::mObject& _o) { RLPStream transactionRLPStream = createRLPStreamFromTransactionFields(_o); RLP transactionRLP(transactionRLPStream.out()); - m_transaction = Transaction(transactionRLP.data(), CheckSignature::Sender); + m_transaction = Transaction(transactionRLP.data(), CheckTransaction::Everything); } } diff --git a/test/blockchain.cpp b/test/blockchain.cpp index a223e8d4e..6c1cfebd6 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -383,7 +383,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) try { - Transaction t(createRLPStreamFromTransactionFields(tx).out(), CheckSignature::Sender); + Transaction t(createRLPStreamFromTransactionFields(tx).out(), CheckTransaction::Everything); txsFromField.push_back(t); } catch (Exception const& _e) @@ -400,7 +400,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) RLP root(blockRLP); for (auto const& tr: root[1]) { - Transaction tx(tr.data(), CheckSignature::Sender); + Transaction tx(tr.data(), CheckTransaction::Everything); txsFromRlp.push_back(tx); } diff --git a/test/transaction.cpp b/test/transaction.cpp index 4c57326ba..7aa073f29 100644 --- a/test/transaction.cpp +++ b/test/transaction.cpp @@ -44,7 +44,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) { bytes stream = importByteArray(o["rlp"].get_str()); RLP rlp(stream); - txFromRlp = Transaction(rlp.data(), CheckSignature::Sender); + txFromRlp = Transaction(rlp.data(), CheckTransaction::Everything); if (!txFromRlp.signature().isValid()) BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); } @@ -64,7 +64,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) BOOST_REQUIRE(o.count("transaction") > 0); mObject tObj = o["transaction"].get_obj(); - Transaction txFromFields(createRLPStreamFromTransactionFields(tObj).out(), CheckSignature::Sender); + Transaction txFromFields(createRLPStreamFromTransactionFields(tObj).out(), CheckTransaction::Everything); //Check the fields restored from RLP to original fields BOOST_CHECK_MESSAGE(txFromFields.data() == txFromRlp.data(), "Data in given RLP not matching the Transaction data!"); @@ -91,7 +91,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) try { - Transaction txFromFields(rlpStream.out(), CheckSignature::Sender); + Transaction txFromFields(rlpStream.out(), CheckTransaction::Everything); if (!txFromFields.signature().isValid()) BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") );