CJentzsch
10 years ago
17 changed files with 1300 additions and 415 deletions
@ -0,0 +1,187 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Network.cpp
|
|||
* @author Alex Leverington <nessence@gmail.com> |
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @author Eric Lombrozo <elombrozo@gmail.com> (Windows version of getInterfaceAddresses()) |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include <sys/types.h> |
|||
#ifndef _WIN32 |
|||
#include <ifaddrs.h> |
|||
#endif |
|||
|
|||
#include <boost/algorithm/string.hpp> |
|||
#include <libdevcore/Common.h> |
|||
#include <libdevcore/CommonIO.h> |
|||
#include <libethcore/Exceptions.h> |
|||
#include "Common.h" |
|||
#include "UPnP.h" |
|||
#include "Network.h" |
|||
|
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::p2p; |
|||
|
|||
std::vector<bi::address> Network::getInterfaceAddresses() |
|||
{ |
|||
std::vector<bi::address> addresses; |
|||
|
|||
#ifdef _WIN32 |
|||
WSAData wsaData; |
|||
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) |
|||
BOOST_THROW_EXCEPTION(NoNetworking()); |
|||
|
|||
char ac[80]; |
|||
if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) |
|||
{ |
|||
clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name."; |
|||
WSACleanup(); |
|||
BOOST_THROW_EXCEPTION(NoNetworking()); |
|||
} |
|||
|
|||
struct hostent* phe = gethostbyname(ac); |
|||
if (phe == 0) |
|||
{ |
|||
clog(NetWarn) << "Bad host lookup."; |
|||
WSACleanup(); |
|||
BOOST_THROW_EXCEPTION(NoNetworking()); |
|||
} |
|||
|
|||
for (int i = 0; phe->h_addr_list[i] != 0; ++i) |
|||
{ |
|||
struct in_addr addr; |
|||
memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); |
|||
char *addrStr = inet_ntoa(addr); |
|||
bi::address address(bi::address::from_string(addrStr)); |
|||
if (!isLocalHostAddress(address)) |
|||
addresses.push_back(address.to_v4()); |
|||
} |
|||
|
|||
WSACleanup(); |
|||
#else |
|||
ifaddrs* ifaddr; |
|||
if (getifaddrs(&ifaddr) == -1) |
|||
BOOST_THROW_EXCEPTION(NoNetworking()); |
|||
|
|||
for (auto ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) |
|||
{ |
|||
if (!ifa->ifa_addr || string(ifa->ifa_name) == "lo0") |
|||
continue; |
|||
|
|||
if (ifa->ifa_addr->sa_family == AF_INET) |
|||
{ |
|||
in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; |
|||
boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr)); |
|||
if (!isLocalHostAddress(address)) |
|||
addresses.push_back(address); |
|||
} |
|||
else if (ifa->ifa_addr->sa_family == AF_INET6) |
|||
{ |
|||
sockaddr_in6* sockaddr = ((struct sockaddr_in6 *)ifa->ifa_addr); |
|||
in6_addr addr = sockaddr->sin6_addr; |
|||
boost::asio::ip::address_v6::bytes_type bytes; |
|||
memcpy(&bytes[0], addr.s6_addr, 16); |
|||
boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id); |
|||
if (!isLocalHostAddress(address)) |
|||
addresses.push_back(address); |
|||
} |
|||
} |
|||
|
|||
if (ifaddr!=NULL) |
|||
freeifaddrs(ifaddr); |
|||
|
|||
#endif |
|||
|
|||
return std::move(addresses); |
|||
} |
|||
|
|||
int Network::listen4(bi::tcp::acceptor& _acceptor, unsigned short _listenPort) |
|||
{ |
|||
int retport = -1; |
|||
for (unsigned i = 0; i < 2; ++i) |
|||
{ |
|||
// try to connect w/listenPort, else attempt net-allocated port
|
|||
bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _listenPort); |
|||
try |
|||
{ |
|||
_acceptor.open(endpoint.protocol()); |
|||
_acceptor.set_option(ba::socket_base::reuse_address(true)); |
|||
_acceptor.bind(endpoint); |
|||
_acceptor.listen(); |
|||
retport = _acceptor.local_endpoint().port(); |
|||
break; |
|||
} |
|||
catch (...) |
|||
{ |
|||
if (i) |
|||
{ |
|||
// both attempts failed
|
|||
cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); |
|||
} |
|||
|
|||
// first attempt failed
|
|||
_acceptor.close(); |
|||
continue; |
|||
} |
|||
} |
|||
return retport; |
|||
} |
|||
|
|||
bi::tcp::endpoint Network::traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr) |
|||
{ |
|||
asserts(_listenPort != 0); |
|||
|
|||
UPnP* upnp = nullptr; |
|||
try |
|||
{ |
|||
upnp = new UPnP; |
|||
} |
|||
// let m_upnp continue as null - we handle it properly.
|
|||
catch (NoUPnPDevice) {} |
|||
|
|||
bi::tcp::endpoint upnpep; |
|||
if (upnp && upnp->isValid()) |
|||
{ |
|||
bi::address paddr; |
|||
int extPort = 0; |
|||
for (auto const& addr: _ifAddresses) |
|||
if (addr.is_v4() && isPrivateAddress(addr) && (extPort = upnp->addRedirect(addr.to_string().c_str(), _listenPort))) |
|||
{ |
|||
paddr = addr; |
|||
break; |
|||
} |
|||
|
|||
auto eip = upnp->externalIP(); |
|||
bi::address eipaddr(bi::address::from_string(eip)); |
|||
if (extPort && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr)) |
|||
{ |
|||
clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << extPort << "."; |
|||
clog(NetNote) << "External addr:" << eip; |
|||
o_upnpifaddr = paddr; |
|||
upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)extPort); |
|||
} |
|||
else |
|||
clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place)."; |
|||
|
|||
if (upnp) |
|||
delete upnp; |
|||
} |
|||
|
|||
return upnpep; |
|||
} |
@ -0,0 +1,63 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Network.h
|
|||
* @author Alex Leverington <nessence@gmail.com> |
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
namespace ba = boost::asio; |
|||
namespace bi = ba::ip; |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
namespace p2p |
|||
{ |
|||
|
|||
struct NetworkPreferences |
|||
{ |
|||
NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), publicIP(i), upnp(u), localNetworking(l) {} |
|||
|
|||
unsigned short listenPort = 30303; |
|||
std::string publicIP; |
|||
bool upnp = true; |
|||
bool localNetworking = false; |
|||
}; |
|||
|
|||
/**
|
|||
* @brief Network Class |
|||
* Static network operations and interface(s). |
|||
*/ |
|||
class Network |
|||
{ |
|||
public: |
|||
/// @returns public and private interface addresses
|
|||
static std::vector<bi::address> getInterfaceAddresses(); |
|||
|
|||
/// Try to bind and listen on _listenPort, else attempt net-allocated port.
|
|||
static int listen4(bi::tcp::acceptor& _acceptor, unsigned short _listenPort); |
|||
|
|||
/// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port.
|
|||
static bi::tcp::endpoint traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr); |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,278 @@ |
|||
|
|||
#include <libsolidity/InterfaceHandler.h> |
|||
#include <libsolidity/AST.h> |
|||
#include <libsolidity/CompilerStack.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
/* -- public -- */ |
|||
|
|||
InterfaceHandler::InterfaceHandler() |
|||
{ |
|||
m_lastTag = DocTagType::NONE; |
|||
} |
|||
|
|||
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(std::shared_ptr<ContractDefinition> _contractDef, |
|||
DocumentationType _type) |
|||
{ |
|||
switch(_type) |
|||
{ |
|||
case DocumentationType::NATSPEC_USER: |
|||
return getUserDocumentation(_contractDef); |
|||
case DocumentationType::NATSPEC_DEV: |
|||
return getDevDocumentation(_contractDef); |
|||
case DocumentationType::ABI_INTERFACE: |
|||
return getABIInterface(_contractDef); |
|||
} |
|||
|
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); |
|||
return nullptr; |
|||
} |
|||
|
|||
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(std::shared_ptr<ContractDefinition> _contractDef) |
|||
{ |
|||
Json::Value methods(Json::arrayValue); |
|||
|
|||
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) |
|||
{ |
|||
Json::Value method; |
|||
Json::Value inputs(Json::arrayValue); |
|||
Json::Value outputs(Json::arrayValue); |
|||
|
|||
auto populateParameters = [](std::vector<ASTPointer<VariableDeclaration>> const& _vars) |
|||
{ |
|||
Json::Value params(Json::arrayValue); |
|||
for (ASTPointer<VariableDeclaration> const& var: _vars) |
|||
{ |
|||
Json::Value input; |
|||
input["name"] = var->getName(); |
|||
input["type"] = var->getType()->toString(); |
|||
params.append(input); |
|||
} |
|||
return params; |
|||
}; |
|||
|
|||
method["name"] = f->getName(); |
|||
method["inputs"] = populateParameters(f->getParameters()); |
|||
method["outputs"] = populateParameters(f->getReturnParameters()); |
|||
methods.append(method); |
|||
} |
|||
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods))); |
|||
} |
|||
|
|||
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(std::shared_ptr<ContractDefinition> _contractDef) |
|||
{ |
|||
Json::Value doc; |
|||
Json::Value methods(Json::objectValue); |
|||
|
|||
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) |
|||
{ |
|||
Json::Value user; |
|||
auto strPtr = f->getDocumentation(); |
|||
if (strPtr) |
|||
{ |
|||
resetUser(); |
|||
parseDocString(*strPtr); |
|||
if (!m_notice.empty()) |
|||
{// since @notice is the only user tag if missing function should not appear
|
|||
user["notice"] = Json::Value(m_notice); |
|||
methods[f->getName()] = user; |
|||
} |
|||
} |
|||
} |
|||
doc["methods"] = methods; |
|||
|
|||
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc))); |
|||
} |
|||
|
|||
std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(std::shared_ptr<ContractDefinition> _contractDef) |
|||
{ |
|||
// LTODO: Somewhere in this function warnings for mismatch of param names
|
|||
// should be thrown
|
|||
Json::Value doc; |
|||
Json::Value methods(Json::objectValue); |
|||
|
|||
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) |
|||
{ |
|||
Json::Value method; |
|||
auto strPtr = f->getDocumentation(); |
|||
if (strPtr) |
|||
{ |
|||
resetDev(); |
|||
parseDocString(*strPtr); |
|||
|
|||
if (!m_dev.empty()) |
|||
method["details"] = Json::Value(m_dev); |
|||
Json::Value params(Json::objectValue); |
|||
for (auto const& pair: m_params) |
|||
params[pair.first] = pair.second; |
|||
|
|||
if (!m_params.empty()) |
|||
method["params"] = params; |
|||
if (!m_return.empty()) |
|||
method["return"] = m_return; |
|||
|
|||
if (!method.empty()) // add the function, only if we have any documentation to add
|
|||
methods[f->getName()] = method; |
|||
} |
|||
} |
|||
doc["methods"] = methods; |
|||
|
|||
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc))); |
|||
} |
|||
|
|||
/* -- private -- */ |
|||
void InterfaceHandler::resetUser() |
|||
{ |
|||
m_notice.clear(); |
|||
} |
|||
|
|||
void InterfaceHandler::resetDev() |
|||
{ |
|||
m_dev.clear(); |
|||
m_return.clear(); |
|||
m_params.clear(); |
|||
} |
|||
|
|||
static inline std::string::const_iterator skipLineOrEOS(std::string::const_iterator _nlPos, |
|||
std::string::const_iterator _end) |
|||
{ |
|||
return (_nlPos == _end) ? _end : ++_nlPos; |
|||
} |
|||
|
|||
std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end, |
|||
std::string& _tagString, |
|||
DocTagType _tagType) |
|||
{ |
|||
auto nlPos = std::find(_pos, _end, '\n'); |
|||
std::copy(_pos, nlPos, back_inserter(_tagString)); |
|||
m_lastTag = _tagType; |
|||
return skipLineOrEOS(nlPos, _end); |
|||
} |
|||
|
|||
std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end) |
|||
{ |
|||
// find param name
|
|||
auto currPos = std::find(_pos, _end, ' '); |
|||
if (currPos == _end) |
|||
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("End of param name not found" + std::string(_pos, _end))); |
|||
|
|||
|
|||
auto paramName = std::string(_pos, currPos); |
|||
|
|||
currPos += 1; |
|||
auto nlPos = std::find(currPos, _end, '\n'); |
|||
auto paramDesc = std::string(currPos, nlPos); |
|||
m_params.push_back(std::make_pair(paramName, paramDesc)); |
|||
|
|||
m_lastTag = DocTagType::PARAM; |
|||
return skipLineOrEOS(nlPos, _end); |
|||
} |
|||
|
|||
std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end) |
|||
{ |
|||
// Should never be called with an empty vector
|
|||
if (asserts(!m_params.empty())) |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Tried to append to empty parameter")); |
|||
|
|||
auto pair = m_params.back(); |
|||
pair.second += " "; |
|||
auto nlPos = std::find(_pos, _end, '\n'); |
|||
std::copy(_pos, nlPos, back_inserter(pair.second)); |
|||
|
|||
m_params.at(m_params.size() - 1) = pair; |
|||
|
|||
return skipLineOrEOS(nlPos, _end); |
|||
} |
|||
|
|||
std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end, |
|||
std::string const& _tag) |
|||
{ |
|||
// LTODO: need to check for @(start of a tag) between here and the end of line
|
|||
// for all cases
|
|||
if (m_lastTag == DocTagType::NONE || _tag != "") |
|||
{ |
|||
if (_tag == "dev") |
|||
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); |
|||
else if (_tag == "notice") |
|||
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); |
|||
else if (_tag == "return") |
|||
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); |
|||
else if (_tag == "param") |
|||
return parseDocTagParam(_pos, _end); |
|||
else |
|||
{ |
|||
// LTODO: Unknown tag, throw some form of warning and not just an exception
|
|||
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered")); |
|||
} |
|||
} |
|||
else |
|||
return appendDocTag(_pos, _end); |
|||
} |
|||
|
|||
std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end) |
|||
{ |
|||
switch (m_lastTag) |
|||
{ |
|||
case DocTagType::DEV: |
|||
m_dev += " "; |
|||
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); |
|||
case DocTagType::NOTICE: |
|||
m_notice += " "; |
|||
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); |
|||
case DocTagType::RETURN: |
|||
m_return += " "; |
|||
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); |
|||
case DocTagType::PARAM: |
|||
return appendDocTagParam(_pos, _end); |
|||
default: |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type")); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end) |
|||
{ |
|||
auto spacePos = std::find(_pos, _end, ' '); |
|||
auto nlPos = std::find(_pos, _end, '\n'); |
|||
return (spacePos < nlPos) ? spacePos : nlPos; |
|||
} |
|||
|
|||
void InterfaceHandler::parseDocString(std::string const& _string) |
|||
{ |
|||
auto currPos = _string.begin(); |
|||
auto end = _string.end(); |
|||
|
|||
while (currPos != end) |
|||
{ |
|||
auto tagPos = std::find(currPos, end, '@'); |
|||
auto nlPos = std::find(currPos, end, '\n'); |
|||
|
|||
if (tagPos != end && tagPos < nlPos) |
|||
{ |
|||
// we found a tag
|
|||
auto tagNameEndPos = getFirstSpaceOrNl(tagPos, end); |
|||
if (tagNameEndPos == end) |
|||
BOOST_THROW_EXCEPTION(DocstringParsingError() << |
|||
errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found")); |
|||
|
|||
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos)); |
|||
} |
|||
else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag
|
|||
currPos = appendDocTag(currPos + 1, end); |
|||
else if (currPos != end) // skip the line if a newline was found
|
|||
currPos = nlPos + 1; |
|||
} |
|||
} |
|||
|
|||
} //solidity NS
|
|||
} // dev NS
|
@ -0,0 +1,110 @@ |
|||
/*
|
|||
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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Lefteris <lefteris@ethdev.com> |
|||
* @date 2014 |
|||
* Takes the parsed AST and produces the Natspec |
|||
* documentation and the ABI interface |
|||
* https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format
|
|||
* |
|||
* Can generally deal with JSON files |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <string> |
|||
#include <memory> |
|||
#include <jsonrpc/json/json.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
// Forward declarations
|
|||
class ContractDefinition; |
|||
enum class DocumentationType: uint8_t; |
|||
|
|||
enum class DocTagType: uint8_t |
|||
{ |
|||
NONE = 0, |
|||
DEV, |
|||
NOTICE, |
|||
PARAM, |
|||
RETURN |
|||
}; |
|||
|
|||
class InterfaceHandler |
|||
{ |
|||
public: |
|||
InterfaceHandler(); |
|||
|
|||
/// Get the given type of documentation
|
|||
/// @param _contractDef The contract definition
|
|||
/// @param _type The type of the documentation. Can be one of the
|
|||
/// types provided by @c DocumentationType
|
|||
/// @return A unique pointer contained string with the json
|
|||
/// representation of provided type
|
|||
std::unique_ptr<std::string> getDocumentation(std::shared_ptr<ContractDefinition> _contractDef, |
|||
DocumentationType _type); |
|||
/// Get the ABI Interface of the contract
|
|||
/// @param _contractDef The contract definition
|
|||
/// @return A unique pointer contained string with the json
|
|||
/// representation of the contract's ABI Interface
|
|||
std::unique_ptr<std::string> getABIInterface(std::shared_ptr<ContractDefinition> _contractDef); |
|||
/// Get the User documentation of the contract
|
|||
/// @param _contractDef The contract definition
|
|||
/// @return A unique pointer contained string with the json
|
|||
/// representation of the contract's user documentation
|
|||
std::unique_ptr<std::string> getUserDocumentation(std::shared_ptr<ContractDefinition> _contractDef); |
|||
/// Get the Developer's documentation of the contract
|
|||
/// @param _contractDef The contract definition
|
|||
/// @return A unique pointer contained string with the json
|
|||
/// representation of the contract's developer documentation
|
|||
std::unique_ptr<std::string> getDevDocumentation(std::shared_ptr<ContractDefinition> _contractDef); |
|||
|
|||
private: |
|||
void resetUser(); |
|||
void resetDev(); |
|||
|
|||
std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end, |
|||
std::string& _tagString, |
|||
DocTagType _tagType); |
|||
std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end); |
|||
std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end); |
|||
void parseDocString(std::string const& _string); |
|||
std::string::const_iterator appendDocTag(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end); |
|||
std::string::const_iterator parseDocTag(std::string::const_iterator _pos, |
|||
std::string::const_iterator _end, |
|||
std::string const& _tag); |
|||
|
|||
Json::StyledWriter m_writer; |
|||
|
|||
// internal state
|
|||
DocTagType m_lastTag; |
|||
std::string m_notice; |
|||
std::string m_dev; |
|||
std::string m_return; |
|||
std::vector<std::pair<std::string, std::string>> m_params; |
|||
}; |
|||
|
|||
} //solidity NS
|
|||
} // dev NS
|
@ -0,0 +1,400 @@ |
|||
/*
|
|||
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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Lefteris Karapetsas <lefteris@ethdev.com> |
|||
* @date 2014 |
|||
* Unit tests for the solidity compiler JSON Interface output. |
|||
*/ |
|||
|
|||
#include <boost/test/unit_test.hpp> |
|||
#include <libsolidity/CompilerStack.h> |
|||
#include <jsonrpc/json/json.h> |
|||
#include <libdevcore/Exceptions.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
namespace test |
|||
{ |
|||
|
|||
class DocumentationChecker |
|||
{ |
|||
public: |
|||
void checkNatspec(std::string const& _code, |
|||
std::string const& _expectedDocumentationString, |
|||
bool _userDocumentation) |
|||
{ |
|||
std::string generatedDocumentationString; |
|||
try |
|||
{ |
|||
m_compilerStack.parse(_code); |
|||
} |
|||
catch (const std::exception& e) |
|||
{ |
|||
std::string const* extra = boost::get_error_info<errinfo_comment>(e); |
|||
std::string msg = std::string("Parsing contract failed with: ") + |
|||
e.what() + std::string("\n"); |
|||
if (extra) |
|||
msg += *extra; |
|||
BOOST_FAIL(msg); |
|||
} |
|||
|
|||
if (_userDocumentation) |
|||
generatedDocumentationString = m_compilerStack.getJsonDocumentation(DocumentationType::NATSPEC_USER); |
|||
else |
|||
generatedDocumentationString = m_compilerStack.getJsonDocumentation(DocumentationType::NATSPEC_DEV); |
|||
Json::Value generatedDocumentation; |
|||
m_reader.parse(generatedDocumentationString, generatedDocumentation); |
|||
Json::Value expectedDocumentation; |
|||
m_reader.parse(_expectedDocumentationString, expectedDocumentation); |
|||
BOOST_CHECK_MESSAGE(expectedDocumentation == generatedDocumentation, |
|||
"Expected " << _expectedDocumentationString << |
|||
"\n but got:\n" << generatedDocumentationString); |
|||
} |
|||
|
|||
private: |
|||
CompilerStack m_compilerStack; |
|||
Json::Reader m_reader; |
|||
}; |
|||
|
|||
BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker) |
|||
|
|||
BOOST_AUTO_TEST_CASE(user_basic_test) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @notice Multiplies `a` by 7\n" |
|||
" function mul(uint a) returns(uint d) { return a * 7; }\n" |
|||
"}\n"; |
|||
|
|||
char const* natspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, natspec, true); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(dev_and_user_basic_test) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @notice Multiplies `a` by 7\n" |
|||
" /// @dev Multiplies a number by 7\n" |
|||
" function mul(uint a) returns(uint d) { return a * 7; }\n" |
|||
"}\n"; |
|||
|
|||
char const* devNatspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul\":{ \n" |
|||
" \"details\": \"Multiplies a number by 7\"\n" |
|||
" }\n" |
|||
" }\n" |
|||
"}}"; |
|||
|
|||
char const* userNatspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, devNatspec, false); |
|||
checkNatspec(sourceCode, userNatspec, true); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(user_multiline_comment) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @notice Multiplies `a` by 7\n" |
|||
" /// and then adds `b`\n" |
|||
" function mul_and_add(uint a, uint256 b) returns(uint256 d)\n" |
|||
" {\n" |
|||
" return (a * 7) + b;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
|
|||
char const* natspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul_and_add\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, natspec, true); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(user_multiple_functions) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @notice Multiplies `a` by 7 and then adds `b`\n" |
|||
" function mul_and_add(uint a, uint256 b) returns(uint256 d)\n" |
|||
" {\n" |
|||
" return (a * 7) + b;\n" |
|||
" }\n" |
|||
"\n" |
|||
" /// @notice Divides `input` by `div`\n" |
|||
" function divide(uint input, uint div) returns(uint d)\n" |
|||
" {\n" |
|||
" return input / div;\n" |
|||
" }\n" |
|||
" /// @notice Subtracts 3 from `input`\n" |
|||
" function sub(int input) returns(int d)\n" |
|||
" {\n" |
|||
" return input - 3;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
|
|||
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`\"}" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, natspec, true); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(user_empty_contract) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
"}\n"; |
|||
|
|||
char const* natspec = "{\"methods\":{} }"; |
|||
|
|||
checkNatspec(sourceCode, natspec, true); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(dev_and_user_no_doc) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function mul(uint a) returns(uint d) { return a * 7; }\n" |
|||
" function sub(int input) returns(int d)\n" |
|||
" {\n" |
|||
" return input - 3;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
|
|||
char const* devNatspec = "{\"methods\":{}}"; |
|||
|
|||
char const* userNatspec = "{\"methods\":{}}"; |
|||
|
|||
checkNatspec(sourceCode, devNatspec, false); |
|||
checkNatspec(sourceCode, userNatspec, true); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(dev_desc_after_nl) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @dev\n" |
|||
" /// Multiplies a number by 7 and adds second parameter\n" |
|||
" /// @param a Documentation for the first parameter\n" |
|||
" /// @param second Documentation for the second parameter\n" |
|||
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" |
|||
"}\n"; |
|||
|
|||
char const* natspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul\":{ \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" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, natspec, false); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(dev_multiple_params) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @dev Multiplies a number by 7 and adds second parameter\n" |
|||
" /// @param a Documentation for the first parameter\n" |
|||
" /// @param second Documentation for the second parameter\n" |
|||
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" |
|||
"}\n"; |
|||
|
|||
char const* natspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul\":{ \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" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, natspec, false); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @dev Multiplies a number by 7 and adds second parameter\n" |
|||
" /// @param a Documentation for the first parameter starts here.\n" |
|||
" /// Since it's a really complicated parameter we need 2 lines\n" |
|||
" /// @param second Documentation for the second parameter\n" |
|||
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" |
|||
"}\n"; |
|||
|
|||
char const* natspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul\":{ \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" |
|||
" \"second\": \"Documentation for the second parameter\"\n" |
|||
" }\n" |
|||
" }\n" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, natspec, false); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(dev_multiple_functions) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @dev Multiplies a number by 7 and adds second parameter\n" |
|||
" /// @param a Documentation for the first parameter\n" |
|||
" /// @param second Documentation for the second parameter\n" |
|||
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" |
|||
" \n" |
|||
" /// @dev Divides 2 numbers\n" |
|||
" /// @param input Documentation for the input parameter\n" |
|||
" /// @param div Documentation for the div parameter\n" |
|||
" function divide(uint input, uint div) returns(uint d)\n" |
|||
" {\n" |
|||
" return input / div;\n" |
|||
" }\n" |
|||
" /// @dev Subtracts 3 from `input`\n" |
|||
" /// @param input Documentation for the input parameter\n" |
|||
" function sub(int input) returns(int d)\n" |
|||
" {\n" |
|||
" return input - 3;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
|
|||
char const* natspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul\":{ \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" |
|||
" \"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" |
|||
" \"details\": \"Subtracts 3 from `input`\",\n" |
|||
" \"params\": {\n" |
|||
" \"input\": \"Documentation for the input parameter\"\n" |
|||
" }\n" |
|||
" }\n" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, natspec, false); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(dev_return) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @dev Multiplies a number by 7 and adds second parameter\n" |
|||
" /// @param a Documentation for the first parameter starts here.\n" |
|||
" /// Since it's a really complicated parameter we need 2 lines\n" |
|||
" /// @param second Documentation for the second parameter\n" |
|||
" /// @return The result of the multiplication\n" |
|||
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" |
|||
"}\n"; |
|||
|
|||
char const* natspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul\":{ \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" |
|||
" \"second\": \"Documentation for the second parameter\"\n" |
|||
" },\n" |
|||
" \"return\": \"The result of the multiplication\"\n" |
|||
" }\n" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, natspec, false); |
|||
} |
|||
BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @dev Multiplies a number by 7 and adds second parameter\n" |
|||
" /// @param a Documentation for the first parameter starts here.\n" |
|||
" /// Since it's a really complicated parameter we need 2 lines\n" |
|||
" /// @param second Documentation for the second parameter\n" |
|||
" /// @return\n" |
|||
" /// The result of the multiplication\n" |
|||
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" |
|||
"}\n"; |
|||
|
|||
char const* natspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul\":{ \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" |
|||
" \"second\": \"Documentation for the second parameter\"\n" |
|||
" },\n" |
|||
" \"return\": \" The result of the multiplication\"\n" |
|||
" }\n" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, natspec, false); |
|||
} |
|||
|
|||
|
|||
BOOST_AUTO_TEST_CASE(dev_multiline_return) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" /// @dev Multiplies a number by 7 and adds second parameter\n" |
|||
" /// @param a Documentation for the first parameter starts here.\n" |
|||
" /// Since it's a really complicated parameter we need 2 lines\n" |
|||
" /// @param second Documentation for the second parameter\n" |
|||
" /// @return The result of the multiplication\n" |
|||
" /// and cookies with nutella\n" |
|||
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" |
|||
"}\n"; |
|||
|
|||
char const* natspec = "{" |
|||
"\"methods\":{" |
|||
" \"mul\":{ \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" |
|||
" \"second\": \"Documentation for the second parameter\"\n" |
|||
" },\n" |
|||
" \"return\": \"The result of the multiplication and cookies with nutella\"\n" |
|||
" }\n" |
|||
"}}"; |
|||
|
|||
checkNatspec(sourceCode, natspec, false); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue