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