diff --git a/CMakeLists.txt b/CMakeLists.txt index d8c517fef..e3a7d9b83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ if (NOT LANGUAGES) add_subdirectory(libqethereum) add_subdirectory(alethzero) add_subdirectory(third) + add_subdirectory(mix) if(QTQML) #add_subdirectory(iethxi) #add_subdirectory(walleth) // resurect once we want to submit ourselves to QML. @@ -171,3 +172,4 @@ add_test(NAME alltests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND testet #unset(TARGET_PLATFORM CACHE) + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index e5506a660..4a32f66b2 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1259,9 +1259,11 @@ void Main::on_blocks_currentItemChanged() s << "
D/TD: 2^" << log2((double)info.difficulty) << "/2^" << log2((double)details.totalDifficulty) << ""; s << "   Children: " << details.children.size() << ""; s << "
Gas used/limit: " << info.gasUsed << "/" << info.gasLimit << ""; - s << "   Minimum gas price: " << formatBalance(info.minGasPrice) << ""; s << "
Coinbase: " << pretty(info.coinbaseAddress).toHtmlEscaped().toStdString() << " " << info.coinbaseAddress; s << "
Nonce: " << info.nonce << ""; + s << "
Hash w/o nonce: " << info.headerHashWithoutNonce() << ""; + s << "
Difficulty: " << info.difficulty << ""; + s << "
Proof-of-Work: " << ProofOfWork::eval(info.headerHashWithoutNonce(), info.nonce) << " <= " << (h256)u256((bigint(1) << 256) / info.difficulty) << ""; s << "
Parent: " << info.parentHash << ""; // s << "
Bloom: " << details.bloom << ""; s << "
Log Bloom: " << info.logBloom << ""; @@ -1282,6 +1284,7 @@ void Main::on_blocks_currentItemChanged() for (auto const& i: block[1]) s << "
" << sha3(i.data()).abridged();// << ": " << i[1].toHash() << " [" << i[2].toInt() << " used]"; s << "
Post: " << info.stateRoot << ""; + s << "
Dump: " << toHex(block[0].data()) << ""; } else { @@ -1665,7 +1668,7 @@ void Main::on_data_textChanged() errs.append("
" + QString::fromStdString(i).toHtmlEscaped() + "
"); } ui->code->setHtml(errs + lll + solidity + "

Code

" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped()); - ui->gas->setMinimum((qint64)Client::txGas(m_data.size(), 0)); + ui->gas->setMinimum((qint64)Client::txGas(m_data, 0)); if (!ui->gas->isEnabled()) ui->gas->setValue(m_backupGas); ui->gas->setEnabled(true); @@ -1676,7 +1679,7 @@ void Main::on_data_textChanged() ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true))); if (ethereum()->codeAt(fromString(ui->destination->currentText()), 0).size()) { - ui->gas->setMinimum((qint64)Client::txGas(m_data.size(), 1)); + ui->gas->setMinimum((qint64)Client::txGas(m_data, 1)); if (!ui->gas->isEnabled()) ui->gas->setValue(m_backupGas); ui->gas->setEnabled(true); @@ -1685,7 +1688,7 @@ void Main::on_data_textChanged() { if (ui->gas->isEnabled()) m_backupGas = ui->gas->value(); - ui->gas->setValue((qint64)Client::txGas(m_data.size())); + ui->gas->setValue((qint64)Client::txGas(m_data)); ui->gas->setEnabled(false); } } diff --git a/eth/main.cpp b/eth/main.cpp index 408654018..9485a11bf 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -337,10 +337,10 @@ int main(int argc, char** argv) web3.connect(remoteHost, remotePort); #if ETH_JSONRPC - auto_ptr jsonrpcServer; + shared_ptr jsonrpcServer; if (jsonrpc > -1) { - jsonrpcServer = auto_ptr(new WebThreeStubServer(new jsonrpc::CorsHttpServer(jsonrpc), web3, {us})); + jsonrpcServer = shared_ptr(new WebThreeStubServer(new jsonrpc::CorsHttpServer(jsonrpc), web3, {us})); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); } @@ -428,7 +428,7 @@ int main(int argc, char** argv) { if (jsonrpc < 0) jsonrpc = 8080; - jsonrpcServer = auto_ptr(new WebThreeStubServer(new jsonrpc::CorsHttpServer(jsonrpc), web3, {us})); + jsonrpcServer = make_shared(new jsonrpc::CorsHttpServer(jsonrpc), web3, vector({us})); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); } @@ -492,14 +492,12 @@ int main(int argc, char** argv) cnote << ssbd.str(); int ssize = sechex.length(); int size = hexAddr.length(); - u256 minGas = (u256)Client::txGas(data.size(), 0); + u256 minGas = (u256)Client::txGas(data, 0); if (size < 40) { if (size > 0) cwarn << "Invalid address length:" << size; } - else if (gasPrice < info.minGasPrice) - cwarn << "Minimum gas price is" << info.minGasPrice; else if (gas < minGas) cwarn << "Minimum gas amount is" << minGas; else if (ssize < 40) @@ -559,9 +557,9 @@ int main(int argc, char** argv) auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); - u256 minGas = (u256)Client::txGas(0, 0); + u256 minGas = (u256)Client::txGas(bytes(), 0); Address dest = h160(fromHex(hexAddr)); - c->transact(us.secret(), amount, dest, bytes(), minGas, info.minGasPrice); + c->transact(us.secret(), amount, dest, bytes(), minGas); } } else @@ -598,11 +596,9 @@ int main(int argc, char** argv) cnote << "Init:"; cnote << ssc.str(); } - u256 minGas = (u256)Client::txGas(init.size(), 0); + u256 minGas = (u256)Client::txGas(init, 0); if (endowment < 0) cwarn << "Invalid endowment"; - else if (gasPrice < info.minGasPrice) - cwarn << "Minimum gas price is" << info.minGasPrice; else if (gas < minGas) cwarn << "Minimum gas amount is" << minGas; else diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index ae073b9b1..55250b418 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.7.10"; +char const* Version = "0.7.12"; } diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 44da9603c..015f8dad6 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -58,9 +58,9 @@ h256 BlockInfo::headerHashWithoutNonce() const void BlockInfo::streamRLP(RLPStream& _s, bool _nonce) const { - _s.appendList(_nonce ? 15 : 14) + _s.appendList(_nonce ? 14 : 13) << parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot << receiptsRoot << logBloom - << difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData; + << difficulty << number << gasLimit << gasUsed << timestamp << extraData; if (_nonce) _s << nonce; } @@ -86,12 +86,11 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce) logBloom = _header[field = 6].toHash(); difficulty = _header[field = 7].toInt(); number = _header[field = 8].toInt(); - minGasPrice = _header[field = 9].toInt(); - gasLimit = _header[field = 10].toInt(); - gasUsed = _header[field = 11].toInt(); - timestamp = _header[field = 12].toInt(); - extraData = _header[field = 13].toBytes(); - nonce = _header[field = 14].toHash(); + gasLimit = _header[field = 9].toInt(); + gasUsed = _header[field = 10].toInt(); + timestamp = _header[field = 11].toInt(); + extraData = _header[field = 12].toBytes(); + nonce = _header[field = 13].toHash(); } catch (Exception const& _e) @@ -147,9 +146,6 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const if (transactionsRoot != t.root()) BOOST_THROW_EXCEPTION(InvalidTransactionsHash(t.root(), transactionsRoot)); - if (minGasPrice > mgp) - BOOST_THROW_EXCEPTION(InvalidMinGasPrice(minGasPrice, mgp)); - if (sha3Uncles != sha3(root[2].data())) BOOST_THROW_EXCEPTION(InvalidUnclesHash()); } diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index d91ff244d..aa7456f72 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -66,7 +66,6 @@ public: h512 logBloom; // TODO LogBloom - get include u256 difficulty; u256 number; - u256 minGasPrice; u256 gasLimit; u256 gasUsed; u256 timestamp; @@ -95,7 +94,6 @@ public: logBloom == _cmp.logBloom && difficulty == _cmp.difficulty && number == _cmp.number && - minGasPrice == _cmp.minGasPrice && gasLimit == _cmp.gasLimit && gasUsed == _cmp.gasUsed && timestamp == _cmp.timestamp && @@ -122,7 +120,7 @@ public: inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi) { _out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " << - _bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " << + _bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.gasLimit << " " << _bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce; return _out; } diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index c12c71774..744e85a27 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -33,8 +33,8 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 44; -const unsigned c_databaseVersion = 4; +const unsigned c_protocolVersion = 46; +const unsigned c_databaseVersion = 5; static const vector> g_units = { diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 765b54627..531005fb2 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -101,9 +101,8 @@ bytes BlockChain::createGenesisBlock() stateRoot = state.root(); } - block.appendList(15) - // TODO: maybe make logbloom correct? - << h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42)); + block.appendList(14) + << h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42)); block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList); return block.out(); @@ -169,8 +168,6 @@ void BlockChain::close() delete m_db; m_lastBlockHash = m_genesisHash; m_details.clear(); - m_blooms.clear(); - m_traces.clear(); m_cache.clear(); } @@ -307,14 +304,10 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) State s(bi.coinbaseAddress, _db); auto tdIncrease = s.enactOn(&_block, bi, *this); auto b = s.oldBloom(); - BlockBlooms bb; - BlockTraces bt; BlockLogBlooms blb; BlockReceipts br; for (unsigned i = 0; i < s.pending().size(); ++i) { - bb.blooms.push_back(s.changesFromPending(i).bloom()); - bt.traces.push_back(s.changesFromPending(i)); blb.blooms.push_back(s.receipt(i).bloom()); br.receipts.push_back(s.receipt(i)); } @@ -330,14 +323,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}, b); m_details[bi.parentHash].children.push_back(newHash); } - { - WriteGuard l(x_blooms); - m_blooms[newHash] = bb; - } - { - WriteGuard l(x_traces); - m_traces[newHash] = bt; - } { WriteGuard l(x_logBlooms); m_logBlooms[newHash] = blb; @@ -349,8 +334,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) m_extrasDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)dev::ref(m_details[newHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(newHash, 1), (ldb::Slice)dev::ref(m_blooms[newHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(newHash, 2), (ldb::Slice)dev::ref(m_traces[newHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, 3), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, 4), (ldb::Slice)dev::ref(m_receipts[newHash].rlp())); m_db->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block)); diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 74d94e164..d03818bd3 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -101,14 +101,6 @@ public: BlockDetails details(h256 _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); } BlockDetails details() const { return details(currentHash()); } - /// Get the transactions' bloom filters of a block (or the most recent mined if none given). Thread-safe. - BlockBlooms blooms(h256 _hash) const { return queryExtras(_hash, m_blooms, x_blooms, NullBlockBlooms); } - BlockBlooms blooms() const { return blooms(currentHash()); } - - /// Get the transactions' trace manifests of a block (or the most recent mined if none given). Thread-safe. - BlockTraces traces(h256 _hash) const { return queryExtras(_hash, m_traces, x_traces, NullBlockTraces); } - BlockTraces traces() const { return traces(currentHash()); } - /// Get the transactions' log blooms of a block (or the most recent mined if none given). Thread-safe. BlockLogBlooms logBlooms(h256 _hash) const { return queryExtras(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); } BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); } @@ -193,10 +185,6 @@ private: /// The caches of the disk DB and their locks. mutable boost::shared_mutex x_details; mutable BlockDetailsHash m_details; - mutable boost::shared_mutex x_blooms; - mutable BlockBloomsHash m_blooms; - mutable boost::shared_mutex x_traces; - mutable BlockTracesHash m_traces; mutable boost::shared_mutex x_logBlooms; mutable BlockLogBloomsHash m_logBlooms; mutable boost::shared_mutex x_receipts; diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h index 973e93070..0c3af5b33 100644 --- a/libethereum/BlockDetails.h +++ b/libethereum/BlockDetails.h @@ -54,24 +54,6 @@ struct BlockDetails h256 bloom; }; -struct BlockBlooms -{ - BlockBlooms() {} - BlockBlooms(RLP const& _r) { blooms = _r.toVector(); } - bytes rlp() const { RLPStream s; s << blooms; return s.out(); } - - h256s blooms; -}; - -struct BlockTraces -{ - BlockTraces() {} - BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); } - bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamRLP(s); return s.out(); } - - Manifests traces; -}; - struct BlockLogBlooms { BlockLogBlooms() {} @@ -91,14 +73,10 @@ struct BlockReceipts }; typedef std::map BlockDetailsHash; -typedef std::map BlockBloomsHash; -typedef std::map BlockTracesHash; typedef std::map BlockLogBloomsHash; typedef std::map BlockReceiptsHash; static const BlockDetails NullBlockDetails; -static const BlockBlooms NullBlockBlooms; -static const BlockTraces NullBlockTraces; static const BlockLogBlooms NullBlockLogBlooms; static const BlockReceipts NullBlockReceipts; diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index c3a8b2a80..6a123875c 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -22,6 +22,7 @@ #include #include #include +#include "Interface.h" #include "Executive.h" #include "State.h" #include "ExtVM.h" @@ -58,15 +59,8 @@ bool Executive::setup(bytesConstRef _rlp) BOOST_THROW_EXCEPTION(InvalidNonce(nonceReq, m_t.nonce())); } - // Don't like transactions whose gas price is too low. NOTE: this won't stay here forever - it's just until we get a proper gas price discovery protocol going. - if (m_t.gasPrice() < m_s.m_currentBlock.minGasPrice) - { - clog(StateDetail) << "Offered gas-price is too low: Require >" << m_s.m_currentBlock.minGasPrice << " Got" << m_t.gasPrice(); - BOOST_THROW_EXCEPTION(GasPriceTooLow()); - } - // Check gas cost is enough. - u256 gasCost = m_t.data().size() * c_txDataGas + c_txGas; + auto gasCost = Interface::txGas(m_t.data()); if (m_t.gas() < gasCost) { @@ -106,9 +100,9 @@ bool Executive::setup(bytesConstRef _rlp) } if (m_t.isCreation()) - return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - gasCost, &m_t.data(), m_sender); + return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasCost, &m_t.data(), m_sender); else - return call(m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - gasCost, m_sender); + return call(m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasCost, m_sender); } bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) diff --git a/libethereum/Interface.h b/libethereum/Interface.h index add9a1bda..d598e1f9b 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -128,7 +128,7 @@ public: virtual Addresses addresses(int _block) const = 0; /// Get the fee associated for a transaction with the given data. - static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; } + 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/MessageFilter.cpp b/libethereum/MessageFilter.cpp index b04d213f9..0519fe28b 100644 --- a/libethereum/MessageFilter.cpp +++ b/libethereum/MessageFilter.cpp @@ -198,7 +198,7 @@ LogEntries LogFilter::matches(TransactionReceipt const& _m) const if (!m_addresses.empty() && !m_addresses.count(e.address)) continue; for (auto const& t: m_topics) - if (!e.topics.count(t)) + if (!std::count(e.topics.begin(), e.topics.end(), t)) continue; ret.push_back(e); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 40327b108..9a0426e18 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -54,7 +54,7 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out) memcpy(&in, _in.data(), min(_in.size(), sizeof(in))); memset(_out.data(), 0, _out.size()); - if (in.v > 28) + if ((u256)in.v > 28) return; SignatureStruct sig{in.r, in.s, (byte)((int)(u256)in.v - 27)}; if (!sig.isValid()) @@ -501,7 +501,6 @@ void State::resetCurrent() m_currentBlock.timestamp = time(0); m_currentBlock.transactionsRoot = h256(); m_currentBlock.sha3Uncles = h256(); - m_currentBlock.minGasPrice = 10 * szabo; m_currentBlock.populateFromParent(m_previousBlock); // Update timestamp according to clock. diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 65761e410..9e6601d0a 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -49,8 +49,8 @@ using LogBloom = h512; struct LogEntry { LogEntry() {} - LogEntry(RLP const& _r) { address = (Address)_r[0]; topics = (h256Set)_r[1]; data = _r[2].toBytes(); } - LogEntry(Address const& _address, h256s const& _ts, bytes&& _d): address(_address), topics(toSet(_ts)), data(std::move(_d)) {} + LogEntry(RLP const& _r) { address = (Address)_r[0]; topics = (h256s)_r[1]; data = _r[2].toBytes(); } + LogEntry(Address const& _address, h256s const& _ts, bytes&& _d): address(_address), topics(_ts), data(std::move(_d)) {} void streamRLP(RLPStream& _s) const { _s.appendList(3) << address << topics << data; } @@ -64,7 +64,7 @@ struct LogEntry } Address address; - h256Set topics; + h256s topics; bytes data; }; diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp index 6d868cac5..3e957118e 100644 --- a/libevm/FeeStructure.cpp +++ b/libevm/FeeStructure.cpp @@ -34,9 +34,13 @@ u256 const dev::eth::c_sstoreResetGas = 100; u256 const dev::eth::c_sstoreRefundGas = 100; u256 const dev::eth::c_createGas = 100; u256 const dev::eth::c_callGas = 20; +u256 const dev::eth::c_expGas = 1; +u256 const dev::eth::c_expByteGas = 1; u256 const dev::eth::c_memoryGas = 1; -u256 const dev::eth::c_txDataGas = 5; +u256 const dev::eth::c_txDataZeroGas = 1; +u256 const dev::eth::c_txDataNonZeroGas = 5; u256 const dev::eth::c_txGas = 500; u256 const dev::eth::c_logGas = 32; u256 const dev::eth::c_logDataGas = 1; u256 const dev::eth::c_logTopicGas = 32; +u256 const dev::eth::c_copyGas = 1; diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index e57f7ccf8..3fb8175e6 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -37,12 +37,16 @@ extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeron extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero. extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction. extern u256 const c_callGas; ///< Once per CALL operation & message call transaction. +extern u256 const c_expGas; ///< Once per EXP instuction. +extern u256 const c_expByteGas; ///< Times ceil(log256(exponent)) for the EXP instruction. extern u256 const c_memoryGas; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. -extern u256 const c_txDataGas; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. +extern u256 const c_txDataZeroGas; ///< Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. +extern u256 const c_txDataNonZeroGas; ///< Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. extern u256 const c_txGas; ///< Per transaction. NOTE: Not payable on data of calls between transactions. extern u256 const c_logGas; ///< Per LOG* operation. extern u256 const c_logDataGas; ///< Per byte in a LOG* operation's data. extern u256 const c_logTopicGas; ///< Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. +extern u256 const c_copyGas; ///< Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. } } diff --git a/libevm/VM.h b/libevm/VM.h index 425eab8c8..5fb46fc68 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -53,9 +53,6 @@ inline Address asAddress(u256 _item) inline u256 fromAddress(Address _a) { return (u160)_a; -// h256 ret; -// memcpy(&ret, &_a, sizeof(_a)); -// return ret; } /** @@ -131,6 +128,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con // FEES... bigint runGas = c_stepGas; bigint newTempSize = m_temp.size(); + bigint copySize = 0; auto onOperation = [&]() { @@ -172,15 +170,15 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con // These all operate on memory and therefore potentially expand it: case Instruction::MSTORE: require(2); - newTempSize = m_stack.back() + 32; + newTempSize = (bigint)m_stack.back() + 32; break; case Instruction::MSTORE8: require(2); - newTempSize = m_stack.back() + 1; + newTempSize = (bigint)m_stack.back() + 1; break; case Instruction::MLOAD: require(1); - newTempSize = m_stack.back() + 32; + newTempSize = (bigint)m_stack.back() + 32; break; case Instruction::RETURN: require(2); @@ -193,14 +191,17 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::CALLDATACOPY: require(3); + copySize = m_stack[m_stack.size() - 3]; newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); break; case Instruction::CODECOPY: require(3); + copySize = m_stack[m_stack.size() - 3]; newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); break; case Instruction::EXTCODECOPY: require(4); + copySize = m_stack[m_stack.size() - 4]; newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]); break; @@ -232,10 +233,17 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::CREATE: { require(3); - auto inOff = m_stack[m_stack.size() - 2]; - auto inSize = m_stack[m_stack.size() - 3]; - newTempSize = inOff + inSize; - runGas = c_createGas; + u256 inOff = m_stack[m_stack.size() - 2]; + u256 inSize = m_stack[m_stack.size() - 3]; + newTempSize = (bigint)inOff + inSize; + runGas = c_createGas; + break; + } + case Instruction::EXP: + { + require(2); + auto expon = m_stack[m_stack.size() - 2]; + runGas = c_expGas + c_expByteGas * (32 - (h256(expon).firstBitSet() / 8)); break; } @@ -304,7 +312,6 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::SDIV: case Instruction::MOD: case Instruction::SMOD: - case Instruction::EXP: case Instruction::LT: case Instruction::GT: case Instruction::SLT: @@ -365,6 +372,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con newTempSize = (newTempSize + 31) / 32 * 32; if (newTempSize > m_temp.size()) runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32; + runGas += c_copyGas * (copySize + 31) / 32; onOperation(); // if (_onOp) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index 4725c8c1a..059810af8 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -27,6 +27,32 @@ using namespace std; using namespace dev; using namespace dev::eth; +unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const +{ + switch (m_type) + { + case Operation: + case Tag: // 1 byte for the JUMPDEST + return 1; + case PushString: + return 33; + case Push: + return 1 + max(1, dev::bytesRequired(m_data)); + case PushSubSize: + return 4; // worst case: a 16MB program + case PushTag: + case PushData: + case PushSub: + return 1 + _addressLength; + case NoOptimizeBegin: + case NoOptimizeEnd: + return 0; + default: + break; + } + BOOST_THROW_EXCEPTION(InvalidOpcode()); +} + int AssemblyItem::deposit() const { switch (m_type) @@ -51,32 +77,7 @@ unsigned Assembly::bytesRequired() const ret += i.second.size(); for (AssemblyItem const& i: m_items) - switch (i.m_type) - { - case Operation: - case Tag: // 1 byte for the JUMPDEST - ret++; - break; - case PushString: - ret += 33; - break; - case Push: - ret += 1 + max(1, dev::bytesRequired(i.m_data)); - break; - case PushSubSize: - ret += 4; // worst case: a 16MB program - break; - case PushTag: - case PushData: - case PushSub: - ret += 1 + br; - break; - case NoOptimizeBegin: - case NoOptimizeEnd: - break; - default: - BOOST_THROW_EXCEPTION(InvalidOpcode()); - } + ret += i.bytesRequired(br); if (dev::bytesRequired(ret) <= br) return ret; } @@ -243,6 +244,18 @@ inline bool popCountIncreased(AssemblyItemsConstRef _pre, AssemblyItems const& _ return count_if(begin(_post), end(_post), isPop) > count_if(begin(_pre), end(_pre), isPop); } +//@todo this has to move to a special optimizer class soon +template +unsigned bytesRequiredBySlice(Iterator _begin, Iterator _end) +{ + // this is only used in the optimizer, so we can provide a guess for the address length + unsigned addressLength = 4; + unsigned size = 0; + for (; _begin != _end; ++_begin) + size += _begin->bytesRequired(addressLength); + return size; +} + struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; }; #define copt dev::LogOutputStream() @@ -258,7 +271,7 @@ Assembly& Assembly::optimise(bool _enable) u256 mask = (u256(1) << testBit) - 1; return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask; }; - map> c_simple = + map> const c_simple = { { Instruction::SUB, [](u256 a, u256 b)->u256{return a - b;} }, { Instruction::DIV, [](u256 a, u256 b)->u256{return a / b;} }, @@ -273,7 +286,7 @@ Assembly& Assembly::optimise(bool _enable) { Instruction::SGT, [](u256 a, u256 b)->u256{return u2s(a) > u2s(b) ? 1 : 0;} }, { Instruction::EQ, [](u256 a, u256 b)->u256{return a == b ? 1 : 0;} }, }; - map> c_associative = + map> const c_associative = { { Instruction::ADD, [](u256 a, u256 b)->u256{return a + b;} }, { Instruction::MUL, [](u256 a, u256 b)->u256{return a * b;} }, @@ -281,6 +294,8 @@ Assembly& Assembly::optimise(bool _enable) { Instruction::OR, [](u256 a, u256 b)->u256{return a | b;} }, { Instruction::XOR, [](u256 a, u256 b)->u256{return a ^ b;} }, }; + std::vector> const c_identities = + { { Instruction::ADD, 0}, { Instruction::MUL, 1}, { Instruction::MOD, 0}, { Instruction::OR, 0}, { Instruction::XOR, 0} }; std::vector>> rules = { { { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, @@ -299,8 +314,11 @@ Assembly& Assembly::optimise(bool _enable) rules.push_back({ { Push, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[1].data(), m[0].data()) }; } }); rules.push_back({ { Push, i.first, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[2].data(), m[0].data()), i.first }; } }); } + for (auto const& i: c_identities) + rules.push_back({{Push, i.first}, [&](AssemblyItemsConstRef m) -> AssemblyItems + { return m[0].data() == i.second ? AssemblyItems() : m.toVector(); }}); // jump to next instruction - rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [&](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }}); + rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }}); // pop optimization, do not compute values that are popped again anyway rules.push_back({ { AssemblyItem(UndefinedItem), Instruction::POP }, [](AssemblyItemsConstRef m) -> AssemblyItems @@ -315,6 +333,29 @@ Assembly& Assembly::optimise(bool _enable) return m.toVector(); return AssemblyItems(info.args, Instruction::POP); } }); + // compute constants close to powers of two by expressions + auto computeConstants = [](AssemblyItemsConstRef m) -> AssemblyItems + { + u256 const& c = m[0].data(); + unsigned const minBits = 4 * 8; + if (c < (bigint(1) << minBits)) + return m.toVector(); // we need at least "PUSH1 PUSH1 <2> EXP" + if (c == u256(-1)) + return {u256(0), Instruction::NOT}; + for (unsigned bits = minBits; bits < 256; ++bits) + { + bigint const diff = c - (bigint(1) << bits); + if (abs(diff) > 0xff) + continue; + AssemblyItems powerOfTwo{u256(bits), u256(2), Instruction::EXP}; + if (diff == 0) + return powerOfTwo; + return AssemblyItems{u256(abs(diff))} + powerOfTwo + + AssemblyItems{diff > 0 ? Instruction::ADD : Instruction::SUB}; + } + return m.toVector(); + }; + rules.push_back({{Push}, computeConstants}); copt << *this; @@ -336,15 +377,27 @@ Assembly& Assembly::optimise(bool _enable) if (matches(vr, &r.first)) { auto rw = r.second(vr); - if (rw.size() < vr.size() || (rw.size() == vr.size() && popCountIncreased(vr, rw))) + unsigned const vrSize = bytesRequiredBySlice(vr.begin(), vr.end()); + unsigned const rwSize = bytesRequiredBySlice(rw.begin(), rw.end()); + //@todo check the actual size (including constant sizes) + if (rwSize < vrSize || (rwSize == vrSize && popCountIncreased(vr, rw))) { copt << vr << "matches" << AssemblyItemsConstRef(&r.first) << "becomes..."; - for (unsigned j = 0; j < vr.size(); ++j) + copt << AssemblyItemsConstRef(&rw); + if (rw.size() > vr.size()) + { + // create hole in the vector + unsigned sizeIncrease = rw.size() - vr.size(); + m_items.resize(m_items.size() + sizeIncrease, AssemblyItem(UndefinedItem)); + move_backward(m_items.begin() + i, m_items.end() - sizeIncrease, m_items.end()); + } + + for (unsigned j = 0; j < max(rw.size(), vr.size()); ++j) if (j < rw.size()) m_items[i + j] = rw[j]; else m_items.erase(m_items.begin() + i + rw.size()); - copt << AssemblyItemsConstRef(&rw); + count++; copt << "Now:\n" << m_items; } diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index b8e59a474..b144dd8d9 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -51,6 +51,9 @@ public: AssemblyItemType type() const { return m_type; } u256 data() const { return m_data; } + /// @returns an upper bound for the number of bytes required by this item, assuming that + /// the value of a jump tag takes @a _addressLength bytes. + unsigned bytesRequired(unsigned _addressLength) const; int deposit() const; bool match(AssemblyItem const& _i) const { return _i.m_type == UndefinedItem || (m_type == _i.m_type && (m_type != Operation || m_data == _i.m_data)); } diff --git a/libevmcore/Instruction.h b/libevmcore/Instruction.h index eb85c0610..f8a0478f1 100644 --- a/libevmcore/Instruction.h +++ b/libevmcore/Instruction.h @@ -168,8 +168,8 @@ enum class Instruction: uint8_t CREATE = 0xf0, ///< create a new account with associated code CALL, ///< message-call into an account + CALLCODE, ///< message-call with another account's code only RETURN, ///< halt execution returning output data - CALLCODE, SUICIDE = 0xff ///< halt execution and register account for later deletion }; diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 85225ab7f..546d54c00 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -79,7 +79,7 @@ std::vector Host::getInterfaceAddresses() char *addrStr = inet_ntoa(addr); bi::address address(bi::address::from_string(addrStr)); if (!isLocalHostAddress(address)) - addresses.push_back(ad.to_v4()); + addresses.push_back(address.to_v4()); } WSACleanup(); @@ -154,9 +154,9 @@ int Host::listen4(bi::tcp::acceptor* _acceptor, unsigned short _listenPort) bi::tcp::endpoint Host::traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr) { - asserts(_listenPort); + asserts(_listenPort != 0); - UPnP* upnp; + UPnP* upnp = nullptr; try { upnp = new UPnP; diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 5c07ec80e..4bd0b2c0e 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -378,6 +378,9 @@ void Assignment::checkTypeRequirements() { m_leftHandSide->checkTypeRequirements(); m_leftHandSide->requireLValue(); + //@todo later, assignments to structs might be possible, but not to mappings + if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue()) + BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue.")); m_rightHandSide->expectType(*m_leftHandSide->getType()); m_type = m_leftHandSide->getType(); if (m_assigmentOperator != Token::ASSIGN) @@ -403,7 +406,7 @@ void Expression::expectType(Type const& _expectedType) void Expression::requireLValue() { - if (!isLvalue()) + if (!isLValue()) BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue.")); m_lvalueRequested = true; } @@ -495,7 +498,8 @@ void MemberAccess::checkTypeRequirements() m_type = type.getMemberType(*m_memberName); if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString())); - m_isLvalue = (type.getCategory() == Type::Category::STRUCT && m_type->getCategory() != Type::Category::MAPPING); + //@todo later, this will not always be STORAGE + m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE; } void IndexAccess::checkTypeRequirements() @@ -507,7 +511,7 @@ void IndexAccess::checkTypeRequirements() MappingType const& type = dynamic_cast(*m_base->getType()); m_index->expectType(*type.getKeyType()); m_type = type.getValueType(); - m_isLvalue = m_type->getCategory() != Type::Category::MAPPING; + m_lvalue = LValueType::STORAGE; } void Identifier::checkTypeRequirements() @@ -521,7 +525,7 @@ void Identifier::checkTypeRequirements() if (!variable->getType()) BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined.")); m_type = variable->getType(); - m_isLvalue = true; + m_lvalue = variable->isLocalVariable() ? LValueType::LOCAL : LValueType::STORAGE; return; } //@todo can we unify these with TypeName::toType()? diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 3fb251d95..87bc3cd40 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -88,11 +88,16 @@ public: Declaration(Location const& _location, ASTPointer const& _name): ASTNode(_location), m_name(_name) {} - /// Returns the declared name. + /// @returns the declared name. ASTString const& getName() const { return *m_name; } + /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. + /// Available only after name and type resolution step. + Declaration* getScope() const { return m_scope; } + void setScope(Declaration* const& _scope) { m_scope = _scope; } private: ASTPointer m_name; + Declaration* m_scope; }; /** @@ -173,14 +178,21 @@ private: class FunctionDefinition: public Declaration { public: - FunctionDefinition(Location const& _location, ASTPointer const& _name, bool _isPublic, - ASTPointer const& _parameters, - bool _isDeclaredConst, - ASTPointer const& _returnParameters, - ASTPointer const& _body): - Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters), - m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), - m_body(_body) {} + FunctionDefinition(Location const& _location, ASTPointer const& _name, + bool _isPublic, + ASTPointer const& _documentation, + ASTPointer const& _parameters, + bool _isDeclaredConst, + ASTPointer const& _returnParameters, + ASTPointer const& _body): + Declaration(_location, _name), m_isPublic(_isPublic), + m_parameters(_parameters), + m_isDeclaredConst(_isDeclaredConst), + m_returnParameters(_returnParameters), + m_body(_body), + m_documentation(_documentation) + {} + virtual void accept(ASTVisitor& _visitor) override; bool isPublic() const { return m_isPublic; } @@ -190,6 +202,9 @@ public: std::vector> const& getReturnParameters() const { return m_returnParameters->getParameters(); } ASTPointer const& getReturnParameterList() const { return m_returnParameters; } Block& getBody() { return *m_body; } + /// @return A shared pointer of an ASTString. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer const& getDocumentation() { return m_documentation; } void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); } std::vector const& getLocalVariables() const { return m_localVariables; } @@ -203,6 +218,7 @@ private: bool m_isDeclaredConst; ASTPointer m_returnParameters; ASTPointer m_body; + ASTPointer m_documentation; std::vector m_localVariables; }; @@ -226,6 +242,8 @@ public: std::shared_ptr const& getType() const { return m_type; } void setType(std::shared_ptr const& _type) { m_type = _type; } + bool isLocalVariable() const { return !!dynamic_cast(getScope()); } + private: ASTPointer m_typeName; ///< can be empty ("var") @@ -510,12 +528,16 @@ private: */ class Expression: public ASTNode { +protected: + enum class LValueType { NONE, LOCAL, STORAGE }; + public: - Expression(Location const& _location): ASTNode(_location), m_isLvalue(false), m_lvalueRequested(false) {} + Expression(Location const& _location): ASTNode(_location), m_lvalue(LValueType::NONE), m_lvalueRequested(false) {} virtual void checkTypeRequirements() = 0; std::shared_ptr const& getType() const { return m_type; } - bool isLvalue() const { return m_isLvalue; } + bool isLValue() const { return m_lvalue != LValueType::NONE; } + bool isLocalLValue() const { return m_lvalue == LValueType::LOCAL; } /// Helper function, infer the type via @ref checkTypeRequirements and then check that it /// is implicitly convertible to @a _expectedType. If not, throw exception. @@ -530,9 +552,9 @@ public: protected: //! Inferred type of the expression, only filled after a call to checkTypeRequirements(). std::shared_ptr m_type; - //! Whether or not this expression is an lvalue, i.e. something that can be assigned to. - //! This is set during calls to @a checkTypeRequirements() - bool m_isLvalue; + //! If this expression is an lvalue (i.e. something that can be assigned to) and is stored + //! locally or in storage. This is set during calls to @a checkTypeRequirements() + LValueType m_lvalue; //! Whether the outer expression requested the address (true) or the value (false) of this expression. bool m_lvalueRequested; }; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 988390d0b..17ad4fd16 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -324,7 +324,7 @@ bool Compiler::visit(ExpressionStatement& _expressionStatement) { Expression& expression = _expressionStatement.getExpression(); ExpressionCompiler::compileExpression(m_context, expression); - Type::Category category = expression.getType()->getCategory(); +// Type::Category category = expression.getType()->getCategory(); for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i) m_context << eth::Instruction::POP; return false; diff --git a/libsolidity/Scope.cpp b/libsolidity/DeclarationContainer.cpp similarity index 78% rename from libsolidity/Scope.cpp rename to libsolidity/DeclarationContainer.cpp index 540c41204..6ea9c28c5 100644 --- a/libsolidity/Scope.cpp +++ b/libsolidity/DeclarationContainer.cpp @@ -20,7 +20,7 @@ * Scope - object that holds declaration of names. */ -#include +#include #include namespace dev @@ -28,7 +28,7 @@ namespace dev namespace solidity { -bool Scope::registerDeclaration(Declaration& _declaration) +bool DeclarationContainer::registerDeclaration(Declaration& _declaration) { if (m_declarations.find(_declaration.getName()) != m_declarations.end()) return false; @@ -36,13 +36,13 @@ bool Scope::registerDeclaration(Declaration& _declaration) return true; } -Declaration* Scope::resolveName(ASTString const& _name, bool _recursive) const +Declaration* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const { auto result = m_declarations.find(_name); if (result != m_declarations.end()) return result->second; - if (_recursive && m_enclosingScope) - return m_enclosingScope->resolveName(_name, true); + if (_recursive && m_enclosingContainer) + return m_enclosingContainer->resolveName(_name, true); return nullptr; } diff --git a/libsolidity/Scope.h b/libsolidity/DeclarationContainer.h similarity index 77% rename from libsolidity/Scope.h rename to libsolidity/DeclarationContainer.h index 637c2d5ce..db6812890 100644 --- a/libsolidity/Scope.h +++ b/libsolidity/DeclarationContainer.h @@ -36,18 +36,20 @@ namespace solidity * Container that stores mappings betwee names and declarations. It also contains a link to the * enclosing scope. */ -class Scope +class DeclarationContainer { public: - explicit Scope(Scope* _enclosingScope = nullptr): m_enclosingScope(_enclosingScope) {} + explicit DeclarationContainer(Declaration* _enclosingDeclaration = nullptr, DeclarationContainer* _enclosingContainer = nullptr): + m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} /// Registers the declaration in the scope unless its name is already declared. Returns true iff /// it was not yet declared. bool registerDeclaration(Declaration& _declaration); Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; - Scope* getEnclosingScope() const { return m_enclosingScope; } + Declaration* getEnclosingDeclaration() const { return m_enclosingDeclaration; } private: - Scope* m_enclosingScope; + Declaration* m_enclosingDeclaration; + DeclarationContainer* m_enclosingContainer; std::map m_declarations; }; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 4dc377791..c3c7116e4 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -363,7 +363,7 @@ void ExpressionCompiler::endVisit(Identifier& _identifier) m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag(); return; } - if (VariableDeclaration* varDef = dynamic_cast(declaration)) + if (/*VariableDeclaration* varDef = */dynamic_cast(declaration)) { m_currentLValue.fromIdentifier(_identifier, *_identifier.getReferencedDeclaration()); m_currentLValue.retrieveValueIfLValueNotRequested(_identifier); diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index e958352fd..d8b637076 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -58,7 +58,7 @@ GlobalContext::GlobalContext(): FunctionType::Location::ECRECOVER)), make_shared("ripemd160", make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), - TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), + TypePointers({std::make_shared(160, IntegerType::Modifier::HASH)}), FunctionType::Location::RIPEMD160))} { } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 225f2a78a..d473348b8 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -78,9 +78,9 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name return m_currentScope->resolveName(_name, _recursive); } -DeclarationRegistrationHelper::DeclarationRegistrationHelper(map& _scopes, +DeclarationRegistrationHelper::DeclarationRegistrationHelper(map& _scopes, ASTNode& _astRoot): - m_scopes(_scopes), m_currentScope(&m_scopes[nullptr]) + m_scopes(_scopes), m_currentScope(nullptr) { _astRoot.accept(*this); } @@ -135,31 +135,30 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) return true; } -void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node) +void DeclarationRegistrationHelper::enterNewSubScope(Declaration& _declaration) { - map::iterator iter; + map::iterator iter; bool newlyAdded; - tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope)); + tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, DeclarationContainer(m_currentScope, &m_scopes[m_currentScope])); if (asserts(newlyAdded)) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to add new scope.")); - m_currentScope = &iter->second; + m_currentScope = &_declaration; } void DeclarationRegistrationHelper::closeCurrentScope() { if (asserts(m_currentScope)) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Closed non-existing scope.")); - m_currentScope = m_currentScope->getEnclosingScope(); + m_currentScope = m_scopes[m_currentScope].getEnclosingDeclaration(); } void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) { - if (asserts(m_currentScope)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration registered without scope.")); - if (!m_currentScope->registerDeclaration(_declaration)) + if (!m_scopes[m_currentScope].registerDeclaration(_declaration)) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation()) << errinfo_comment("Identifier already declared.")); //@todo the exception should also contain the location of the first declaration + _declaration.setScope(m_currentScope); if (_opensScope) enterNewSubScope(_declaration); } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 64f3c89db..797eca605 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -25,7 +25,7 @@ #include #include -#include +#include #include namespace dev @@ -61,9 +61,9 @@ private: /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration, /// where nullptr denotes the global scope. Note that structs are not scope since they do /// not contain code. - std::map m_scopes; + std::map m_scopes; - Scope* m_currentScope; + DeclarationContainer* m_currentScope; }; /** @@ -73,7 +73,7 @@ private: class DeclarationRegistrationHelper: private ASTVisitor { public: - DeclarationRegistrationHelper(std::map& _scopes, ASTNode& _astRoot); + DeclarationRegistrationHelper(std::map& _scopes, ASTNode& _astRoot); private: bool visit(ContractDefinition& _contract); @@ -85,12 +85,12 @@ private: void endVisit(VariableDefinition& _variableDefinition); bool visit(VariableDeclaration& _declaration); - void enterNewSubScope(ASTNode& _node); + void enterNewSubScope(Declaration& _declaration); void closeCurrentScope(); void registerDeclaration(Declaration& _declaration, bool _opensScope); - std::map& m_scopes; - Scope* m_currentScope; + std::map& m_scopes; + Declaration* m_currentScope; FunctionDefinition* m_currentFunction; }; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 276da0728..0506bc3e3 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -117,6 +117,10 @@ ASTPointer Parser::parseContractDefinition() ASTPointer Parser::parseFunctionDefinition(bool _isPublic) { ASTNodeFactory nodeFactory(*this); + ASTPointer docstring; + if (m_scanner->getCurrentCommentLiteral() != "") + docstring = std::make_shared(m_scanner->getCurrentCommentLiteral()); + expectToken(Token::FUNCTION); ASTPointer name(expectIdentifierToken()); ASTPointer parameters(parseParameterList()); @@ -142,8 +146,9 @@ ASTPointer Parser::parseFunctionDefinition(bool _isPublic) } ASTPointer block = parseBlock(); nodeFactory.setEndPositionFromNode(block); - return nodeFactory.createNode(name, _isPublic, parameters, - isDeclaredConst, returnParameters, block); + return nodeFactory.createNode(name, _isPublic, docstring, + parameters, + isDeclaredConst, returnParameters, block); } ASTPointer Parser::parseStructDefinition() diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index dd18a320f..2f5f8d37a 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -102,18 +102,55 @@ int hexValue(char c) } } // end anonymous namespace + + +/// Scoped helper for literal recording. Automatically drops the literal +/// if aborting the scanning before it's complete. +enum LiteralType { + LITERAL_TYPE_STRING, + LITERAL_TYPE_NUMBER, // not really different from string type in behaviour + LITERAL_TYPE_COMMENT +}; + +class LiteralScope +{ +public: + explicit LiteralScope(Scanner* _self, enum LiteralType _type): m_type(_type) + , m_scanner(_self) + , m_complete(false) + { + if (_type == LITERAL_TYPE_COMMENT) + m_scanner->m_nextSkippedComment.literal.clear(); + else + m_scanner->m_nextToken.literal.clear(); + } + ~LiteralScope() + { + if (!m_complete) + { + if (m_type == LITERAL_TYPE_COMMENT) + m_scanner->m_nextSkippedComment.literal.clear(); + else + m_scanner->m_nextToken.literal.clear(); + } + } + void complete() { m_complete = true; } + +private: + enum LiteralType m_type; + Scanner* m_scanner; + bool m_complete; +}; // end of LiteralScope class + + void Scanner::reset(CharStream const& _source) { - bool foundDocComment; m_source = _source; m_char = m_source.get(); skipWhitespace(); - foundDocComment = scanToken(); + scanToken(); - // special version of Scanner:next() taking the previous scanToken() result into account - m_currentToken = m_nextToken; - if (scanToken() || foundDocComment) - m_skippedComment = m_nextSkippedComment; + next(); } @@ -142,8 +179,9 @@ BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); Token::Value Scanner::next() { m_currentToken = m_nextToken; - if (scanToken()) - m_skippedComment = m_nextSkippedComment; + m_skippedComment = m_nextSkippedComment; + scanToken(); + return m_currentToken.token; } @@ -156,7 +194,6 @@ Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _ return _else; } - bool Scanner::skipWhitespace() { int const startPosition = getSourcePos(); @@ -166,7 +203,6 @@ bool Scanner::skipWhitespace() return getSourcePos() != startPosition; } - Token::Value Scanner::skipSingleLineComment() { // The line terminator at the end of the line is not considered @@ -177,13 +213,28 @@ Token::Value Scanner::skipSingleLineComment() return Token::WHITESPACE; } -/// For the moment this function simply consumes a single line triple slash doc comment Token::Value Scanner::scanDocumentationComment() { - LiteralScope literal(this); + LiteralScope literal(this, LITERAL_TYPE_COMMENT); advance(); //consume the last '/' - while (!isSourcePastEndOfInput() && !isLineTerminator(m_char)) + while (!isSourcePastEndOfInput()) { + if (isLineTerminator(m_char)) + { + // check if next line is also a documentation comment + skipWhitespace(); + if (!m_source.isPastEndOfInput(3) && + m_source.get(0) == '/' && + m_source.get(1) == '/' && + m_source.get(2) == '/') + { + addCommentLiteralChar('\n'); + m_char = m_source.advanceAndGet(3); + } + else + break; // next line is not a documentation comment, we are done + + } addCommentLiteralChar(m_char); advance(); } @@ -214,10 +265,10 @@ Token::Value Scanner::skipMultiLineComment() return Token::ILLEGAL; } -bool Scanner::scanToken() +void Scanner::scanToken() { - bool foundDocComment = false; m_nextToken.literal.clear(); + m_nextSkippedComment.literal.clear(); Token::Value token; do { @@ -329,7 +380,6 @@ bool Scanner::scanToken() m_nextSkippedComment.location.end = getSourcePos(); m_nextSkippedComment.token = comment; token = Token::WHITESPACE; - foundDocComment = true; } else token = skipSingleLineComment(); @@ -425,8 +475,6 @@ bool Scanner::scanToken() while (token == Token::WHITESPACE); m_nextToken.location.end = getSourcePos(); m_nextToken.token = token; - - return foundDocComment; } bool Scanner::scanEscape() @@ -474,7 +522,7 @@ Token::Value Scanner::scanString() { char const quote = m_char; advance(); // consume quote - LiteralScope literal(this); + LiteralScope literal(this, LITERAL_TYPE_STRING); while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char)) { char c = m_char; @@ -494,18 +542,16 @@ Token::Value Scanner::scanString() return Token::STRING_LITERAL; } - void Scanner::scanDecimalDigits() { while (isDecimalDigit(m_char)) addLiteralCharAndAdvance(); } - Token::Value Scanner::scanNumber(char _charSeen) { enum { DECIMAL, HEX, BINARY } kind = DECIMAL; - LiteralScope literal(this); + LiteralScope literal(this, LITERAL_TYPE_NUMBER); if (_charSeen == '.') { // we have already seen a decimal point of the float @@ -572,209 +618,41 @@ Token::Value Scanner::scanNumber(char _charSeen) // ---------------------------------------------------------------------------- // Keyword Matcher -#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ - KEYWORD_GROUP('a') \ - KEYWORD("address", Token::ADDRESS) \ - KEYWORD_GROUP('b') \ - KEYWORD("break", Token::BREAK) \ - KEYWORD("bool", Token::BOOL) \ - KEYWORD_GROUP('c') \ - KEYWORD("case", Token::CASE) \ - KEYWORD("const", Token::CONST) \ - KEYWORD("continue", Token::CONTINUE) \ - KEYWORD("contract", Token::CONTRACT) \ - KEYWORD_GROUP('d') \ - KEYWORD("default", Token::DEFAULT) \ - KEYWORD("delete", Token::DELETE) \ - KEYWORD("do", Token::DO) \ - KEYWORD_GROUP('e') \ - KEYWORD("else", Token::ELSE) \ - KEYWORD("extends", Token::EXTENDS) \ - KEYWORD_GROUP('f') \ - KEYWORD("false", Token::FALSE_LITERAL) \ - KEYWORD("for", Token::FOR) \ - KEYWORD("function", Token::FUNCTION) \ - KEYWORD_GROUP('h') \ - KEYWORD("hash", Token::HASH) \ - KEYWORD("hash8", Token::HASH8) \ - KEYWORD("hash16", Token::HASH16) \ - KEYWORD("hash24", Token::HASH24) \ - KEYWORD("hash32", Token::HASH32) \ - KEYWORD("hash40", Token::HASH40) \ - KEYWORD("hash48", Token::HASH48) \ - KEYWORD("hash56", Token::HASH56) \ - KEYWORD("hash64", Token::HASH64) \ - KEYWORD("hash72", Token::HASH72) \ - KEYWORD("hash80", Token::HASH80) \ - KEYWORD("hash88", Token::HASH88) \ - KEYWORD("hash96", Token::HASH96) \ - KEYWORD("hash104", Token::HASH104) \ - KEYWORD("hash112", Token::HASH112) \ - KEYWORD("hash120", Token::HASH120) \ - KEYWORD("hash128", Token::HASH128) \ - KEYWORD("hash136", Token::HASH136) \ - KEYWORD("hash144", Token::HASH144) \ - KEYWORD("hash152", Token::HASH152) \ - KEYWORD("hash160", Token::HASH160) \ - KEYWORD("hash168", Token::HASH168) \ - KEYWORD("hash178", Token::HASH176) \ - KEYWORD("hash184", Token::HASH184) \ - KEYWORD("hash192", Token::HASH192) \ - KEYWORD("hash200", Token::HASH200) \ - KEYWORD("hash208", Token::HASH208) \ - KEYWORD("hash216", Token::HASH216) \ - KEYWORD("hash224", Token::HASH224) \ - KEYWORD("hash232", Token::HASH232) \ - KEYWORD("hash240", Token::HASH240) \ - KEYWORD("hash248", Token::HASH248) \ - KEYWORD("hash256", Token::HASH256) \ - KEYWORD_GROUP('i') \ - KEYWORD("if", Token::IF) \ - KEYWORD("in", Token::IN) \ - KEYWORD("int", Token::INT) \ - KEYWORD("int8", Token::INT8) \ - KEYWORD("int16", Token::INT16) \ - KEYWORD("int24", Token::INT24) \ - KEYWORD("int32", Token::INT32) \ - KEYWORD("int40", Token::INT40) \ - KEYWORD("int48", Token::INT48) \ - KEYWORD("int56", Token::INT56) \ - KEYWORD("int64", Token::INT64) \ - KEYWORD("int72", Token::INT72) \ - KEYWORD("int80", Token::INT80) \ - KEYWORD("int88", Token::INT88) \ - KEYWORD("int96", Token::INT96) \ - KEYWORD("int104", Token::INT104) \ - KEYWORD("int112", Token::INT112) \ - KEYWORD("int120", Token::INT120) \ - KEYWORD("int128", Token::INT128) \ - KEYWORD("int136", Token::INT136) \ - KEYWORD("int144", Token::INT144) \ - KEYWORD("int152", Token::INT152) \ - KEYWORD("int160", Token::INT160) \ - KEYWORD("int168", Token::INT168) \ - KEYWORD("int178", Token::INT176) \ - KEYWORD("int184", Token::INT184) \ - KEYWORD("int192", Token::INT192) \ - KEYWORD("int200", Token::INT200) \ - KEYWORD("int208", Token::INT208) \ - KEYWORD("int216", Token::INT216) \ - KEYWORD("int224", Token::INT224) \ - KEYWORD("int232", Token::INT232) \ - KEYWORD("int240", Token::INT240) \ - KEYWORD("int248", Token::INT248) \ - KEYWORD("int256", Token::INT256) \ - KEYWORD_GROUP('l') \ - KEYWORD_GROUP('m') \ - KEYWORD("mapping", Token::MAPPING) \ - KEYWORD_GROUP('n') \ - KEYWORD("new", Token::NEW) \ - KEYWORD("null", Token::NULL_LITERAL) \ - KEYWORD_GROUP('p') \ - KEYWORD("private", Token::PRIVATE) \ - KEYWORD("public", Token::PUBLIC) \ - KEYWORD_GROUP('r') \ - KEYWORD("real", Token::REAL) \ - KEYWORD("return", Token::RETURN) \ - KEYWORD("returns", Token::RETURNS) \ - KEYWORD_GROUP('s') \ - KEYWORD("string", Token::STRING_TYPE) \ - KEYWORD("struct", Token::STRUCT) \ - KEYWORD("switch", Token::SWITCH) \ - KEYWORD_GROUP('t') \ - KEYWORD("text", Token::TEXT) \ - KEYWORD("true", Token::TRUE_LITERAL) \ - KEYWORD_GROUP('u') \ - KEYWORD("uint", Token::UINT) \ - KEYWORD("uint8", Token::UINT8) \ - KEYWORD("uint16", Token::UINT16) \ - KEYWORD("uint24", Token::UINT24) \ - KEYWORD("uint32", Token::UINT32) \ - KEYWORD("uint40", Token::UINT40) \ - KEYWORD("uint48", Token::UINT48) \ - KEYWORD("uint56", Token::UINT56) \ - KEYWORD("uint64", Token::UINT64) \ - KEYWORD("uint72", Token::UINT72) \ - KEYWORD("uint80", Token::UINT80) \ - KEYWORD("uint88", Token::UINT88) \ - KEYWORD("uint96", Token::UINT96) \ - KEYWORD("uint104", Token::UINT104) \ - KEYWORD("uint112", Token::UINT112) \ - KEYWORD("uint120", Token::UINT120) \ - KEYWORD("uint128", Token::UINT128) \ - KEYWORD("uint136", Token::UINT136) \ - KEYWORD("uint144", Token::UINT144) \ - KEYWORD("uint152", Token::UINT152) \ - KEYWORD("uint160", Token::UINT160) \ - KEYWORD("uint168", Token::UINT168) \ - KEYWORD("uint178", Token::UINT176) \ - KEYWORD("uint184", Token::UINT184) \ - KEYWORD("uint192", Token::UINT192) \ - KEYWORD("uint200", Token::UINT200) \ - KEYWORD("uint208", Token::UINT208) \ - KEYWORD("uint216", Token::UINT216) \ - KEYWORD("uint224", Token::UINT224) \ - KEYWORD("uint232", Token::UINT232) \ - KEYWORD("uint240", Token::UINT240) \ - KEYWORD("uint248", Token::UINT248) \ - KEYWORD("uint256", Token::UINT256) \ - KEYWORD("ureal", Token::UREAL) \ - KEYWORD_GROUP('v') \ - KEYWORD("var", Token::VAR) \ - KEYWORD_GROUP('w') \ - KEYWORD("while", Token::WHILE) \ - - -static Token::Value KeywordOrIdentifierToken(string const& _input) + +static Token::Value keywordOrIdentifierToken(string const& _input) { - if (asserts(!_input.empty())) - BOOST_THROW_EXCEPTION(InternalCompilerError()); - int const kMinLength = 2; - int const kMaxLength = 10; - if (_input.size() < kMinLength || _input.size() > kMaxLength) - return Token::IDENTIFIER; - switch (_input[0]) - { - default: -#define KEYWORD_GROUP_CASE(ch) \ - break; \ - case ch: -#define KEYWORD(keyword, token) \ - { \ - /* 'keyword' is a char array, so sizeof(keyword) is */ \ - /* strlen(keyword) plus 1 for the NUL char. */ \ - int const keywordLength = sizeof(keyword) - 1; \ - BOOST_STATIC_ASSERT(keywordLength >= kMinLength); \ - BOOST_STATIC_ASSERT(keywordLength <= kMaxLength); \ - if (_input == keyword) \ - return token; \ - } - KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) - } - return Token::IDENTIFIER; + // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored + // and keywords to be put inside the keywords variable. +#define KEYWORD(name, string, precedence) {string, Token::name}, +#define TOKEN(name, string, precedence) + static const map keywords({TOKEN_LIST(TOKEN, KEYWORD)}); +#undef KEYWORD +#undef TOKEN + auto it = keywords.find(_input); + return it == keywords.end() ? Token::IDENTIFIER : it->second; } Token::Value Scanner::scanIdentifierOrKeyword() { if (asserts(isIdentifierStart(m_char))) BOOST_THROW_EXCEPTION(InternalCompilerError()); - LiteralScope literal(this); + LiteralScope literal(this, LITERAL_TYPE_STRING); addLiteralCharAndAdvance(); // Scan the rest of the identifier characters. while (isIdentifierPart(m_char)) addLiteralCharAndAdvance(); literal.complete(); - return KeywordOrIdentifierToken(m_nextToken.literal); + return keywordOrIdentifierToken(m_nextToken.literal); } -char CharStream::advanceAndGet() +char CharStream::advanceAndGet(size_t _chars) { if (isPastEndOfInput()) return 0; - ++m_pos; + m_pos += _chars; if (isPastEndOfInput()) return 0; - return get(); + return m_source[m_pos]; } char CharStream::rollback(size_t _amount) diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 957f02b1f..49ac3651c 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -74,9 +74,9 @@ public: CharStream(): m_pos(0) {} explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {} int getPos() const { return m_pos; } - bool isPastEndOfInput() const { return m_pos >= m_source.size(); } - char get() const { return m_source[m_pos]; } - char advanceAndGet(); + bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_pos + _charsForward) >= m_source.size(); } + char get(size_t _charsForward = 0) const { return m_source[m_pos + _charsForward]; } + char advanceAndGet(size_t _chars=1); char rollback(size_t _amount); ///@{ @@ -93,22 +93,11 @@ private: }; + class Scanner { + friend class LiteralScope; public: - /// Scoped helper for literal recording. Automatically drops the literal - /// if aborting the scanning before it's complete. - class LiteralScope - { - public: - explicit LiteralScope(Scanner* self): m_scanner(self), m_complete(false) { m_scanner->startNewLiteral(); } - ~LiteralScope() { if (!m_complete) m_scanner->dropLiteral(); } - void complete() { m_complete = true; } - - private: - Scanner* m_scanner; - bool m_complete; - }; Scanner() { reset(CharStream()); } explicit Scanner(CharStream const& _source) { reset(_source); } @@ -133,8 +122,12 @@ public: ///@{ ///@name Information about the current comment token + Location getCurrentCommentLocation() const { return m_skippedComment.location; } std::string const& getCurrentCommentLiteral() const { return m_skippedComment.literal; } + /// Called by the parser during FunctionDefinition parsing to clear the current comment + void clearCurrentCommentLiteral() { m_skippedComment.literal.clear(); } + ///@} ///@{ @@ -165,10 +158,8 @@ private: ///@{ ///@name Literal buffer support - inline void startNewLiteral() { m_nextToken.literal.clear(); } inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); } inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); } - inline void dropLiteral() { m_nextToken.literal.clear(); } inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); } ///@} @@ -181,9 +172,8 @@ private: bool scanHexByte(char& o_scannedByte); - /// Scans a single Solidity token. Returns true if the scanned token was - /// a skipped documentation comment. False in all other cases. - bool scanToken(); + /// Scans a single Solidity token. + void scanToken(); /// Skips all whitespace and @returns true if something was skipped. bool skipWhitespace(); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 8974ca1ad..f1a94af35 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -314,25 +314,11 @@ public: } // Predicates - static bool isKeyword(Value tok) { return m_tokenType[tok] == 'K'; } - static bool isIdentifier(Value tok) { return tok == IDENTIFIER; } static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; } static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; } static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; } - static bool isTruncatingBinaryOp(Value op) { return BIT_OR <= op && op <= SHR; } static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; } static bool isCompareOp(Value op) { return EQ <= op && op <= IN; } - static bool isOrderedRelationalCompareOp(Value op) - { - return op == LT || op == LTE || op == GT || op == GTE; - } - static bool isEqualityOp(Value op) { return op == EQ; } - static bool isInequalityOp(Value op) { return op == NE; } - static bool isArithmeticCompareOp(Value op) - { - return isOrderedRelationalCompareOp(op) || - isEqualityOp(op) || isInequalityOp(op); - } static Value AssignmentToBinaryOp(Value op) { diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 4ab53bf86..7e07b1162 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -295,9 +295,9 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const u256 offset; for (ASTPointer variable: m_struct.getMembers()) { - offset += variable->getType()->getStorageSize(); if (variable->getName() == _name) return offset; + offset += variable->getType()->getStorageSize(); } BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); } @@ -397,11 +397,11 @@ MagicType::MagicType(MagicType::Kind _kind): break; case Kind::MSG: m_members = MemberList({{"sender", make_shared(0, IntegerType::Modifier::ADDRESS)}, + {"gas", make_shared(256)}, {"value", make_shared(256)}}); break; case Kind::TX: m_members = MemberList({{"origin", make_shared(0, IntegerType::Modifier::ADDRESS)}, - {"gas", make_shared(256)}, {"gasprice", make_shared(256)}}); break; default: diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 8ee9ee722..7607b7a95 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -51,7 +51,6 @@ static Json::Value toJson(dev::eth::BlockInfo const& _bi) res["transactionsRoot"] = toJS(_bi.transactionsRoot); res["difficulty"] = toJS(_bi.difficulty); res["number"] = (int)_bi.number; - res["minGasPrice"] = toJS(_bi.minGasPrice); res["gasLimit"] = (int)_bi.gasLimit; res["timestamp"] = (int)_bi.timestamp; res["extraData"] = jsFromBinary(_bi.extraData); diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 0fb7a206c..9b26e3260 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -156,6 +156,12 @@ void WhisperHost::uninstallWatch(unsigned _i) m_filters.erase(fit); } +void WhisperHost::doWork() +{ + for (auto& i: peers()) + i->cap()->sendMessages(); +} + void WhisperHost::cleanup() { // remove old messages. diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index 91934dd98..b38964f8e 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include "Common.h" @@ -38,7 +39,7 @@ namespace dev namespace shh { -class WhisperHost: public HostCapability, public Interface +class WhisperHost: public HostCapability, public Interface, public Worker { friend class WhisperPeer; @@ -64,7 +65,13 @@ public: void cleanup(); +protected: + void doWork(); + private: + virtual void onStarting() { startWorking(); } + virtual void onStopping() { stopWorking(); } + void streamMessage(h256 _m, RLPStream& _s) const; void noteChanged(h256 _messageHash, h256 _filter); diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index 56f4e456e..c3a28e3c3 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -72,7 +72,6 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r) for (auto i: _r) if (n++) host()->inject(Envelope(i), this); - sendMessages(); break; } default: @@ -97,10 +96,15 @@ void WhisperPeer::sendMessages() } } - if (!n) - // pause for a bit if no messages to send - this is horrible and broken. - // the message subsystem should really just keep pumping out messages while m_unseen.size() and there's bandwidth for them. - this_thread::sleep_for(chrono::milliseconds(20)); + // the message subsystem should really just keep pumping out messages while m_unseen.size() and there's bandwidth for them. + auto diff = chrono::duration_cast(chrono::system_clock::now() - m_timer); + if (n || diff.count() > 0) + { + RLPStream s; + prep(s, MessagesPacket, n).appendRaw(amalg.out(), n); + sealAndSend(s); + m_timer = chrono::system_clock::now(); + } { RLPStream s; diff --git a/libwhisper/WhisperPeer.h b/libwhisper/WhisperPeer.h index f60de8f01..faac2d870 100644 --- a/libwhisper/WhisperPeer.h +++ b/libwhisper/WhisperPeer.h @@ -68,6 +68,8 @@ private: mutable dev::Mutex x_unseen; std::map m_unseen; ///< Rated according to what they want. + + std::chrono::system_clock::time_point m_timer = std::chrono::system_clock::now(); }; } diff --git a/mix/.gitignore b/mix/.gitignore new file mode 100644 index 000000000..f96209dc3 --- /dev/null +++ b/mix/.gitignore @@ -0,0 +1 @@ +*.pro diff --git a/mix/ApplicationCtx.cpp b/mix/ApplicationCtx.cpp new file mode 100644 index 000000000..f97478f3c --- /dev/null +++ b/mix/ApplicationCtx.cpp @@ -0,0 +1,36 @@ +/* + This file is part of cpp-ethereum. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ApplicationCtx.cpp + * @author Yann yann@ethdev.com + * @date 2014 + * Provide an access to the current QQmlApplicationEngine which is used to add QML file on the fly. + * In the future this class can be extended to add more variable related to the context of the application. + */ + +#include +#include "ApplicationCtx.h" +using namespace dev::mix; + +ApplicationCtx* ApplicationCtx::Instance = nullptr; + +QQmlApplicationEngine* ApplicationCtx::appEngine() +{ + return m_applicationEngine; +} + +void ApplicationCtx::setApplicationContext(QQmlApplicationEngine* _engine) +{ + if (Instance == nullptr) + Instance = new ApplicationCtx(_engine); +} diff --git a/mix/ApplicationCtx.h b/mix/ApplicationCtx.h new file mode 100644 index 000000000..37166ea05 --- /dev/null +++ b/mix/ApplicationCtx.h @@ -0,0 +1,55 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ApplicationCtx.h + * @author Yann yann@ethdev.com + * @date 2014 + * Provide an access to the current QQmlApplicationEngine which is used to add QML file on the fly. + * In the future this class can be extended to add more variable related to the context of the application. + */ + +#pragma once + +#include + +namespace dev +{ + +namespace mix +{ + +class ApplicationCtx: public QObject +{ + Q_OBJECT + +public: + ApplicationCtx(QQmlApplicationEngine* _engine) { m_applicationEngine = _engine; } + ~ApplicationCtx() { delete m_applicationEngine; } + static ApplicationCtx* getInstance() { return Instance; } + static void setApplicationContext(QQmlApplicationEngine* _engine); + QQmlApplicationEngine* appEngine(); + +private: + static ApplicationCtx* Instance; + QQmlApplicationEngine* m_applicationEngine; + +public slots: + void quitApplication() { delete Instance; } +}; + +} + +} diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt new file mode 100644 index 000000000..da214fd29 --- /dev/null +++ b/mix/CMakeLists.txt @@ -0,0 +1,94 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) +aux_source_directory(. SRC_LIST) +include_directories(..) + + +if (APPLE) + # Add homebrew path for qt5 + set(CMAKE_PREFIX_PATH /usr/local/opt/qt5) + include_directories(/usr/local/opt/qt5/include /usr/local/include) +elseif ("${TARGET_PLATFORM}" STREQUAL "w64") + set(SRC_LIST ${SRC_LIST} ../windows/qt_plugin_import.cpp) + include_directories(/usr/x86_64-w64-mingw32/include /usr/x86_64-w64-mingw32/include/QtCore /usr/x86_64-w64-mingw32/include/QtGui /usr/x86_64-w64-mingw32/include/QtQuick /usr/x86_64-w64-mingw32/include/QtQml /usr/x86_64-w64-mingw32/include/QtNetwork /usr/x86_64-w64-mingw32/include/QtWidgets /usr/x86_64-w64-mingw32/include/QtWebKit /usr/x86_64-w64-mingw32/include/QtWebKitWidgets) +elseif (UNIX) + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ";$ENV{QTDIR}/lib/cmake") +endif () + +find_package(Qt5Core REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Quick REQUIRED) +find_package(Qt5Qml REQUIRED) +find_package(Qt5Network REQUIRED) +find_package(Qt5Widgets REQUIRED) +find_package(Qt5WebKit REQUIRED) +find_package(Qt5WebKitWidgets REQUIRED) + +#qt5_wrap_ui(ui_Main.h Main.ui) + +qt5_add_resources(UI_RESOURCES qml.qrc) + +# Set name of binary and add_executable() +file(GLOB HEADERS "*.h") +if (APPLE) + set(EXECUTEABLE mix) + set(BIN_INSTALL_DIR ".") + set(DOC_INSTALL_DIR ".") + + set(PROJECT_VERSION "${ETH_VERSION}") + set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") + set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}") + set(MACOSX_BUNDLE_BUNDLE_NAME ${EXECUTEABLE}) + set(MACOSX_BUNDLE_ICON_FILE mix) + include(BundleUtilities) + + add_executable(${EXECUTEABLE} MACOSX_BUNDLE ${SRC_LIST} ${HEADERS} ${UI_RESOURCES}) + set_target_properties(${EXECUTEABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/EthereumMacOSXBundleInfo.plist.in") + SET_SOURCE_FILES_PROPERTIES(${EXECUTEABLE} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) + SET_SOURCE_FILES_PROPERTIES(${MACOSX_BUNDLE_ICON_FILE}.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + +else () + set(EXECUTEABLE mix) + add_executable(${EXECUTEABLE} ${SRC_LIST} ${HEADERS} ${UI_RESOURCES}) +endif () + +qt5_use_modules(${EXECUTEABLE} Core)# Gui Widgets Network WebKit WebKitWidgets) +target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore devcrypto secp256k1 gmp ${CRYPTOPP_LS} serpent lll solidity evmcore devcore web3jsonrpc jsqrc) + +if (APPLE) + # First have qt5 install plugins and frameworks + add_custom_command(TARGET ${EXECUTEABLE} POST_BUILD + COMMAND /usr/local/opt/qt5/bin/macdeployqt ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTEABLE}.app + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + + # This tool and next will inspect linked libraries in order to determine which dependencies are required + if (${CMAKE_CFG_INTDIR} STREQUAL ".") + set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${EXECUTEABLE}.app") + else () + set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTEABLE}.app") + endif () + install(CODE " + include(BundleUtilities) + set(BU_CHMOD_BUNDLE_ITEMS 1) + fixup_bundle(\"${APP_BUNDLE_PATH}\" \"${BUNDLELIBS}\" \"../libqethereum ../libethereum ../secp256k1\") + " COMPONENT RUNTIME ) + # Cleanup duplicate libs from macdeployqt + install(CODE " + file(GLOB LINGER_RM \"${APP_BUNDLE_PATH}/Contents/Frameworks/*.dylib\") + if (LINGER_RM) + file(REMOVE \${LINGER_RM}) + endif () + ") +elseif (UNIX) +else () + target_link_libraries(${EXECUTEABLE} boost_system) + target_link_libraries(${EXECUTEABLE} boost_filesystem) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTEABLE} ${CMAKE_THREAD_LIBS_INIT}) + install( TARGETS ${EXECUTEABLE} RUNTIME DESTINATION bin ) +endif () + +qt5_use_modules(${EXECUTEABLE} Core Gui) diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp new file mode 100644 index 000000000..596aea165 --- /dev/null +++ b/mix/CodeEditorExtensionManager.cpp @@ -0,0 +1,89 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file CodeEditorExtensionMan.cpp + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include +#include +#include "ConstantCompilationCtrl.h" +#include "features.h" +#include "ApplicationCtx.h" +#include "CodeEditorExtensionManager.h" +using namespace dev::mix; + +CodeEditorExtensionManager::~CodeEditorExtensionManager() +{ + m_features.clear(); +} + +void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) +{ + if (!_editor) + return; + try + { + QVariant doc = _editor->property("textDocument"); + if (doc.canConvert()) + { + QQuickTextDocument* qqdoc = doc.value(); + if (qqdoc) + m_doc = qqdoc->textDocument(); + } + } + catch (...) + { + qDebug() << "unable to load editor: "; + } +} + +void CodeEditorExtensionManager::initExtensions() +{ + //only one for now + std::shared_ptr constantCompilation = std::make_shared(m_doc); + if (constantCompilation.get()->contentUrl() != "") + { + try + { + constantCompilation.get()->addContentOn(m_tabView); + } + catch (...) + { + qDebug() << "Exception when adding content into view."; + return; + } + } + constantCompilation.get()->start(); + m_features.append(constantCompilation); +} + +void CodeEditorExtensionManager::setEditor(QQuickItem* _editor) +{ + this->loadEditor(_editor); + this->initExtensions(); +} + +void CodeEditorExtensionManager::setTabView(QQuickItem* _tabView) +{ + m_tabView = _tabView; +} diff --git a/mix/CodeEditorExtensionManager.h b/mix/CodeEditorExtensionManager.h new file mode 100644 index 000000000..2b8402bf2 --- /dev/null +++ b/mix/CodeEditorExtensionManager.h @@ -0,0 +1,61 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file CodeEditorExtensionMan.h + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#pragma once + +#include "memory" +#include +#include +#include +#include "ConstantCompilationCtrl.h" + +namespace dev +{ + +namespace mix +{ + +class CodeEditorExtensionManager: public QObject +{ + Q_OBJECT + + Q_PROPERTY(QQuickItem* editor MEMBER m_editor WRITE setEditor) + Q_PROPERTY(QQuickItem* tabView MEMBER m_tabView WRITE setTabView) + +public: + CodeEditorExtensionManager() {} + ~CodeEditorExtensionManager(); + void initExtensions(); + void setEditor(QQuickItem*); + void setTabView(QQuickItem*); + +private: + QQuickItem* m_editor; + QVector> m_features; + QQuickItem* m_tabView; + QTextDocument* m_doc; + void loadEditor(QQuickItem*); +}; + +} + +} diff --git a/mix/ConstantCompilationCtrl.cpp b/mix/ConstantCompilationCtrl.cpp new file mode 100644 index 000000000..06b9c0284 --- /dev/null +++ b/mix/ConstantCompilationCtrl.cpp @@ -0,0 +1,97 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ConstantCompilation.cpp + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include +#include +#include "ConstantCompilationCtrl.h" +#include "ConstantCompilationModel.h" +using namespace dev::mix; + +ConstantCompilationCtrl::ConstantCompilationCtrl(QTextDocument* _doc) +{ + m_editor = _doc; + m_compilationModel = new ConstantCompilationModel(); +} + +ConstantCompilationCtrl::~ConstantCompilationCtrl() +{ + delete m_compilationModel; +} + +QString ConstantCompilationCtrl::contentUrl() const +{ + return QStringLiteral("qrc:/qml/BasicContent.qml"); +} + +QString ConstantCompilationCtrl::title() const +{ + return "compiler"; +} + +void ConstantCompilationCtrl::start() const +{ + connect(m_editor, SIGNAL(contentsChange(int,int,int)), this, SLOT(compile())); +} + +void ConstantCompilationCtrl::compile() +{ + QString codeContent = m_editor->toPlainText().replace("\n", ""); + if (codeContent.isEmpty()) + { + resetOutPut(); + return; + } + CompilerResult res = m_compilationModel->compile(m_editor->toPlainText()); + writeOutPut(res); +} + +void ConstantCompilationCtrl::resetOutPut() +{ + QObject* status = m_view->findChild("status", Qt::FindChildrenRecursively); + QObject* content = m_view->findChild("content", Qt::FindChildrenRecursively); + status->setProperty("text", ""); + content->setProperty("text", ""); +} + +void ConstantCompilationCtrl::writeOutPut(CompilerResult const& _res) +{ + QObject* status = m_view->findChild("status", Qt::FindChildrenRecursively); + QObject* content = m_view->findChild("content", Qt::FindChildrenRecursively); + if (_res.success) + { + status->setProperty("text", "succeeded"); + status->setProperty("color", "green"); + content->setProperty("text", _res.hexCode); + qDebug() << QString("compile succeeded " + _res.hexCode); + } + else + { + status->setProperty("text", "failure"); + status->setProperty("color", "red"); + content->setProperty("text", _res.comment); + qDebug() << QString("compile failed " + _res.comment); + } +} diff --git a/mix/ConstantCompilationCtrl.h b/mix/ConstantCompilationCtrl.h new file mode 100644 index 000000000..e4661c800 --- /dev/null +++ b/mix/ConstantCompilationCtrl.h @@ -0,0 +1,55 @@ +/* + This file is part of cpp-ethereum. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ConstantCompilation.h + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#pragma once + +#include +#include "ConstantCompilationModel.h" +#include "Extension.h" + +namespace dev +{ + +namespace mix +{ + +class ConstantCompilationCtrl: public Extension +{ + Q_OBJECT + +public: + ConstantCompilationCtrl(QTextDocument*); + ~ConstantCompilationCtrl(); + void start() const override; + QString title() const override; + QString contentUrl() const override; + +private: + QTextDocument* m_editor; + ConstantCompilationModel* m_compilationModel; + void writeOutPut(CompilerResult const&); + void resetOutPut(); + +public Q_SLOTS: + void compile(); +}; + +} + +} diff --git a/mix/ConstantCompilationModel.cpp b/mix/ConstantCompilationModel.cpp new file mode 100644 index 000000000..e06734f59 --- /dev/null +++ b/mix/ConstantCompilationModel.cpp @@ -0,0 +1,61 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ApplicationCtx.h + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include +#include "ConstantCompilationModel.h" +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace dev::mix; + +CompilerResult ConstantCompilationModel::compile(QString _code) +{ + dev::solidity::CompilerStack compiler; + dev::bytes m_data; + CompilerResult res; + try + { + m_data = compiler.compile(_code.toStdString(), true); + res.success = true; + res.comment = "ok"; + res.hexCode = QString::fromStdString(dev::eth::disassemble(m_data)); + } + catch (dev::Exception const& _exception) + { + ostringstream error; + solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler.getScanner()); + res.success = false; + res.comment = QString::fromStdString(error.str()).toHtmlEscaped(); + res.hexCode = ""; + } + catch (...) + { + res.success = false; + res.comment = "Uncaught exception."; + res.hexCode = ""; + } + return res; +} diff --git a/mix/ConstantCompilationModel.h b/mix/ConstantCompilationModel.h new file mode 100644 index 000000000..4a17853f6 --- /dev/null +++ b/mix/ConstantCompilationModel.h @@ -0,0 +1,51 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ApplicationCtx.h + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#pragma once + +#include + +namespace dev +{ + +namespace mix +{ + +struct CompilerResult +{ + QString hexCode; + QString comment; + bool success; +}; + +class ConstantCompilationModel +{ + +public: + ConstantCompilationModel() {} + ~ConstantCompilationModel() {} + CompilerResult compile(QString code); +}; + +} + +} diff --git a/mix/Extension.cpp b/mix/Extension.cpp new file mode 100644 index 000000000..5aeb0cc17 --- /dev/null +++ b/mix/Extension.cpp @@ -0,0 +1,44 @@ +/* + This file is part of cpp-ethereum. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Feature.cpp + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#include +#include +#include +#include "Extension.h" +#include "ApplicationCtx.h" +using namespace dev; +using namespace dev::mix; + +void Extension::addContentOn(QObject* _tabView) +{ + if (contentUrl() == "") + return; + + QVariant returnValue; + QQmlComponent* component = new QQmlComponent( + ApplicationCtx::getInstance()->appEngine(), + QUrl(this->contentUrl()), _tabView); + + QMetaObject::invokeMethod(_tabView, "addTab", + Q_RETURN_ARG(QVariant, returnValue), + Q_ARG(QVariant, this->title()), + Q_ARG(QVariant, QVariant::fromValue(component))); + + m_view = qvariant_cast(returnValue); +} diff --git a/mix/Extension.h b/mix/Extension.h new file mode 100644 index 000000000..f8fef0aa6 --- /dev/null +++ b/mix/Extension.h @@ -0,0 +1,48 @@ +/* + This file is part of cpp-ethereum. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Feature.h + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#pragma once + +#include +#include + +namespace dev +{ + +namespace mix +{ + +class Extension: public QObject +{ + Q_OBJECT + +public: + Extension() {} + virtual QString contentUrl() const { return ""; } + virtual QString title() const { return ""; } + virtual void start() const {} + void addContentOn(QObject* tabView); + +protected: + QObject* m_view; +}; + +} + +} diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp new file mode 100644 index 000000000..e67ca1b12 --- /dev/null +++ b/mix/MixApplication.cpp @@ -0,0 +1,45 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file main.cpp + * @author Yann yann@ethdev.com + * @date 2014 + */ + +#include +#include "MixApplication.h" +using namespace dev::mix; + +MixApplication::MixApplication(int _argc, char *_argv[]): QApplication(_argc, _argv) +{ +} + +bool MixApplication::notify(QObject* _receiver, QEvent* _event) +{ + try + { + return MixApplication::notify(_receiver, _event); + } + catch (std::exception& _ex) + { + qDebug() << "std::exception was caught " << _ex.what(); + } + catch (...) + { + qDebug() << "uncaught exception "; + } + return false; +} diff --git a/mix/MixApplication.h b/mix/MixApplication.h new file mode 100644 index 000000000..fdc506268 --- /dev/null +++ b/mix/MixApplication.h @@ -0,0 +1,46 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file main.cpp + * @author Yann yann@ethdev.com + * @date 2014 + * This class will be use instead of QApplication to launch the application. the method 'notify' allows to catch all exceptions. + * Not use for now: TODO. + */ + +#pragma once + +#include + +namespace dev +{ + +namespace mix +{ + +class MixApplication: public QApplication +{ + Q_OBJECT + +public: + MixApplication(int _argc, char* _argv[]); + virtual ~MixApplication() {} + virtual bool notify(QObject* _receiver, QEvent* _event); +}; + +} + +} diff --git a/mix/main.cpp b/mix/main.cpp new file mode 100644 index 000000000..537941290 --- /dev/null +++ b/mix/main.cpp @@ -0,0 +1,40 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file main.cpp + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#include +#include +#include +#include "CodeEditorExtensionManager.h" +#include "ApplicationCtx.h" +#include "MixApplication.h" +using namespace dev::mix; + +int main(int _argc, char *_argv[]) +{ + QApplication app(_argc, _argv); + qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); + QQmlApplicationEngine* engine = new QQmlApplicationEngine(); + ApplicationCtx::setApplicationContext(engine); + QObject::connect(&app, SIGNAL(lastWindowClosed()), ApplicationCtx::getInstance(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff + engine->load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); + return app.exec(); +} diff --git a/mix/qml.qrc b/mix/qml.qrc new file mode 100644 index 000000000..267427ce5 --- /dev/null +++ b/mix/qml.qrc @@ -0,0 +1,8 @@ + + + qml/BasicContent.qml + qml/main.qml + qml/MainContent.qml + qml/TabStyle.qml + + diff --git a/mix/qml/BasicContent.qml b/mix/qml/BasicContent.qml new file mode 100644 index 000000000..0049e6127 --- /dev/null +++ b/mix/qml/BasicContent.qml @@ -0,0 +1,35 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +Rectangle { + anchors.fill: parent + width: parent.width + height: parent.height + color: "lightgray" + Text { + font.pointSize: 7 + anchors.left: parent.left + anchors.top: parent.top + anchors.topMargin: 3 + anchors.leftMargin: 3 + height: 9 + font.family: "Sego UI light" + objectName: "status" + id: status + } + TextArea { + readOnly: true + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.top: status.bottom + anchors.topMargin: 3 + font.pointSize: 7 + font.family: "Sego UI light" + height: parent.height * 0.8 + width: parent.width - 20 + wrapMode: Text.Wrap + backgroundVisible: false + objectName: "content" + id: content + } +} diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml new file mode 100644 index 000000000..f243b16ac --- /dev/null +++ b/mix/qml/MainContent.qml @@ -0,0 +1,54 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.0 +import QtQuick.Controls.Styles 1.2 +import CodeEditorExtensionManager 1.0 + +Rectangle { + anchors.fill: parent + height: parent.height + width: parent.width; + id:root + SplitView { + anchors.fill: parent + orientation: Qt.Vertical + Rectangle { + anchors.top: parent.top + id: contentView + width: parent.width + height: parent.height * 0.7 + TextArea { + id: codeEditor + height: parent.height + font.family: "Verdana" + font.pointSize: 9 + width: parent.width + anchors.centerIn: parent + tabChangesFocus: false + Keys.onPressed: { + if (event.key === Qt.Key_Tab) { + codeEditor.insert(codeEditor.cursorPosition, "\t"); + event.accepted = true; + } + } + } + } + Rectangle { + anchors.bottom: parent.bottom + id: contextualView + width: parent.width + Layout.minimumHeight: 20 + height: parent.height * 0.3 + TabView { + id: contextualTabs + antialiasing: true + anchors.fill: parent + style: TabStyle {} + } + } + CodeEditorExtensionManager{ + tabView: contextualTabs + editor: codeEditor + } + } +} diff --git a/mix/qml/TabStyle.qml b/mix/qml/TabStyle.qml new file mode 100644 index 000000000..5f78f8947 --- /dev/null +++ b/mix/qml/TabStyle.qml @@ -0,0 +1,23 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.2 + +TabViewStyle { + frameOverlap: 1 + tabBar: Rectangle { + color: "lightgray" + } + tab: Rectangle { + color: "lightsteelblue" + implicitWidth: Math.max(text.width + 4, 80) + implicitHeight: 20 + radius: 2 + Text { + id: text + anchors.centerIn: parent + text: styleData.title + color: styleData.selected ? "white" : "black" + } + } + frame: Rectangle { color: "steelblue" } +} diff --git a/mix/qml/main.qml b/mix/qml/main.qml new file mode 100644 index 000000000..8a1b86cee --- /dev/null +++ b/mix/qml/main.qml @@ -0,0 +1,24 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.2 +import CodeEditorExtensionManager 1.0 + +ApplicationWindow { + visible: true + width: 1000 + height: 480 + minimumWidth: 400 + minimumHeight: 300 + title: qsTr("mix") + menuBar: MenuBar { + Menu { + title: qsTr("File") + MenuItem { + text: qsTr("Exit") + onTriggered: Qt.quit(); + } + } + } + MainContent{ + } +} diff --git a/neth/main.cpp b/neth/main.cpp index 6be555fbb..82968bf03 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -475,10 +475,10 @@ int main(int argc, char** argv) c.startMining(); #if ETH_JSONRPC - auto_ptr jsonrpcServer; + shared_ptr jsonrpcServer; if (jsonrpc > -1) { - jsonrpcServer = auto_ptr(new WebThreeStubServer(new jsonrpc::HttpServer(jsonrpc), web3, {us})); + jsonrpcServer = make_shared(new jsonrpc::HttpServer(jsonrpc), web3, vector({us})); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); } @@ -552,7 +552,7 @@ int main(int argc, char** argv) { if (jsonrpc < 0) jsonrpc = 8080; - jsonrpcServer = auto_ptr(new WebThreeStubServer(new jsonrpc::HttpServer(jsonrpc), web3, {us})); + jsonrpcServer = make_shared(new jsonrpc::HttpServer(jsonrpc), web3, vector({us})); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); } @@ -603,7 +603,7 @@ int main(int argc, char** argv) vector l; l.push_back("Amount"); stringstream label; - label << "Gas price (" << info.minGasPrice << ")"; + label << "Gas price"; l.push_back(label.str()); l.push_back("Gas"); vector b; @@ -646,14 +646,12 @@ int main(int argc, char** argv) ssbd << bbd; cnote << ssbd.str(); int ssize = fields[4].length(); - u256 minGas = (u256)Client::txGas(data.size(), 0); + u256 minGas = (u256)Client::txGas(data, 0); if (size < 40) { if (size > 0) cwarn << "Invalid address length:" << size; } - else if (gasPrice < info.minGasPrice) - cwarn << "Minimum gas price is" << info.minGasPrice; else if (gas < minGas) cwarn << "Minimum gas amount is" << minGas; else if (ssize < 40) @@ -702,9 +700,9 @@ int main(int argc, char** argv) auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); - u256 minGas = (u256)Client::txGas(0, 0); + u256 minGas = (u256)Client::txGas(bytes(), 0); Address dest = h160(fromHex(fields[0])); - c.transact(us.secret(), amount, dest, bytes(), minGas, info.minGasPrice); + c.transact(us.secret(), amount, dest, bytes(), minGas); } } } @@ -718,7 +716,7 @@ int main(int argc, char** argv) vector l; l.push_back("Endowment"); stringstream label; - label << "Gas price (" << info.minGasPrice << ")"; + label << "Gas price"; l.push_back(label.str()); l.push_back("Gas"); vector b; @@ -763,16 +761,14 @@ int main(int argc, char** argv) cnote << "Init:"; cnote << ssc.str(); } - u256 minGas = (u256)Client::txGas(init.size(), 0); + u256 minGas = (u256)Client::txGas(init, 0); if (endowment < 0) cwarn << "Invalid endowment"; - else if (gasPrice < info.minGasPrice) - cwarn << "Minimum gas price is" << info.minGasPrice; else if (gas < minGas) cwarn << "Minimum gas amount is" << minGas; else { - c.transact(us.secret(), endowment, init, gas, gasPrice); + c.transact(us.secret(), endowment, init, gas); } } } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index b3d1bbe80..e64264748 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -72,6 +72,7 @@ ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller): m_TestObject(_o if (!isFiller) { importState(_o["post"].get_obj(), m_statePost); + m_environment.sub.logs = importLog(_o["logs"].get_obj()); } } @@ -148,6 +149,9 @@ void ImportTest::exportTest(bytes _output, State& _statePost) // export output m_TestObject["out"] = "0x" + toHex(_output); + // export logs + m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); + // export post state json_spirit::mObject postState; @@ -255,6 +259,44 @@ bytes importCode(json_spirit::mObject& _o) return code; } +LogEntries importLog(json_spirit::mObject& _o) +{ + LogEntries logEntries; + for (auto const& l: _o) + { + json_spirit::mObject o = l.second.get_obj(); + // cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest) + assert(o.count("address") > 0); + assert(o.count("topics") > 0); + assert(o.count("data") > 0); + LogEntry log; + log.address = Address(o["address"].get_str()); + for (auto const& t: o["topics"].get_array()) + log.topics.push_back(h256(t.get_str())); + log.data = importData(o); + logEntries.push_back(log); + } + return logEntries; +} + +json_spirit::mObject exportLog(eth::LogEntries _logs) +{ + json_spirit::mObject ret; + if (_logs.size() == 0) return ret; + for (LogEntry const& l: _logs) + { + json_spirit::mObject o; + o["address"] = toString(l.address); + json_spirit::mArray topics; + for (auto const& t: l.topics) + topics.push_back(toString(t)); + o["topics"] = topics; + o["data"] = "0x" + toHex(l.data); + ret[toString(l.bloom())] = o; + } + return ret; +} + void checkOutput(bytes const& _output, json_spirit::mObject& _o) { int j = 0; diff --git a/test/TestHelper.h b/test/TestHelper.h index 7a2b8da51..c5c3a083d 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -68,6 +68,8 @@ u256 toInt(json_spirit::mValue const& _v); byte toByte(json_spirit::mValue const& _v); bytes importCode(json_spirit::mObject& _o); bytes importData(json_spirit::mObject& _o); +eth::LogEntries importLog(json_spirit::mObject& _o); +json_spirit::mObject exportLog(eth::LogEntries _logs); void checkOutput(bytes const& _output, json_spirit::mObject& _o); void checkStorage(std::map _expectedStore, std::map _resultStore, Address _expectedAddr); void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs); diff --git a/test/createRandomTest.cpp b/test/createRandomTest.cpp index 60a2039c8..1647ce810 100644 --- a/test/createRandomTest.cpp +++ b/test/createRandomTest.cpp @@ -121,14 +121,14 @@ void doMyTests(json_spirit::mValue& v) { for (auto& i: v.get_obj()) { + cnote << i.first; mObject& o = i.second.get_obj(); assert(o.count("env") > 0); assert(o.count("pre") > 0); assert(o.count("exec") > 0); - eth::VM vm; - test::FakeExtVM fev; + dev::test::FakeExtVM fev; fev.importEnv(o["env"].get_obj()); fev.importState(o["pre"].get_obj()); @@ -141,17 +141,20 @@ void doMyTests(json_spirit::mValue& v) fev.code = fev.thisTxCode; } - vm.reset(fev.gas); bytes output; + eth::VM vm(fev.gas); + u256 gas; + bool vmExceptionOccured = false; try { - output = vm.go(fev).toBytes(); + output = vm.go(fev, fev.simpleTrace()).toBytes(); + gas = vm.gas(); } catch (eth::VMException const& _e) { cnote << "VM did throw an exception: " << diagnostic_information(_e); - gas = 0; + vmExceptionOccured = true; } catch (Exception const& _e) { @@ -180,9 +183,13 @@ void doMyTests(json_spirit::mValue& v) o["env"] = mValue(fev.exportEnv()); o["exec"] = mValue(fev.exportExec()); - o["post"] = mValue(fev.exportState()); - o["callcreates"] = fev.exportCallCreates(); - o["out"] = "0x" + toHex(output); - fev.push(o, "gas", gas); + if (!vmExceptionOccured) + { + o["post"] = mValue(fev.exportState()); + o["callcreates"] = fev.exportCallCreates(); + o["out"] = "0x" + toHex(output); + fev.push(o, "gas", gas); + o["logs"] = mValue(test::exportLog(fev.sub.logs)); + } } } diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index a19e7450c..8804c519c 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -244,6 +244,36 @@ BOOST_AUTO_TEST_CASE(balance_invalid) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } +BOOST_AUTO_TEST_CASE(assignment_to_mapping) +{ + char const* text = "contract test {\n" + " struct str {\n" + " mapping(uint=>uint) map;\n" + " }\n" + " str data;" + " function fun() {\n" + " var a = data.map;\n" + " data.map = a;\n" + " }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(assignment_to_struct) +{ + char const* text = "contract test {\n" + " struct str {\n" + " mapping(uint=>uint) map;\n" + " }\n" + " str data;" + " function fun() {\n" + " var a = data;\n" + " data = a;\n" + " }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 9319a02c5..6a97a5d99 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -37,13 +37,14 @@ namespace test namespace { -ASTPointer parseText(std::string const& _source) +ASTPointer parseText(std::string const& _source) { Parser parser; return parser.parse(std::make_shared(CharStream(_source))); } } + BOOST_AUTO_TEST_SUITE(SolidityParser) BOOST_AUTO_TEST_CASE(smoke_test) @@ -91,6 +92,164 @@ BOOST_AUTO_TEST_CASE(single_function_param) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(function_natspec_documentation) +{ + ASTPointer contract; + ASTPointer function; + char const* text = "contract test {\n" + " uint256 stateVar;\n" + " /// This is a test function\n" + " function functionName(hash hashin) returns (hash hashout) {}\n" + "}\n"; + BOOST_REQUIRE_NO_THROW(contract = parseText(text)); + auto functions = contract->getDefinedFunctions(); + BOOST_REQUIRE_NO_THROW(function = functions.at(0)); + BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is a test function"); +} + +BOOST_AUTO_TEST_CASE(function_normal_comments) +{ + ASTPointer contract; + ASTPointer function; + char const* text = "contract test {\n" + " uint256 stateVar;\n" + " // We won't see this comment\n" + " function functionName(hash hashin) returns (hash hashout) {}\n" + "}\n"; + BOOST_REQUIRE_NO_THROW(contract = parseText(text)); + auto functions = contract->getDefinedFunctions(); + BOOST_REQUIRE_NO_THROW(function = functions.at(0)); + BOOST_CHECK_MESSAGE(function->getDocumentation() == nullptr, + "Should not have gotten a Natspect comment for this function"); +} + +BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation) +{ + ASTPointer contract; + ASTPointer function; + char const* text = "contract test {\n" + " uint256 stateVar;\n" + " /// This is test function 1\n" + " function functionName1(hash hashin) returns (hash hashout) {}\n" + " /// This is test function 2\n" + " function functionName2(hash hashin) returns (hash hashout) {}\n" + " // nothing to see here\n" + " function functionName3(hash hashin) returns (hash hashout) {}\n" + " /// This is test function 4\n" + " function functionName4(hash hashin) returns (hash hashout) {}\n" + "}\n"; + BOOST_REQUIRE_NO_THROW(contract = parseText(text)); + auto functions = contract->getDefinedFunctions(); + + BOOST_REQUIRE_NO_THROW(function = functions.at(0)); + BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 1"); + + BOOST_REQUIRE_NO_THROW(function = functions.at(1)); + BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 2"); + + BOOST_REQUIRE_NO_THROW(function = functions.at(2)); + BOOST_CHECK_MESSAGE(function->getDocumentation() == nullptr, + "Should not have gotten natspec comment for functionName3()"); + + BOOST_REQUIRE_NO_THROW(function = functions.at(3)); + BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 4"); +} + +BOOST_AUTO_TEST_CASE(multiline_function_documentation) +{ + ASTPointer contract; + ASTPointer function; + char const* text = "contract test {\n" + " uint256 stateVar;\n" + " /// This is a test function\n" + " /// and it has 2 lines\n" + " function functionName1(hash hashin) returns (hash hashout) {}\n" + "}\n"; + BOOST_REQUIRE_NO_THROW(contract = parseText(text)); + auto functions = contract->getDefinedFunctions(); + + BOOST_REQUIRE_NO_THROW(function = functions.at(0)); + BOOST_CHECK_EQUAL(*function->getDocumentation(), + " This is a test function\n" + " and it has 2 lines"); +} + +BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body) +{ + ASTPointer contract; + ASTPointer function; + char const* text = "contract test {\n" + " /// fun1 description\n" + " function fun1(uint256 a) {\n" + " var b;\n" + " /// I should not interfere with actual natspec comments\n" + " uint256 c;\n" + " mapping(address=>hash) d;\n" + " string name = \"Solidity\";" + " }\n" + " uint256 stateVar;\n" + " /// This is a test function\n" + " /// and it has 2 lines\n" + " function fun(hash hashin) returns (hash hashout) {}\n" + "}\n"; + BOOST_REQUIRE_NO_THROW(contract = parseText(text)); + auto functions = contract->getDefinedFunctions(); + + BOOST_REQUIRE_NO_THROW(function = functions.at(0)); + BOOST_CHECK_EQUAL(*function->getDocumentation(), " fun1 description"); + + BOOST_REQUIRE_NO_THROW(function = functions.at(1)); + BOOST_CHECK_EQUAL(*function->getDocumentation(), + " This is a test function\n" + " and it has 2 lines"); +} + +BOOST_AUTO_TEST_CASE(natspec_docstring_between_keyword_and_signature) +{ + ASTPointer contract; + ASTPointer function; + char const* text = "contract test {\n" + " uint256 stateVar;\n" + " function ///I am in the wrong place \n" + " fun1(uint256 a) {\n" + " var b;\n" + " /// I should not interfere with actual natspec comments\n" + " uint256 c;\n" + " mapping(address=>hash) d;\n" + " string name = \"Solidity\";" + " }\n" + "}\n"; + BOOST_REQUIRE_NO_THROW(contract = parseText(text)); + auto functions = contract->getDefinedFunctions(); + + BOOST_REQUIRE_NO_THROW(function = functions.at(0)); + BOOST_CHECK_MESSAGE(!function->getDocumentation(), + "Shouldn't get natspec docstring for this function"); +} + +BOOST_AUTO_TEST_CASE(natspec_docstring_after_signature) +{ + ASTPointer contract; + ASTPointer function; + char const* text = "contract test {\n" + " uint256 stateVar;\n" + " function fun1(uint256 a) {\n" + " /// I should have been above the function signature\n" + " var b;\n" + " /// I should not interfere with actual natspec comments\n" + " uint256 c;\n" + " mapping(address=>hash) d;\n" + " string name = \"Solidity\";" + " }\n" + "}\n"; + BOOST_REQUIRE_NO_THROW(contract = parseText(text)); + auto functions = contract->getDefinedFunctions(); + + BOOST_REQUIRE_NO_THROW(function = functions.at(0)); + BOOST_CHECK_MESSAGE(!function->getDocumentation(), + "Shouldn't get natspec docstring for this function"); +} + BOOST_AUTO_TEST_CASE(struct_definition) { char const* text = "contract test {\n" diff --git a/test/stExampleFiller.json b/test/stExampleFiller.json new file mode 100644 index 000000000..7acf695ed --- /dev/null +++ b/test/stExampleFiller.json @@ -0,0 +1,37 @@ +{ + "add11" : { + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "1000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "code" : "0x6001600101600055", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "code" : "0x", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : { + "data" : "", + "gasLimit" : "10000", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000" + } + } +} diff --git a/test/stSystemOperationsTestFiller.json b/test/stSystemOperationsTestFiller.json index e62753089..edd803641 100644 --- a/test/stSystemOperationsTestFiller.json +++ b/test/stSystemOperationsTestFiller.json @@ -12,7 +12,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 28) }", + "code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 28) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -46,7 +46,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 1000 4 28) }", + "code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 1000 4 28) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -80,7 +80,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 23 0xfffffffffff 28) }", + "code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 0xfffffffffff 28) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -114,7 +114,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 0xfffffffffff) }", + "code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 0xfffffffffff) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -195,7 +195,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x6001600155603760005360026000f2", + "code" : "0x6001600155603760005360026000f3", "nonce" : "0", "storage" : { } @@ -321,7 +321,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x6001600155603760005360026000f2", + "code" : "0x6001600155603760005360026000f3", "nonce" : "0", "storage" : { } diff --git a/test/state.cpp b/test/state.cpp index 8ee7b2e96..5fc23f149 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -81,6 +81,9 @@ void doStateTests(json_spirit::mValue& v, bool _fillin) // check output checkOutput(output, o); + // check logs + checkLog(theState.pending().size() ? theState.log(0) : LogEntries(), importer.m_environment.sub.logs); + // check addresses auto expectedAddrs = importer.m_statePost.addresses(); auto resultAddrs = theState.addresses(); diff --git a/test/vm.cpp b/test/vm.cpp index edb983714..d7bc0612a 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -149,7 +149,7 @@ void FakeExtVM::importLog(mObject& _o) LogEntry log; log.address = Address(o["address"].get_str()); for (auto const& t: o["topics"].get_array()) - log.topics.insert(h256(t.get_str())); + log.topics.push_back(h256(t.get_str())); log.data = importData(o); sub.logs.push_back(log); } @@ -384,7 +384,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) o["callcreates"] = fev.exportCallCreates(); o["out"] = "0x" + toHex(output); fev.push(o, "gas", gas); - o["logs"] = mValue(fev.exportLog()); + o["logs"] = mValue(exportLog(fev.sub.logs)); } } else @@ -402,7 +402,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) dev::test::FakeExtVM test; test.importState(o["post"].get_obj()); test.importCallCreates(o["callcreates"].get_array()); - test.importLog(o["logs"].get_obj()); + test.sub.logs = importLog(o["logs"].get_obj()); checkOutput(output, o); diff --git a/test/vm.h b/test/vm.h index eb98aa0ab..fb0346d51 100644 --- a/test/vm.h +++ b/test/vm.h @@ -66,14 +66,14 @@ public: u256 doPosts(); json_spirit::mObject exportEnv(); void importEnv(json_spirit::mObject& _o); - json_spirit::mObject exportLog(); - void importLog(json_spirit::mObject& _o); json_spirit::mObject exportState(); void importState(json_spirit::mObject& _object); json_spirit::mObject exportExec(); void importExec(json_spirit::mObject& _o); json_spirit::mArray exportCallCreates(); void importCallCreates(json_spirit::mArray& _callcreates); + json_spirit::mObject exportLog(); + void importLog(json_spirit::mObject& _o); eth::OnOpFunc simpleTrace();