diff --git a/CodingStandards.txt b/CodingStandards.txt index 672a20958..9d4b5f9d0 100644 --- a/CodingStandards.txt +++ b/CodingStandards.txt @@ -162,3 +162,19 @@ d. In general expressions should be roughly as important/semantically meaningful a. Comments should be doxygen-compilable, using @notation rather than \notation. +12. Include Headers + +a. Includes should go in order of lower level (STL -> boost -> libdevcore -> libdevcrypto -> libethcore -> libethereum) to higher level. Lower levels are basically dependencies to the higher levels. For example: + +#include +#include + +#include +#include +#include +#include +#include +#include + +b. The only exception to the above rule is the top of a .cpp file where its corresponding header should be located. + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 01b112b50..117d500cc 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include #include @@ -158,7 +158,7 @@ Main::Main(QWidget *parent) : m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/AlethZero", false, {"eth", "shh"})); m_qwebConnector.reset(new QWebThreeConnector()); - m_server.reset(new OurWebThreeStubServer(*m_qwebConnector, *web3(), keysAsVector(m_myKeys))); + m_server.reset(new OurWebThreeStubServer(*m_qwebConnector, *web3(), keysAsVector(m_myKeys), this)); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); m_server->setIdentities(keysAsVector(owned())); m_server->StartListening(); @@ -1635,6 +1635,29 @@ static shh::Topic topicFromText(QString _s) return ret; } + +bool Main::sourceIsSolidity(std::string const& _source) +{ + // TODO: Improve this heuristic + return (_source.substr(0, 8) == "contract" || _source.substr(0, 5) == "//sol"); +} + +string const Main::getFunctionHashes(dev::solidity::CompilerStack const &_compiler, + string const& _contractName) +{ + string ret = ""; + auto const& contract = _compiler.getContractDefinition(_contractName); + auto interfaceFunctions = contract.getInterfaceFunctions(); + + for (auto const& it: interfaceFunctions) + { + ret += it.first.abridged(); + ret += " :"; + ret += it.second->getName() + "\n"; + } + return ret; +} + void Main::on_data_textChanged() { m_pcWarp.clear(); @@ -1648,7 +1671,7 @@ void Main::on_data_textChanged() { m_data = fromHex(src); } - else if (src.substr(0, 8) == "contract" || src.substr(0, 5) == "//sol") // improve this heuristic + else if (sourceIsSolidity(src)) { dev::solidity::CompilerStack compiler; try @@ -1657,6 +1680,7 @@ void Main::on_data_textChanged() solidity = "

Solidity

"; solidity += "
" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + "
"; solidity += "
" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "
"; + solidity += "
" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "
"; } catch (dev::Exception const& exception) { @@ -1872,7 +1896,28 @@ void Main::on_send_clicked() debugFinished(); Secret s = i.secret(); if (isCreation()) + { + // If execution is a contract creation, add Natspec to + // a local Natspec LEVELDB ethereum()->transact(s, value(), m_data, ui->gas->value(), gasPrice()); + string src = ui->data->toPlainText().toStdString(); + if (sourceIsSolidity(src)) + try + { + dev::solidity::CompilerStack compiler; + m_data = compiler.compile(src, m_enableOptimizer); + for (std::string& s: compiler.getContractNames()) + { + h256 contractHash = compiler.getContractCodeHash(s); + m_natspecDB.add(contractHash, + compiler.getMetadata(s, dev::solidity::DocumentationType::NATSPEC_USER)); + } + } + catch (...) + { + statusBar()->showMessage("Couldn't compile Solidity Contract."); + } + } else ethereum()->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); return; @@ -2257,6 +2302,16 @@ void Main::on_post_clicked() whisper()->inject(m.seal(from, topicFromText(ui->shhTopic->toPlainText()), ui->shhTtl->value(), ui->shhWork->value())); } +std::string Main::lookupNatSpec(dev::h256 const& _contractHash) const +{ + return m_natspecDB.retrieve(_contractHash); +} + +std::string Main::lookupNatSpecUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionData) +{ + return m_natspecDB.getUserNotice(_contractHash, _transactionData); +} + void Main::refreshWhispers() { ui->whispers->clear(); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 18cf5113b..bcfa8a0fc 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -36,6 +36,9 @@ #include #include #include +#include + +#include "NatspecHandler.h" namespace Ui { class Main; @@ -80,7 +83,8 @@ public: dev::eth::Client* ethereum() const { return m_webThree->ethereum(); } std::shared_ptr whisper() const { return m_webThree->whisper(); } - std::string lookupNatSpec(dev::h256 const& _contractCode) const { (void)_contractCode; return ""; } // TODO: actually implement with leveldb & a UI. + std::string lookupNatSpec(dev::h256 const& _contractHash) const; + std::string lookupNatSpecUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionData); QList owned() const { return m_myIdentities + m_myKeys; } @@ -227,6 +231,12 @@ private: void refreshBlockCount(); void refreshBalances(); + /// Attempts to infer that @c _source contains Solidity code + bool sourceIsSolidity(std::string const& _source); + /// @eturns all method hashes of a Solidity contract in a string + std::string const getFunctionHashes(dev::solidity::CompilerStack const &_compiler, + std::string const& _contractName = ""); + std::unique_ptr ui; std::unique_ptr m_webThree; @@ -269,4 +279,5 @@ private: QWebThree* m_qweb = nullptr; static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr); + NatspecHandler m_natspecDB; }; diff --git a/alethzero/NatspecHandler.cpp b/alethzero/NatspecHandler.cpp new file mode 100644 index 000000000..25cc13d4a --- /dev/null +++ b/alethzero/NatspecHandler.cpp @@ -0,0 +1,103 @@ +/* + 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 NatspecHandler.cpp + * @author Lefteris Karapetsas + * @date 2015 + */ +#include "NatspecHandler.h" +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace dev; +using namespace dev::eth; +using namespace std; + +NatspecHandler::NatspecHandler() +{ + string path = Defaults::dbPath(); + boost::filesystem::create_directories(path); + ldb::Options o; + o.create_if_missing = true; + ldb::DB::Open(o, path + "/natspec", &m_db); +} + +NatspecHandler::~NatspecHandler() +{ + delete m_db; +} + +void NatspecHandler::add(dev::h256 const& _contractHash, string const& _doc) +{ + bytes k = _contractHash.asBytes(); + string v = _doc; + m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size())); +} + +string NatspecHandler::retrieve(dev::h256 const& _contractHash) const +{ + bytes k = _contractHash.asBytes(); + string ret; + m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret); + return ret; +} + +string NatspecHandler::getUserNotice(string const& json, dev::bytes const& _transactionData) +{ + Json::Value natspec; + Json::Value userNotice; + string retStr; + m_reader.parse(json, natspec); + bytes transactionFunctionPart(_transactionData.begin(), _transactionData.begin() + 4); + FixedHash<4> transactionFunctionHash(transactionFunctionPart); + + Json::Value methods = natspec["methods"]; + for (Json::ValueIterator it = methods.begin(); it != methods.end(); ++it) + { + Json::Value keyValue = it.key(); + if (!keyValue.isString()) + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Illegal Natspec JSON detected")); + + string functionSig = keyValue.asString(); + FixedHash<4> functionHash(dev::sha3(functionSig)); + + if (functionHash == transactionFunctionHash) + { + Json::Value val = (*it)["notice"]; + if (!val.isString()) + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Illegal Natspec JSON detected")); + return val.asString(); + } + } + + // not found + return string(); +} + +string NatspecHandler::getUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionData) +{ + return getUserNotice(retrieve(_contractHash), _transactionData); +} + + diff --git a/alethzero/NatspecHandler.h b/alethzero/NatspecHandler.h new file mode 100644 index 000000000..edd9281d7 --- /dev/null +++ b/alethzero/NatspecHandler.h @@ -0,0 +1,58 @@ +/* + 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 NatspecHandler.h + * @author Lefteris Karapetsas + * @date 2015 + */ + +#pragma once + +#pragma warning(push) +#pragma warning(disable: 4100 4267) +#include +#pragma warning(pop) +#include +#include + +namespace ldb = leveldb; + +class NatspecHandler +{ + public: + NatspecHandler(); + ~NatspecHandler(); + + /// Stores locally in a levelDB a key value pair of contract code hash to natspec documentation + void add(dev::h256 const& _contractHash, std::string const& _doc); + /// Retrieves the natspec documentation as a string given a contract code hash + std::string retrieve(dev::h256 const& _contractHash) const; + + /// Given a json natspec string and the transaction data return the user notice + std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData); + /// Given a contract code hash and the transaction's data retrieve the natspec documention's + /// user notice for that transaction. + /// @returns The user notice or an empty string if no natspec for the contract exists + /// or if the existing natspec does not document the @c _methodName + std::string getUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionDacta); + + private: + ldb::ReadOptions m_readOptions; + ldb::WriteOptions m_writeOptions; + ldb::DB* m_db; + Json::Reader m_reader; +}; diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index 0d840e8d4..ce1f28507 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -22,14 +22,17 @@ #include "OurWebThreeStubServer.h" #include +#include #include #include "MainWin.h" + using namespace std; using namespace dev; using namespace dev::eth; -OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector const& _accounts): - WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3) +OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, + std::vector const& _accounts, Main* main): + WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(main) {} std::string OurWebThreeStubServer::shh_newIdentity() @@ -39,36 +42,31 @@ std::string OurWebThreeStubServer::shh_newIdentity() return toJS(kp.pub()); } -bool OurWebThreeStubServer::authenticate(dev::TransactionSkeleton const& _t) const +bool OurWebThreeStubServer::showAuthenticationPopup(std::string const& _title, std::string const& _text) const { - return true; - - // To get the balance of the sender - cnote << "Sender has ETH: " << m_web3->ethereum()->postState().balance(_t.from); - - Main* main; // don't know this yet, should be a member and set at construction time by Main, who will construct us. + QMessageBox userInput; + userInput.setText(QString::fromStdString(_title)); + userInput.setInformativeText(QString::fromStdString(_text + "\n Do you wish to allow this?")); + userInput.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + userInput.button(QMessageBox::Ok)->setText("Allow"); + userInput.button(QMessageBox::Cancel)->setText("Reject"); + userInput.setDefaultButton(QMessageBox::Cancel); + return userInput.exec() == QMessageBox::Ok; +} +bool OurWebThreeStubServer::authenticate(dev::TransactionSkeleton const& _t) const +{ h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to); - if (contractCodeHash == EmptySHA3) - { // recipient has no code - nothing special about this transaction. // TODO: show basic message for value transfer. - return true; // or whatever. - } - - std::string natspecJson = main->lookupNatSpec(contractCodeHash); - - if (natspecJson.empty()) - { - // TODO: HUGE warning - we don't know what this will do! - return false; // or whatever. - } - - // otherwise it's a transaction to contract for which we have the natspec: - // determine the actual message (embellish with real data) and ask user. + return true; -// QMessageBox::question(); + std::string userNotice = m_main->lookupNatSpecUserNotice(contractCodeHash, _t.data); + if (userNotice.empty()) + return showAuthenticationPopup("Unverified Pending Transaction", + "An undocumented transaction is about to be executed."); - return true; + // otherwise it's a transaction to a contract for which we have the natspec + return showAuthenticationPopup("Pending Transaction", userNotice); } diff --git a/alethzero/OurWebThreeStubServer.h b/alethzero/OurWebThreeStubServer.h index 9ff973371..a67af0827 100644 --- a/alethzero/OurWebThreeStubServer.h +++ b/alethzero/OurWebThreeStubServer.h @@ -24,12 +24,15 @@ #include #include +class Main; + class OurWebThreeStubServer: public QObject, public WebThreeStubServer { Q_OBJECT public: - OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector const& _accounts); + OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, + std::vector const& _accounts, Main* main); virtual std::string shh_newIdentity() override; virtual bool authenticate(dev::TransactionSkeleton const& _t) const; @@ -38,5 +41,8 @@ signals: void onNewId(QString _s); private: + bool showAuthenticationPopup(std::string const& _title, std::string const& _text) const; + dev::WebThreeDirect* m_web3; + Main* m_main; }; diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 6fab67452..2573995c5 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -110,6 +110,9 @@ inline std::string toBigEndianString(u160 _val) { std::string ret(20, '\0'); toB inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); return ret; } +/// Convenience function for conversion of a u256 to hex +inline std::string toHex(u256 val) { return toHex(toBigEndian(val)); } + /// Convenience function for toBigEndian. /// @returns a byte array just big enough to represent @a _val. template diff --git a/libjsqrc/ethereumjs/example/natspec_contract.html b/libjsqrc/ethereumjs/example/natspec_contract.html new file mode 100644 index 000000000..8d12b4a81 --- /dev/null +++ b/libjsqrc/ethereumjs/example/natspec_contract.html @@ -0,0 +1,77 @@ + + + + + + + + + +

contract

+
+
+ +
+ +
+ + + diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 782a7efe2..bd6571b9a 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -50,10 +50,9 @@ void Compiler::compileContract(ContractDefinition const& _contract, vectoraccept(*this); // Swap the runtime context with the creation-time context - CompilerContext runtimeContext; - swap(m_context, runtimeContext); + swap(m_context, m_runtimeContext); initializeContext(_contract, _magicGlobals, _contracts); - packIntoContractCreator(_contract, runtimeContext); + packIntoContractCreator(_contract, m_runtimeContext); } void Compiler::initializeContext(ContractDefinition const& _contract, vector const& _magicGlobals, diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index e83d1ed3a..c229a7a8a 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -35,6 +35,7 @@ public: void compileContract(ContractDefinition const& _contract, std::vector const& _magicGlobals, std::map const& _contracts); bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } + bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);} void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } private: @@ -70,6 +71,7 @@ private: bool const m_optimize; CompilerContext m_context; + CompilerContext m_runtimeContext; std::vector m_breakTags; ///< tag to jump to for a "break" statement std::vector m_continueTags; ///< tag to jump to for a "continue" statement eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 904c77c5a..5532d74bc 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -30,6 +30,8 @@ #include #include +#include + using namespace std; namespace dev @@ -117,6 +119,7 @@ void CompilerStack::compile(bool _optimize) contractBytecode); Contract& compiledContract = m_contracts[contract->getName()]; compiledContract.bytecode = compiler->getAssembledBytecode(); + compiledContract.runtimeBytecode = compiler->getRuntimeBytecode(); compiledContract.compiler = move(compiler); contractBytecode[compiledContract.contract] = &compiledContract.bytecode; } @@ -134,6 +137,16 @@ bytes const& CompilerStack::getBytecode(string const& _contractName) const return getContract(_contractName).bytecode; } +bytes const& CompilerStack::getRuntimeBytecode(string const& _contractName) const +{ + return getContract(_contractName).runtimeBytecode; +} + +dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const +{ + return dev::sha3(getRuntimeBytecode(_contractName)); +} + void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const { getContract(_contractName).compiler->streamAssembly(_outStream); diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index afc9a5162..aa55abe50 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace dev { namespace solidity { @@ -75,7 +76,13 @@ public: /// @returns the compiled bytecode bytes const& compile(std::string const& _sourceCode, bool _optimize = false); + /// @returns the assembled bytecode for a contract. bytes const& getBytecode(std::string const& _contractName = "") const; + /// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor. + bytes const& getRuntimeBytecode(std::string const& _contractName = "") const; + /// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor. + dev::h256 getContractCodeHash(std::string const& _contractName = "") const; + /// Streams a verbose version of the assembly to @a _outStream. /// Prerequisite: Successful compilation. void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const; @@ -121,6 +128,7 @@ private: ContractDefinition const* contract = nullptr; std::shared_ptr compiler; bytes bytecode; + bytes runtimeBytecode; std::shared_ptr interfaceHandler; mutable std::unique_ptr interface; mutable std::unique_ptr solidityInterface; diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index a5c6f4a1d..4c7ae5f45 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -106,7 +106,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); - methods[it.second->getName()] = user; + methods[it.second->getCanonicalSignature()] = user; } } } @@ -162,7 +162,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["return"] = m_return; if (!method.empty()) // add the function, only if we have any documentation to add - methods[it.second->getName()] = method; + methods[it.second->getCanonicalSignature()] = method; } } doc["methods"] = methods; diff --git a/test/SolidityNatspecJSON.cpp b/test/SolidityNatspecJSON.cpp index 7edb97a7b..d43aebc2b 100644 --- a/test/SolidityNatspecJSON.cpp +++ b/test/SolidityNatspecJSON.cpp @@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(user_basic_test) char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}" + " \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}" "}}"; checkNatspec(sourceCode, natspec, true); @@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test) char const* devNatspec = "{" "\"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256)\":{ \n" " \"details\": \"Multiplies a number by 7\"\n" " }\n" " }\n" @@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test) char const* userNatspec = "{" "\"methods\":{" - " \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}" + " \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}" "}}"; checkNatspec(sourceCode, devNatspec, false); @@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(user_multiline_comment) char const* natspec = "{" "\"methods\":{" - " \"mul_and_add\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}" + " \"mul_and_add(uint256,uint256)\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}" "}}"; checkNatspec(sourceCode, natspec, true); @@ -157,9 +157,9 @@ BOOST_AUTO_TEST_CASE(user_multiple_functions) char const* natspec = "{" "\"methods\":{" - " \"mul_and_add\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}," - " \"divide\":{ \"notice\": \"Divides `input` by `div`\"}," - " \"sub\":{ \"notice\": \"Subtracts 3 from `input`\"}" + " \"mul_and_add(uint256,uint256)\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}," + " \"divide(uint256,uint256)\":{ \"notice\": \"Divides `input` by `div`\"}," + " \"sub(int256)\":{ \"notice\": \"Subtracts 3 from `input`\"}" "}}"; checkNatspec(sourceCode, natspec, true); @@ -205,7 +205,7 @@ BOOST_AUTO_TEST_CASE(dev_desc_after_nl) char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \" Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" " \"a\": \"Documentation for the first parameter\",\n" @@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params) char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" " \"a\": \"Documentation for the first parameter\",\n" @@ -252,7 +252,7 @@ BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" @@ -289,21 +289,21 @@ BOOST_AUTO_TEST_CASE(dev_multiple_functions) char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" " \"a\": \"Documentation for the first parameter\",\n" " \"second\": \"Documentation for the second parameter\"\n" " }\n" " },\n" - " \"divide\":{ \n" + " \"divide(uint256,uint256)\":{ \n" " \"details\": \"Divides 2 numbers\",\n" " \"params\": {\n" " \"input\": \"Documentation for the input parameter\",\n" " \"div\": \"Documentation for the div parameter\"\n" " }\n" " },\n" - " \"sub\":{ \n" + " \"sub(int256)\":{ \n" " \"details\": \"Subtracts 3 from `input`\",\n" " \"params\": {\n" " \"input\": \"Documentation for the input parameter\"\n" @@ -327,7 +327,7 @@ BOOST_AUTO_TEST_CASE(dev_return) char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" @@ -353,7 +353,7 @@ BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl) char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return) char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" @@ -410,7 +410,7 @@ BOOST_AUTO_TEST_CASE(dev_multiline_comment) char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" @@ -432,7 +432,7 @@ BOOST_AUTO_TEST_CASE(dev_contract_no_doc) char const* natspec = "{" " \"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Mul function\"\n" " }\n" " }\n" @@ -454,7 +454,7 @@ BOOST_AUTO_TEST_CASE(dev_contract_doc) " \"author\": \"Lefteris\"," " \"title\": \"Just a test contract\"," " \"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Mul function\"\n" " }\n" " }\n" @@ -477,7 +477,7 @@ BOOST_AUTO_TEST_CASE(dev_author_at_function) " \"author\": \"Lefteris\"," " \"title\": \"Just a test contract\"," " \"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Mul function\",\n" " \"author\": \"John Doe\",\n" " }\n" @@ -501,7 +501,7 @@ BOOST_AUTO_TEST_CASE(dev_title_at_function_error) " \"author\": \"Lefteris\"," " \"title\": \"Just a test contract\"," " \"methods\":{" - " \"mul\":{ \n" + " \"mul(uint256,uint256)\":{ \n" " \"details\": \"Mul function\"\n" " }\n" " }\n"