Skip to content

NODE_EXT_SERVICE: Advertise other services also available at this node. #4657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ BITCOIN_CORE_H = \
core_io.h \
crypter.h \
db.h \
extservices.h \
hash.h \
init.h \
key.h \
Expand Down Expand Up @@ -141,6 +142,7 @@ libbitcoin_server_a_SOURCES = \
alert.cpp \
bloom.cpp \
checkpoints.cpp \
extservices.cpp \
init.cpp \
leveldbwrapper.cpp \
main.cpp \
Expand Down
193 changes: 193 additions & 0 deletions src/extservices.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you at least try to respect the coding style rules we have (include ordering etc.)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this continues to produce hectoring, then the most appropriate action is to remove that rule from doc/coding.md. At some point you need to ask yourself whether this is satisfying your personal OCD versus making the bitcoin project and its developers more (or less!) productive.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Diapolo is quite right to point this out if it goes against coding standards and @jgarzik is quite right to point out that the coding standards may need revising.

May I take this moment to suggest reading the "four agreements"? (http://crossfitcetro.com/wp-content/uploads/2012/08/the_four_agreements.jpg) - I'm seeing all too often people taking things a bit too personally on here, however, it could almost go without saying that I for one am grateful to @jgarzik for so many of the code changes he's contributed, and I hope other user's sterling efforts (OCD fuelled or not) to enforce style rules don't deter future contributions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to have a well-defined coarse include order. I.e. first your own include, then dependencies within the project, then dependencies outside the project. This makes sure that the compiler checks that your own include file (in this case extservices.h) includes everything it needs on its own.

As for alphabetical sorting, it could be argued that that is a step too far, although if you want to define some fixed ordering within the groups alphabetical makes most sense. For example it makes it easier to see duplicates.

#include <vector>
#include "serialize.h"
#include "util.h"
#include "version.h"
#include "net.h"
#include "extservices.h"
#include <boost/foreach.hpp>
#include "json/json_spirit_value.h"
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"

using namespace std;
using namespace json_spirit;

static const unsigned int MAX_JSON_SRV_SZ = (640 * 1024); // enough for anybody

class CExtServices {
private:
vector<CExtService> srvList;

public:
void getArray(Array& arr);
void getVec(vector<CExtService>& vec);
void add(const CExtService& val) { srvList.push_back(val); }
size_t count() const { return srvList.size(); }
};

static CExtServices extServices;

bool CExtService::parseValue(const Value& val)
{
if (val.type() != obj_type)
return false;

const Object& srvObj = val.get_obj();

const Value& vName = find_value(srvObj, "name");
if (vName.type() != str_type)
return false;

name = vName.get_str();

const Value& vPort = find_value(srvObj, "port");
if (vPort.type() == null_type) {
// do nothing
} else if (vPort.type() == int_type) {
int i = vPort.get_int();
if (i < 1 || i > 65535)
return false;

port = i;
} else
return false;

const Value& vAttrib = find_value(srvObj, "attrib");
if (vAttrib.type() == null_type) {
// do nothing
} else if (vAttrib.type() == obj_type) {
const Object& attribObj = vAttrib.get_obj();
BOOST_FOREACH(const Pair& s, attribObj) {
const Value& av = s.value_;
if (av.type() != str_type)
return false;

CKeyValue kv(s.name_, av.get_str());
if (kv.key.size() < 1 || kv.key.size() > 100 ||
kv.value.size() > 1000)
return false;

attrib.push_back(kv);
}
} else
return false;

return true;
}

void CExtServices::getArray(Array& arr)
{
BOOST_FOREACH(const CExtService& srv, srvList) {
Object obj;
obj.push_back(Pair("name", srv.name));
if (srv.port > 0)
obj.push_back(Pair("port", srv.port));

arr.push_back(obj);
}
}

void CExtServices::getVec(vector<CExtService>& vec)
{
BOOST_FOREACH(const CExtService& srv, srvList) {
vec.push_back(srv);
}
}

static bool ReadJsonFile(const string& filename, size_t maxSz,
Value& valOut, string& strErr)
{
CAutoFile srvfile(fopen(filename.c_str(), "r"), SER_DISK, CLIENT_VERSION);
if (!srvfile) {
strErr = strprintf("Cannot open %s\n", filename);
return false;
}

string rawJson;
while (1) {
char buf[4096];

size_t bread = fread(buf, 1, sizeof(buf), srvfile);
rawJson.append(buf, bread);

if (maxSz && (rawJson.size() > maxSz)) {
strErr = strprintf("Error reading %s, too large\n", filename);
return false;
}

if (bread < sizeof(buf)) {
if (ferror(srvfile)) {
strErr = strprintf("Error reading %s\n", filename);
return false;
}

// eof
break;
}
}

if (!read_string(rawJson, valOut) ||
(valOut.type() != obj_type)) {
strErr = strprintf("Error parsing JSON in %s\n", filename);
return false;
}

return true;
}

static bool ReadExtServiceFile(const string& filename)
{
Value valSrv;
string strErr;
if (!ReadJsonFile(filename, MAX_JSON_SRV_SZ, valSrv, strErr)) {
LogPrintf("%s: %s", filename, strErr);
return false;
}

if (valSrv.type() != array_type) {
LogPrintf("Unable to parse array in %s\n", filename);
return false;
}

const Array& serviceList = valSrv.get_array();
for (unsigned int i = 0; i < serviceList.size(); i++) {
if (serviceList[i].type() != obj_type) {
LogPrintf("Service list member not object\n");
return false;
}

CExtService srv;
if (!srv.parseValue(serviceList[i]))
return false;

extServices.add(srv);
}

return true;
}

bool ReadExtServices()
{
if (!mapArgs.count("-extservices"))
return true;
if (!ReadExtServiceFile(mapArgs["-extservice"]))
return false;

if (extServices.count() > 0)
nLocalServices |= NODE_EXT_SERVICES;

return true;
}

Array ListExtServices()
{
Array ret;
extServices.getArray(ret);
return ret;
}

void GetExtServicesVec(vector<CExtService>& vec)
{
extServices.getVec(vec);
}
62 changes: 62 additions & 0 deletions src/extservices.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#ifndef __BITCOIN_EXTSERVICES_H__
#define __BITCOIN_EXTSERVICES_H__

#include <stdint.h>
#include <string>
#include <vector>
#include "json/json_spirit_value.h"
#include "serialize.h"

class CKeyValue {
public:
std::string key;
std::string value;

CKeyValue()
{
SetNull();
}
CKeyValue(std::string key_, std::string value_)
{
key = key_;
value = value_;
}

IMPLEMENT_SERIALIZE
(
READWRITE(key);
READWRITE(value);
)

void SetNull()
{
key.clear();
value.clear();
}
};

class CExtService {
public:
std::string name;
int32_t port;
std::vector<CKeyValue> attrib;

CExtService(const std::string& name_ = "", int32_t port_ = -1) {
name = name_;
port = port_;
}
bool parseValue(const json_spirit::Value& val);

IMPLEMENT_SERIALIZE
(
READWRITE(name);
READWRITE(port);
READWRITE(attrib);
)
};

extern bool ReadExtServices();
extern json_spirit::Array ListExtServices();
extern void GetExtServicesVec(std::vector<CExtService>& vec);

#endif // __BITCOIN_EXTSERVICES_H__
5 changes: 5 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "txdb.h"
#include "ui_interface.h"
#include "util.h"
#include "extservices.h"
#ifdef ENABLE_WALLET
#include "db.h"
#include "wallet.h"
Expand Down Expand Up @@ -241,6 +242,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + _("(default: 1)") + "\n";
strUsage += " -dnsseed " + _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect)") + "\n";
strUsage += " -externalip=<ip> " + _("Specify your own public address") + "\n";
strUsage += " -extservices=<file> " + _("Specify JSON file containing array of external/extended service descriptions") + "\n";
strUsage += " -forcednsseed " + _("Always query for peer addresses via DNS lookup (default: 0)") + "\n";
strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n";
strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n";
Expand Down Expand Up @@ -1045,6 +1047,9 @@ bool AppInit2(boost::thread_group& threadGroup)
if (est_filein)
mempool.ReadFeeEstimates(est_filein);

if (!ReadExtServices())
return false;

// ********************************************************* Step 8: load wallet
#ifdef ENABLE_WALLET
if (fDisableWallet) {
Expand Down
9 changes: 9 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "txmempool.h"
#include "ui_interface.h"
#include "util.h"
#include "extservices.h"

#include <sstream>

Expand Down Expand Up @@ -3655,6 +3656,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}


else if ((strCommand == "getextsrv") && (nLocalServices & NODE_EXT_SERVICES))
{
vector<CExtService> vSrv;
GetExtServicesVec(vSrv);
pfrom->PushMessage("extservices", vSrv);
}


else if (strCommand == "addr")
{
vector<CAddress> vAddr;
Expand Down
1 change: 1 addition & 0 deletions src/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class CMessageHeader
enum
{
NODE_NETWORK = (1 << 0),
NODE_EXT_SERVICES = (1 << 2),

// Bits 24-31 are reserved for temporary experiments. Just pick a bit that
// isn't getting used, or one not being used much, and notify the
Expand Down
2 changes: 2 additions & 0 deletions src/rpcnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "sync.h"
#include "timedata.h"
#include "util.h"
#include "extservices.h"

#include <boost/foreach.hpp>
#include "json/json_spirit_value.h"
Expand Down Expand Up @@ -371,6 +372,7 @@ Value getnetworkinfo(const Array& params, bool fHelp)
obj.push_back(Pair("version", (int)CLIENT_VERSION));
obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices)));
obj.push_back(Pair("extservices", ListExtServices()));
obj.push_back(Pair("timeoffset", GetTimeOffset()));
obj.push_back(Pair("connections", (int)vNodes.size()));
obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string())));
Expand Down