]> Git Repo - VerusCoin.git/blob - src/pbaas/crosschainrpc.cpp
de23ea98f2103c759ac5ee11cc1e6b678a3e8728
[VerusCoin.git] / src / pbaas / crosschainrpc.cpp
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
44 using namespace std;
45
46 extern string PBAAS_HOST;
47 extern string PBAAS_USERPASS;
48 extern int32_t PBAAS_PORT;
49
50 //
51 // Exception thrown on connection error.  This error is used to determine
52 // when to wait if -rpcwait is given.
53 //
54 class CConnectionFailed : public std::runtime_error
55 {
56 public:
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 */
65 struct HTTPReply
66 {
67     HTTPReply(): status(0), error(-1) {}
68
69     int status;
70     int error;
71     std::string body;
72 };
73
74 const 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
96 static 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
122 static 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
129 UniValue CCrossChainRPCData::ToUniValue() const
130 {
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));
135     return obj;
136 }
137
138 static CCrossChainRPCData LoadFromConfig(std::string name)
139 {
140     map<string, string> settings;
141     map<string, vector<string>> settingsmulti;
142     CCrossChainRPCData ret;
143
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))
146     {
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";
155     }
156     return ret;
157 }
158
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)
161 {
162     // Used for inter-daemon communicatoin to enable merge mining and notarization without a client
163     //
164
165     // Obtain event base
166     raii_event_base base = obtain_event_base();
167
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);
171
172     HTTPReply response;
173     raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
174     if (req == NULL)
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);
178 #endif
179
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());
185
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());
191
192     int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/");
193     req.release(); // ownership moved to evcon in above call
194     if (r != 0) {
195         throw CConnectionFailed("send http request failed");
196     }
197
198     event_base_dispatch(base.get());
199
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");
208
209     // Parse reply
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();
214     if (reply.empty())
215         throw std::runtime_error("expected reply to have result, error and id properties");
216
217     return reply;
218 }
219
220 UniValue RPCCallRoot(const string& strMethod, const UniValue& params)
221 {
222     string host, credentials;
223     int port;
224     map<string, string> settings;
225     map<string, vector<string>> settingsmulti;
226
227     if (PBAAS_HOST != "" && PBAAS_PORT != 0)
228     {
229         return RPCCall(strMethod, params, PBAAS_USERPASS, PBAAS_PORT, PBAAS_HOST);
230     }
231     else if (ReadConfigFile(PBAAS_TESTMODE ? "VRSCTEST" : "VRSC", settings, settingsmulti))
232     {
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];
236         if (!host.size())
237         {
238             host = "127.0.0.1";
239         }
240         return RPCCall(strMethod, params, credentials, port, host);
241     }
242     return UniValue(UniValue::VNULL);
243 }
244
245 int32_t uni_get_int(UniValue uv, int32_t def)
246 {
247     try
248     {
249         return uv.get_int();
250     }
251     catch(const std::exception& e)
252     {
253         return def;
254     }
255 }
256
257 int64_t uni_get_int64(UniValue uv, int64_t def)
258 {
259     try
260     {
261         return uv.get_int64();
262     }
263     catch(const std::exception& e)
264     {
265         return def;
266     }
267 }
268
269 std::string uni_get_str(UniValue uv, std::string def)
270 {
271     try
272     {
273         return uv.get_str();
274     }
275     catch(const std::exception& e)
276     {
277         return def;
278     }
279 }
280
281 std::vector<UniValue> uni_getValues(UniValue uv, std::vector<UniValue> def)
282 {
283     try
284     {
285         return uv.getValues();
286     }
287     catch(const std::exception& e)
288     {
289         return def;
290     }
291 }
292
293 uint160 CCrossChainRPCData::GetConditionID(uint160 cid, int32_t condition)
294 {
295     CHashWriter hw(SER_GETHASH, PROTOCOL_VERSION);
296     hw << condition;
297     hw << cid;
298     uint256 chainHash = hw.GetHash();
299
300     uint160 output = Hash160(chainHash.begin(), chainHash.end());
301     return Hash160(chainHash.begin(), chainHash.end());
302 }
303
304 uint160 CCrossChainRPCData::GetConditionID(std::string name, int32_t condition)
305 {
306     uint160 cid = GetChainID(name);
307
308     CHashWriter hw(SER_GETHASH, PROTOCOL_VERSION);
309     hw << condition;
310     hw << cid;
311     uint256 chainHash = hw.GetHash();
312
313     uint160 output = Hash160(chainHash.begin(), chainHash.end());
314     return Hash160(chainHash.begin(), chainHash.end());
315 }
This page took 0.032985 seconds and 2 git commands to generate.