1 /********************************************************************
2 * (C) 2019 Michael Toutonghi
4 * Distributed under the MIT software license, see the accompanying
5 * file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 * This provides support for PBaaS cross chain communication.
9 * In merge mining and notarization, Verus acts as a hub that other PBaaS chains
10 * call via RPC in order to get information that allows earning and submitting
13 * All PBaaS chains communicate with their primary reserve chain, which is either Verus
14 * or the chain that is their reserve coin. The child PBaaS chain initiates all of
15 * the communication with the parent / reserve daemon.
17 * Generally, the PBaaS chain will call the Verus chain to either get information needed
18 * to create an earned or accepted notarization. If there is no Verus daemon available
19 * staking and mining of a PBaaS chain proceeds as usual, but without notarization
20 * reward opportunities.
24 #include "chainparamsbase.h"
25 #include "clientversion.h"
26 #include "rpc/client.h"
27 #include "rpc/protocol.h"
29 #include "utilstrencodings.h"
31 #include <boost/filesystem/operations.hpp>
34 #include <event2/buffer.h>
35 #include <event2/keyvalq_struct.h>
36 #include "support/events.h"
42 #include "pbaas/crosschainrpc.h"
46 extern string PBAAS_HOST;
47 extern string PBAAS_USERPASS;
48 extern int32_t PBAAS_PORT;
51 // Exception thrown on connection error. This error is used to determine
52 // when to wait if -rpcwait is given.
54 class CConnectionFailed : public std::runtime_error
58 explicit inline CConnectionFailed(const std::string& msg) :
59 std::runtime_error(msg)
64 /** Reply structure for request_done to fill in */
67 HTTPReply(): status(0), error(-1) {}
74 const char *http_errorstring(int code)
77 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
78 case EVREQ_HTTP_TIMEOUT:
79 return "timeout reached";
82 case EVREQ_HTTP_INVALID_HEADER:
83 return "error while reading header, or invalid header";
84 case EVREQ_HTTP_BUFFER_ERROR:
85 return "error encountered while reading or writing";
86 case EVREQ_HTTP_REQUEST_CANCEL:
87 return "request was canceled";
88 case EVREQ_HTTP_DATA_TOO_LONG:
89 return "response body is larger than allowed";
96 static void http_request_done(struct evhttp_request *req, void *ctx)
98 HTTPReply *reply = static_cast<HTTPReply*>(ctx);
101 /* If req is NULL, it means an error occurred while connecting: the
102 * error code will have been passed to http_error_cb.
108 reply->status = evhttp_request_get_response_code(req);
110 struct evbuffer *buf = evhttp_request_get_input_buffer(req);
113 size_t size = evbuffer_get_length(buf);
114 const char *data = (const char*)evbuffer_pullup(buf, size);
116 reply->body = std::string(data, size);
117 evbuffer_drain(buf, size);
121 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
122 static void http_error_cb(enum evhttp_request_error err, void *ctx)
124 HTTPReply *reply = static_cast<HTTPReply*>(ctx);
129 UniValue CCrossChainRPCData::ToUniValue() const
131 UniValue obj(UniValue::VOBJ);
132 obj.push_back(Pair("host", host));
133 obj.push_back(Pair("port", port));
134 obj.push_back(Pair("credentials", credentials));
138 static CCrossChainRPCData LoadFromConfig(std::string name)
140 map<string, string> settings;
141 map<string, vector<string>> settingsmulti;
142 CCrossChainRPCData ret;
144 // if we are requested to automatically load the information from the Verus chain, do it if we can find the daemon
145 if (ReadConfigFile(name, settings, settingsmulti))
147 auto rpcuser = settings.find("-rpcuser");
148 auto rpcpwd = settings.find("-rpcpassword");
149 auto rpcport = settings.find("-rpcport");
150 auto rpchost = settings.find("-rpchost");
151 ret.credentials = rpcuser != settings.end() ? rpcuser->second + ":" : "";
152 ret.credentials += rpcpwd != settings.end() ? rpcpwd->second : "";
153 ret.port = rpcport != settings.end() ? atoi(rpcport->second) : (name == "VRSC" ? 27486 : 0);
154 ret.host = rpchost != settings.end() ? rpchost->second : "127.0.0.1";
159 // credentials for now are "user:password"
160 UniValue RPCCall(const string& strMethod, const UniValue& params, const string credentials, int port, const string host, int timeout)
162 // Used for inter-daemon communicatoin to enable merge mining and notarization without a client
166 raii_event_base base = obtain_event_base();
168 // Synchronously look up hostname
169 raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
170 evhttp_connection_set_timeout(evcon.get(), timeout);
173 raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
175 throw std::runtime_error("create http request failed");
176 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
177 evhttp_request_set_error_cb(req.get(), http_error_cb);
180 struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
181 assert(output_headers);
182 evhttp_add_header(output_headers, "Host", host.c_str());
183 evhttp_add_header(output_headers, "Connection", "close");
184 evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(credentials)).c_str());
186 // Attach request data
187 std::string strRequest = JSONRPCRequest(strMethod, params, 1);
188 struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
189 assert(output_buffer);
190 evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
192 int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/");
193 req.release(); // ownership moved to evcon in above call
195 throw CConnectionFailed("send http request failed");
198 event_base_dispatch(base.get());
200 if (response.status == 0)
201 throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
202 else if (response.status == HTTP_UNAUTHORIZED)
203 throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
204 else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
205 throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
206 else if (response.body.empty())
207 throw std::runtime_error("no response from server");
210 UniValue valReply(UniValue::VSTR);
211 if (!valReply.read(response.body))
212 throw std::runtime_error("couldn't parse reply from server");
213 const UniValue& reply = valReply.get_obj();
215 throw std::runtime_error("expected reply to have result, error and id properties");
220 UniValue RPCCallRoot(const string& strMethod, const UniValue& params)
222 string host, credentials;
224 map<string, string> settings;
225 map<string, vector<string>> settingsmulti;
227 if (PBAAS_HOST != "" && PBAAS_PORT != 0)
229 return RPCCall(strMethod, params, PBAAS_USERPASS, PBAAS_PORT, PBAAS_HOST);
231 else if (ReadConfigFile(PBAAS_TESTMODE ? "VRSCTEST" : "VRSC", settings, settingsmulti))
233 credentials = settingsmulti.find("-rpcuser")->second[0] + ":" + settingsmulti.find("-rpcpassword")->second[0];
234 port = atoi(settingsmulti.find("-rpcport")->second[0]);
235 host = settingsmulti.find("-rpchost")->second[0];
240 return RPCCall(strMethod, params, credentials, port, host);
242 return UniValue(UniValue::VNULL);
245 int32_t uni_get_int(UniValue uv, int32_t def)
251 catch(const std::exception& e)
257 int64_t uni_get_int64(UniValue uv, int64_t def)
261 return uv.get_int64();
263 catch(const std::exception& e)
269 std::string uni_get_str(UniValue uv, std::string def)
275 catch(const std::exception& e)
281 std::vector<UniValue> uni_getValues(UniValue uv, std::vector<UniValue> def)
285 return uv.getValues();
287 catch(const std::exception& e)
293 uint160 CCrossChainRPCData::GetConditionID(uint160 cid, int32_t condition)
295 CHashWriter hw(SER_GETHASH, PROTOCOL_VERSION);
298 uint256 chainHash = hw.GetHash();
300 uint160 output = Hash160(chainHash.begin(), chainHash.end());
301 return Hash160(chainHash.begin(), chainHash.end());
304 uint160 CCrossChainRPCData::GetConditionID(std::string name, int32_t condition)
306 uint160 cid = GetChainID(name);
308 CHashWriter hw(SER_GETHASH, PROTOCOL_VERSION);
311 uint256 chainHash = hw.GetHash();
313 uint160 output = Hash160(chainHash.begin(), chainHash.end());
314 return Hash160(chainHash.begin(), chainHash.end());