]> Git Repo - VerusCoin.git/blame - src/pbaas/crosschainrpc.cpp
Fix initial chain definition at PBaaS activation
[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>
56fe75cb 32#include <boost/format.hpp>
b2a98c42
MT
33#include <stdio.h>
34
35#include <event2/buffer.h>
36#include <event2/keyvalq_struct.h>
37#include "support/events.h"
38
39#include <univalue.h>
40
41#include "uint256.h"
42#include "hash.h"
43#include "pbaas/crosschainrpc.h"
2acb304a 44#include "pbaas/identity.h"
b2a98c42
MT
45
46using namespace std;
47
9f0c14b2
MT
48extern string PBAAS_HOST;
49extern string PBAAS_USERPASS;
50extern int32_t PBAAS_PORT;
51
b2a98c42
MT
52//
53// Exception thrown on connection error. This error is used to determine
54// when to wait if -rpcwait is given.
55//
56class CConnectionFailed : public std::runtime_error
57{
58public:
59
60 explicit inline CConnectionFailed(const std::string& msg) :
61 std::runtime_error(msg)
62 {}
63
64};
65
66/** Reply structure for request_done to fill in */
67struct HTTPReply
68{
69 HTTPReply(): status(0), error(-1) {}
70
71 int status;
72 int error;
73 std::string body;
74};
75
76const char *http_errorstring(int code)
77{
78 switch(code) {
79#if LIBEVENT_VERSION_NUMBER >= 0x02010300
80 case EVREQ_HTTP_TIMEOUT:
81 return "timeout reached";
82 case EVREQ_HTTP_EOF:
83 return "EOF reached";
84 case EVREQ_HTTP_INVALID_HEADER:
85 return "error while reading header, or invalid header";
86 case EVREQ_HTTP_BUFFER_ERROR:
87 return "error encountered while reading or writing";
88 case EVREQ_HTTP_REQUEST_CANCEL:
89 return "request was canceled";
90 case EVREQ_HTTP_DATA_TOO_LONG:
91 return "response body is larger than allowed";
92#endif
93 default:
94 return "unknown";
95 }
96}
97
98static void http_request_done(struct evhttp_request *req, void *ctx)
99{
100 HTTPReply *reply = static_cast<HTTPReply*>(ctx);
101
102 if (req == NULL) {
103 /* If req is NULL, it means an error occurred while connecting: the
104 * error code will have been passed to http_error_cb.
105 */
106 reply->status = 0;
107 return;
108 }
109
110 reply->status = evhttp_request_get_response_code(req);
111
112 struct evbuffer *buf = evhttp_request_get_input_buffer(req);
113 if (buf)
114 {
115 size_t size = evbuffer_get_length(buf);
116 const char *data = (const char*)evbuffer_pullup(buf, size);
117 if (data)
118 reply->body = std::string(data, size);
119 evbuffer_drain(buf, size);
120 }
121}
122
123#if LIBEVENT_VERSION_NUMBER >= 0x02010300
124static void http_error_cb(enum evhttp_request_error err, void *ctx)
125{
126 HTTPReply *reply = static_cast<HTTPReply*>(ctx);
127 reply->error = err;
128}
129#endif
130
9f0c14b2 131static CCrossChainRPCData LoadFromConfig(std::string name)
b2a98c42 132{
9f0c14b2
MT
133 map<string, string> settings;
134 map<string, vector<string>> settingsmulti;
135 CCrossChainRPCData ret;
136
137 // if we are requested to automatically load the information from the Verus chain, do it if we can find the daemon
138 if (ReadConfigFile(name, settings, settingsmulti))
139 {
140 auto rpcuser = settings.find("-rpcuser");
141 auto rpcpwd = settings.find("-rpcpassword");
142 auto rpcport = settings.find("-rpcport");
143 auto rpchost = settings.find("-rpchost");
144 ret.credentials = rpcuser != settings.end() ? rpcuser->second + ":" : "";
145 ret.credentials += rpcpwd != settings.end() ? rpcpwd->second : "";
146 ret.port = rpcport != settings.end() ? atoi(rpcport->second) : (name == "VRSC" ? 27486 : 0);
147 ret.host = rpchost != settings.end() ? rpchost->second : "127.0.0.1";
148 }
149 return ret;
b2a98c42
MT
150}
151
152// credentials for now are "user:password"
153UniValue RPCCall(const string& strMethod, const UniValue& params, const string credentials, int port, const string host, int timeout)
154{
155 // Used for inter-daemon communicatoin to enable merge mining and notarization without a client
156 //
157
158 // Obtain event base
159 raii_event_base base = obtain_event_base();
160
161 // Synchronously look up hostname
162 raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
163 evhttp_connection_set_timeout(evcon.get(), timeout);
164
165 HTTPReply response;
166 raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
167 if (req == NULL)
168 throw std::runtime_error("create http request failed");
169#if LIBEVENT_VERSION_NUMBER >= 0x02010300
170 evhttp_request_set_error_cb(req.get(), http_error_cb);
171#endif
172
173 struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
174 assert(output_headers);
175 evhttp_add_header(output_headers, "Host", host.c_str());
176 evhttp_add_header(output_headers, "Connection", "close");
177 evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(credentials)).c_str());
178
179 // Attach request data
180 std::string strRequest = JSONRPCRequest(strMethod, params, 1);
181 struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
182 assert(output_buffer);
183 evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
184
185 int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/");
186 req.release(); // ownership moved to evcon in above call
187 if (r != 0) {
188 throw CConnectionFailed("send http request failed");
189 }
190
191 event_base_dispatch(base.get());
192
193 if (response.status == 0)
194 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));
195 else if (response.status == HTTP_UNAUTHORIZED)
196 throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
197 else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
198 throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
199 else if (response.body.empty())
200 throw std::runtime_error("no response from server");
201
202 // Parse reply
203 UniValue valReply(UniValue::VSTR);
204 if (!valReply.read(response.body))
205 throw std::runtime_error("couldn't parse reply from server");
206 const UniValue& reply = valReply.get_obj();
207 if (reply.empty())
208 throw std::runtime_error("expected reply to have result, error and id properties");
209
210 return reply;
211}
9f0c14b2 212
2156d3d0 213UniValue RPCCallRoot(const string& strMethod, const UniValue& params, int timeout)
9f0c14b2
MT
214{
215 string host, credentials;
216 int port;
217 map<string, string> settings;
218 map<string, vector<string>> settingsmulti;
219
220 if (PBAAS_HOST != "" && PBAAS_PORT != 0)
221 {
222 return RPCCall(strMethod, params, PBAAS_USERPASS, PBAAS_PORT, PBAAS_HOST);
223 }
224 else if (ReadConfigFile(PBAAS_TESTMODE ? "VRSCTEST" : "VRSC", settings, settingsmulti))
225 {
7af5cf39 226 PBAAS_USERPASS = settingsmulti.find("-rpcuser")->second[0] + ":" + settingsmulti.find("-rpcpassword")->second[0];
227 PBAAS_PORT = atoi(settingsmulti.find("-rpcport")->second[0]);
228 PBAAS_HOST = settingsmulti.find("-rpchost")->second[0];
229 if (!PBAAS_HOST.size())
9f0c14b2 230 {
7af5cf39 231 PBAAS_HOST = "127.0.0.1";
9f0c14b2 232 }
2156d3d0 233 return RPCCall(strMethod, params, credentials, port, host, timeout);
9f0c14b2
MT
234 }
235 return UniValue(UniValue::VNULL);
236}
f8f61a6d 237
b7c685b8 238bool uni_get_bool(UniValue uv, bool def)
239{
240 try
241 {
996f6c33 242 if (uv.isStr())
243 {
244 std::string boolStr;
245 if ((boolStr = uni_get_str(uv, def ? "true" : "false")) == "true" || boolStr == "1")
246 {
247 return true;
248 }
249 else if (boolStr == "false" || boolStr == "0")
250 {
251 return false;
252 }
253 return def;
254 }
255 else
256 {
257 return uv.get_bool();
258 }
b7c685b8 259 }
260 catch(const std::exception& e)
261 {
262 return def;
263 }
264}
265
f8f61a6d 266int32_t uni_get_int(UniValue uv, int32_t def)
267{
268 try
269 {
270 return uv.get_int();
271 }
272 catch(const std::exception& e)
273 {
274 return def;
275 }
276}
277
278int64_t uni_get_int64(UniValue uv, int64_t def)
279{
280 try
281 {
282 return uv.get_int64();
283 }
284 catch(const std::exception& e)
285 {
286 return def;
287 }
288}
289
290std::string uni_get_str(UniValue uv, std::string def)
291{
292 try
293 {
294 return uv.get_str();
295 }
296 catch(const std::exception& e)
297 {
298 return def;
299 }
300}
301
302std::vector<UniValue> uni_getValues(UniValue uv, std::vector<UniValue> def)
303{
304 try
305 {
306 return uv.getValues();
307 }
308 catch(const std::exception& e)
309 {
310 return def;
311 }
312}
313
ef54c6e1 314UniValue CCrossChainRPCData::ToUniValue() const
315{
316 UniValue obj(UniValue::VOBJ);
317 obj.push_back(Pair("host", host));
318 obj.push_back(Pair("port", port));
319 obj.push_back(Pair("credentials", credentials));
320 return obj;
321}
322
56fe75cb 323CNodeData::CNodeData(const UniValue &obj)
324{
325 networkAddress = uni_get_str(find_value(obj, "networkaddress"));
326 CTxDestination dest = DecodeDestination(uni_get_str(find_value(obj, "nodeidentity")));
327 if (dest.which() != COptCCParams::ADDRTYPE_ID)
328 {
329 nodeIdentity = uint160();
330 }
331 else
332 {
333 nodeIdentity = GetDestinationID(dest);
334 }
335}
336
337CNodeData::CNodeData(std::string netAddr, std::string paymentAddr) :
338 networkAddress(netAddr)
339{
340 nodeIdentity = GetDestinationID(DecodeDestination(paymentAddr));
341}
342
343UniValue CNodeData::ToUniValue() const
344{
345 UniValue obj(UniValue::VOBJ);
346 obj.push_back(Pair("networkaddress", networkAddress));
347 obj.push_back(Pair("nodeidentity", EncodeDestination(CIdentityID(nodeIdentity))));
348 return obj;
349}
350
2f416b17 351uint160 CurrencyNameToChainID(std::string currencyStr)
352{
353 std::string extraName;
354 uint160 retVal;
355 currencyStr = TrimSpaces(currencyStr);
356 if (!currencyStr.size())
357 {
358 return retVal;
359 }
360 ParseSubNames(currencyStr, extraName, true);
361 if (currencyStr.back() == '@' || extraName != "")
362 {
363 return retVal;
364 }
365 CTxDestination currencyDest = DecodeDestination(currencyStr);
366 if (currencyDest.which() == COptCCParams::ADDRTYPE_INVALID)
367 {
368 currencyDest = DecodeDestination(currencyStr + "@");
369 }
370 if (currencyDest.which() != COptCCParams::ADDRTYPE_INVALID)
371 {
372 retVal = GetDestinationID(currencyDest);
373 }
374 return retVal;
375}
56fe75cb 376
377CCurrencyDefinition::CCurrencyDefinition(const UniValue &obj)
378{
379 try
380 {
381 nVersion = PBAAS_VERSION;
382 name = std::string(uni_get_str(find_value(obj, "name")), 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
56fe75cb 383
384 std::string parentStr = uni_get_str(find_value(obj, "parent"));
2f416b17 385 if (parentStr != "")
56fe75cb 386 {
387 CTxDestination parentDest = DecodeDestination(parentStr);
388 parent = GetDestinationID(parentDest);
389 // if we have a destination, but it is invalid, the json for this definition cannot be valid
390 if (parentDest.which() == COptCCParams::ADDRTYPE_INVALID)
391 {
392 nVersion = PBAAS_VERSION_INVALID;
393 }
394 }
395
2f416b17 396 name = CleanName(name, parent);
397
56fe75cb 398 options = (uint32_t)uni_get_int64(find_value(obj, "options"));
399
3b45a268 400 idRegistrationAmount = AmountFromValueNoErr(find_value(obj, "idregistrationprice"));
56fe75cb 401 idReferralLevels = uni_get_int(find_value(obj, "idreferrallevels"));
402
403 UniValue notaryArr = find_value(obj, "notaries");
8f02d50d 404 minNotariesConfirm = 0;
56fe75cb 405 if (notaryArr.isArray())
406 {
407 for (int i = 0; i < notaryArr.size(); i++)
408 {
409 CIdentityID notaryID;
410 CTxDestination notaryDest = DecodeDestination(uni_get_str(notaryArr[i]));
411 notaryID = GetDestinationID(notaryDest);
412 // if we have a destination, but it is invalid, the json for this definition cannot be valid
413 if (notaryID.IsNull())
414 {
415 nVersion = PBAAS_VERSION_INVALID;
416 }
417 else
418 {
419 notaries.push_back(notaryID);
420 }
421 }
422 minNotariesConfirm = uni_get_int(find_value(obj, "minnotariesconfirm"));
423 }
424 billingPeriod = uni_get_int(find_value(obj, "billingperiod"));
3b45a268 425 notarizationReward = AmountFromValueNoErr(find_value(obj, "notarizationreward"));
56fe75cb 426
427 startBlock = uni_get_int(find_value(obj, "startblock"));
428 endBlock = uni_get_int(find_value(obj, "endblock"));
429
2f416b17 430 proofProtocol = (EProofProtocol)uni_get_int(find_value(obj, "proofprotocol"), (int32_t)PROOF_PBAASMMR);
431 notarizationProtocol = (ENotarizationProtocol)uni_get_int(find_value(obj, "notarizationprotocol"), (int32_t)NOTARIZATION_AUTO);
3b45a268 432 int32_t totalReserveWeight = AmountFromValueNoErr(find_value(obj, "reserveratio"));
56fe75cb 433 UniValue currencyArr = find_value(obj, "currencies");
434 UniValue weightArr = find_value(obj, "weights");
435 UniValue conversionArr = find_value(obj, "conversions");
436 UniValue minPreconvertArr = find_value(obj, "minpreconversion");
437 UniValue maxPreconvertArr = find_value(obj, "maxpreconversion");
438 UniValue initialContributionArr = find_value(obj, "initialcontributions");
439 UniValue preConversionsArr = find_value(obj, "preconversions");
440
441 bool convertible = false;
442
443 if (currencyArr.isArray() && currencyArr.size())
444 {
445 // if weights are defined, make sure they are defined correctly, if not, define them
446 if (weightArr.isArray() && weightArr.size())
447 {
448 if (weightArr.size() != currencyArr.size())
449 {
450 LogPrintf("%s: reserve currency weights must be specified for all currencies\n", __func__);
451 nVersion = PBAAS_VERSION_INVALID;
452 }
453 else
454 {
455 for (int i = 0; i < currencyArr.size(); i++)
456 {
3b45a268 457 int32_t weight = (int32_t)AmountFromValueNoErr(weightArr[i]);
56fe75cb 458 if (weight <= 0)
459 {
460 nVersion = PBAAS_VERSION_INVALID;
461 break;
462 }
463 weights.push_back(weight);
464 }
465 }
466 }
467 else if (totalReserveWeight)
468 {
469 uint32_t oneWeight = totalReserveWeight / SATOSHIDEN;
470 uint32_t mod = totalReserveWeight % SATOSHIDEN;
471 for (int i = 0; i < currencyArr.size(); i++)
472 {
473 // distribute remainder of weight among first come currencies
474 int32_t weight = oneWeight;
475 if (mod > 0)
476 {
477 weight++;
478 mod--;
479 }
480 weights.push_back(weight);
481 }
482 }
483
484 // if we have weights, we can be a reserve currency
485 if (weights.size())
486 {
487 // if we are a reserve currency, explicit conversion values are not valid
488 // and are based on non-zero, initial contributions relative to supply
489 if ((conversionArr.isArray() && conversionArr.size()) ||
490 !initialContributionArr.isArray() ||
491 initialContributionArr.size() != currencyArr.size() ||
492 weights.size() != currencyArr.size() ||
493 !IsReserve())
494 {
495 LogPrintf("%s: reserve currencies must have weights, initial contributions in every currency, and no explicit conversion rates\n", __func__);
496 nVersion = PBAAS_VERSION_INVALID;
497 }
498 else
499 {
500 convertible = true;
501 }
502 }
503 else
504 {
505 // if we are not a reserve currency, we either have a conversion vector, or we are not convertible at all
506 if (conversionArr.isArray() && conversionArr.size() && conversionArr.size() != currencyArr.size())
507 {
508 LogPrintf("%s: non-reserve currencies must define all conversion rates for supported currencies if they define any\n", __func__);
509 nVersion = PBAAS_VERSION_INVALID;
510 }
511 else if (initialContributionArr.isArray() && initialContributionArr.size() != currencyArr.size())
512 {
513 LogPrintf("%s: initial contributions for currencies must all be specified if any are specified\n", __func__);
514 nVersion = PBAAS_VERSION_INVALID;
515 }
516 else
517 {
518 convertible = true;
519 }
520 }
521
522 if (convertible && nVersion != PBAAS_VERSION_INVALID)
523 {
524 if (minPreconvertArr.isArray() && minPreconvertArr.size() && minPreconvertArr.size() != currencyArr.size())
525 {
526 LogPrintf("%s: currencies with minimum conversion required must define all minimums if they define any\n", __func__);
527 nVersion = PBAAS_VERSION_INVALID;
528 }
529 if (maxPreconvertArr.isArray() && maxPreconvertArr.size() && maxPreconvertArr.size() != currencyArr.size())
530 {
531 LogPrintf("%s: currencies that include maximum conversions on pre-launch must specify all maximums\n", __func__);
532 nVersion = PBAAS_VERSION_INVALID;
533 }
534 if (initialContributionArr.isArray() && initialContributionArr.size() && initialContributionArr.size() != currencyArr.size())
535 {
536 LogPrintf("%s: currencies that include initial contributions in one currency on pre-launch must specify all currency amounts\n", __func__);
537 nVersion = PBAAS_VERSION_INVALID;
538 }
539 }
540
541 bool isInitialContributions = initialContributionArr.isArray() && initialContributionArr.size();
542 bool isPreconvertMin = minPreconvertArr.isArray() && minPreconvertArr.size();
543 bool isPreconvertMax = maxPreconvertArr.isArray() && maxPreconvertArr.size();
544 bool explicitConversions = conversionArr.isArray() && conversionArr.size();
545 bool havePreConversions = preConversionsArr.isArray() && preConversionsArr.size();
546
547 if (IsReserve() && explicitConversions)
548 {
549 LogPrintf("%s: cannot specify explicit conversion values for reserve currencies\n", __func__);
550 nVersion = PBAAS_VERSION_INVALID;
551 }
552
553 for (int i = 0; nVersion != PBAAS_VERSION_INVALID && i < currencyArr.size(); i++)
554 {
2f416b17 555 uint160 currencyID = CurrencyNameToChainID(uni_get_str(currencyArr[i]));
56fe75cb 556 // if we have a destination, but it is invalid, the json for this definition cannot be valid
557 if (currencyID.IsNull())
558 {
559 nVersion = PBAAS_VERSION_INVALID;
560 break;
561 }
562 else
563 {
564 currencies.push_back(currencyID);
565 }
566
567 if (isInitialContributions)
568 {
3b45a268 569 int64_t contrib = AmountFromValueNoErr(initialContributionArr[i]);
56fe75cb 570 if (IsReserve() && contrib < MIN_RESERVE_CONTRIBUTION)
571 {
572 LogPrintf("%s: all fractional reserve currencies must start with %s minimum initial contribution in each currency\n", __func__, ValueFromAmount(MIN_RESERVE_CONTRIBUTION).write().c_str());
573 nVersion = PBAAS_VERSION_INVALID;
574 break;
575 }
576 contributions.push_back(contrib);
577 preconverted.push_back(contrib);
578 }
579
580 if (havePreConversions && i < preConversionsArr.size())
581 {
3b45a268 582 int64_t contrib = AmountFromValueNoErr(preConversionsArr[i]);
56fe75cb 583 preconverted.push_back(contrib);
584 }
585
586 int64_t minPre = 0;
587 if (isPreconvertMin)
588 {
3b45a268 589 minPre = AmountFromValueNoErr(minPreconvertArr[i]);
56fe75cb 590 if (minPre < 0)
591 {
592 LogPrintf("%s: minimum preconversions for any currency may not be less than 0\n", __func__);
593 nVersion = PBAAS_VERSION_INVALID;
594 break;
595 }
596 minPreconvert.push_back(minPre);
597 }
598 if (isPreconvertMax)
599 {
3b45a268 600 int64_t maxPre = AmountFromValueNoErr(maxPreconvertArr[i]);
56fe75cb 601 if (maxPre < 0 || maxPre < minPre)
602 {
603 LogPrintf("%s: maximum preconversions for any currency may not be less than 0 or minimum\n", __func__);
604 nVersion = PBAAS_VERSION_INVALID;
605 break;
606 }
607 maxPreconvert.push_back(maxPre);
608 }
609 if (explicitConversions)
610 {
3b45a268 611 int64_t conversion = AmountFromValueNoErr(conversionArr[i]);
56fe75cb 612 if (conversion < 0)
613 {
614 LogPrintf("%s: conversions for any currency must be greater than 0\n", __func__);
615 nVersion = PBAAS_VERSION_INVALID;
616 break;
617 }
618 conversions.push_back(conversion);
619 }
620 }
621 }
622
3b45a268 623 preAllocationRatio = AmountFromValueNoErr(find_value(obj, "preallocationratio"));
56fe75cb 624
625 UniValue preallocationArr = find_value(obj, "preallocation");
626 if (preallocationArr.isArray())
627 {
628 for (int i = 0; i < preallocationArr.size(); i++)
629 {
630 std::vector<std::string> preallocationKey = preallocationArr[i].getKeys();
631 std::vector<UniValue> preallocationValue = preallocationArr[i].getValues();
632 if (preallocationKey.size() != 1 || preallocationValue.size() != 1)
633 {
634 LogPrintf("%s: each preallocation entry must contain one destination identity and one amount\n", __func__);
2f416b17 635 printf("%s: each preallocation entry must contain one destination identity and one amount\n", __func__);
56fe75cb 636 nVersion = PBAAS_VERSION_INVALID;
637 break;
638 }
639
640 CTxDestination preallocDest = DecodeDestination(preallocationKey[0]);
31609f35 641
2f416b17 642 if (preallocDest.which() != COptCCParams::ADDRTYPE_ID && preallocDest.which() != COptCCParams::ADDRTYPE_INVALID)
56fe75cb 643 {
2f416b17 644 LogPrintf("%s: preallocation destination must be an identity\n", __func__);
56fe75cb 645 nVersion = PBAAS_VERSION_INVALID;
646 break;
647 }
648
3b45a268 649 CAmount preAllocAmount = AmountFromValueNoErr(preallocationValue[0]);
2f416b17 650 if (preAllocAmount <= 0)
56fe75cb 651 {
2f416b17 652 LogPrintf("%s: preallocation values must be greater than zero\n", __func__);
56fe75cb 653 nVersion = PBAAS_VERSION_INVALID;
654 break;
655 }
656 preAllocation.push_back(make_pair(CIdentityID(GetDestinationID(preallocDest)), preAllocAmount));
657 }
658 }
659
660 auto vEras = uni_getValues(find_value(obj, "eras"));
661 if (vEras.size() > ASSETCHAINS_MAX_ERAS)
662 {
663 vEras.resize(ASSETCHAINS_MAX_ERAS);
664 }
665
666 for (auto era : vEras)
667 {
668 rewards.push_back(uni_get_int64(find_value(era, "reward")));
669 rewardsDecay.push_back(uni_get_int64(find_value(era, "decay")));
670 halving.push_back(uni_get_int64(find_value(era, "halving")));
671 eraEnd.push_back(uni_get_int64(find_value(era, "eraend")));
672 }
673 }
674 catch (exception e)
675 {
676 LogPrintf("%s: exception reading currency definition JSON\n", __func__, e.what());
677 nVersion = PBAAS_VERSION_INVALID;
678 }
679}
680
681int64_t CCurrencyDefinition::CalculateRatioOfValue(int64_t value, int64_t ratio)
682{
683 arith_uint256 bigAmount(value);
684 static const arith_uint256 bigSatoshi(SATOSHIDEN);
685
686 int64_t retVal = ((bigAmount * arith_uint256(ratio)) / bigSatoshi).GetLow64();
687 return retVal;
688}
689
690// this will only return an accurate result after total preconversion has been updated and before any emission
691int64_t CCurrencyDefinition::GetTotalPreallocation() const
692{
693 CAmount totalPreconvertedNative = 0;
694 CAmount totalPreallocatedNative = 0;
695 if (preAllocationRatio == 0)
696 {
697 for (auto onePreallocation : preAllocation)
698 {
699 totalPreallocatedNative += onePreallocation.second;
700 }
701 return totalPreallocatedNative;
702 }
703 for (auto oneConversion : preconverted)
704 {
705 totalPreconvertedNative += oneConversion;
706 }
707 if (!totalPreconvertedNative || preAllocationRatio >= SATOSHIDEN)
708 {
709 return 0;
710 }
711 arith_uint256 bigAmount(totalPreconvertedNative);
712 static const arith_uint256 bigSatoshi(SATOSHIDEN);
713 arith_uint256 conversionPercent(preAllocationRatio);
714
715 CAmount newAmount = ((bigAmount * bigSatoshi) / (bigSatoshi - conversionPercent)).GetLow64();
716 CAmount retAmount = CalculateRatioOfValue(newAmount, preAllocationRatio);
717 newAmount = totalPreconvertedNative + retAmount;
718
719 retAmount = CalculateRatioOfValue(newAmount, preAllocationRatio); // again to account for minimum fee
720 retAmount += totalPreconvertedNative - (newAmount - retAmount); // add any additional difference
721 return retAmount;
722}
723
724std::vector<std::pair<uint160, int64_t>> CCurrencyDefinition::GetPreAllocationAmounts() const
725{
726 if (!preAllocationRatio)
727 {
728 return preAllocation;
729 }
730 if (preAllocationRatio >= SATOSHIDEN)
731 {
732 return std::vector<std::pair<uint160, int64_t>>();
733 }
734 int64_t totalPreAllocation = GetTotalPreallocation();
735 int64_t totalPreAllocUnits = 0;
736 for (auto &onePreAlloc : preAllocation)
737 {
738 totalPreAllocUnits += onePreAlloc.second;
739 }
740 static arith_uint256 bigSatoshi(SATOSHIDEN);
741
742 std::vector<std::pair<uint160, int64_t>> retVal(preAllocation.size());
743 for (int i = 0; i < preAllocation.size(); i++)
744 {
745 if (i == preAllocation.size() - 1)
746 {
747 retVal[i] = make_pair(preAllocation[i].first, totalPreAllocation);
748 }
749 else
750 {
751 CAmount subRatio = ((arith_uint256(preAllocation[i].second) * bigSatoshi) / totalPreAllocUnits).GetLow64();
752 CAmount thisPreAlloc = CalculateRatioOfValue(totalPreAllocation, subRatio);
753 retVal[i] = make_pair(preAllocation[i].first, thisPreAlloc);
754 totalPreAllocation -= thisPreAlloc;
755 }
756 }
757 return retVal;
758}
759
760UniValue CCurrencyDefinition::ToUniValue() const
761{
762 UniValue obj(UniValue::VOBJ);
763
764 obj.push_back(Pair("name", name));
765 obj.push_back(Pair("version", (int64_t)nVersion));
197568cb 766 obj.push_back(Pair("options", (int64_t)options));
56fe75cb 767 obj.push_back(Pair("parent", EncodeDestination(CIdentityID(parent))));
768 obj.push_back(Pair("systemid", EncodeDestination(CIdentityID(systemID))));
769 obj.push_back(Pair("currencyid", EncodeDestination(CIdentityID(GetID()))));
770 obj.push_back(Pair("notarizationprotocol", (int)notarizationProtocol));
771 obj.push_back(Pair("proofprotocol", (int)proofProtocol));
772
773 obj.push_back(Pair("idregistrationprice", idRegistrationAmount));
774 obj.push_back(Pair("idreferrallevels", idReferralLevels));
775
776 // notaries are identities that perform specific functions for the currency's operation
777 // related to notarizing an external currency source, as well as proving imports
778 if (notaries.size())
779 {
780 UniValue notaryArr(UniValue::VARR);
781 for (auto &notary : notaries)
782 {
783 notaryArr.push_back(EncodeDestination(CIdentityID(notary)));
784 }
785 obj.push_back(Pair("notaries", notaryArr));
786 }
787 obj.push_back(Pair("minnotariesconfirm", minNotariesConfirm));
788
789 obj.push_back(Pair("billingperiod", billingPeriod));
790 obj.push_back(Pair("notarizationreward", notarizationReward));
791 obj.push_back(Pair("startblock", (int32_t)startBlock));
792 obj.push_back(Pair("endblock", (int32_t)endBlock));
793
794 // notaries are identities that perform specific functions for the currency's operation
795 // related to notarizing an external currency source, as well as proving imports
796 if (currencies.size())
797 {
798 UniValue currencyArr(UniValue::VARR);
799 for (auto &currency : currencies)
800 {
801 currencyArr.push_back(EncodeDestination(CIdentityID(currency)));
802 }
803 obj.push_back(Pair("currencies", currencyArr));
804 }
805
806 if (weights.size())
807 {
808 UniValue weightArr(UniValue::VARR);
809 for (auto &weight : weights)
810 {
811 weightArr.push_back(ValueFromAmount(weight));
812 }
813 obj.push_back(Pair("weights", weightArr));
814 }
815
816 if (conversions.size())
817 {
818 UniValue conversionArr(UniValue::VARR);
819 for (auto &conversion : conversions)
820 {
821 conversionArr.push_back(ValueFromAmount(conversion));
822 }
823 obj.push_back(Pair("conversions", conversionArr));
824 }
825
826 if (minPreconvert.size())
827 {
828 UniValue minPreconvertArr(UniValue::VARR);
829 for (auto &oneMin : minPreconvert)
830 {
831 minPreconvertArr.push_back(ValueFromAmount(oneMin));
832 }
833 obj.push_back(Pair("minpreconversion", minPreconvertArr));
834 }
835
836 if (maxPreconvert.size())
837 {
838 UniValue maxPreconvertArr(UniValue::VARR);
839 for (auto &oneMax : maxPreconvert)
840 {
841 maxPreconvertArr.push_back(ValueFromAmount(oneMax));
842 }
843 obj.push_back(Pair("maxpreconversion", maxPreconvertArr));
844 }
845
56fe75cb 846 if (preAllocationRatio)
847 {
848 obj.push_back(Pair("preallocationratio", ValueFromAmount(preAllocationRatio)));
849 }
850
851 if (preAllocation.size())
852 {
853 UniValue preAllocationArr(UniValue::VARR);
854 for (auto &onePreAllocation : preAllocation)
855 {
856 UniValue onePreAlloc(UniValue::VOBJ);
857 onePreAlloc.push_back(Pair(EncodeDestination(CIdentityID(onePreAllocation.first)), ValueFromAmount(onePreAllocation.second)));
858 preAllocationArr.push_back(onePreAlloc);
859 }
2f416b17 860 obj.push_back(Pair("preallocation", preAllocationArr));
56fe75cb 861 }
862
863 if (contributions.size())
864 {
865 UniValue initialContributionArr(UniValue::VARR);
866 for (auto &oneCurContributions : contributions)
867 {
868 initialContributionArr.push_back(ValueFromAmount(oneCurContributions));
869 }
870 obj.push_back(Pair("initialcontributions", initialContributionArr));
871 }
872
873 if (preconverted.size())
874 {
875 UniValue preconversionArr(UniValue::VARR);
876 for (auto &onePreconversion : preconverted)
877 {
878 preconversionArr.push_back(ValueFromAmount(onePreconversion));
879 }
880 obj.push_back(Pair("preconversions", preconversionArr));
881 }
882
883 UniValue eraArr(UniValue::VARR);
884 for (int i = 0; i < rewards.size(); i++)
885 {
886 UniValue era(UniValue::VOBJ);
887 era.push_back(Pair("reward", rewards.size() > i ? rewards[i] : (int64_t)0));
888 era.push_back(Pair("decay", rewardsDecay.size() > i ? rewardsDecay[i] : (int64_t)0));
889 era.push_back(Pair("halving", halving.size() > i ? (int32_t)halving[i] : (int32_t)0));
890 era.push_back(Pair("eraend", eraEnd.size() > i ? (int32_t)eraEnd[i] : (int32_t)0));
891 eraArr.push_back(era);
892 }
893 obj.push_back(Pair("eras", eraArr));
894 return obj;
895}
896
This page took 0.221322 seconds and 4 git commands to generate.