]> Git Repo - VerusCoin.git/blame - src/pbaas/crosschainrpc.cpp
Update version
[VerusCoin.git] / src / pbaas / crosschainrpc.cpp
CommitLineData
b2a98c42
MT
1/********************************************************************
2 * (C) 2019 Michael Toutonghi
3 *
4 * Distributed under the MIT software license, see the accompanying
5 * file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 *
7 * This provides support for PBaaS cross chain communication.
8 *
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
11 * notarizations.
12 *
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.
16 *
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.
21 *
22 */
23
24#include "chainparamsbase.h"
25#include "clientversion.h"
26#include "rpc/client.h"
27#include "rpc/protocol.h"
28#include "util.h"
29#include "utilstrencodings.h"
30
31#include <boost/filesystem/operations.hpp>
32#include <stdio.h>
33
34#include <event2/buffer.h>
35#include <event2/keyvalq_struct.h>
36#include "support/events.h"
37
38#include <univalue.h>
39
40#include "uint256.h"
41#include "hash.h"
42#include "pbaas/crosschainrpc.h"
43
44using namespace std;
45
9f0c14b2
MT
46extern string PBAAS_HOST;
47extern string PBAAS_USERPASS;
48extern int32_t PBAAS_PORT;
49
b2a98c42
MT
50//
51// Exception thrown on connection error. This error is used to determine
52// when to wait if -rpcwait is given.
53//
54class CConnectionFailed : public std::runtime_error
55{
56public:
57
58 explicit inline CConnectionFailed(const std::string& msg) :
59 std::runtime_error(msg)
60 {}
61
62};
63
64/** Reply structure for request_done to fill in */
65struct HTTPReply
66{
67 HTTPReply(): status(0), error(-1) {}
68
69 int status;
70 int error;
71 std::string body;
72};
73
74const char *http_errorstring(int code)
75{
76 switch(code) {
77#if LIBEVENT_VERSION_NUMBER >= 0x02010300
78 case EVREQ_HTTP_TIMEOUT:
79 return "timeout reached";
80 case EVREQ_HTTP_EOF:
81 return "EOF 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";
90#endif
91 default:
92 return "unknown";
93 }
94}
95
96static void http_request_done(struct evhttp_request *req, void *ctx)
97{
98 HTTPReply *reply = static_cast<HTTPReply*>(ctx);
99
100 if (req == NULL) {
101 /* If req is NULL, it means an error occurred while connecting: the
102 * error code will have been passed to http_error_cb.
103 */
104 reply->status = 0;
105 return;
106 }
107
108 reply->status = evhttp_request_get_response_code(req);
109
110 struct evbuffer *buf = evhttp_request_get_input_buffer(req);
111 if (buf)
112 {
113 size_t size = evbuffer_get_length(buf);
114 const char *data = (const char*)evbuffer_pullup(buf, size);
115 if (data)
116 reply->body = std::string(data, size);
117 evbuffer_drain(buf, size);
118 }
119}
120
121#if LIBEVENT_VERSION_NUMBER >= 0x02010300
122static void http_error_cb(enum evhttp_request_error err, void *ctx)
123{
124 HTTPReply *reply = static_cast<HTTPReply*>(ctx);
125 reply->error = err;
126}
127#endif
128
9f0c14b2 129static CCrossChainRPCData LoadFromConfig(std::string name)
b2a98c42 130{
9f0c14b2
MT
131 map<string, string> settings;
132 map<string, vector<string>> settingsmulti;
133 CCrossChainRPCData ret;
134
135 // if we are requested to automatically load the information from the Verus chain, do it if we can find the daemon
136 if (ReadConfigFile(name, settings, settingsmulti))
137 {
138 auto rpcuser = settings.find("-rpcuser");
139 auto rpcpwd = settings.find("-rpcpassword");
140 auto rpcport = settings.find("-rpcport");
141 auto rpchost = settings.find("-rpchost");
142 ret.credentials = rpcuser != settings.end() ? rpcuser->second + ":" : "";
143 ret.credentials += rpcpwd != settings.end() ? rpcpwd->second : "";
144 ret.port = rpcport != settings.end() ? atoi(rpcport->second) : (name == "VRSC" ? 27486 : 0);
145 ret.host = rpchost != settings.end() ? rpchost->second : "127.0.0.1";
146 }
147 return ret;
b2a98c42
MT
148}
149
150// credentials for now are "user:password"
151UniValue RPCCall(const string& strMethod, const UniValue& params, const string credentials, int port, const string host, int timeout)
152{
153 // Used for inter-daemon communicatoin to enable merge mining and notarization without a client
154 //
155
156 // Obtain event base
157 raii_event_base base = obtain_event_base();
158
159 // Synchronously look up hostname
160 raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
161 evhttp_connection_set_timeout(evcon.get(), timeout);
162
163 HTTPReply response;
164 raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
165 if (req == NULL)
166 throw std::runtime_error("create http request failed");
167#if LIBEVENT_VERSION_NUMBER >= 0x02010300
168 evhttp_request_set_error_cb(req.get(), http_error_cb);
169#endif
170
171 struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
172 assert(output_headers);
173 evhttp_add_header(output_headers, "Host", host.c_str());
174 evhttp_add_header(output_headers, "Connection", "close");
175 evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(credentials)).c_str());
176
177 // Attach request data
178 std::string strRequest = JSONRPCRequest(strMethod, params, 1);
179 struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
180 assert(output_buffer);
181 evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
182
183 int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/");
184 req.release(); // ownership moved to evcon in above call
185 if (r != 0) {
186 throw CConnectionFailed("send http request failed");
187 }
188
189 event_base_dispatch(base.get());
190
191 if (response.status == 0)
192 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));
193 else if (response.status == HTTP_UNAUTHORIZED)
194 throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
195 else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
196 throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
197 else if (response.body.empty())
198 throw std::runtime_error("no response from server");
199
200 // Parse reply
201 UniValue valReply(UniValue::VSTR);
202 if (!valReply.read(response.body))
203 throw std::runtime_error("couldn't parse reply from server");
204 const UniValue& reply = valReply.get_obj();
205 if (reply.empty())
206 throw std::runtime_error("expected reply to have result, error and id properties");
207
208 return reply;
209}
9f0c14b2 210
2156d3d0 211UniValue RPCCallRoot(const string& strMethod, const UniValue& params, int timeout)
9f0c14b2
MT
212{
213 string host, credentials;
214 int port;
215 map<string, string> settings;
216 map<string, vector<string>> settingsmulti;
217
218 if (PBAAS_HOST != "" && PBAAS_PORT != 0)
219 {
220 return RPCCall(strMethod, params, PBAAS_USERPASS, PBAAS_PORT, PBAAS_HOST);
221 }
222 else if (ReadConfigFile(PBAAS_TESTMODE ? "VRSCTEST" : "VRSC", settings, settingsmulti))
223 {
7af5cf39 224 PBAAS_USERPASS = settingsmulti.find("-rpcuser")->second[0] + ":" + settingsmulti.find("-rpcpassword")->second[0];
225 PBAAS_PORT = atoi(settingsmulti.find("-rpcport")->second[0]);
226 PBAAS_HOST = settingsmulti.find("-rpchost")->second[0];
227 if (!PBAAS_HOST.size())
9f0c14b2 228 {
7af5cf39 229 PBAAS_HOST = "127.0.0.1";
9f0c14b2 230 }
2156d3d0 231 return RPCCall(strMethod, params, credentials, port, host, timeout);
9f0c14b2
MT
232 }
233 return UniValue(UniValue::VNULL);
234}
f8f61a6d 235
236int32_t uni_get_int(UniValue uv, int32_t def)
237{
238 try
239 {
240 return uv.get_int();
241 }
242 catch(const std::exception& e)
243 {
244 return def;
245 }
246}
247
248int64_t uni_get_int64(UniValue uv, int64_t def)
249{
250 try
251 {
252 return uv.get_int64();
253 }
254 catch(const std::exception& e)
255 {
256 return def;
257 }
258}
259
260std::string uni_get_str(UniValue uv, std::string def)
261{
262 try
263 {
264 return uv.get_str();
265 }
266 catch(const std::exception& e)
267 {
268 return def;
269 }
270}
271
272std::vector<UniValue> uni_getValues(UniValue uv, std::vector<UniValue> def)
273{
274 try
275 {
276 return uv.getValues();
277 }
278 catch(const std::exception& e)
279 {
280 return def;
281 }
282}
283
ef54c6e1 284UniValue CCrossChainRPCData::ToUniValue() const
285{
286 UniValue obj(UniValue::VOBJ);
287 obj.push_back(Pair("host", host));
288 obj.push_back(Pair("port", port));
289 obj.push_back(Pair("credentials", credentials));
290 return obj;
291}
292
db336898 293uint160 CCrossChainRPCData::GetConditionID(uint160 cid, int32_t condition)
294{
db336898 295 CHashWriter hw(SER_GETHASH, PROTOCOL_VERSION);
296 hw << condition;
297 hw << cid;
298 uint256 chainHash = hw.GetHash();
db336898 299 return Hash160(chainHash.begin(), chainHash.end());
300}
301
302uint160 CCrossChainRPCData::GetConditionID(std::string name, int32_t condition)
303{
304 uint160 cid = GetChainID(name);
db336898 305
306 CHashWriter hw(SER_GETHASH, PROTOCOL_VERSION);
307 hw << condition;
308 hw << cid;
309 uint256 chainHash = hw.GetHash();
db336898 310 return Hash160(chainHash.begin(), chainHash.end());
311}
This page took 0.1039 seconds and 4 git commands to generate.